Skip to main content

Blocking vs. Nonblocking Assignment

Verilog has two assignment operators that look almost interchangeable — = and <= — but describe genuinely different hardware update behavior. Using the wrong one doesn't just produce a style warning; it produces a circuit that behaves differently from the one you intended, while often still "working" in the simplest test cases, which is exactly what makes this mistake so easy to ship.

Blocking assignment (=)​

A blocking assignment executes immediately, in program order, before the next statement in the same block runs — the same left-to-right, top-to-bottom execution a software statement would have:

always @(*) begin
w1 = a ^ b; // computed first
sum = w1 ^ cin; // uses w1's just-updated value
end

Here that's exactly the right behavior — sum genuinely depends on w1's new value, matching the full adder's actual data dependency (w1 feeds sum's XOR gate, per Gate-Level Modeling). Blocking assignment is the correct choice for combinational always blocks precisely because combinational logic has no notion of "simultaneously" — one gate's output really does feed the next gate's input in sequence, and blocking assignment mirrors that.

Nonblocking assignment (<=)​

A nonblocking assignment schedules its update to happen at the end of the current simulation time step, after every other nonblocking assignment in the same time step has had its right-hand side evaluated using the old values — effectively, "everyone reads the old values first, then everyone updates together."

always @(posedge clk) begin
q1 <= d; // both right-hand sides are evaluated
q2 <= q1; // using q1's *old* (pre-update) value
end

Worked example: why this matters — a 2-stage shift register​

Consider two D flip-flops in series, q1 feeding q2, per Digital Design's Registers & Shift Registers:

d feeds FF q1, producing q1, which feeds FF q2, producing q2; both flip-flops share clk

The intended behavior: on every clock edge, q2 takes q1's previous value, and q1 takes d's value — data shifts one stage per clock, the way a real two-flip-flop chain does, because both flip-flops sample their inputs at the exact same clock edge, simultaneously, using whatever voltage was on their input the instant before the edge.

Correct — nonblocking:

always @(posedge clk) begin
q1 <= d;
q2 <= q1;
end

Both right-hand sides (d and q1) are read using their values before this edge, then both q1 and q2 update together — exactly matching the real hardware, where both flip-flops sample simultaneously.

Wrong — blocking:

always @(posedge clk) begin
q1 = d; // q1 updates immediately...
q2 = q1; // ...so q2 reads the *new* q1, not the old one
end

Because = executes immediately, q2 ends up reading q1's brand-new value in the same clock edge — collapsing two stages of delay into one. After one clock edge, both q1 and q2 already equal d, when the correct hardware would still have q2 holding whatever q1 was before this edge. Reordering the two lines "fixes" this one specific case (mere coincidence of write order, not a real fix) but the underlying problem — blocking assignment doesn't model "these flip-flops update simultaneously" — will resurface the moment the block is restructured or a third stage is added.

This "everyone reads old values first, then everyone updates together" behavior is also what makes nonblocking assignment the safer choice whenever more than one always block might touch related signals in the same clock edge — the general term for the bug that shows up when execution order between two separate procedural blocks silently changes the result is a race condition. Nonblocking assignment sidesteps it for register updates specifically because every read of the old value happens before any write of the new one, regardless of which block runs "first" — there's no ordering to race over.

The rule of thumb​

Sequential blocks: nonblocking. Combinational blocks: blocking.
  • always @(posedge clk) ... (sequential, clocked logic) → use <= for every assignment in the block. This correctly models multiple flip-flops updating simultaneously on the same edge.
  • always @(*) ... (combinational logic) → use = for every assignment in the block. This correctly models a chain of combinational gates where one output genuinely does feed the next in sequence within the same evaluation.
  • Never mix = and <= for different signals within the same always block. Most style guides and lint tools flag this outright, because it makes the block's actual update semantics far harder to reason about, even in cases where it happens not to cause a bug.

This rule of thumb is a consequence of the deeper synthesis rules covered in Combinational vs. Sequential always Blocks — that page explains why mixing sensitivity-list style and assignment style is what actually determines whether an always block synthesizes to combinational gates, a latch, or a flip-flop.

What's next​

Section B has now covered all three ways to describe the same circuit — gates, dataflow, behavioral — plus the assignment-operator rule that keeps behavioral sequential logic correct. Section C turns to the procedural vocabulary used inside behavioral blocks: if/case statements, loops, functions and tasks, and timing controls — starting with conditionals and case statements.