"Why did I infer a latch?" — the warning, the cause, the two-line fix

It's the most-Googled synthesis warning in FPGA design:

WARNING: [Synth 8-327] inferring latch for variable 'y_reg'

Good news: the fix is mechanical once you see why it happens, and the habit that prevents it costs one line per block. Let's take it apart.

What the tool is trying to tell you

A combinational block (always @* in Verilog, process (all) in VHDL) promises: outputs are pure functions of current inputs, no memory. The tool holds you to it. If any path through the block leaves an output unassigned, the semantics say "keep the old value" — and keeping an old value requires memory. Level-sensitive memory, in this case: a latch, transparent while the enabling condition holds.

always @* begin
    if (sel)
        y = a;        // and when sel is 0... y holds. Latch!
end

You didn't ask for storage — you just forgot the else — but the tool must build what the language semantics demand, so it builds a latch and files the warning as its protest.

Why a latch is (almost always) unwanted on an FPGA

The three classic causes

1. Incomplete if:

always @* begin
    if (sel) y = a;          // no else → latch
end

2. Incomplete case:

always @* begin
    case (state)             // 3 of 4 encodings covered → latch
        2'd0: y = a;
        2'd1: y = b;
        2'd2: y = c;
    endcase                  // no default
end

3. The subtle one — assigning only some outputs on some paths:

always @* begin
    y = a;                   // y is fine on every path...
    if (mode) z = b;         // ...but z latches when mode is 0
end

Multi-output blocks make cause 3 easy to miss in review, which is why the mechanical defense below beats vigilance.

The two-line fix (and habit)

Assign defaults at the top of the block, unconditionally:

always @* begin
    y = 1'b0;                // defaults first: every output,
    z = 1'b0;                // every path, guaranteed assigned
    y = a;
    if (mode) z = b;
end

Later assignments win (that's how blocking assignments in a combinational block work), so the logic reads naturally and no path can escape unassigned. For case, add default: even when you think you've covered everything — encodings change.

In SystemVerilog, write always_comb instead of always @*: the language then requires pure combinational behavior, upgrading the warning to an error you can't ship. In VHDL-2008, the same discipline applies to process (all) — assign every signal on every path.

And if you want the warning before you even open the tools: our online Verilog linter runs Verilator's LATCH check (among ~60 others) on pasted code, instantly. The FSM generator writes latch-free state-machine boilerplate for you — its default assignment idiom is exactly the habit above.

When a latch is legitimate

Rarely, and deliberately: some low-power ASIC techniques, time-borrowing designs, and a few vendor primitives use latches on purpose. If that's you, you already knew, and you'll instantiate the primitive explicitly rather than inferring it by accident. For everyone else: defaults first, default: always, always_comb where you can — and the warning disappears for good.