Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutRoadmapCareersInternshipsFAQContactPrivacyTerms
Skip to lesson

Course contents

  • How to Use This Course
  • Reading Paths
  • Glossary
  • Metrics Reference
  • Formal Cheat Sheet
  • Formal Foundations19
  • Properties and SVA16
  • Property Development and Constraints19
  • Proof Engines and Convergence23
  • Formal Applications20
  • Equivalence Checking14
  • Formal Coverage and Signoff15
  • Methodology and Debug13
  • Interview Prep15
  • Capstone: A Chip-Block Verification Lab24
    • The Brief
    • Stage 1: FIFO and Handshake
    • Stage 2: Registers and Arbiter
    • Stage 3: Pipeline and Equivalence
    • Stage 4: Induction and Signoff
    • Solutions
      • Solutions: FIFO Property Set
      • Solutions: Bridge and the Assumption Trap
      • Solutions: Registers and the Property Mutants
      • Solutions: Arbiter Wait Bound
      • Solutions: Pairs and Chain
      • Solutions: Pipeline, Twin and ECO
      • Solutions: Counter and Induction
      • Solutions: Ledger and Final Manifest
    • Appendix: Run It Yourself on an Open-Source Flow
Loading lesson

←Solutions
  1. Topics
  2. Formal Verification
  3. Capstone: A Chip-Block Verification Lab
  4. Solutions
  5. Solutions: Arbiter Wait Bound
