if, case, and Avoiding Latches
Clean decision logic that never builds an accidental latch
Most combinational logic is a set of decisions: if this, do that. Verilog gives you if/else and case. The trick is writing them so they describe clean logic and never an accidental latch.
The default-first pattern (your safety net)
Set every output to a safe default at the very top of the block. Then override it in the branches. Because there is always a default, no branch can ever leave an output undefined - so no latch can be inferred.
always @(*) begin
y = 1'b0; // default first - this is the safety net
if (enable)
y = a & b; // override only when needed
endcase - the clean way to handle many options
When one signal selects among several outcomes, case is clearer than a stack of if/else.
// A 4-to-1 multiplexer
always @(*) begin
y = 1'b0; // default first
case (sel) // sel is 2 bits wide
2'b00: y = in0;
2'b01: y = in1;
2'b10: y = in2;
2'b11: y = in3;
default: y = 1'b0; // covers any leftover, including x
endcase
endAlways add a default branch to a case, even when you think you listed every value. It protects you when a select line is x in simulation, and it tells the synthesis tool you really meant to cover everything. Combined with the default-first assignment, your combinational logic will be latch-free every time.
| Symptom | Likely cause | Fix |
|---|---|---|
| Tool warning: "inferred latch" | An output not assigned in some path | Add default-first assignment |
| Output stuck at old value | Missing else or default | Cover every branch |
| Works in sim, breaks in chip | Latch behaving differently in real timing | Remove the latch with defaults |
Inside a combinational always @(*), stick to blocking assignment (=). Save the non-blocking arrow (<=) for clocked blocks in the next lessons. Mixing them up is the second most common beginner bug after inferred latches.