What causes inferred latches in Verilog?
This guide covers Verilog and SystemVerilog. There’s a VHDL version too.
A latch gets inferred whenever your combinational always block has a path
through it that doesn’t assign the output. The synthesizer takes you at
your word: if there’s an input combination for which grant_out gets no new
value, then grant_out must keep its old value, and keeping a value in
hardware requires storage. Since there’s no clock edge in sight, that storage
is a level-sensitive latch, not a flip-flop.
See one get inferred
This block only assigns grant_out when enable is high. When enable is
low, no branch runs, so the tools have to remember the old value:
always @(*) begin
if (enable)
grant_out = req; // no else: grant_out must remember its value
end
Here is that exact code, synthesized. The box in the middle is a $dlatch:
storage you probably didn’t mean to build. Edit the code and press Run to
experiment (try adding the else):
Interactive schematic by RapidRTL
Why latches are (usually) a bug
- You probably wanted combinational logic. A latch means your “combinational” block now has state, and its behaviour depends on the history of its inputs.
- They’re transparent. While the enable is high, glitches on the data input flow straight through to the output.
- Timing analysis gets harder. Latch-based timing (time borrowing) is a legitimate expert technique, but not one you want by accident, and FPGA tools map latches inefficiently.
Real designs do use latches deliberately (some clock-gating cells, some low-power techniques). The problem isn’t the latch, it’s the accident.
The three fixes
1. Add the missing else (or assign in every branch):
Interactive schematic by RapidRTL
The storage is gone. What’s left is a plain AND of enable and req,
which is what this code meant all along.
2. Assign a default at the top of the block. Scales better than else
chains when there are many outputs:
always @(*) begin
grant_out = 1'b0; // default: every path now assigns
if (enable)
grant_out = req;
end
3. In SystemVerilog, say what you mean with always_comb. The same
missing-else under always_comb is a hard error rather than a silent
latch: the tool refuses to build storage in a block you declared
combinational. That’s the strongest fix, because the bug becomes impossible
to miss. Hit that error and want the details? See
the always_comb latch error, explained.
The case variant
The same rule bites case statements without a default:
always @(*) begin
case (sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
// sel == 2'b11: y keeps its old value -> latch
endcase
end
Add a default: arm (or a top-of-block default assignment) for the same
reason as before: every path must assign every output. More on case and
its wildcard relatives casez and casex in
the case statement family guide.
How to spot them
- In the schematic: look for
$dlatchcells (or vendor latch primitives likeLDCE) in what should be a purely combinational cloud. The live schematics above make this easy to see. - In synthesis logs: Yosys prints latch warnings; RapidRTL surfaces them as editor warnings on the offending signal.
- By construction: prefer
always_combin SystemVerilog, and the missing-assignment class of latch simply can’t slip through.