Unreachability and Coverage Closure

Ask three different questions of a coverage bin, prove the dead ones dead, reach the live ones, and write a waiver that survives review.

Lesson
F5.7
Time
15 min
Runs on
SymbiYosys
Check
4 questions

A regression sat at 99.4 % coverage for a month while three engineers chased the last twelve bins. One bin was waived as unreachable after a formal run, and it was not. The run had been a bounded cover at depth 20, the bin needed 30 cycles of setup, and nobody had written the depth down.

Bin B needs 24 idle cycles first
01234567clkfreerstfreebusyfreestartidle_cnt181920212223240expiredidle_cnt 24, startreported step 26

Measured with smtbmc yices, mode cover, depth 30. The last steps shown; steps 1 to 24 are all idle. A depth-20 run cannot hold this trace.

Three Questions About One Bin

Three Questions, Three MeaningsThe same coverage bin asked three ways. Only the middle answer supports a waiver. Arrows mean "the run says".
Three questions, three meaningsThe same coverage bin asked three ways. Only the middle answer supports a waiver. Arrows mean "the run says". component A coverage bin a Boolean condition onthe design component cover(bin),reached reachable: here is thetrace, a directedtest component assert(!bin),Prove PASS unreachable fromreset, for ever,under theseassumptions component cover(bin),unreached at N not within N steps: ahint about depth component Waive with theproof attached mode, engine,assumptions, revision component Raise N or try aproof never a waiver byitself
Read the diagram as text
  • A coverage bin: a Boolean condition on the design
  • cover(bin), reached: reachable: here is the trace, a directed test
  • assert(!bin), Prove PASS: unreachable from reset, for ever, under these assumptions
  • cover(bin), unreached at N: not within N steps: a hint about depth
  • Waive with the proof attached: mode, engine, assumptions, revision
  • Raise N or try a proof: never a waiver by itself
  • A coverage bin → cover(bin), reached
  • A coverage bin → assert(!bin), Prove PASS
  • A coverage bin → cover(bin), unreached at N
  • assert(!bin), Prove PASS → Waive with the proof attached
  • cover(bin), unreached at N → Raise N or try a proof

The story's team waived a bin on the bottom row and called it the middle one.

A coverage bin is a condition, and formal can ask three things of it. Does some trace reach it (a cover)? Can no trace ever reach it (an assert of its negation under prove)? Does a trace reach it within N steps (a cover at depth N)? Only the second supports a waiver.

The third is the trap. SBY's wording for it is "Unreached cover statement", and the task status is FAIL. That is a fact about N, not about the design (F3.5).

Two Bins on seq25

BinQuestionMeasured resultTime
A: idle_cnt > 24assert(!bin), abc pdrProve PASS (abc pdr)489 ms
Aassert(!bin), k-inductionUNKNOWN at k = 1 (base case passed, induction step failed); Prove PASS (k = 2)492 ms
B: idle_cnt == 24 && startcover(bin), depth 20Unreached cover statement at top: bin_b510 ms
Bcover(bin), depth 30Cover reached, reported step 26554 ms
B, leftover assume(!start)cover(bin), depth 30Unreached cover statement at top: bin_b532 ms

seq25 as fixed in F1.5; smtbmc yices unless named, reset assumed in step 0.

Bin A is the top of a five-bit counter's range: values 25 to 31 that a covergroup would list. The proof shows no trace from reset reaches them. Bin B is live: 25 steps of setup, beyond depth 20 and inside 30 (F1.5).

The last row is the dangerous one. With a leftover assume(!start) the live bin is unreached at any depth: the environment, not the design, removed it (F3.2).

The Waiver Card

A waiver that survives review

bin:         idle_cnt > 24 (idle_cnt[25:31])
proof:       assert(!bin), mode prove, abc pdr
result:      Prove PASS (abc pdr)
assumptions: reset in step 0; none on inputs
revision:    seq25 as fixed in F1.5, 2026-11-07

Bin, proof, assumptions, revision: anyone can rerun it.

A waiver that should be refused

bin:    idle_cnt == 24 && start
proof:  cover not hit, bmc depth 20
result: unreachable

Bounded, and the bin is reached at step 26 of a deeper run.

