Safety and Liveness, Bounded and Unbounded

Tell a property with a failing step from one whose counterexample is a loop, and say what BMC PASS at depth 20 does and does not promise.

Lesson
F1.5
Time
20 min
Runs on
SymbiYosys
Check
4 questions

The sign-off report said "all properties PASS". The run was bmc at the default depth 20, and the design had a 24-cycle idle timer with a bug that fired once the timer had expired, in sampled state 26. No input sequence of 20 cycles could reach it, so the tool, truthfully, reported no failure within its bound. The report never said "within 20", and one missing phrase cost a respin.

Steps 21 to 27 of the depth-30 counterexample
0123456clkfreebusyfreestartexpiredidle_cnt20212223242526phase23idle_cnt 24, start 1idle_cnt 25: violationSBY reports FAIL

Measured at depth 30: FAIL at reported step 27, 665 ms. Steps 0 to 20 are the reset step and twenty idle cycles counting 0 to 19.

A Failing Step, or a Loop

A Liveness Counterexample Is a Loop"Every request is eventually granted" on an arbiter. Arrows are clock cycles. After the prefix, the design can go round the loop forever while requester 1 waits. No single step is wrong, so no finite trace can show the failure.
A liveness counterexample is a loop"Every request is eventually granted" on an arbiter. Arrows are clock cycles. After the prefix, the design can go round the loop forever while requester 1 waits. No single step is wrong, so no finite trace can show the failure. component Reset prefix starts component req[1] pending requester 1 asks component grant 0 requester 0 wins component req[0] again requester 0 asksagain component grant 0 held requester 1 stillwaits looplooploop
Read the diagram as text
  • Reset: prefix starts
  • req[1] pending: requester 1 asks
  • grant 0: requester 0 wins
  • req[0] again: requester 0 asks again
  • grant 0 held: requester 1 still waits
  • Reset → req[1] pending
  • req[1] pending → grant 0
  • grant 0 → req[0] again: loop
  • req[0] again → grant 0 held: loop
  • grant 0 held → grant 0: loop

A drawing, not a measured trace. SBY's live mode searches for such a lasso; the runnable version waits for F3.7.

PropertyKindWhat a violation looks likeOn this site
idle_cnt never exceeds 24safetyA finite trace with a step where it breaks: the trace above.Modes bmc and prove, this lesson.
Never two grants (F1.1)safetyA finite trace, reported step 5.Modes bmc and prove.
Every request is eventually grantedlivenessA lasso: a prefix, then a loop the design repeats forever without granting.Mode live with s_eventually; the runnable lab is F3.7. In simulation this is a strong property (A3.1 Weak and strong).

Safety says something bad never happens. Liveness says something good eventually happens. Most of industrial formal is safety; F3 to F8 stay with safety until F3.7.

If a safety property is violated, there is a step where it breaks, and a finite trace shows it. A liveness property has no such step: the grant might still come, so no finite trace can refute it. Its counterexample is a loop, a cycle of states the design can repeat forever without granting. A tool must reason about loops to check it, which is why live mode uses a different engine and needs fairness assumptions about the environment (F3.7).

What PASS at Depth 20 Promises

The bound against the bug

Depth 20 checks clocked samples 0 to 18 and passes. The violation is sampled state 26, reported at step 27. Depth 28 is the smallest bound that sees it (samples 0 to 26).

Linear scale. 0 to 32 steps.

Checked bound
20
Required bound
28
First reported failure
27
Claim
Counterexample found

Required bound here means the smallest depth that reaches the bug. F4.1 shows how to compute a required depth from the deepest counter before you know where the bug is.

TaskSamples checkedMeasured resultTime
bmc, depth 200 to 18BMC PASS to depth 20675 ms
bmc, depth 240 to 22BMC PASS to depth 24661 ms
bmc, depth 270 to 25BMC PASS to depth 27718 ms
bmc, depth 280 to 26FAIL at reported step 27668 ms
bmc, depth 300 to 28FAIL at reported step 27665 ms

The buggy seq25, smtbmc yices. The one thing that changes is the depth. The summary line of a passing run says nothing about cycle 27.

bmc at depth N checks every input sequence from reset up to its bound and nothing beyond. PASS means "no violation within the bound". A bug at sampled state 26 is invisible at depth 20 and visible at 28. Write the depth into every bounded result, because a bounded PASS without its depth is not a result.

How deep is deep enough? Count the deepest counter and the longest transaction in the design. Here the idle timer alone needs 24 idle cycles after reset to reach 24, so a bound of 20 could never have seen the bug. The sign-off team could have known that before running. F4.1 turns this count into a required depth and F7.3 into a sign-off rule.

