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.
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
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.
| Property | Kind | What a violation looks like | On this site |
|---|---|---|---|
idle_cnt never exceeds 24 | safety | A finite trace with a step where it breaks: the trace above. | Modes bmc and prove, this lesson. |
| Never two grants (F1.1) | safety | A finite trace, reported step 5. | Modes bmc and prove. |
| Every request is eventually granted | liveness | A 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
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.
| Task | Samples checked | Measured result | Time |
|---|---|---|---|
bmc, depth 20 | 0 to 18 | BMC PASS to depth 20 | 675 ms |
bmc, depth 24 | 0 to 22 | BMC PASS to depth 24 | 661 ms |
bmc, depth 27 | 0 to 25 | BMC PASS to depth 27 | 718 ms |
bmc, depth 28 | 0 to 26 | FAIL at reported step 27 | 668 ms |
bmc, depth 30 | 0 to 28 | FAIL at reported step 27 | 665 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
| Design | Mode and engine | Measured result | Time |
|---|---|---|---|
| fixed | bmc depth 20, smtbmc yices | BMC PASS to depth 20 | 620 ms |
| fixed | prove k = 2, smtbmc yices | Prove PASS (k = 2) | 574 ms |
| fixed | prove, abc pdr | Prove PASS (k = 20) | 552 ms |
| buggy | prove k = 2, smtbmc yices | UNKNOWN (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
| Question | Answer |
|---|---|
| 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.
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.svDepth 20
Run as it is: BMC PASS to depth 20. Read the summary line. Is the design proven?
Depth 30
Set the depth to 30 and run: FAIL at reported step 27. In the trace find the timer at 24 with
starthigh, then 25.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.
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
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.