Blocking vs nonblocking assignments in Verilog
When to use = and when to use <= in Verilog: what each assignment does, why mixing them breaks a shift register, and the rules that prevent simulation races.
On this page
Key takeaways
- A blocking assignment (
=) updates its target before the next statement runs; a nonblocking assignment (<=) updates it at the end of the time step. - Use
<=in clocked blocks and=in combinational blocks, and never mix the two in one block. - Blocking assignments in a clocked block make the result depend on statement order, and can collapse a shift register into a single stage.
Verilog has two ways to assign a value inside an always block, and they differ in exactly one thing: when the target changes. Get that one thing right and most simulation races, along with the simulation and synthesis mismatches they cause, never happen. This guide shows what each assignment does, the bug that mixing them produces, and the rules that prevent it.
What each assignment does
Blocking = |
Nonblocking <= |
|
|---|---|---|
| Target updates | Immediately | At the end of the time step |
| Statement order | Changes the result | Does not change the result |
| Use in | always @(*), always_comb |
always @(posedge clk), always_ff |
| Models | Combinational logic | Registers |
A two stage shift register
The clearest way to see the difference is a register chain that should delay a by two clocks. Here it is in all three languages:
module shift2 (
input wire clk,
input wire a,
output reg c
);
reg b;
always @(posedge clk) begin
b <= a; // stage 1
c <= b; // stage 2 reads the old b
end
endmodule
module shift2 (input logic clk, a, output logic c);
logic b;
always_ff @(posedge clk) begin
b <= a;
c <= b;
end
endmodule
library ieee;
use ieee.std_logic_1164.all;
entity shift2 is
port (clk, a : in std_logic; c : out std_logic);
end entity;
architecture rtl of shift2 is
signal b : std_logic;
begin
process (clk)
begin
if rising_edge(clk) then
b <= a; -- a signal updates after the process suspends,
c <= b; -- so this reads the old b: a second stage
end if;
end process;
end architecture;
On every rising edge both right hand sides are read first, so c takes the value b held before the edge. Two flops, two cycles of delay:
Timing diagram
WaveJSON source
{ "signal": [
{ "name": "clk", "wave": "p......" },
{ "name": "a", "wave": "01.0..." },
{ "name": "b", "wave": "x01.0.." },
{ "name": "c, nonblocking", "wave": "x.01.0." },
{ "name": "c, blocking", "wave": "x01.0.." }
]}What blocking does to the same code
Write the two stages with = and the first line has already changed b by the time the second line reads it:
always @(posedge clk) begin
- b = a;
- c = b;
+ b <= a;
+ c <= b;
end
With =, c gets the new b, which is a. Synthesis builds two flops that both load a, and the second stage is gone. Swap the two blocking lines and the shift register comes back, which is the real problem: the meaning of the block now depends on the order of its statements.
Races between always blocks
Blocking assignments cause a second, worse problem when the stages live in separate blocks:
always @(posedge clk) b = a;
always @(posedge clk) c = b;
Both blocks wake on the same edge, and the language does not say which runs first. If the first block runs first, c gets the new b; if the second runs first, it gets the old one. Two simulators can disagree, and synthesis always builds the shift register. With <= in both blocks the order stops mattering, because every right hand side is read before any register changes.
Combinational logic: use blocking
Inside a combinational block the rule flips. An intermediate value should be visible to the next statement at once:
always @(*) begin
tmp = a & b;
y = tmp | c;
end
Written with <=, y is first computed from the old tmp. With @(*) the block runs again when tmp changes and settles, at the cost of an extra pass. With a hand written sensitivity list such as @(a or b or c), nothing wakes the block again: y stays stale in simulation while the synthesized gates do not.
The rules
These are the core of the guidelines in Clifford Cummings' paper Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill! (SNUG 2000), and they are still the standard:
- Use
<=in clockedalwaysblocks, and for latches. - Use
=in combinationalalwaysblocks. - When one block holds both sequential and combinational logic, use
<=. - Do not mix
=and<=in the same block. - Do not assign the same variable from more than one block.
- Print nonblocking results with
$strobe, not$display.
Interview question: swap two registers
Show the solution
The first swaps a and b on every edge: both right hand sides are read before either register changes, so no temporary is needed. The second copies b into a, then copies that new a back into b, so both end up holding the old b.
VHDL has the same split
A VHDL signal assignment (<=) inside a process takes effect only after the process suspends, which is how a nonblocking assignment behaves. A variable assignment (:=) takes effect at once, like a blocking one. The VHDL tab in the shift register above works for the same reason the Verilog one does.
Questions
Can I use nonblocking assignments in combinational logic?
It simulates, but each intermediate value costs another pass through the block, and with an incomplete sensitivity list the simulation stops matching the hardware. Use = in always @(*) and always_comb.
Why does $display show the old value after a nonblocking assignment?
$display runs before the end of the time step, when the update has not happened yet. $strobe and $monitor print at the end of the time step and show the new value.
Does the assignment type change the hardware size or speed?
No. It changes what the code means, not how a given meaning is built. The same behaviour written either way synthesizes to the same logic; the risk is writing a behaviour you did not intend.