← All guides

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

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