What causes inferred latches in VHDL?
This guide covers VHDL. There’s a Verilog version too.
A latch gets inferred whenever a combinational process has a path through it that doesn’t assign the output. VHDL’s simulation semantics say a signal that isn’t assigned keeps its previous value, and synthesis must build hardware that behaves the same way. Keeping a value needs storage, and with no clock edge in the process, that storage is a level-sensitive latch rather than a flip-flop.
See one get inferred
This process only drives grant_out while enable is '1'. When it’s
'0', no assignment runs, so the old value must survive:
process (req, enable) is
begin
if enable = '1' then
grant_out <= req; -- no else: grant_out must keep its value
end if;
end process;
Here is that exact code, synthesized. The cell in the middle is a latch:
storage you probably didn’t mean to build. Edit the code and press Run
(try adding the else):
Interactive schematic by RapidRTL
Why it’s usually a bug
- You wanted combinational logic. The process now has state: its output depends on the history of its inputs, not just their current values.
- Latches are transparent. While
enableis high, glitches onreqpass straight through togrant_out. - FPGA flows dislike them. Most FPGA fabrics have no clean latch primitive in the LUT fabric, so tools emulate them inefficiently and timing analysis gets murkier.
Deliberate latch design exists, but it should look deliberate, not like a
forgotten else.
The fixes
1. Complete the if:
Interactive schematic by RapidRTL
The storage is gone. The netlist collapses to the AND gate this code meant all along.
2. Default assignment at the top of the process. The idiom that scales when a process drives many signals:
process (req, enable) is
begin
grant_out <= '0'; -- default: every path now assigns
if enable = '1' then
grant_out <= req;
end if;
end process;
Signal assignments in a process take the last value written before the
process suspends, so the default costs nothing when the if overrides it.
3. Cover every choice in case. The same rule bites a case whose
alternatives don’t all assign:
case sel is
when "00" => y <= a;
when "01" => y <= b;
when "10" => y <= c;
when others => y <= '0'; -- without this arm: latch
end case;
VHDL forces you to write when others for coverage of choices, but it
doesn’t force every arm to assign. An empty when others => null; still
infers the latch. The wider case family, including VHDL-2008’s don’t-care
case?, is compared in
the case statement family guide.
The sensitivity-list trap next door
A related but different classic: a complete if/else with an incomplete
sensitivity list (say, process (enable) while reading req). Synthesis
tools generally build the combinational logic you meant anyway, but your
simulation won’t match it, because the process never wakes on req
changes. VHDL-2008’s process (all) ends that class of bug; use it where
your flow supports it.
Unlike SystemVerilog, where always_comb turns a missing assignment into a
hard error, VHDL has no process kind that outlaws the latch. That makes the
warnings your tools print the main net: synthesis reports the inferred latch,
and RapidRTL surfaces it as a warning naming the signal.
How to spot them
- In the schematic: storage cells sitting in what should be a purely combinational cloud. The live examples above make them easy to see.
- In the logs: GHDL/Yosys report inferred latches by signal name.
- By idiom: top-of-process defaults, complete
if/else, assigningwhen othersarms, andprocess (all)where VHDL-2008 is available.