Property checking asks whether a design follows rules you wrote. Equivalence checking asks a different question: do two versions of a design behave exactly the same? It needs no properties at all, and it runs on every chip project, after every synthesis run and every netlist change. This chapter shows equivalence checking with Yosys and SymbiYosys, how to read a failure, and the formal apps that package other standard questions.
Synthesis turns RTL into a netlist of gates. Optimizations, technology mapping, test insertion and hand-made fixes all change that netlist. An equivalence check proves that the netlist still computes exactly what the RTL computes, for every input. This article explains how, using a 4-bit adder, its gate netlist from Yosys, and a hand-edited netlist with one wrong gate.
9 min read | Intermediate
What You'll Learn
- Explain what combinational equivalence checking proves and when it applies
- Build a miter that compares two designs with one formal question
- Run an equivalence check with Yosys, and as a property check in the lab
- Read the counterexample of a failed equivalence check
The Miter
An equivalence check connects the two designs, called gold (the reference) and gate (the implementation), to the same inputs and compares every output. The combined circuit is called a miter. Its single output, often called trigger, is 1 whenever any output pair differs. The formal question is: can trigger ever be 1?
If the answer is no, the designs are equivalent for every input. If yes, the solver returns the input that makes them differ.
Combinational Equivalence
When both designs have the same registers, as RTL and its synthesized netlist usually do, the check can compare the logic between registers only. Each register output becomes an input of the miter and each register input becomes an output. The question is then purely combinational: one frame, no unrolling. This is why equivalence checking scales to whole chips with millions of gates: each cone between registers is checked separately, and most are small.
Registers are matched by name or by structure. When synthesis renames or merges registers, the tool has to find the pairs first, and when it moves registers across logic (retiming), the check becomes sequential, as the sequential equivalence article shows.
Quick Check
Q: Why does an equivalence check need no properties?
Show Answer
The gold design is the specification. The only rule is "the two designs agree on every output", which the miter builds automatically.
The Example: an Adder and Its Netlist
module add4 (input [3:0] a, input [3:0] b, input cin, output [3:0] s, output cout);
assign {cout, s} = a + b + cin;
endmodule
Yosys synthesizes it into 20 gates, written out as a netlist module add4_gate. A part of it:
assign _06_ = _03_ | _05_; // carry into bit 1
assign _07_ = a[1] ^ b[1]; // propagate of bit 1
assign _08_ = _06_ & _07_; // carry passes through bit 1
assign _09_ = _02_ | _08_; // carry into bit 2
...
assign s[1] = _06_ ^ _07_;
The check with Yosys builds the miter and asks the SAT solver to prove that trigger is always 0:
read_verilog rtl.v read_verilog gate.v proc miter -equiv -flatten -make_outputs -ignore_gold_x add4 add4_gate miter hierarchy -top miter sat -verify -prove trigger 0 -show-inputs -show-outputs miter
Solving problem with 326 variables and 853 clauses.. SAT proof finished - no model found: SUCCESS!
"No model found" means the solver found no input that makes the outputs differ. The netlist is equivalent. Yosys also has a dedicated flow, equiv_make, equiv_simple and equiv_status, which matches internal signals as well and reports "Equivalence successfully proven!" for this netlist.
A Hand Edit Goes Wrong
Late in a project, small netlist fixes are made by hand, called engineering change orders (ECO). Suppose an engineer edits one gate and types an OR instead of an AND:
assign _08_ = _06_ | _07_; // was: _06_ & _07_
The same check now fails:
SAT proof finished - model found: FAIL! Signal Name Dec Hex Bin --------------- ----------- --------- ------------- \gate_cout 1 1 1 \gate_s 2 2 0010 \gold_cout 0 0 0 \gold_s 14 e 1110 \in_a 8 8 1000 \in_b 6 6 0110 \in_cin 0 0 0 \trigger 1 1 1
For a = 8, b = 6 and no carry in, the RTL gives 14 and the netlist gives 2 with a carry out. The when equivalence fails article shows how to trace such a difference back to the wrong gate.
Quick Check
Q: A simulation of the edited netlist with 1000 random inputs passes. Is that possible?
Show Answer
Unlikely for this bug, which affects many inputs, but in general yes. A wrong gate deep in a large cone may matter only for a tiny fraction of inputs. Equivalence checking finds even a single differing input, which is why it is used instead of simulation for netlist sign-off.
Running It in ChipVerify Lab
The lab panel runs property checks, so an equivalence check becomes a property: a wrapper instantiates both versions, connects their inputs, and asserts that the outputs match. That wrapper is exactly a miter:
module add4_miter (input [3:0] a, input [3:0] b, input cin);
// the same inputs drive both versions
wire [3:0] s_rtl, s_gate;
wire c_rtl, c_gate;
add4 u_rtl (.a(a), .b(b), .cin(cin), .s(s_rtl), .cout(c_rtl));
add4_gate u_gate (.a(a), .b(b), .cin(cin), .s(s_gate), .cout(c_gate));
`ifdef FORMAL
// the outputs must always be equal
always @(*) assert (s_rtl == s_gate && c_rtl == c_gate);
`endif
endmodule
The example contains the RTL, the edited netlist and the miter. Run it with the top module add4_miter: it fails in step 0, with inputs that make the two adders disagree. Correct the gate and it passes.
Common Mistakes
Mistake #1: Using simulation to sign off a netlist change
Simulation samples inputs; equivalence checks all of them. Every netlist change, automatic or by hand, should go through an equivalence check.
Mistake #2: Treating "equivalent" as "correct"
Equivalence proves the netlist matches the RTL, bugs included. The RTL itself still needs property checking and simulation.
Mistake #3: Ignoring unmatched registers
If the tool cannot pair some registers, it may leave their cones unchecked or report them separately. Every unmatched point must be explained, or the check is incomplete.
Practice Exercise
Challenge: Find the Wrong Gate
Open the example and run it. Use the input values in the counterexample to compute the carries of the adder by hand, bit by bit. Find the gate in the netlist whose output is wrong for these inputs, fix it, and run again.
Hint
Compare the RTL sum and the netlist sum bit by bit. The lowest bit that differs tells you where the carry chain first goes wrong.
Solution
The faulty line is assign _08_ = _06_ | _07_;, the carry through bit 1. It should be _06_ & _07_: a carry passes through a bit only if the carry arrives and the bit propagates. With OR, any propagating bit 1 creates a carry into bit 2, which corrupts bits 2 and 3 and the carry out. With the AND restored, the miter passes.
Open the ready-made example in the lab and run the formal check from the formal panel. The free tier allows a depth of up to 10 cycles, and PRO allows up to 100.
Summary
- Equivalence checking proves that two designs give the same outputs for every input, with no properties to write.
- A miter connects both designs to the same inputs and flags any output difference; the solver tries to make it fire.
- With matching registers, the check is combinational per register-to-register cone, which scales to whole chips.
- A hand-edited gate in a 4-bit adder netlist was found with inputs 8 and 6, where the RTL gave 14 and the netlist 2.
- In the lab, a wrapper with both designs and an equality assertion is a miter.