Blocking vs nonblocking assignment
Verilog has two assignment operators, and mixing them up is probably the
single most common beginner mistake in the language. The rules themselves
fit in one line: <= in clocked blocks, = in combinational blocks.
This page is about why those rules exist, because the why is what lets you
answer follow-up questions instead of just reciting.
What the words mean
A blocking assignment (=) works like a line of software: it finishes
before the next statement runs, so later lines see the new value.
A nonblocking assignment (<=) splits the work: all the right-hand
sides in the block are read first, then all the updates land together at
the end of the time step. No statement sees another’s new value within the
same clock edge.
That second behaviour sounds exotic until you notice it’s exactly what real flip-flops do. At a clock edge, every register in the design samples its input simultaneously, based on the values that existed before the edge. Nonblocking assignment is that physics, written down.
Watch the hardware change
This is a two-stage shift register: d enters stage1, and stage1’s
old value moves to stage2.
always @(posedge clk) begin
stage1 <= d; // swap both <= for = and press Run:
stage2 <= stage1; // watch the shift register change shape
end
Do what the comment says. With =, the first line finishes before the
second reads stage1, so stage2 gets d too, and the chain of two
flip-flops becomes two parallel flip-flops loading the same value. One
character, a different circuit:
Interactive schematic by RapidRTL
Notice what else changed: with =, the circuit now depends on the order
of the two lines. Swap them and you get the shift register back. With <=,
order doesn’t matter at all; you can shuffle the lines freely and the
hardware is identical. In a block describing registers, order-dependence is
pure fragility, and nonblocking assignment is how you opt out of it.
The swap test
The cleanest demonstration that nonblocking reads happen “all at once”: exchanging two registers in one clock edge, with no temporary.
Interactive schematic by RapidRTL
a <= b; b <= a; genuinely swaps, because both right-hand sides are read
before either update lands. Try writing that with blocking assignments and
you’ll find you can’t, not without a temporary, which is precisely how
software behaves. It’s a nice interview litmus test for whether someone has
internalised the difference.
So why use = at all?
Because combinational logic really is sequential evaluation. In an
always @(*) or always_comb block you often build a result in steps:
always @(*) begin
sum = a + b; // intermediate value, used on the next line
result = sum >> 1;
end
Here you want the second line to see the first line’s result; that’s what
a chain of gates does. Use <= in a block like this and each read gets the
signal’s stale value from the previous evaluation, so simulation needs
extra passes to settle and can disagree with the synthesized logic along
the way. Intermediates that are computed and consumed in the same block
need =.
Why simulation is the real victim
Here’s the honest part: synthesis is often forgiving. Yosys and friends
compute the net effect of a blocking-assignment clocked block and usually
build something sensible. The damage happens in simulation, where blocking
assignments between parallel always blocks turn into races: whichever
block the simulator happens to run first wins, and the same code can behave
differently on different tools or seeds. Nonblocking assignment makes every
clocked handoff deterministic, which is why the
two-flop synchronizer
is written with <=: with blocking assignments, sync = meta = async_in
would collapse both stages into one and quietly delete the safety margin
the circuit exists to provide.
The rules, with their reasons
<=in clocked blocks (always @(posedge clk),always_ff): models simultaneous sampling, kills order-dependence and cross-block races.=in combinational blocks (always @(*),always_comb): intermediates must be visible to the next line, like gates feeding gates.- Never mix the two in one block. Even where it’s legal, a reader now has to simulate the scheduler in their head to know what the block does.
- Never assign the same signal from two blocks. Not an operator rule, but it’s how the race you just avoided sneaks back in.
The VHDL aside
VHDL splits the same idea across two kinds of object instead of two
operators: signal assignment (<=) behaves like nonblocking, and
variable assignment (:=, inside a process) behaves like blocking.
A clocked VHDL process using signals gets shift-register behaviour for
free, which is why the naive VHDL translation of this page’s first example
has no trap in it. The trap moves: it’s choosing variable where you
meant signal, and it has
its own page, including the same
shift register losing a flip-flop to one :=.
Related guides
A clocked block with these rules right can still hide the other classic:
a combinational block that doesn’t assign on every path. That’s the
inferred latch guide,
and always_comb’s checked version of it is
explained here.