← All guides

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

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 :=.

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.