Blocking vs Non-Blocking Assignments in Verilog

A complete, practical guide to = versus <= in Verilog: what each one means, why non-blocking matches real flip-flops, the classic cascade bug, and the two rules that keep you out of trouble.

Updated 2026-07-10

Blocking (=) versus non-blocking (<=) assignment is the single most common source of confusion for people learning Verilog. The syntax difference is tiny — one extra character — but the hardware it describes can be completely different. This guide explains what each operator means, why the distinction exists, and the simple rules that keep your clocked logic correct.

The one-sentence version

Inside a clocked always @(posedge clk) block, use non-blocking (<=). Inside a combinational always @(*) block, use blocking (=). If you remember nothing else, remember that.

The rest of this article explains why, because the reason is what makes the rule stick.

What blocking assignment means

A blocking assignment (=) executes in order, like a line of software. The name is literal: each statement "blocks" the ones after it until it finishes, so a later line sees the value a earlier line just wrote.

stage1 = din;
stage2 = stage1;

Read top to bottom: stage1 becomes din, and then stage2 becomes the new stage1, which is din. So after these two lines, both stage1 and stage2 equal din. The second statement saw the result of the first.

What non-blocking assignment means

A non-blocking assignment (<=) works differently. All the right-hand sides are evaluated first, using the old values, and only then are the left-hand sides updated — together, at the end of the time step.

stage1 <= din;
stage2 <= stage1;

Here stage2 <= stage1 uses the value stage1 had before this edge, not the din being loaded into stage1 on the same edge. So din lands in stage1 now, and only reaches stage2 on the next edge. The two updates happen simultaneously rather than in sequence.

Why non-blocking matches real hardware

This is the heart of it. In a real chip, every flip-flop driven by the same clock samples its input at the same instant — the rising edge. None of them can see the new value of another flip-flop that is updating on the very same edge, because they all latch together.

Non-blocking assignment models exactly that behavior: read all the old values, then update all the outputs at once. Blocking assignment models a sequence of steps, which is how software runs but not how a bank of flip-flops behaves. That mismatch is why blocking assignment inside a clocked block produces hardware you did not intend.

The classic bug: a cascade that collapses

The cleanest way to see the difference is a two-stage cascade — two flip-flops in a chain, where data should take two clock edges to travel from input to output.

With non-blocking assignment you get the intended pipeline:

always @(posedge clk) begin
  stage1 <= din;
  stage2 <= stage1;
end

On each edge, din moves into stage1, and the previous stage1 moves into stage2. A new value of din needs two edges to reach stage2. This synthesizes to two separate flip-flops — a true two-stage pipeline.

Now the blocking version:

always @(posedge clk) begin
  stage1 = din;
  stage2 = stage1;
end

Because stage2 = stage1 runs after stage1 = din and sees the new stage1, both stages get din on the same edge. The pipeline collapses to a single flip-flop. The design still compiles and still runs — it is just wrong, and the bug only shows up as incorrect timing, not a compile error. Sequential bugs are like that: the logic looks fine, but the values arrive on the wrong cycle.

Why combinational blocks use blocking

If non-blocking is so faithful to hardware, why not use it everywhere? In a combinational always @(*) block, you often build up a result across several lines — compute an intermediate, then use it. There you want each line to see the previous line's result, which is exactly what blocking assignment gives you. Using non-blocking in combinational logic can produce simulation behavior that does not match synthesis, so the convention is the reverse of clocked logic: combinational blocks use =.

The two rules

  1. Clocked logic (always @(posedge clk)): use non-blocking <=. This models flip-flops sampling together on the edge.
  2. Combinational logic (always @(*)): use blocking =. This models a computed result built up in order.

Do not mix the two styles for the same signal inside one block. Following these two rules avoids the cascade-collapse bug, keeps simulation and synthesis in agreement, and makes clocked designs behave the way the waveform says they should.

Quick self-check

If someone hands you a clocked block full of = signs, treat it as suspicious. It may work by accident, but as soon as one flip-flop's output feeds another on the same edge, the ordering of the lines starts changing the hardware — and that is a bug waiting to surface as a one-cycle timing error.