A proof holds only for the environment it ran in. Running the unreachability check with no input assumptions at all is the safe default: a bin unreachable even when the inputs may do anything is unreachable for real. Every assumption you add must be listed on the card.

Asked in Interviews

QuestionAnswer
How does formal help close coverage?It proves the dead bins dead with assert(!bin) under prove, so they can be waived with a proof, and writes traces to the live ones that replay as directed tests.
What does a bounded "unreached" prove?Nothing beyond the depth.
What does an unreachable cover tell you?Dead logic or an over-constraint; the assumption list decides which.

Try It

The two bins in SVA

assert property (@(posedge clk) disable iff (rst) !(idle_cnt > 5'd24));
cover property (@(posedge clk) !rst && idle_cnt == 5'd24 && start);

Bin A as a proof obligation, bin B as a reachability question.

As this engine runs them

  always @(posedge clk) begin
    if ($initstate) begin assume(rst); end else begin assume(!rst); end
  end
  always @(posedge clk) if (!rst) begin
    bin_b: cover(idle_cnt == 5'd24 && start);
  end

Swap the cover line for the assert to ask about bin A.

The cover task at depth 20. Bin A uses mode prove with abc pdr.
[options]
mode cover
depth 20
[engines]
smtbmc yices
[script]
read_verilog -formal -sv design.sv
prep -top seq25
[files]
design.sv
  1. Cover at 20

    Run as it is and read the exact log line for the unreached cover.

  2. Cover at 30

    Set depth 30 and run: Cover reached, reported step 26. Count the idle steps in the trace.

  3. Prove bin A

    Replace the cover with assert(!(idle_cnt > 5'd24)) and run mode prove with abc pdr: Prove PASS (abc pdr).

  4. Plant an assumption

    Add always @(posedge clk) assume(!start); and rerun the depth-30 cover. Did bin B become unreachable?

  5. Write the card

    Write the waiver card for bin A from the measured log: bin, proof line, assumptions, revision.

design.sv

props.sv

Use assert property (@(posedge clk) ...);. Leave blank for immediate assertions in the design.

Inputs are free unless constrained. Run to find a counterexample or check a proof.

Model: one positive-edge clock, binary data. Sampled history requires declaration-initialized variables; primary-input history is unsupported.

SBY summary and log

Check Yourself

Predict the outputseq25 fixed, cover(idle_cnt == 24 && start), mode cover, depth 20, smtbmc yices, reset assumed in step 0. The cover result is:
module seq25(input clk, rst, busy, start, output expired, output reg [1:0] phase=0);
  reg [4:0] idle_cnt=0;
  assign expired = idle_cnt == 5'd24;
  always @(posedge clk) begin
    if (rst) begin idle_cnt<=0; phase<=0; end
    else begin
      if (busy) idle_cnt <= 0;
      else if (expired) idle_cnt <= 0;
      else idle_cnt <= idle_cnt + 5'd1;
      if (expired) phase <= 2'd0;
      else if (start) phase <= phase + 2'd1;
    end
  end
`ifdef FORMAL
  always @(posedge clk) begin
    if ($initstate) begin assume(rst); end else begin assume(!rst); end
  end
  always @(posedge clk) if (!rst) begin
    bin_b: cover(idle_cnt == 5'd24 && start);
  end
`endif
endmodule
ConceptWhich waiver line is not acceptable?
ConceptWhy must a formal waiver list its assumptions?
Read the waveformIn the bin-B trace, at which step does the bin hold, and what happens after it?
Bin B needs 24 idle cycles first
01234567clkfreerstfreebusyfreestartidle_cnt181920212223240expiredidle_cnt 24, startreported step 26

Measured with smtbmc yices, mode cover, depth 30. The last steps shown; steps 1 to 24 are all idle. A depth-20 run cannot hold this trace.

Remember

  • Formal can ask three things of a bin: reachable (cover), unreachable for ever (assert of its negation under prove), unreached within N (bounded cover).
  • Only a proof supports a waiver; a bounded unreached cover is a hint about depth.
  • A waiver card carries the bin, the proof line, the assumptions and the revision.
  • An over-constraint makes a live bin look dead; run unreachability with as few assumptions as possible.