IC3/PDR: Invariants Found by the Tool
Swap one engine line and watch PDR prove what k-induction could not, by building the helper invariant itself, one clause at a time.
- Lesson
- F2.5
- Time
- 20 min
- Runs on
- SymbiYosys
- Check
- 4 questions
A FIFO scoreboard property timed out in BMC on every SMT solver the team had, and induction stayed UNKNOWN through two days of helper writing. Someone changed one line of the task file to abc pdr. The proof came back in under two seconds, with no helpers at all.
Frames Instead of Copies
- Ask one step
Can a state in Fk step into a bad state? One SAT query on one copy of the logic, never an unrolled strip.
- Take the cube
If yes, the solver hands back such a state s, as a cube: a few state bits fixed.
- Block it relative to Fk-1
Show that no state in Fk-1 steps into s. Then ¬s is a new clause for Fk, generalized to cover more states. If that fails, recurse backwards; reaching F0 is a real counterexample.
- Converge
Clauses are pushed forward frame by frame. When two neighbouring frames become equal, that frame is an inductive invariant and the property is proven.
Read the diagram as text
- F0: the initial states
- F1: reachable in ≤ 1 step, or more
- Fk: reachable in ≤ k steps, or more
- Bad states: every Fi must exclude them
- Cube s: a state in Fk with a step into Bad
- Clause ¬s: learned, then pushed forward
- F0 → F1: ⊆
- F1 → Fk: ⊆
- Fk → Bad states: one step?
- Fk → Cube s: yes: take s
- Cube s → Clause ¬s: unreachable from Fk-1
IC3 (Bradley 2011); PDR is the ABC implementation (Eén, Mishchenko, Brayton 2011). Drawn, not measured.
IC3 never unrolls. It keeps frames F0 to Fk. F0 is the initial states. Each later frame is a set of clauses, small lemmas about the state, that every state reachable in that many steps satisfies. Every frame excludes the bad states.
The Same FIFO, One Engine Line Changed
| Task | Engine | Measured result | Time |
|---|---|---|---|
| fifo16, no helpers, depth 12 | smtbmc yices | UNKNOWN | 0.5 s |
| fifo16, no helpers | abc pdr | PASS | 1.5 s |
| fifo16, two helpers, depth 2 | smtbmc yices | PASS | 0.5 s |
Whole-task time on this machine. PDR has no depth: the runner passes one, and PDR ignores it.
k-induction only has the property and a window of k states. PDR strengthens the property itself until it is inductive. Each clause it learns is a tiny helper invariant, proven relative to the frame before it. That is why it needs none from you on finite control logic.
The Invariant It Found
abc pdr -d wrote 220 clauses over 18 latch bits. The first 8 are shown..i 18
.o 1
.p 220
---------------1-0 1
---------------11- 1
-------------11--- 1
------------1-1--- 1
-----------1--1--- 1
----------1---1--- 1
----0----0----1--- 1
----0----100000--- 1
...Each row is one lemma over the latch bits: 0 or 1 where it tests a bit, - where it ignores one. The 18 bits are the two 5-bit pointers and the 5-bit count, two flops that sample the assertion, and the first-step latch. The memory is gone: no property reads it.
Your two helpers say the same thing in two lines. PDR says it in 220 bit-level lemmas. The count changes from run to run, because ABC's PDR uses internal time limits. In this run the log reached frame 36 and ended with "Proved output 0 in frame 1 (converged)."
Its Counterexamples Start at Reset
Measured abc pdr trace for the buggy FIFO. Step 0 is reset; the failure is sampled at 21 and reported at 22.
PDR reports a failure only after tracing a bad cube back to F0, so its traces are real counterexamples from reset. They need not be the shortest: on this bug PDR reported step 22, while BMC at depth 20 reported step 19.
Where It Shines, and Where It Stalls
| Four-entry FIFO scoreboard | Measured result | Time |
|---|---|---|
bmc, depth 20, smtbmc yices | time limit, no verdict | 20.1 s |
bmc, depth 20, smtbmc boolector | time limit, no verdict | 20.1 s |
bmc, depth 20, smtbmc bitwuzla | time limit, no verdict | 20.1 s |
bmc, depth 20, smtbmc z3 | time limit, no verdict | 20.1 s |
prove, depth 20, smtbmc yices | time limit, no verdict | 20.1 s |
prove, abc pdr | PASS | 1.8 s |
The engine study FIFO with its own queue model (F4.4 builds it). A time limit claims nothing about the property.
PDR works on the bit-level AIGER. Wide arithmetic becomes thousands of gates, and there it can stall where word-level induction with a helper finishes quickly. F2.6 measures one such case.
Asked in Interviews
| Question | Answer |
|---|---|
| What does IC3/PDR do differently from induction? | No unrolling. It builds an inductive strengthening of the property from relatively inductive clauses kept in frames. |
| Why do its counterexamples never start in unreachable states? | It reports a failure only after tracing a bad state back to the initial states. |
| When would you not use PDR? | On wide datapaths, where bit-blasting hurts and word-level induction with a helper is faster. |
Try It
The lab runs fifo16 without helpers under abc pdr. Measured: PASS in 1.5 s.
Change one line
Switch the engine to
smtbmc yices, depth 12, and re-run: UNKNOWN. Switch back toabc pdr. What did the engine know that the window did not?The engineer’s route
Add the two F2.4 helpers and run
smtbmc yicesat depth 2. Compare the time with PDR’s.Break a pointer
Make
rd_ptrwrap at 16:rd_ptr <= rd_ptr == 5'd15 ? 5'd0 : rd_ptr + 5'd1;. Runabc pdrand read step 0 of its trace.
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
- IC3/PDR keeps frames of clauses instead of unrolling.
- Each clause is proven relative to the previous frame; equal frames are an inductive invariant.
- PDR proves fifo16 with no helpers; its invariant is many bit-level lemmas.
- Its counterexamples start at reset but need not be the shortest.