The interviewer shows an always_comb block with an if without an else and a case without a default, then asks whether it infers a latch and how to fix it.
The question tests complete assignment. Every combinational output must be assigned on every path, or synthesis holds the old value in a latch.
A latch is inferred whenever a combinational output keeps its previous value on some path. The fix is defaults first: assign every output at the top of the block, then override.
A default branch alone is not enough. It covers the case but not an earlier incomplete if, while defaults cover both.
Shape | Result |
|---|---|
| Latch on the skipped path |
| Latch on the missing values |
Defaults first, then | Clean combinational logic |
Single-condition cone |
|
Read each panel as the logic synthesis must build for grant_b. The only difference is what drives the mux's 0 input.
Left: nothing is assigned for sel 0 or 3, so the 0 input is the output itself. A loop with no clock is a latch.
Right: the default gives the 0 input a value, so the loop disappears.
How is it checked? A synthesis log latch report plus lint, and a testbench that sweeps every select value so a held value fails.
Defaults first or a `default` branch? Defaults first. A default branch covers only the case, while defaults also cover incomplete if statements above it.
What about an intentional latch? It is coded explicitly as a storage element, never inferred by omission. Accidental inference is always a bug.
“It simulates correctly, so synthesis is fine” misses the point. Simulation shows the held value working, while timing closure and scan shift break on silicon.
“A default branch fixes everything” is incomplete. It leaves earlier partial if statements uncovered.
// bad-example
module grant_bad (
input logic req_a, req_b,
input logic [1:0] sel,
output logic grant_a, grant_b
);
always_comb begin
case (sel)
2'd0: grant_a = req_a;
2'd1: grant_b = req_b;
2'd2: begin grant_a = req_a; grant_b = req_b; end
endcase
end
endmodulecase above gains a default branch that clears both grants. Is the block latch-free now?No. The existing select-0 branch still leaves grant_b unassigned and select-1 still leaves grant_a unassigned. Assign both defaults before the case, then override the selected grants, or assign both outputs in every branch.
The latch-free grant block with defaults, plus a testbench that sweeps all 4 selects against all 4 request combinations.
module grant_logic (
input logic req_a, req_b,
input logic [1:0] sel,
output logic grant_a, grant_b,
output logic [7:0] y
);
always_comb begin
grant_a = 1'b0;
grant_b = 1'b0;
y = 8'h00;
case (sel)
2'd0: begin grant_a = req_a; y = 8'hA1; end
2'd1: begin grant_b = req_b; y = 8'hB2; end
2'd2: begin grant_a = req_a; grant_b = req_b; y = 8'hC3; end
default: y = 8'hD4;
endcase
end
endmodule