LEC: Combinational Equivalence
Turn "is the netlist the RTL?" into a safety property, split it at matched flops, and read an equivalence report, including the failures that are not real.
- Lesson
- F6.1
- Time
- 20 min
- Runs on
- SymbiYosys
- Check
- 5 questions
A new synthesis option restructured a FIFO's write-accept logic, and the gate-level regression passed. The netlist now let a write through when the FIFO was full and a read arrived in the same cycle, a corner the slow gate-level run never reached. An equivalence check finds it in under a second.
| Run | Strategy | Partitions | Result | Wall time |
|---|---|---|---|---|
| RTL as A8.1 writes it, against its netlist | sat | 19 | 18 proved, aw not proven | 0.7 s |
The same, sby (word level, depth 1) | sby | 19 | aw not proven | 3.5 s |
The same, with merge aw full | sat | 18 | all proved | 0.7 s |
Write accept written !full | sat | 19 | all proved | 0.7 s |
Edited netlist, with merge aw full | sat | 18 | aw fails | 0.7 s |
EQY from the pinned suite, run directly so the partition commands and the sat strategy are available. The lab below uses the runner's fixed script.
The Miter
Read the diagram as text
- Shared inputs: write_en, read_en, write_data, reset, initialize
- Gold: hardware_fifo RTL: the design you verified
- Gate: Yosys netlist: 570 lines, 47 single-bit flops
- XOR on every output pair: full, empty, read_valid, read_data
- Property: the XOR is never 1: a safety property on the combined design
- Shared inputs → Gold: hardware_fifo RTL
- Shared inputs → Gate: Yosys netlist
- Gold: hardware_fifo RTL → XOR on every output pair
- Gate: Yosys netlist → XOR on every output pair
- XOR on every output pair → Property: the XOR is never 1
Inputs that make an XOR 1 are a counterexample. Everything from F1 to F4 applies to this bench.
Put the RTL and the netlist side by side and drive both from the same inputs. XOR each pair of outputs. If any XOR can be 1, the designs differ, and the values that make it 1 are the counterexample.
"The XOR is never 1" is a safety property on the combined design. Equivalence is model checking with a second design as the specification. Here the gold is the A8.1 FIFO and the gate is what synthesis (R1.1) made of it.
Key Points: One Cone per Matched Flop
Read the diagram as text
- Key points in: inputs, plus every matched flop output
- One cone per key point: gold logic and gate logic, side by side
- Key points out: each flop input and each output
- Flops paired by name: wp, used, rp, response_valid, response, memory
- Key points in → One cone per key point
- One cone per key point → Key points out
- Flops paired by name → One cone per key point
EQY made 19 partitions for this FIFO: the flops above, eight read_data bits, and the named wires aw, ar, full, empty, read_valid.
Combinational equivalence, the LEC that signs off synthesis, adds one assumption: both designs have the same flops, paired by name. Each pair is cut. Its output becomes a free input on both sides, and its input becomes a point to compare.
Every check is now a cone of logic between key points. Nothing is unrolled and no state is explored, which is why LEC runs on whole chips while property checking struggles with a single block.
A False Failure
sat is a bit-level SAT check per partition.[gold]
read_verilog -sv fifo.sv
prep -top hardware_fifo
[gate]
read_verilog fifo_net.v
prep -top hardware_fifo
[strategy sat]
use sat| Signal at the cut | Value |
|---|---|
full | 0 |
used | 3 |
write_en | 1 |
aw (gate) | 1 |
aw (gold) | 0 |
The counterexample for partition aw. The netlist computes aw from full; the RTL computes it from used. Both are cut points, so the solver may set them apart.
full = 0 with used = 3 is impossible: full is defined as used == 3. The cut threw away the logic that ties the two together. This is a false negative, and EQY prints it in the same words as a real failure.
Merging the two partitions ([partition *] then merge aw full) puts that logic back, and all 18 partitions prove. Writing the RTL's accept as !full does the same from the other side. The function is unchanged; only the key points line up.
A Failure That Is Real
- assign aw = write_en & ~(full);
+ assign aw = write_en & ~(full & ~read_en);| Signal at the cut | Value |
|---|---|
read_en | 1 |
used | 3 |
write_en | 1 |
aw (gate) | 1 |
aw (gold) | 0 |
full (gate) | 1 |
full (gold) | 1 |
The merged partition's counterexample. Every value is consistent: three entries, a write and a read offered together.
With used = 3 the FIFO is full, so the RTL refuses the write and the edited netlist takes it. The inputs are legal and the state is reachable: a real bug.
What LEC cannot check: any change to the flops themselves, such as retiming, re-encoding or an added pipeline stage. That needs sequential equivalence (F6.2). Compare it with simulation (F1.1): a gate-level regression samples the space the RTL regression sampled, slower. LEC decides every cone for every value.
Asked in Interviews
| Question | Answer |
|---|---|
| LEC versus SEC? | LEC pairs the same flops in both designs and compares the logic cones between them. SEC lets the flops differ and must reason over sequences of states. |
| What is a miter? | Both designs on shared inputs with an XOR on each output pair; equivalence is the safety property "the XOR is never 1". |
| Why sign off synthesis with LEC and not gate-level simulation? | LEC checks every cone for every input value in seconds; simulation samples. |
| Your LEC run reports a mismatch. What first? | Check whether the counterexample is reachable: inconsistent values at cut points mean a false negative to fix in the setup, not in the design. |
Try It
[gold]
read_verilog -sv design.sv
prep -top hardware_fifo
[gate]
read_verilog -sv gate.sv
prep -top hardware_fifo
[strategy sby]
use sby
depth 1
engine smtbmc yicesread_verilog -sv fifo.sv; synth -top hardware_fifo; write_verilog -noattr fifo_net.vRun it
As loaded: UNKNOWN: equivalence not proven, in about 3.8 s. Open the log and find the one partition that failed.
Line up the key points
In design.sv change
used < DEPTHto!fulland run again: EQY PASS: every partition proved.Edit the netlist
In gate.sv find
assign aw = write_en & ~(full);and make itassign aw = write_en & ~(full & ~read_en);. Run: UNKNOWN: equivalence not proven. Is this one real? The table above answers it.
design.sv
gate.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
- Equivalence is a safety property on a miter: shared inputs, an XOR per output pair, "never 1".
- Combinational LEC pairs flops by name and compares one cone per key point, with no unrolling.
- Cut points lose the relations between signals; a counterexample with impossible values is a false negative.
- A real difference has a consistent, reachable counterexample. Check that before calling it a bug.