Conditions, case, and unintended latches
Write complete choices, make priorities visible, and avoid storage created by omission.
What an if statement describes
Inside a combinational block, an if followed by else if branches describes a priority. The first true condition selects the result.
module request_priority (
input wire [2:0] i_request,
output reg [2:0] o_grant,
output reg o_valid
);
always @* begin
o_grant = 3'b000;
o_valid = 1'b1;
if (i_request[2])
o_grant = 3'b100;
else if (i_request[1])
o_grant = 3'b010;
else if (i_request[0])
o_grant = 3'b001;
else
o_valid = 1'b0;
end
endmoduleRequest 2 has priority over the other two. The default values also cover the case where no request is active.
Using case for a value-based choice
case is useful when several values of the same expression select a result.
module mux4_byte (
input wire [1:0] i_select,
input wire [7:0] i_a,
input wire [7:0] i_b,
input wire [7:0] i_c,
input wire [7:0] i_d,
output reg [7:0] o_data
);
always @* begin
o_data = 8'h00;
case (i_select)
2'b00: o_data = i_a;
2'b01: o_data = i_b;
2'b10: o_data
All four binary values are listed, and a default branch handles unknown values seen during simulation. The initial assignment to o_data also makes the combinational intent clear.
How a latch appears
This block is incomplete:
module incomplete_gate (
input wire i_enable,
input wire [7:0] i_data,
output reg [7:0] o_data
);
always @* begin
if (i_enable)
o_data = i_data;
end
endmoduleWhen i_enable is 0, no new value is given to o_data. To preserve this behavior, the circuit must keep the previous value. Synthesis therefore infers a latch.
If the goal was to output zero while the block is disabled, write that behavior explicitly:
always @* begin
o_data = 8'h00;
if (i_enable)
o_data = i_data;
endA latch is not a language error. It becomes a problem when it appears by accident in logic that was meant to be combinational.
Default values
For a multi-line combinational block:
- assign a default value to every output;
- replace those values in the required branches;
- check every
if,else, andcasepath; - review latch warnings after lint or synthesis.
This method is often easier to read than repeating every output in every branch.
case, casez, and casex
case compares bits as they are, including x and z during simulation. casez can ignore selected bits marked as high impedance or do-not-care.
casex also treats x values as do-not-care. This can hide an unknown input and make the simulation too optimistic. For beginner RTL decoders, plain case with a default branch is safer.
Conditions inside a clocked block
Inside always @(posedge i_clk), a missing branch does not infer a latch. It simply asks the register to keep its value.
always @(posedge i_clk) begin
if (i_load)
o_data <= i_data;
endRead the context before drawing a conclusion:
- a missing branch in
always @*means combinational storage, often an unintended latch; - a missing branch in a clocked block means normal register hold behavior.
Key points
- An
if/else ifchain creates visible priority. caseis useful for selecting from values of one signal.- A combinational block must assign every output on every path.
- A missing output assignment can create a latch.
casexcan hide unknown values; use plaincaseby default.
📝 Test your knowledge - Chapter quiz