always @(*), if & case — and Accidental Latches
Describing combinational logic in always blocks, and the most common beginner bug: an incomplete if that creates a latch.
always @(*) describes combinational logic procedurally: the block re-runs whenever any signal it reads changes. Inside it you can use if, case and loops. The rule that keeps it combinational is simple: every output must be assigned on every path through the block.
A 4:1 multiplexer
always @(*) re-runs the block whenever sel or d changes.
Blocking assignment (=) is the right choice in combinational blocks.
default makes the case complete, so y is always assigned.
The fix: assign on every path
always @(*) case (op) 2'd0: y = a; 2'd1: y = b; endcase // op = 2, 3 not covered always @(*) case (op) 2'd0: y = a; 2'd1: y = b; default: y = 1'b0; endcase
Unlisted case values leave y unassigned, inferring a latch. A default branch makes the logic fully combinational.
always @(*) begin if (sel) begin x = a; y = b; end else x = c; // y missing end always @(*) begin x = c; y = 1'b0; // defaults if (sel) begin x = a; y = b; end end
Every output must be assigned on every path. Defaults at the top guarantee it.
Why is a latch inferred from an incomplete if in always @(*)?
On the path where the output is not assigned, the language says it keeps its previous value. Holding a value without a clock edge requires a level-sensitive storage element: a latch.
Why prefer always @(*) to listing the signals yourself?
@(*) automatically includes every signal the block reads, so the simulation always matches the combinational hardware. A hand-written list that misses a signal makes simulation behave differently from the synthesized circuit.
always @(*) if (a) y = 1'b1; else if (b) y = 1'b0; — is a latch inferred?
Yes. When a and b are both 0, y is not assigned, so it must hold its value. Add a final else (or a default assignment).