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
    • Cover Properties, Vacuity and Reachability
      • Antecedent Covers and Witness Traces
      • Over-Constraint Seen Through Covers
      • Bounded Cover Miss Is Inconclusive
      • Vacuity by Mutation and Sensitivity
    • Formal Coverage Metrics
    • Bounded Proof Signoff
    • The Formal Signoff Package
  • Methodology and Debug13
  • Interview Prep15
  • Capstone: A Chip-Block Verification Lab24
Loading lesson

←Cover Properties, Vacuity and Reachability
  1. Topics
  2. Formal Verification
  3. Formal Coverage and Signoff
  4. Cover Properties, Vacuity and Reachability
  5. Bounded Cover Miss Is Inconclusive
On this page

    Part 7 of 10, lesson 3 of 15Part 7 of 10/Formal Coverage and Signoff/Lesson 3 of 15All levels

    Bounded Cover Miss Is Inconclusive

    A cover missed within its bound proves nothing; only an inductive invariant makes a scenario unreachable.

    Why it matters

    A report says a scenario was not reached within depth 10, and the reader files it as impossible. That reading is wrong in exactly the way that hides deep bugs: the scenario may simply need eleven cycles. The decade counter is the perfect exhibit, because its deepest scenario first occurs at step 14 while an everyday scenario needs step 10.

    The core idea

    A cover search to depth N checks steps 0 to N minus 1 only. Three outcomes, three wordings, no synonyms: reached with a witness; not reached within N, which is inconclusive; proved unreachable, which requires an engine proof, normally an inductive invariant that excludes the state. Only the third wording may be read as impossible. A bounded miss is never reported as unreachable.

    Worked example: the counter that needs fourteen steps

    The decade counter wraps once and later shows count 3 with the wrap flag set. Counting from the reset at step 0: the count reads 9 at step 10, wraps to 0 at step 11 and reads 3 at step 14, so that cover first fires at step 14 and depth 15 (steps 0 to 14) is the smallest depth that reaches it. The depth was chosen because it was measured on this counter, not because it is a round number. Depth 10 misses it, and so does every other cover at step 10: the nine-count and the terminal pulse both first occur at step 10, outside a depth-10 search. The runs below use the identical module; only the bound changes.

    mod10_counter.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
    // formal: cover 15 expect reached slang top mod10_counter
    `define FORMAL
    // mod10_counter: 4-bit decade counter, counts 0..9 and wraps to 0.
    //   rst (synchronous, active high) : count <= 0
    //   en  = 1                        : count <= (count == 9) ? 0 : count + 1
    //   en  = 0                        : count holds
    //   tc  = en && (count == 9)       : terminal-count pulse (combinational)
    // States 10..15 exist in the 4-bit register but are unreachable from reset.
    // BUG: 0 none, 1 wraps at 10 instead of 9 (counts 0..10, a mod-11 counter).
    // HELPER: 1 (default) includes the helper invariant count <= 9; 0 leaves count != 15 alone.
    module mod10_counter #(parameter BUG = 0, parameter HELPER = 1) (
      input  wire       clk,
      input  wire       rst,
      input  wire       en,
      output reg  [3:0] count,
      output wire       tc
    );
      wire [3:0] last = (BUG == 1) ? 4'd10 : 4'd9;
      assign tc = en && (count == last);
    
      always @(posedge clk) begin
        if (rst)      count <= 4'd0;
        else if (en)  count <= (count == last) ? 4'd0 : count + 4'd1;
      end
    
    `ifdef FORMAL
      reg init = 1'b1;
      always @(posedge clk) init <= 1'b0;
      always @(*) if (init) assume (rst);
    
      // P2: count is never 15. True, but NOT inductive on its own: the unreachable states
      // 10..14 satisfy it and lead to 15 (and en = 0 lets a state repeat forever, so no k
      // of k-induction closes it).
      always @(*) if (!init) a_not15: assert (count != 4'd15);
      // P1 / helper: count <= 9. Inductive on its own (k = 1), and it makes a_not15 inductive.
      generate if (HELPER) begin : g_helper
        always @(*) if (!init) a_range: assert (count <= 4'd9);
      end endgenerate
    
      always @(*) if (!init) a_tc: assert (tc == (en && count == 4'd9));
      always @(posedge clk) if (!init && !$past(rst)) begin
        a_step: assert (!$past(en) || count == (($past(count) == 4'd9) ? 4'd0 : $past(count) + 4'd1));
        a_hold: assert ($past(en) || count == $past(count));
      end
    
      // sticky flag: the counter has wrapped at least once
      reg wrapped;
      always @(posedge clk) begin
        if (rst) wrapped <= 1'b0;
        else if (tc) wrapped <= 1'b1;
      end
      always @(*) if (!init) begin
        c_nine: cover (count == 4'd9);
        c_tc:   cover (tc);
        c_wrap: cover (wrapped && count == 4'd3);
      end
    `endif
    endmodule
    mod10_counter.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
    // formal: cover 10 expect fail slang top mod10_counter
    `define FORMAL
    // mod10_counter: 4-bit decade counter, counts 0..9 and wraps to 0.
    //   rst (synchronous, active high) : count <= 0
    //   en  = 1                        : count <= (count == 9) ? 0 : count + 1
    //   en  = 0                        : count holds
    //   tc  = en && (count == 9)       : terminal-count pulse (combinational)
    // States 10..15 exist in the 4-bit register but are unreachable from reset.
    // BUG: 0 none, 1 wraps at 10 instead of 9 (counts 0..10, a mod-11 counter).
    // HELPER: 1 (default) includes the helper invariant count <= 9; 0 leaves count != 15 alone.
    module mod10_counter #(parameter BUG = 0, parameter HELPER = 1) (
      input  wire       clk,
      input  wire       rst,
      input  wire       en,
      output reg  [3:0] count,
      output wire       tc
    );
      wire [3:0] last = (BUG == 1) ? 4'd10 : 4'd9;
      assign tc = en && (count == last);
    
      always @(posedge clk) begin
        if (rst)      count <= 4'd0;
        else if (en)  count <= (count == last) ? 4'd0 : count + 4'd1;
      end
    
    `ifdef FORMAL
      reg init = 1'b1;
      always @(posedge clk) init <= 1'b0;
      always @(*) if (init) assume (rst);
    
      // P2: count is never 15. True, but NOT inductive on its own: the unreachable states
      // 10..14 satisfy it and lead to 15 (and en = 0 lets a state repeat forever, so no k
      // of k-induction closes it).
      always @(*) if (!init) a_not15: assert (count != 4'd15);
      // P1 / helper: count <= 9. Inductive on its own (k = 1), and it makes a_not15 inductive.
      generate if (HELPER) begin : g_helper
        always @(*) if (!init) a_range: assert (count <= 4'd9);
      end endgenerate
    
      always @(*) if (!init) a_tc: assert (tc == (en && count == 4'd9));
      always @(posedge clk) if (!init && !$past(rst)) begin
        a_step: assert (!$past(en) || count == (($past(count) == 4'd9) ? 4'd0 : $past(count) + 4'd1));
        a_hold: assert ($past(en) || count == $past(count));
      end
    
      // sticky flag: the counter has wrapped at least once
      reg wrapped;
      always @(posedge clk) begin
        if (rst) wrapped <= 1'b0;
        else if (tc) wrapped <= 1'b1;
      end
      always @(*) if (!init) begin
        c_nine: cover (count == 4'd9);
        c_tc:   cover (tc);
        c_wrap: cover (wrapped && count == 4'd3);
      end
    `endif
    endmodule

    The second run misses all three covers: the nine-count and the pulse need step 10 and the wrap scenario needs step 14, while depth 10 searches steps 0 to 9. Nothing about the design changed. The correct report line is not reached within 10, inconclusive, with the deepest required scenario named beside it. One run closes the gap from the other side: at depth 14 the nine-count and the pulse are reached but the wrap cover is still missed, because steps 0 to 13 do not include step 14.

    mod10_counter.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
    // formal: cover 14 expect fail slang top mod10_counter
    `define FORMAL
    // mod10_counter: 4-bit decade counter, counts 0..9 and wraps to 0.
    //   rst (synchronous, active high) : count <= 0
    //   en  = 1                        : count <= (count == 9) ? 0 : count + 1
    //   en  = 0                        : count holds
    //   tc  = en && (count == 9)       : terminal-count pulse (combinational)
    // States 10..15 exist in the 4-bit register but are unreachable from reset.
    // BUG: 0 none, 1 wraps at 10 instead of 9 (counts 0..10, a mod-11 counter).
    // HELPER: 1 (default) includes the helper invariant count <= 9; 0 leaves count != 15 alone.
    module mod10_counter #(parameter BUG = 0, parameter HELPER = 1) (
      input  wire       clk,
      input  wire       rst,
      input  wire       en,
      output reg  [3:0] count,
      output wire       tc
    );
      wire [3:0] last = (BUG == 1) ? 4'd10 : 4'd9;
      assign tc = en && (count == last);
    
      always @(posedge clk) begin
        if (rst)      count <= 4'd0;
        else if (en)  count <= (count == last) ? 4'd0 : count + 4'd1;
      end
    
    `ifdef FORMAL
      reg init = 1'b1;
      always @(posedge clk) init <= 1'b0;
      always @(*) if (init) assume (rst);
    
      // P2: count is never 15. True, but NOT inductive on its own: the unreachable states
      // 10..14 satisfy it and lead to 15 (and en = 0 lets a state repeat forever, so no k
      // of k-induction closes it).
      always @(*) if (!init) a_not15: assert (count != 4'd15);
      // P1 / helper: count <= 9. Inductive on its own (k = 1), and it makes a_not15 inductive.
      generate if (HELPER) begin : g_helper
        always @(*) if (!init) a_range: assert (count <= 4'd9);
      end endgenerate
    
      always @(*) if (!init) a_tc: assert (tc == (en && count == 4'd9));
      always @(posedge clk) if (!init && !$past(rst)) begin
        a_step: assert (!$past(en) || count == (($past(count) == 4'd9) ? 4'd0 : $past(count) + 4'd1));
        a_hold: assert ($past(en) || count == $past(count));
      end
    
      // sticky flag: the counter has wrapped at least once
      reg wrapped;
      always @(posedge clk) begin
        if (rst) wrapped <= 1'b0;
        else if (tc) wrapped <= 1'b1;
      end
      always @(*) if (!init) begin
        c_nine: cover (count == 4'd9);
        c_tc:   cover (tc);
        c_wrap: cover (wrapped && count == 4'd3);
      end
    `endif
    endmodule

    Claiming unreachable

    Unreachable is a proof obligation, not a reading of a miss. On this counter, the invariant count at most 9 excludes the unreachable states 10 to 15 and closes inductively at k equal to 1 on its own; with it alongside, the not-15 check closes too. The full proof below states the positive case: proven for this model under these assumptions, with the helper named.

    mod10_counter.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
    // formal: prove 4 expect pass slang top mod10_counter
    `define FORMAL
    // mod10_counter: 4-bit decade counter, counts 0..9 and wraps to 0.
    //   rst (synchronous, active high) : count <= 0
    //   en  = 1                        : count <= (count == 9) ? 0 : count + 1
    //   en  = 0                        : count holds
    //   tc  = en && (count == 9)       : terminal-count pulse (combinational)
    // States 10..15 exist in the 4-bit register but are unreachable from reset.
    // BUG: 0 none, 1 wraps at 10 instead of 9 (counts 0..10, a mod-11 counter).
    // HELPER: 1 (default) includes the helper invariant count <= 9; 0 leaves count != 15 alone.
    module mod10_counter #(parameter BUG = 0, parameter HELPER = 1) (
      input  wire       clk,
      input  wire       rst,
      input  wire       en,
      output reg  [3:0] count,
      output wire       tc
    );
      wire [3:0] last = (BUG == 1) ? 4'd10 : 4'd9;
      assign tc = en && (count == last);
    
      always @(posedge clk) begin
        if (rst)      count <= 4'd0;
        else if (en)  count <= (count == last) ? 4'd0 : count + 4'd1;
      end
    
    `ifdef FORMAL
      reg init = 1'b1;
      always @(posedge clk) init <= 1'b0;
      always @(*) if (init) assume (rst);
    
      // P2: count is never 15. True, but NOT inductive on its own: the unreachable states
      // 10..14 satisfy it and lead to 15 (and en = 0 lets a state repeat forever, so no k
      // of k-induction closes it).
      always @(*) if (!init) a_not15: assert (count != 4'd15);
      // P1 / helper: count <= 9. Inductive on its own (k = 1), and it makes a_not15 inductive.
      generate if (HELPER) begin : g_helper
        always @(*) if (!init) a_range: assert (count <= 4'd9);
      end endgenerate
    
      always @(*) if (!init) a_tc: assert (tc == (en && count == 4'd9));
      always @(posedge clk) if (!init && !$past(rst)) begin
        a_step: assert (!$past(en) || count == (($past(count) == 4'd9) ? 4'd0 : $past(count) + 4'd1));
        a_hold: assert ($past(en) || count == $past(count));
      end
    
      // sticky flag: the counter has wrapped at least once
      reg wrapped;
      always @(posedge clk) begin
        if (rst) wrapped <= 1'b0;
        else if (tc) wrapped <= 1'b1;
      end
      always @(*) if (!init) begin
        c_nine: cover (count == 4'd9);
        c_tc:   cover (tc);
        c_wrap: cover (wrapped && count == 4'd3);
      end
    `endif
    endmodule

    What the reviewer looks for

    • Every cover row in the report uses one of the three wordings; bounded appears only with its bound.

    • Any row claiming unreachable names the invariant or engine proof behind the word.

    • The depth of each cover run is justified against the deepest required scenario, not a round number.

    Verify and debug

    When a cover misses, extend the bound before concluding anything: double it once and see whether the result moves. If the miss persists, check the trigger against the encoding and the assumptions, exactly as in the previous two lessons. If the scenario is genuinely impossible, write the invariant that excludes it, prove that invariant, and only then change the wording to unreachable.

    The bounded proof mistaken for a full proof: symptom is a report that reads pass at depth 10 while the behaviour needs step 14. Cause is treating a bounded miss as absence instead of inconclusive. Fix is the three-word vocabulary with the bound always attached, plus a deeper run or an invariant before any stronger claim. It is found by re-running at a larger depth and by naming the deepest required scenario in the report.

    Expect the probe: your cover missed at depth N, is the scenario impossible? Answer no, and give the recipe: extend the bound, audit trigger and assumptions, and either reach it or prove an invariant that excludes it. Quote this counter as the example that fits in one breath: missed at 10, reached at 15, unreachability proved only by the range invariant.

    Check yourself A cover is not reached within 40 on a block whose diameter is proven to be 12. What may you claim?

    Unreachable, because the bound exceeds the diameter: every reachable state is visited within 12 steps, so a miss at 40 excludes the scenario. This is the diameter argument, and the report must cite the diameter, not just the bound.

    Related topics

    • Next: Vacuity by Mutation and Sensitivity

    • Formal: Bounded vs Unbounded Proofs

    Previous

    119 / 183

    Next: Vacuity by Mutation and Sensitivity

    Up next

    Up nextVacuity by Mutation and Sensitivity

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Antecedent Covers and Witness Traces