A Bounded Check or a Proof

DesignMode and engineMeasured resultTime
fixedbmc depth 20, smtbmc yicesBMC PASS to depth 20620 ms
fixedprove k = 2, smtbmc yicesProve PASS (k = 2)574 ms
fixedprove, abc pdrProve PASS (k = 20)552 ms
buggyprove k = 2, smtbmc yicesUNKNOWN (base case passed, induction step failed at k = 2)617 ms

The induction trace of the last row starts at idle_cnt = 24, a state the solver chose, not one reached from reset. F2.4 explains why that is UNKNOWN and not FAIL.

prove mode asks for an unbounded answer: the property holds in every reachable state, forever. SBY's smtbmc proves it by k-induction (F2.4), abc pdr by IC3 (F2.5). Here both close on the fixed design, and the property is inductive on its own: when idle_cnt is at most 24, the next value is 0 or at most 24. A prove PASS is a proof. A bmc PASS is a bounded result and must carry its depth.

On the buggy design the base case passes and the induction step fails, so prove returns UNKNOWN: a proof not yet found, which is neither PASS nor FAIL. Only bmc at depth 28 or more turns the bug into a FAIL with a trace from reset.

Asked in Interviews

QuestionAnswer
What is a safety property and what is a liveness property?Safety: a finite trace can violate it, at a step. Liveness: only an infinite behaviour, a loop, can violate it.
What does PASS at depth 20 mean? Is the design proven?No. It is a bounded result to 20 cycles. Proving it needs prove mode (induction or IC3) or a depth argument (F4.1).
Why do sign-off reports state the depth?Because a bounded PASS without its depth is not a result.

Try It

Concurrent SVA

assert property (@(posedge clk) disable iff (rst) idle_cnt <= 5'd24);

The idle timer never passes 24, outside reset.

As this engine runs it

always @(posedge clk) if (!rst)
  assert(idle_cnt <= 5'd24);

One clocked immediate check; no history needed.

The first run, depth 20. The Depth box below changes depth; the Mode selector changes mode bmc to mode prove.
[options]
mode bmc
depth 20
[engines]
smtbmc yices
[script]
read_verilog -formal -sv design.sv
prep -top seq25
[files]
design.sv
  1. Depth 20

    Run as it is: BMC PASS to depth 20. Read the summary line. Is the design proven?

  2. Depth 30

    Set the depth to 30 and run: FAIL at reported step 27. In the trace find the timer at 24 with start high, then 25.

  3. The boundary

    Try depths 27 and 28. The smallest depth that fails is 28: it checks samples 0 to 26, and the violation is sample 26.

  4. Prove it

    Fix the clear (else if (expired) idle_cnt <= 0;), set the mode to prove and the depth to 2, run: Prove PASS. Switch the engine to abc pdr: PASS again. Then run prove on the buggy design and read UNKNOWN.

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 with the bug, mode bmc, depth 24, reset assumed in step 0, smtbmc yices. The summary verdict 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 && !start) idle_cnt <= 0;   // bug: start on the expiry cycle skips the clear
      else idle_cnt <= idle_cnt + 5'd1;
      if (expired) phase <= start ? 2'd3 : 2'd0;
      else if (start) phase <= phase + 2'd1;
    end
  end
  always @(posedge clk) begin
    if ($initstate) begin assume(rst); end else begin assume(!rst); end
  end
  always @(posedge clk) if (!rst) assert(idle_cnt <= 5'd24);
endmodule
ConceptWhich of these four properties is a liveness property?
ConceptWhat does bmc PASS at depth 20 mean?
Read the waveformIn the depth-30 trace, at which step does idle_cnt hold 24 for the last legal time?
Steps 21 to 27 of the depth-30 counterexample
0123456clkfreebusyfreestartexpiredidle_cnt20212223242526phase23idle_cnt 24, start 1idle_cnt 25: violationSBY reports FAIL

Measured at depth 30: FAIL at reported step 27, 665 ms. Steps 0 to 20 are the reset step and twenty idle cycles counting 0 to 19.

Remember

  • Safety: something bad never happens; a finite trace violates it at a step. Liveness: something good eventually happens; only a loop violates it.
  • BMC PASS at depth N means no violation within N cycles of reset. Always write the depth.
  • Prove mode (k-induction, IC3) gives an unbounded answer; UNKNOWN means no proof yet, not a bug.
  • The same bug is invisible at depth 20 and a FAIL at depth 28.