Writing clean and reliable Verilog code is essential for designing predictable and efficient digital circuits. One common pitfall that many designers encounter is unintended latch inference. This subtle issue can cause your design to behave unexpectedly, leading to timing problems and simulation mismatches.
In this post, we’ll explain what latch inference is, why it happens, and most importantly, how to avoid latch inference by properly assigning outputs in all branches of your combinational always
blocks.
What is Latch Inference?
When you write combinational logic in Verilog using always @(*)
blocks, the synthesis tool expects every output signal to be assigned a value for every possible input condition. If some outputs are left unassigned in certain branches of your code, the tool assumes you want to remember the previous value—and it infers a level-sensitive latch to do so.
While latches can be useful in specific scenarios, unintended latch inference is usually a design bug that can cause glitches, timing issues, and mismatches between simulation and hardware.
Why Does Latch Inference Happen?
Consider this example:
always @(*) begin
if (sel) begin
out = in1;
end
// No else branch here!
end
If sel
is false, out
is not assigned any new value. To preserve the last value of out
, the synthesis tool infers a latch. This is because the output must hold its value until explicitly changed.
How to Avoid Latch Inference: Assign All Outputs in All Branches
The simplest and most effective way to prevent unintended latches is to assign every output signal in every possible branch of your combinational logic.
Corrected Example:
always @(*) begin
if (sel) begin
out = in1;
end else begin
out = in2; // Assigned in else branch to avoid latch
end
end
Now, out
is always assigned a value, no matter what sel
is, so the synthesis tool infers pure combinational logic without any latches.
Alternative Approach: Use Default Assignments
Another common practice is to assign a default value at the beginning of the always
block. This ensures outputs have a defined value even if some branches don’t explicitly assign them.
always @(*) begin
out = default_value; // Default assignment
if (sel) begin
out = in1;
end
// No else needed since out already has a default
end
This approach simplifies your code and guarantees that out
is never left unassigned.
Unintended latch inference is a common source of bugs in Verilog designs, but it’s easy to avoid with proper coding habits. Always make sure your combinational always
blocks assign values to all outputs in every possible branch. Using default assignments is a clean and effective way to keep your code robust and your hardware predictable.