On this page

    Part 10 of 10, lesson 18 of 24Part 10 of 10/Capstone: A Chip-Block Verification Lab/Lesson 18 of 24All levels

    Solutions: Arbiter Wait Bound

    The cause of the starvation fault read at two steps, the other two arbiter faults, the proof that the bound of three is exact, and the mutant for work conservation.

    Why it matters

    Structural arbiter checks pass on a design that starves three of four requesters. Only a bound on waiting sees it, and the same fault shows at two different steps depending on whether the helper assertions are in the model. This lesson gives the causes, reads the starvation trace step by step with a model that mirrors the listing, and shows by a failing property that a bound of two is violated, so three is exact.

    The three faults

    Variant

    Cause in the listing

    Fix

    Fails (step), smallest depth

    W1

    The pointer is not advanced when requester 0 wins, so last stays at 3 and requester 0 wins every cycle.

    Update last on every grant.

    h_wt3 (2) with helpers, depth 3; a_wt1 to a_wt3 (5) without, depth 6

    W2

    The low-priority grant is ORed in even when a higher-priority request exists.

    Gate it with the empty high-priority part.

    a_onehot0 (2); depth 3

    W3

    The low-priority search includes the previous holder: req | (1 << last).

    Search req only.

    a_no_phantom (1); depth 2

    Worked example: W1 at two steps

    All four requesters hold their request from step 1. The pointer never moves, so only requester 0 is granted, and the wait counters of the others climb by one a step. The helper says a requester's wait plus the number of grants still ahead of it never exceeds three; for requester 3 with the pointer stuck at 3 that sum is already four at step 2. The requirement itself fails later, when the wait of requester 1 reaches four.

    example.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    def run(buggy, steps):
        last, wt, rows = 3, [0, 0, 0, 0], []
        for step in range(1, steps + 1):
            req = [True] * 4
            g = next((last + o) % 4 for o in range(1, 5))             # all four request: the first one after last
            ahead3 = (3 - last - 1) % 4
            rows.append(dict(step=step, last=last, grant=g, wt1=wt[1], wt3=wt[3], h3=wt[3] + ahead3))
            wt = [w + 1 if i != g else 0 for i, w in enumerate(wt)]
            if not (buggy and g == 0):
                last = g
        return rows
    bad, good = run(True, 6), run(False, 6)
    assert [(r["step"], r["last"], r["grant"], r["wt1"], r["wt3"], r["h3"]) for r in bad[:5]] == [
        (1, 3, 0, 0, 0, 3), (2, 3, 0, 1, 1, 4), (3, 3, 0, 2, 2, 5), (4, 3, 0, 3, 3, 6), (5, 3, 0, 4, 4, 7)]
    assert next(r["step"] for r in bad if r["h3"] > 3) == 2, "the helper fails first, at step 2"
    assert next(r["step"] for r in bad if r["wt1"] > 3) == 5, "the requirement fails at step 5"
    assert all(r["h3"] <= 3 and r["wt1"] <= 3 for r in good), "the correct pointer never breaks either"
    assert max(r["wt3"] for r in good) == 3
    print("PASS: the starved requester breaks the helper at step 2 and the bound at step 5; the correct pointer breaks neither")

    Step

    Pointer last

    Grant

    wait of requester 1

    wait of requester 3

    helper sum for requester 3 (limit 3)

    1

    3

    requester 0

    0

    0

    3

    2

    3

    requester 0

    1

    1

    4: helper fails

    3

    3

    requester 0

    2

    2

    5

    4

    3

    requester 0

    3

    3

    6

    5

    3

    requester 0

    4: bound fails

    4

    7

    rr_arbiter.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    // formal: bmc 3 expect fail slang top rr_arbiter define BUG=1
    `define FORMAL
    // rr_arbiter: 4-requester round-robin arbiter. The grant is combinational from req and the 2-bit
    // pointer last (the index granted most recently). The search starts at last + 1 and wraps. A grant is
    // given in the cycle a request is seen and lasts one cycle (no lock). Synchronous active-high reset:
    // last = 3, so requester 0 has priority first. BUG selects a variant (0 is the design as specified);
    // HELPER=0 removes the helper assertions.
    module rr_arbiter #(parameter BUG = 0, parameter HELPER = 1) (
      input  wire       clk,
      input  wire       rst,
      input  wire [3:0] req,
      output wire [3:0] grant
    );
      reg [1:0] last;
    
      wire [3:0] hi_mask = (last == 2'd0) ? 4'b1110 :
                           (last == 2'd1) ? 4'b1100 :
                           (last == 2'd2) ? 4'b1000 : 4'b0000;
      wire [3:0] hi_req  = req & hi_mask;
      wire [3:0] lo_req  = (BUG == 3) ? (req | (4'b0001 << last)) : req;
    
      function [3:0] lowest;           // lowest set bit, one-hot (0 if none)
        input [3:0] v;
        lowest = v & (~v + 4'd1);
      endfunction
    
      wire [3:0] g_hi = lowest(hi_req);
      wire [3:0] g_lo = lowest(lo_req) & {4{(hi_req == 4'b0) || (BUG == 2)}};
      assign grant = g_hi | g_lo;
    
      wire [1:0] gidx = grant[0] ? 2'd0 : grant[1] ? 2'd1 : grant[2] ? 2'd2 : 2'd3;
    
      always @(posedge clk) begin
        if (rst) last <= 2'd3;
        else if (|grant && !(BUG == 1 && grant[0])) last <= gidx;
      end
    
    `ifdef FORMAL
      reg init = 1'b1;
      always @(posedge clk) init <= 1'b0;
      always @(*) if (init) assume (rst);
    
      // wt[i]: consecutive cycles requester i has requested without being granted
      reg [2:0] wt0, wt1, wt2, wt3;
      always @(posedge clk) begin
        if (rst) begin wt0 <= 3'd0; wt1 <= 3'd0; wt2 <= 3'd0; wt3 <= 3'd0; end
        else begin
          wt0 <= (req[0] && !grant[0]) ? wt0 + 3'd1 : 3'd0;
          wt1 <= (req[1] && !grant[1]) ? wt1 + 3'd1 : 3'd0;
          wt2 <= (req[2] && !grant[2]) ? wt2 + 3'd1 : 3'd0;
          wt3 <= (req[3] && !grant[3]) ? wt3 + 3'd1 : 3'd0;
        end
      end
    
      always @(*) if (!init) begin
        a_onehot0:  assert ((grant & (grant - 4'd1)) == 4'd0);
        a_no_phantom: assert ((grant & ~req) == 4'd0);
        a_work_cons:  assert (!(|req) || (|grant));
        // bounded response (safety form of starvation freedom): nobody waits more than 3 cycles
        a_wt0: assert (wt0 <= 3'd3);
        a_wt1: assert (wt1 <= 3'd3);
        a_wt2: assert (wt2 <= 3'd3);
        a_wt3: assert (wt3 <= 3'd3);
      end
    
      // helper invariants for the induction step of the wait bounds
      function [1:0] ahead;             // how many grants can still precede requester i
        input [1:0] i;
        ahead = i - last - 2'd1;        // 0..3 (mod 4)
      endfunction
      generate if (HELPER) begin : g_helper
        always @(*) if (!init) begin
          h_wt0: assert (wt0 + {1'b0, ahead(2'd0)} <= 3'd3);
          h_wt1: assert (wt1 + {1'b0, ahead(2'd1)} <= 3'd3);
          h_wt2: assert (wt2 + {1'b0, ahead(2'd2)} <= 3'd3);
          h_wt3: assert (wt3 + {1'b0, ahead(2'd3)} <= 3'd3);
        end
      end endgenerate
    
      // covers
      always @(*) if (!init) begin
        c_g0: cover (grant[0]);
        c_g1: cover (grant[1]);
        c_g2: cover (grant[2]);
        c_g3: cover (grant[3]);
        c_contend: cover (req == 4'b1111 && wt3 == 3'd3);
        c_idle: cover (req == 4'b0000 && grant == 4'd0);
      end
    `endif
    endmodule

    Depth 5 passes and depth 6 fails when the helpers are removed: the requirement alone needs step 5.

    rr_arbiter.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    // formal: bmc 5 expect pass slang top rr_arbiter define BUG=1 define HELPER=0
    `define FORMAL
    // rr_arbiter: 4-requester round-robin arbiter. The grant is combinational from req and the 2-bit
    // pointer last (the index granted most recently). The search starts at last + 1 and wraps. A grant is
    // given in the cycle a request is seen and lasts one cycle (no lock). Synchronous active-high reset:
    // last = 3, so requester 0 has priority first. BUG selects a variant (0 is the design as specified);
    // HELPER=0 removes the helper assertions.
    module rr_arbiter #(parameter BUG = 0, parameter HELPER = 1) (
      input  wire       clk,
      input  wire       rst,
      input  wire [3:0] req,
      output wire [3:0] grant
    );
      reg [1:0] last;
    
      wire [3:0] hi_mask = (last == 2'd0) ? 4'b1110 :
                           (last == 2'd1) ? 4'b1100 :
                           (last == 2'd2) ? 4'b1000 : 4'b0000;
      wire [3:0] hi_req  = req & hi_mask;
      wire [3:0] lo_req  = (BUG == 3) ? (req | (4'b0001 << last)) : req;
    
      function [3:0] lowest;           // lowest set bit, one-hot (0 if none)
        input [3:0] v;
        lowest = v & (~v + 4'd1);
      endfunction
    
      wire [3:0] g_hi = lowest(hi_req);
      wire [3:0] g_lo = lowest(lo_req) & {4{(hi_req == 4'b0) || (BUG == 2)}};
      assign grant = g_hi | g_lo;
    
      wire [1:0] gidx = grant[0] ? 2'd0 : grant[1] ? 2'd1 : grant[2] ? 2'd2 : 2'd3;
    
      always @(posedge clk) begin
        if (rst) last <= 2'd3;
        else if (|grant && !(BUG == 1 && grant[0])) last <= gidx;
      end
    
    `ifdef FORMAL
      reg init = 1'b1;
      always @(posedge clk) init <= 1'b0;
      always @(*) if (init) assume (rst);
    
      // wt[i]: consecutive cycles requester i has requested without being granted
      reg [2:0] wt0, wt1, wt2, wt3;
      always @(posedge clk) begin
        if (rst) begin wt0 <= 3'd0; wt1 <= 3'd0; wt2 <= 3'd0; wt3 <= 3'd0; end
        else begin
          wt0 <= (req[0] && !grant[0]) ? wt0 + 3'd1 : 3'd0;
          wt1 <= (req[1] && !grant[1]) ? wt1 + 3'd1 : 3'd0;
          wt2 <= (req[2] && !grant[2]) ? wt2 + 3'd1 : 3'd0;
          wt3 <= (req[3] && !grant[3]) ? wt3 + 3'd1 : 3'd0;
        end
      end
    
      always @(*) if (!init) begin
        a_onehot0:  assert ((grant & (grant - 4'd1)) == 4'd0);
        a_no_phantom: assert ((grant & ~req) == 4'd0);
        a_work_cons:  assert (!(|req) || (|grant));
        // bounded response (safety form of starvation freedom): nobody waits more than 3 cycles
        a_wt0: assert (wt0 <= 3'd3);
        a_wt1: assert (wt1 <= 3'd3);
        a_wt2: assert (wt2 <= 3'd3);
        a_wt3: assert (wt3 <= 3'd3);
      end
    
      // helper invariants for the induction step of the wait bounds
      function [1:0] ahead;             // how many grants can still precede requester i
        input [1:0] i;
        ahead = i - last - 2'd1;        // 0..3 (mod 4)
      endfunction
      generate if (HELPER) begin : g_helper
        always @(*) if (!init) begin
          h_wt0: assert (wt0 + {1'b0, ahead(2'd0)} <= 3'd3);
          h_wt1: assert (wt1 + {1'b0, ahead(2'd1)} <= 3'd3);
          h_wt2: assert (wt2 + {1'b0, ahead(2'd2)} <= 3'd3);
          h_wt3: assert (wt3 + {1'b0, ahead(2'd3)} <= 3'd3);
        end
      end endgenerate
    
      // covers
      always @(*) if (!init) begin
        c_g0: cover (grant[0]);
        c_g1: cover (grant[1]);
        c_g2: cover (grant[2]);
        c_g3: cover (grant[3]);
        c_contend: cover (req == 4'b1111 && wt3 == 3'd3);
        c_idle: cover (req == 4'b0000 && grant == 4'd0);
      end
    `endif
    endmodule
    rr_arbiter.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    // formal: bmc 6 expect fail slang top rr_arbiter define BUG=1 define HELPER=0
    `define FORMAL
    // rr_arbiter: 4-requester round-robin arbiter. The grant is combinational from req and the 2-bit
    // pointer last (the index granted most recently). The search starts at last + 1 and wraps. A grant is
    // given in the cycle a request is seen and lasts one cycle (no lock). Synchronous active-high reset:
    // last = 3, so requester 0 has priority first. BUG selects a variant (0 is the design as specified);
    // HELPER=0 removes the helper assertions.
    module rr_arbiter #(parameter BUG = 0, parameter HELPER = 1) (
      input  wire       clk,
      input  wire       rst,
      input  wire [3:0] req,
      output wire [3:0] grant
    );
      reg [1:0] last;
    
      wire [3:0] hi_mask = (last == 2'd0) ? 4'b1110 :
                           (last == 2'd1) ? 4'b1100 :
                           (last == 2'd2) ? 4'b1000 : 4'b0000;
      wire [3:0] hi_req  = req & hi_mask;
      wire [3:0] lo_req  = (BUG == 3) ? (req | (4'b0001 << last)) : req;
    
      function [3:0] lowest;           // lowest set bit, one-hot (0 if none)
        input [3:0] v;
        lowest = v & (~v + 4'd1);
      endfunction
    
      wire [3:0] g_hi = lowest(hi_req);
      wire [3:0] g_lo = lowest(lo_req) & {4{(hi_req == 4'b0) || (BUG == 2)}};
      assign grant = g_hi | g_lo;
    
      wire [1:0] gidx = grant[0] ? 2'd0 : grant[1] ? 2'd1 : grant[2] ? 2'd2 : 2'd3;
    
      always @(posedge clk) begin
        if (rst) last <= 2'd3;
        else if (|grant && !(BUG == 1 && grant[0])) last <= gidx;
      end
    
    `ifdef FORMAL
      reg init = 1'b1;
      always @(posedge clk) init <= 1'b0;
      always @(*) if (init) assume (rst);
    
      // wt[i]: consecutive cycles requester i has requested without being granted
      reg [2:0] wt0, wt1, wt2, wt3;
      always @(posedge clk) begin
        if (rst) begin wt0 <= 3'd0; wt1 <= 3'd0; wt2 <= 3'd0; wt3 <= 3'd0; end
        else begin
          wt0 <= (req[0] && !grant[0]) ? wt0 + 3'd1 : 3'd0;
          wt1 <= (req[1] && !grant[1]) ? wt1 + 3'd1 : 3'd0;
          wt2 <= (req[2] && !grant[2]) ? wt2 + 3'd1 : 3'd0;
          wt3 <= (req[3] && !grant[3]) ? wt3 + 3'd1 : 3'd0;
        end
      end
    
      always @(*) if (!init) begin
        a_onehot0:  assert ((grant & (grant - 4'd1)) == 4'd0);
        a_no_phantom: assert ((grant & ~req) == 4'd0);
        a_work_cons:  assert (!(|req) || (|grant));
        // bounded response (safety form of starvation freedom): nobody waits more than 3 cycles
        a_wt0: assert (wt0 <= 3'd3);
        a_wt1: assert (wt1 <= 3'd3);
        a_wt2: assert (wt2 <= 3'd3);
        a_wt3: assert (wt3 <= 3'd3);
      end
    
      // helper invariants for the induction step of the wait bounds
      function [1:0] ahead;             // how many grants can still precede requester i
        input [1:0] i;
        ahead = i - last - 2'd1;        // 0..3 (mod 4)
      endfunction
      generate if (HELPER) begin : g_helper
        always @(*) if (!init) begin
          h_wt0: assert (wt0 + {1'b0, ahead(2'd0)} <= 3'd3);
          h_wt1: assert (wt1 + {1'b0, ahead(2'd1)} <= 3'd3);
          h_wt2: assert (wt2 + {1'b0, ahead(2'd2)} <= 3'd3);
          h_wt3: assert (wt3 + {1'b0, ahead(2'd3)} <= 3'd3);
        end
      end endgenerate
    
      // covers
      always @(*) if (!init) begin
        c_g0: cover (grant[0]);
        c_g1: cover (grant[1]);
        c_g2: cover (grant[2]);
        c_g3: cover (grant[3]);
        c_contend: cover (req == 4'b1111 && wt3 == 3'd3);
        c_idle: cover (req == 4'b0000 && grant == 4'd0);
      end
    `endif
    endmodule

    The other two faults are structural and need no history.

    rr_arbiter.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    // formal: bmc 3 expect fail slang top rr_arbiter define BUG=2
    `define FORMAL
    // rr_arbiter: 4-requester round-robin arbiter. The grant is combinational from req and the 2-bit
    // pointer last (the index granted most recently). The search starts at last + 1 and wraps. A grant is
    // given in the cycle a request is seen and lasts one cycle (no lock). Synchronous active-high reset:
    // last = 3, so requester 0 has priority first. BUG selects a variant (0 is the design as specified);
    // HELPER=0 removes the helper assertions.
    module rr_arbiter #(parameter BUG = 0, parameter HELPER = 1) (
      input  wire       clk,
      input  wire       rst,
      input  wire [3:0] req,
      output wire [3:0] grant
    );
      reg [1:0] last;
    
      wire [3:0] hi_mask = (last == 2'd0) ? 4'b1110 :
                           (last == 2'd1) ? 4'b1100 :
                           (last == 2'd2) ? 4'b1000 : 4'b0000;
      wire [3:0] hi_req  = req & hi_mask;
      wire [3:0] lo_req  = (BUG == 3) ? (req | (4'b0001 << last)) : req;
    
      function [3:0] lowest;           // lowest set bit, one-hot (0 if none)
        input [3:0] v;
        lowest = v & (~v + 4'd1);
      endfunction
    
      wire [3:0] g_hi = lowest(hi_req);
      wire [3:0] g_lo = lowest(lo_req) & {4{(hi_req == 4'b0) || (BUG == 2)}};
      assign grant = g_hi | g_lo;
    
      wire [1:0] gidx = grant[0] ? 2'd0 : grant[1] ? 2'd1 : grant[2] ? 2'd2 : 2'd3;
    
      always @(posedge clk) begin
        if (rst) last <= 2'd3;
        else if (|grant && !(BUG == 1 && grant[0])) last <= gidx;
      end
    
    `ifdef FORMAL
      reg init = 1'b1;
      always @(posedge clk) init <= 1'b0;
      always @(*) if (init) assume (rst);
    
      // wt[i]: consecutive cycles requester i has requested without being granted
      reg [2:0] wt0, wt1, wt2, wt3;
      always @(posedge clk) begin
        if (rst) begin wt0 <= 3'd0; wt1 <= 3'd0; wt2 <= 3'd0; wt3 <= 3'd0; end
        else begin
          wt0 <= (req[0] && !grant[0]) ? wt0 + 3'd1 : 3'd0;
          wt1 <= (req[1] && !grant[1]) ? wt1 + 3'd1 : 3'd0;
          wt2 <= (req[2] && !grant[2]) ? wt2 + 3'd1 : 3'd0;
          wt3 <= (req[3] && !grant[3]) ? wt3 + 3'd1 : 3'd0;
        end
      end
    
      always @(*) if (!init) begin
        a_onehot0:  assert ((grant & (grant - 4'd1)) == 4'd0);
        a_no_phantom: assert ((grant & ~req) == 4'd0);
        a_work_cons:  assert (!(|req) || (|grant));
        // bounded response (safety form of starvation freedom): nobody waits more than 3 cycles
        a_wt0: assert (wt0 <= 3'd3);
        a_wt1: assert (wt1 <= 3'd3);
        a_wt2: assert (wt2 <= 3'd3);
        a_wt3: assert (wt3 <= 3'd3);
      end
    
      // helper invariants for the induction step of the wait bounds
      function [1:0] ahead;             // how many grants can still precede requester i
        input [1:0] i;
        ahead = i - last - 2'd1;        // 0..3 (mod 4)
      endfunction
      generate if (HELPER) begin : g_helper
        always @(*) if (!init) begin
          h_wt0: assert (wt0 + {1'b0, ahead(2'd0)} <= 3'd3);
          h_wt1: assert (wt1 + {1'b0, ahead(2'd1)} <= 3'd3);
          h_wt2: assert (wt2 + {1'b0, ahead(2'd2)} <= 3'd3);
          h_wt3: assert (wt3 + {1'b0, ahead(2'd3)} <= 3'd3);
        end
      end endgenerate
    
      // covers
      always @(*) if (!init) begin
        c_g0: cover (grant[0]);
        c_g1: cover (grant[1]);
        c_g2: cover (grant[2]);
        c_g3: cover (grant[3]);
        c_contend: cover (req == 4'b1111 && wt3 == 3'd3);
        c_idle: cover (req == 4'b0000 && grant == 4'd0);
      end
    `endif
    endmodule
    rr_arbiter.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    // formal: bmc 2 expect fail slang top rr_arbiter define BUG=3
    `define FORMAL
    // rr_arbiter: 4-requester round-robin arbiter. The grant is combinational from req and the 2-bit
    // pointer last (the index granted most recently). The search starts at last + 1 and wraps. A grant is
    // given in the cycle a request is seen and lasts one cycle (no lock). Synchronous active-high reset:
    // last = 3, so requester 0 has priority first. BUG selects a variant (0 is the design as specified);
    // HELPER=0 removes the helper assertions.
    module rr_arbiter #(parameter BUG = 0, parameter HELPER = 1) (
      input  wire       clk,
      input  wire       rst,
      input  wire [3:0] req,
      output wire [3:0] grant
    );
      reg [1:0] last;
    
      wire [3:0] hi_mask = (last == 2'd0) ? 4'b1110 :
                           (last == 2'd1) ? 4'b1100 :
                           (last == 2'd2) ? 4'b1000 : 4'b0000;
      wire [3:0] hi_req  = req & hi_mask;
      wire [3:0] lo_req  = (BUG == 3) ? (req | (4'b0001 << last)) : req;
    
      function [3:0] lowest;           // lowest set bit, one-hot (0 if none)
        input [3:0] v;
        lowest = v & (~v + 4'd1);
      endfunction
    
      wire [3:0] g_hi = lowest(hi_req);
      wire [3:0] g_lo = lowest(lo_req) & {4{(hi_req == 4'b0) || (BUG == 2)}};
      assign grant = g_hi | g_lo;
    
      wire [1:0] gidx = grant[0] ? 2'd0 : grant[1] ? 2'd1 : grant[2] ? 2'd2 : 2'd3;
    
      always @(posedge clk) begin
        if (rst) last <= 2'd3;
        else if (|grant && !(BUG == 1 && grant[0])) last <= gidx;
      end
    
    `ifdef FORMAL
      reg init = 1'b1;
      always @(posedge clk) init <= 1'b0;
      always @(*) if (init) assume (rst);
    
      // wt[i]: consecutive cycles requester i has requested without being granted
      reg [2:0] wt0, wt1, wt2, wt3;
      always @(posedge clk) begin
        if (rst) begin wt0 <= 3'd0; wt1 <= 3'd0; wt2 <= 3'd0; wt3 <= 3'd0; end
        else begin
          wt0 <= (req[0] && !grant[0]) ? wt0 + 3'd1 : 3'd0;
          wt1 <= (req[1] && !grant[1]) ? wt1 + 3'd1 : 3'd0;
          wt2 <= (req[2] && !grant[2]) ? wt2 + 3'd1 : 3'd0;
          wt3 <= (req[3] && !grant[3]) ? wt3 + 3'd1 : 3'd0;
        end
      end
    
      always @(*) if (!init) begin
        a_onehot0:  assert ((grant & (grant - 4'd1)) == 4'd0);
        a_no_phantom: assert ((grant & ~req) == 4'd0);
        a_work_cons:  assert (!(|req) || (|grant));
        // bounded response (safety form of starvation freedom): nobody waits more than 3 cycles
        a_wt0: assert (wt0 <= 3'd3);
        a_wt1: assert (wt1 <= 3'd3);
        a_wt2: assert (wt2 <= 3'd3);
        a_wt3: assert (wt3 <= 3'd3);
      end
    
      // helper invariants for the induction step of the wait bounds
      function [1:0] ahead;             // how many grants can still precede requester i
        input [1:0] i;
        ahead = i - last - 2'd1;        // 0..3 (mod 4)
      endfunction
      generate if (HELPER) begin : g_helper
        always @(*) if (!init) begin
          h_wt0: assert (wt0 + {1'b0, ahead(2'd0)} <= 3'd3);
          h_wt1: assert (wt1 + {1'b0, ahead(2'd1)} <= 3'd3);
          h_wt2: assert (wt2 + {1'b0, ahead(2'd2)} <= 3'd3);
          h_wt3: assert (wt3 + {1'b0, ahead(2'd3)} <= 3'd3);
        end
      end endgenerate
    
      // covers
      always @(*) if (!init) begin
        c_g0: cover (grant[0]);
        c_g1: cover (grant[1]);
        c_g2: cover (grant[2]);
        c_g3: cover (grant[3]);
        c_contend: cover (req == 4'b1111 && wt3 == 3'd3);
        c_idle: cover (req == 4'b0000 && grant == 4'd0);
      end
    `endif
    endmodule

    The bound is exact

    Three is not just safe: the same proof with the four wait properties tightened to two fails at step 4, the step at which the contention cover is reached. The first block is that property mutant. The next two blocks measure the induction window: with the helpers a window of one closes the proof; without them a window of three fails and four passes.

    rr_arbiter.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    // formal: bmc 5 expect fail slang top rr_arbiter
    `define FORMAL
    // property mutation: the wait bound is tightened from 3 to 2
    // rr_arbiter: 4-requester round-robin arbiter. The grant is combinational from req and the 2-bit
    // pointer last (the index granted most recently). The search starts at last + 1 and wraps. A grant is
    // given in the cycle a request is seen and lasts one cycle (no lock). Synchronous active-high reset:
    // last = 3, so requester 0 has priority first. BUG selects a variant (0 is the design as specified);
    // HELPER=0 removes the helper assertions.
    module rr_arbiter #(parameter BUG = 0, parameter HELPER = 1) (
      input  wire       clk,
      input  wire       rst,
      input  wire [3:0] req,
      output wire [3:0] grant
    );
      reg [1:0] last;
    
      wire [3:0] hi_mask = (last == 2'd0) ? 4'b1110 :
                           (last == 2'd1) ? 4'b1100 :
                           (last == 2'd2) ? 4'b1000 : 4'b0000;
      wire [3:0] hi_req  = req & hi_mask;
      wire [3:0] lo_req  = (BUG == 3) ? (req | (4'b0001 << last)) : req;
    
      function [3:0] lowest;           // lowest set bit, one-hot (0 if none)
        input [3:0] v;
        lowest = v & (~v + 4'd1);
      endfunction
    
      wire [3:0] g_hi = lowest(hi_req);
      wire [3:0] g_lo = lowest(lo_req) & {4{(hi_req == 4'b0) || (BUG == 2)}};
      assign grant = g_hi | g_lo;
    
      wire [1:0] gidx = grant[0] ? 2'd0 : grant[1] ? 2'd1 : grant[2] ? 2'd2 : 2'd3;
    
      always @(posedge clk) begin
        if (rst) last <= 2'd3;
        else if (|grant && !(BUG == 1 && grant[0])) last <= gidx;
      end
    
    `ifdef FORMAL
      reg init = 1'b1;
      always @(posedge clk) init <= 1'b0;
      always @(*) if (init) assume (rst);
    
      // wt[i]: consecutive cycles requester i has requested without being granted
      reg [2:0] wt0, wt1, wt2, wt3;
      always @(posedge clk) begin
        if (rst) begin wt0 <= 3'd0; wt1 <= 3'd0; wt2 <= 3'd0; wt3 <= 3'd0; end
        else begin
          wt0 <= (req[0] && !grant[0]) ? wt0 + 3'd1 : 3'd0;
          wt1 <= (req[1] && !grant[1]) ? wt1 + 3'd1 : 3'd0;
          wt2 <= (req[2] && !grant[2]) ? wt2 + 3'd1 : 3'd0;
          wt3 <= (req[3] && !grant[3]) ? wt3 + 3'd1 : 3'd0;
        end
      end
    
      always @(*) if (!init) begin
        a_onehot0:  assert ((grant & (grant - 4'd1)) == 4'd0);
        a_no_phantom: assert ((grant & ~req) == 4'd0);
        a_work_cons:  assert (!(|req) || (|grant));
        // bounded response (safety form of starvation freedom): nobody waits more than 3 cycles
        a_wt0: assert (wt0 <= 3'd2);
        a_wt1: assert (wt1 <= 3'd2);
        a_wt2: assert (wt2 <= 3'd2);
        a_wt3: assert (wt3 <= 3'd2);
      end
    
      // helper invariants for the induction step of the wait bounds
      function [1:0] ahead;             // how many grants can still precede requester i
        input [1:0] i;
        ahead = i - last - 2'd1;        // 0..3 (mod 4)
      endfunction
      generate if (HELPER) begin : g_helper
        always @(*) if (!init) begin
          h_wt0: assert (wt0 + {1'b0, ahead(2'd0)} <= 3'd3);
          h_wt1: assert (wt1 + {1'b0, ahead(2'd1)} <= 3'd3);
          h_wt2: assert (wt2 + {1'b0, ahead(2'd2)} <= 3'd3);
          h_wt3: assert (wt3 + {1'b0, ahead(2'd3)} <= 3'd3);
        end
      end endgenerate
    
      // covers
      always @(*) if (!init) begin
        c_g0: cover (grant[0]);
        c_g1: cover (grant[1]);
        c_g2: cover (grant[2]);
        c_g3: cover (grant[3]);
        c_contend: cover (req == 4'b1111 && wt3 == 3'd3);
        c_idle: cover (req == 4'b0000 && grant == 4'd0);
      end
    `endif
    endmodule
    rr_arbiter.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    // formal: prove 1 expect pass slang top rr_arbiter
    `define FORMAL
    // rr_arbiter: 4-requester round-robin arbiter. The grant is combinational from req and the 2-bit
    // pointer last (the index granted most recently). The search starts at last + 1 and wraps. A grant is
    // given in the cycle a request is seen and lasts one cycle (no lock). Synchronous active-high reset:
    // last = 3, so requester 0 has priority first. BUG selects a variant (0 is the design as specified);
    // HELPER=0 removes the helper assertions.
    module rr_arbiter #(parameter BUG = 0, parameter HELPER = 1) (
      input  wire       clk,
      input  wire       rst,
      input  wire [3:0] req,
      output wire [3:0] grant
    );
      reg [1:0] last;
    
      wire [3:0] hi_mask = (last == 2'd0) ? 4'b1110 :
                           (last == 2'd1) ? 4'b1100 :
                           (last == 2'd2) ? 4'b1000 : 4'b0000;
      wire [3:0] hi_req  = req & hi_mask;
      wire [3:0] lo_req  = (BUG == 3) ? (req | (4'b0001 << last)) : req;
    
      function [3:0] lowest;           // lowest set bit, one-hot (0 if none)
        input [3:0] v;
        lowest = v & (~v + 4'd1);
      endfunction
    
      wire [3:0] g_hi = lowest(hi_req);
      wire [3:0] g_lo = lowest(lo_req) & {4{(hi_req == 4'b0) || (BUG == 2)}};
      assign grant = g_hi | g_lo;
    
      wire [1:0] gidx = grant[0] ? 2'd0 : grant[1] ? 2'd1 : grant[2] ? 2'd2 : 2'd3;
    
      always @(posedge clk) begin
        if (rst) last <= 2'd3;
        else if (|grant && !(BUG == 1 && grant[0])) last <= gidx;
      end
    
    `ifdef FORMAL
      reg init = 1'b1;
      always @(posedge clk) init <= 1'b0;
      always @(*) if (init) assume (rst);
    
      // wt[i]: consecutive cycles requester i has requested without being granted
      reg [2:0] wt0, wt1, wt2, wt3;
      always @(posedge clk) begin
        if (rst) begin wt0 <= 3'd0; wt1 <= 3'd0; wt2 <= 3'd0; wt3 <= 3'd0; end
        else begin
          wt0 <= (req[0] && !grant[0]) ? wt0 + 3'd1 : 3'd0;
          wt1 <= (req[1] && !grant[1]) ? wt1 + 3'd1 : 3'd0;
          wt2 <= (req[2] && !grant[2]) ? wt2 + 3'd1 : 3'd0;
          wt3 <= (req[3] && !grant[3]) ? wt3 + 3'd1 : 3'd0;
        end
      end
    
      always @(*) if (!init) begin
        a_onehot0:  assert ((grant & (grant - 4'd1)) == 4'd0);
        a_no_phantom: assert ((grant & ~req) == 4'd0);
        a_work_cons:  assert (!(|req) || (|grant));
        // bounded response (safety form of starvation freedom): nobody waits more than 3 cycles
        a_wt0: assert (wt0 <= 3'd3);
        a_wt1: assert (wt1 <= 3'd3);
        a_wt2: assert (wt2 <= 3'd3);
        a_wt3: assert (wt3 <= 3'd3);
      end
    
      // helper invariants for the induction step of the wait bounds
      function [1:0] ahead;             // how many grants can still precede requester i
        input [1:0] i;
        ahead = i - last - 2'd1;        // 0..3 (mod 4)
      endfunction
      generate if (HELPER) begin : g_helper
        always @(*) if (!init) begin
          h_wt0: assert (wt0 + {1'b0, ahead(2'd0)} <= 3'd3);
          h_wt1: assert (wt1 + {1'b0, ahead(2'd1)} <= 3'd3);
          h_wt2: assert (wt2 + {1'b0, ahead(2'd2)} <= 3'd3);
          h_wt3: assert (wt3 + {1'b0, ahead(2'd3)} <= 3'd3);
        end
      end endgenerate
    
      // covers
      always @(*) if (!init) begin
        c_g0: cover (grant[0]);
        c_g1: cover (grant[1]);
        c_g2: cover (grant[2]);
        c_g3: cover (grant[3]);
        c_contend: cover (req == 4'b1111 && wt3 == 3'd3);
        c_idle: cover (req == 4'b0000 && grant == 4'd0);
      end
    `endif
    endmodule
    rr_arbiter.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    // formal: prove 3 expect unproven slang top rr_arbiter define HELPER=0
    `define FORMAL
    // rr_arbiter: 4-requester round-robin arbiter. The grant is combinational from req and the 2-bit
    // pointer last (the index granted most recently). The search starts at last + 1 and wraps. A grant is
    // given in the cycle a request is seen and lasts one cycle (no lock). Synchronous active-high reset:
    // last = 3, so requester 0 has priority first. BUG selects a variant (0 is the design as specified);
    // HELPER=0 removes the helper assertions.
    module rr_arbiter #(parameter BUG = 0, parameter HELPER = 1) (
      input  wire       clk,
      input  wire       rst,
      input  wire [3:0] req,
      output wire [3:0] grant
    );
      reg [1:0] last;
    
      wire [3:0] hi_mask = (last == 2'd0) ? 4'b1110 :
                           (last == 2'd1) ? 4'b1100 :
                           (last == 2'd2) ? 4'b1000 : 4'b0000;
      wire [3:0] hi_req  = req & hi_mask;
      wire [3:0] lo_req  = (BUG == 3) ? (req | (4'b0001 << last)) : req;
    
      function [3:0] lowest;           // lowest set bit, one-hot (0 if none)
        input [3:0] v;
        lowest = v & (~v + 4'd1);
      endfunction
    
      wire [3:0] g_hi = lowest(hi_req);
      wire [3:0] g_lo = lowest(lo_req) & {4{(hi_req == 4'b0) || (BUG == 2)}};
      assign grant = g_hi | g_lo;
    
      wire [1:0] gidx = grant[0] ? 2'd0 : grant[1] ? 2'd1 : grant[2] ? 2'd2 : 2'd3;
    
      always @(posedge clk) begin
        if (rst) last <= 2'd3;
        else if (|grant && !(BUG == 1 && grant[0])) last <= gidx;
      end
    
    `ifdef FORMAL
      reg init = 1'b1;
      always @(posedge clk) init <= 1'b0;
      always @(*) if (init) assume (rst);
    
      // wt[i]: consecutive cycles requester i has requested without being granted
      reg [2:0] wt0, wt1, wt2, wt3;
      always @(posedge clk) begin
        if (rst) begin wt0 <= 3'd0; wt1 <= 3'd0; wt2 <= 3'd0; wt3 <= 3'd0; end
        else begin
          wt0 <= (req[0] && !grant[0]) ? wt0 + 3'd1 : 3'd0;
          wt1 <= (req[1] && !grant[1]) ? wt1 + 3'd1 : 3'd0;
          wt2 <= (req[2] && !grant[2]) ? wt2 + 3'd1 : 3'd0;
          wt3 <= (req[3] && !grant[3]) ? wt3 + 3'd1 : 3'd0;
        end
      end
    
      always @(*) if (!init) begin
        a_onehot0:  assert ((grant & (grant - 4'd1)) == 4'd0);
        a_no_phantom: assert ((grant & ~req) == 4'd0);
        a_work_cons:  assert (!(|req) || (|grant));
        // bounded response (safety form of starvation freedom): nobody waits more than 3 cycles
        a_wt0: assert (wt0 <= 3'd3);
        a_wt1: assert (wt1 <= 3'd3);
        a_wt2: assert (wt2 <= 3'd3);
        a_wt3: assert (wt3 <= 3'd3);
      end
    
      // helper invariants for the induction step of the wait bounds
      function [1:0] ahead;             // how many grants can still precede requester i
        input [1:0] i;
        ahead = i - last - 2'd1;        // 0..3 (mod 4)
      endfunction
      generate if (HELPER) begin : g_helper
        always @(*) if (!init) begin
          h_wt0: assert (wt0 + {1'b0, ahead(2'd0)} <= 3'd3);
          h_wt1: assert (wt1 + {1'b0, ahead(2'd1)} <= 3'd3);
          h_wt2: assert (wt2 + {1'b0, ahead(2'd2)} <= 3'd3);
          h_wt3: assert (wt3 + {1'b0, ahead(2'd3)} <= 3'd3);
        end
      end endgenerate
    
      // covers
      always @(*) if (!init) begin
        c_g0: cover (grant[0]);
        c_g1: cover (grant[1]);
        c_g2: cover (grant[2]);
        c_g3: cover (grant[3]);
        c_contend: cover (req == 4'b1111 && wt3 == 3'd3);
        c_idle: cover (req == 4'b0000 && grant == 4'd0);
      end
    `endif
    endmodule
    rr_arbiter.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    // formal: prove 4 expect pass slang top rr_arbiter define HELPER=0
    `define FORMAL
    // rr_arbiter: 4-requester round-robin arbiter. The grant is combinational from req and the 2-bit
    // pointer last (the index granted most recently). The search starts at last + 1 and wraps. A grant is
    // given in the cycle a request is seen and lasts one cycle (no lock). Synchronous active-high reset:
    // last = 3, so requester 0 has priority first. BUG selects a variant (0 is the design as specified);
    // HELPER=0 removes the helper assertions.
    module rr_arbiter #(parameter BUG = 0, parameter HELPER = 1) (
      input  wire       clk,
      input  wire       rst,
      input  wire [3:0] req,
      output wire [3:0] grant
    );
      reg [1:0] last;
    
      wire [3:0] hi_mask = (last == 2'd0) ? 4'b1110 :
                           (last == 2'd1) ? 4'b1100 :
                           (last == 2'd2) ? 4'b1000 : 4'b0000;
      wire [3:0] hi_req  = req & hi_mask;
      wire [3:0] lo_req  = (BUG == 3) ? (req | (4'b0001 << last)) : req;
    
      function [3:0] lowest;           // lowest set bit, one-hot (0 if none)
        input [3:0] v;
        lowest = v & (~v + 4'd1);
      endfunction
    
      wire [3:0] g_hi = lowest(hi_req);
      wire [3:0] g_lo = lowest(lo_req) & {4{(hi_req == 4'b0) || (BUG == 2)}};
      assign grant = g_hi | g_lo;
    
      wire [1:0] gidx = grant[0] ? 2'd0 : grant[1] ? 2'd1 : grant[2] ? 2'd2 : 2'd3;
    
      always @(posedge clk) begin
        if (rst) last <= 2'd3;
        else if (|grant && !(BUG == 1 && grant[0])) last <= gidx;
      end
    
    `ifdef FORMAL
      reg init = 1'b1;
      always @(posedge clk) init <= 1'b0;
      always @(*) if (init) assume (rst);
    
      // wt[i]: consecutive cycles requester i has requested without being granted
      reg [2:0] wt0, wt1, wt2, wt3;
      always @(posedge clk) begin
        if (rst) begin wt0 <= 3'd0; wt1 <= 3'd0; wt2 <= 3'd0; wt3 <= 3'd0; end
        else begin
          wt0 <= (req[0] && !grant[0]) ? wt0 + 3'd1 : 3'd0;
          wt1 <= (req[1] && !grant[1]) ? wt1 + 3'd1 : 3'd0;
          wt2 <= (req[2] && !grant[2]) ? wt2 + 3'd1 : 3'd0;
          wt3 <= (req[3] && !grant[3]) ? wt3 + 3'd1 : 3'd0;
        end
      end
    
      always @(*) if (!init) begin
        a_onehot0:  assert ((grant & (grant - 4'd1)) == 4'd0);
        a_no_phantom: assert ((grant & ~req) == 4'd0);
        a_work_cons:  assert (!(|req) || (|grant));
        // bounded response (safety form of starvation freedom): nobody waits more than 3 cycles
        a_wt0: assert (wt0 <= 3'd3);
        a_wt1: assert (wt1 <= 3'd3);
        a_wt2: assert (wt2 <= 3'd3);
        a_wt3: assert (wt3 <= 3'd3);
      end
    
      // helper invariants for the induction step of the wait bounds
      function [1:0] ahead;             // how many grants can still precede requester i
        input [1:0] i;
        ahead = i - last - 2'd1;        // 0..3 (mod 4)
      endfunction
      generate if (HELPER) begin : g_helper
        always @(*) if (!init) begin
          h_wt0: assert (wt0 + {1'b0, ahead(2'd0)} <= 3'd3);
          h_wt1: assert (wt1 + {1'b0, ahead(2'd1)} <= 3'd3);
          h_wt2: assert (wt2 + {1'b0, ahead(2'd2)} <= 3'd3);
          h_wt3: assert (wt3 + {1'b0, ahead(2'd3)} <= 3'd3);
        end
      end endgenerate
    
      // covers
      always @(*) if (!init) begin
        c_g0: cover (grant[0]);
        c_g1: cover (grant[1]);
        c_g2: cover (grant[2]);
        c_g3: cover (grant[3]);
        c_contend: cover (req == 4'b1111 && wt3 == 3'd3);
        c_idle: cover (req == 4'b0000 && grant == 4'd0);
      end
    `endif
    endmodule

    The mutant for work conservation

    Mutant

    Change

    Killer

    Smallest depth

    arb-m-work-cons

    the low-priority part of the search never grants

    a_work_cons

    2

    rr_arbiter.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    // formal: bmc 2 expect fail slang top rr_arbiter
    `define FORMAL
    // mutant arb-m-work-cons: the low-priority part of the search never grants
    // rr_arbiter: 4-requester round-robin arbiter. The grant is combinational from req and the 2-bit
    // pointer last (the index granted most recently). The search starts at last + 1 and wraps. A grant is
    // given in the cycle a request is seen and lasts one cycle (no lock). Synchronous active-high reset:
    // last = 3, so requester 0 has priority first. BUG selects a variant (0 is the design as specified);
    // HELPER=0 removes the helper assertions.
    module rr_arbiter #(parameter BUG = 0, parameter HELPER = 1) (
      input  wire       clk,
      input  wire       rst,
      input  wire [3:0] req,
      output wire [3:0] grant
    );
      reg [1:0] last;
    
      wire [3:0] hi_mask = (last == 2'd0) ? 4'b1110 :
                           (last == 2'd1) ? 4'b1100 :
                           (last == 2'd2) ? 4'b1000 : 4'b0000;
      wire [3:0] hi_req  = req & hi_mask;
      wire [3:0] lo_req  = (BUG == 3) ? (req | (4'b0001 << last)) : req;
    
      function [3:0] lowest;           // lowest set bit, one-hot (0 if none)
        input [3:0] v;
        lowest = v & (~v + 4'd1);
      endfunction
    
      wire [3:0] g_hi = lowest(hi_req);
      wire [3:0] g_lo = 4'b0000 & lowest(lo_req) & {4{(hi_req == 4'b0) || (BUG == 2)}};
      assign grant = g_hi | g_lo;
    
      wire [1:0] gidx = grant[0] ? 2'd0 : grant[1] ? 2'd1 : grant[2] ? 2'd2 : 2'd3;
    
      always @(posedge clk) begin
        if (rst) last <= 2'd3;
        else if (|grant && !(BUG == 1 && grant[0])) last <= gidx;
      end
    
    `ifdef FORMAL
      reg init = 1'b1;
      always @(posedge clk) init <= 1'b0;
      always @(*) if (init) assume (rst);
    
      // wt[i]: consecutive cycles requester i has requested without being granted
      reg [2:0] wt0, wt1, wt2, wt3;
      always @(posedge clk) begin
        if (rst) begin wt0 <= 3'd0; wt1 <= 3'd0; wt2 <= 3'd0; wt3 <= 3'd0; end
        else begin
          wt0 <= (req[0] && !grant[0]) ? wt0 + 3'd1 : 3'd0;
          wt1 <= (req[1] && !grant[1]) ? wt1 + 3'd1 : 3'd0;
          wt2 <= (req[2] && !grant[2]) ? wt2 + 3'd1 : 3'd0;
          wt3 <= (req[3] && !grant[3]) ? wt3 + 3'd1 : 3'd0;
        end
      end
    
      always @(*) if (!init) begin
        a_onehot0:  assert ((grant & (grant - 4'd1)) == 4'd0);
        a_no_phantom: assert ((grant & ~req) == 4'd0);
        a_work_cons:  assert (!(|req) || (|grant));
        // bounded response (safety form of starvation freedom): nobody waits more than 3 cycles
        a_wt0: assert (wt0 <= 3'd3);
        a_wt1: assert (wt1 <= 3'd3);
        a_wt2: assert (wt2 <= 3'd3);
        a_wt3: assert (wt3 <= 3'd3);
      end
    
      // helper invariants for the induction step of the wait bounds
      function [1:0] ahead;             // how many grants can still precede requester i
        input [1:0] i;
        ahead = i - last - 2'd1;        // 0..3 (mod 4)
      endfunction
      generate if (HELPER) begin : g_helper
        always @(*) if (!init) begin
          h_wt0: assert (wt0 + {1'b0, ahead(2'd0)} <= 3'd3);
          h_wt1: assert (wt1 + {1'b0, ahead(2'd1)} <= 3'd3);
          h_wt2: assert (wt2 + {1'b0, ahead(2'd2)} <= 3'd3);
          h_wt3: assert (wt3 + {1'b0, ahead(2'd3)} <= 3'd3);
        end
      end endgenerate
    
      // covers
      always @(*) if (!init) begin
        c_g0: cover (grant[0]);
        c_g1: cover (grant[1]);
        c_g2: cover (grant[2]);
        c_g3: cover (grant[3]);
        c_contend: cover (req == 4'b1111 && wt3 == 3'd3);
        c_idle: cover (req == 4'b0000 && grant == 4'd0);
      end
    `endif
    endmodule

    What the review looks for

    • The bound is derived and exact: the model gives three and the tightened property fails.

    • W1 is read at both steps, and the gap is explained: an invariant failing is not yet a requirement failing.

    • The register for this design has no assumption about the requesters: the bound holds for every pattern.

    Verify and debug

    If the bound fails on the good design, check that the wait counters clear on a grant and on a request release. A helper that fires before any requirement is an early witness: confirm the requirement follows at the later step before calling the design broken.

    Fairness assumed on a design output: a liveness proof passes because the assumption forces the answer. Symptom is a starvation proof that passes with a fault planted. Cause is constraining a grant the design drives. Fix: fairness lives on inputs only, and here nothing needs assuming. Found by removing each assume in turn.

    “Prove every request is eventually served.” Answer: with no liveness engine, prove the bounded form: a wait of at most requesters minus one, with a counter per requester and no assumption on the requesters, then show the bound is exact. Quote the pair: helper at step 2, requirement at step 5.

    Check yourself Why does the helper fail at step 2 while the requirement fails at step 5?

    The helper bounds wait plus the grants still ahead; with the pointer stuck, that sum exceeds three as soon as one cycle has passed. The requirement fails only when a counter itself reaches four.

    Check yourself What does the tightened bound show that the passing proof does not?

    That three is reached: the contention shape drives a wait of three, so a bound of two fails. A bound that nothing approaches may be too loose to catch a fault.

    Related topics

    • Back: Stage 2 Arbiter Grant Latency and Starvation

    • Next: Solutions, Pairs and Chain

    • Earlier: Arbiter Grant Latency Proof

    Previous

    177 / 183

    Next: Solutions: Pairs and Chain

    Up next

    Up nextSolutions: Pairs and Chain

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Solutions: FIFO Property Set
    • Solutions: Bridge and the Assumption Trap
    • Solutions: Pipeline, Twin and ECO