Skip to content

Verilog · Chapter 14.3 · Behavioural Modeling

Blocking vs Non-Blocking Assignments in Verilog — = vs <= (The Golden Rule)

This is the most important and most misunderstood rule in Verilog, the number one interview topic and the number one source of RTL bugs. Inside a procedural block an assignment is either blocking or non-blocking, and the two model fundamentally different behaviour. A blocking assignment executes immediately and in order, like a software statement, which is what combinational logic needs. A non-blocking assignment evaluates its right-hand side immediately but defers the update to the end of the time-step, so every block reads the old values first and all updates apply together, exactly how real flip-flops behave on a clock edge. From this comes the golden rule every RTL engineer lives by: blocking for combinational logic, non-blocking for sequential logic. This page proves it with the canonical shift-register example, explains the scheduling regions, and catalogs the races that follow from getting it wrong.

Foundation22 min readVerilogBlockingNon-BlockingSequential LogicShift RegisterRTL Design

Chapter 14 · Section 14.3 · Behavioural Modeling

1. The Engineering Problem

An engineer builds a 3-stage shift register — d shifts through q1, q2, q3, one stage per clock — using blocking assignments:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
always @(posedge clk) begin
    q1 = d;          // blocking
    q2 = q1;         // blocking
    q3 = q2;         // blocking
end

It is not a shift register. On each clock edge, the statements execute in order, immediately: q1 becomes d, then q2 reads the new q1 (which is d), then q3 reads the new q2 (also d). So q1 = q2 = q3 = d — all three stages get d in a single cycle. The shift register collapsed into one flop's worth of behaviour.

The fix is one character per line — non-blocking assignment:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
always @(posedge clk) begin
    q1 <= d;         // non-blocking
    q2 <= q1;        // reads OLD q1
    q3 <= q2;        // reads OLD q2
end                  // all three update TOGETHER → a real 3-stage shift register  ✓

With <=, all three right-hand sides are read first (using the old values), then all three updates apply together — so q2 captures the previous q1, q3 the previous q2. That is exactly how three flip-flops behave on a clock edge: each captures its neighbour's old value. The shift register works.

Blocking (=) executes immediately and in order; non-blocking (<=) reads all right-hand sides first and updates all left-hand sides together. The golden rule: = for combinational logic, <= for sequential logic.

This page proves the rule, explains why, and shows the failures from breaking it. It is the most important page in the track.

2. Mental Model — = Is Immediate and Ordered; <= Is Deferred and Parallel

Visual A — blocking vs non-blocking within a block

Blocking versus non-blocking assignment behaviourblocking =update now, in ordernext stmt sees NEWvalue→ combinationalnon-blocking <=read all old, update alltogetherall see OLD values→ sequential (registers)12
Inside a block, blocking (=) updates each left-hand side immediately, so the next statement sees the new value (ordered, like software) — correct for combinational. Non-blocking (<=) reads every right-hand side first using the old values, then applies all updates together at the end of the time-step (parallel) — correct for sequential, because real flip-flops all sample their old inputs on the edge and update simultaneously.

3. Blocking Assignment (=)

A blocking assignment evaluates its right-hand side and updates its left-hand side immediately, before the next statement executes:

blocking.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   always @(*) begin
       temp = a & b;        // temp updated NOW
       y    = temp | c;     // uses the NEW temp → correct combinational chain
   end

The statements run in order, each seeing the results of the previous one — exactly like software, and exactly what combinational logic needs: you compute temp, then immediately use the updated temp to compute y. Blocking assignments are evaluated in the Active region of the time-step (Chapter 8.1), so their effects are visible immediately within the block. Use = for combinational logic in always @(*).

4. Non-Blocking Assignment (<=)

A non-blocking assignment evaluates its right-hand side immediately but defers the left-hand side update to the end of the time-step:

non-blocking.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   always @(posedge clk) begin
       q1 <= d;             // RHS (d) read now; q1 update DEFERRED
       q2 <= q1;            // RHS reads OLD q1 (its update hasn't applied)
   end                      // both updates apply TOGETHER at end of step

Within the block, every right-hand side is evaluated using the current (old) values, and the updates are all applied together at the end of the time-step. So q2 reads the old q1, not the value just assigned to it — which is precisely how two flip-flops behave: both sample their inputs on the clock edge (the old values) and update simultaneously. The right-hand side is read in the Active region, but the left-hand side update happens in the NBA (non-blocking assignment) region, which runs after all Active-region events (Chapter 8.1). Use <= for sequential logic in always @(posedge clk).

5. The Shift Register — The Canonical Proof

The §1 example, both ways, side by side — the definitive demonstration:

shift-register.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // NON-BLOCKING (<=) — CORRECT 3-stage shift register:
   always @(posedge clk) begin
       q1 <= d;     // q1 ← d
       q2 <= q1;    // q2 ← OLD q1
       q3 <= q2;    // q3 ← OLD q2
   end
   // Each stage captures its neighbour's PREVIOUS value → proper shift.
 
   // BLOCKING (=) — WRONG, collapses to one flop:
   always @(posedge clk) begin
       q1 = d;      // q1 = d NOW
       q2 = q1;     // q2 = NEW q1 = d
       q3 = q2;     // q3 = NEW q2 = d
   end
   // All three become d in one cycle → NOT a shift register.

With non-blocking, the three stages read old values and update together, so d advances one stage per clock — a real shift register. With blocking, each statement sees the just-updated value, so d races all the way through in a single cycle, collapsing three stages into one. This single example is why the rule exists: sequential logic must use non-blocking so registers update together, not in a chain.

Visual B — the shift register, both ways

Shift register: non-blocking works, blocking collapses

data flow
Shift register: non-blocking works, blocking collapses<= : q1←d, q2←oldq1, q3←old q2reads old, updates together3-stage shiftregister ✓d advances one stage/clock= : q1=d, q2=newq1, q3=new q2each sees the new valueone flop ✗d races through in one cycle
Non-blocking assignments read the old values and update together, so each stage captures its neighbour's previous value — a correct 3-stage shift register. Blocking assignments update immediately, so each statement sees the just-updated value and d propagates through all three stages in a single cycle — the shift register collapses to one flop. Sequential logic requires non-blocking.

6. The Scheduling Regions — Why <= Models Registers

The reason non-blocking models registers is the simulator's scheduling regions within one time-step (the Active-vs-NBA distinction from Chapter 8.1):

  • Active region — blocking assignments execute (update immediately), and non-blocking right-hand sides are evaluated (reading current values).
  • NBA region — non-blocking left-hand side updates apply, after all Active-region activity.

So when several <= assignments fire on a clock edge, they all read their right-hand sides in the Active region (old values), and all their updates land in the NBA region (together). This two-phase "read all, then update all" is exactly the behaviour of a bank of flip-flops sampling on the edge — which is why non-blocking correctly models sequential logic, and why blocking (immediate update) does not. (This is also why $display shows the old value and $strobe the new — Chapter 8.1.)

This page teaches the rule; the simulator mechanism behind it — the stratified event queue and why the NBA region applies last — is drilled in Chapter 19.2 The NBA Region. Together they close the loop: 19.2 explains why <= updates last, and the rule here is its practical form.

Visual C — Active and NBA regions

One clock edge: Active then NBA

data flow
One clock edge: Active then NBAActive region= executes; <= RHS read (old values)NBA region<= LHS updates apply (together)registers updatedall read old, all wrote new
Within one clock edge, non-blocking right-hand sides are read in the Active region (old values) and the left-hand-side updates apply in the NBA region afterward. This 'read all, then update all' is exactly how a bank of flip-flops samples on the edge and updates together — which is why non-blocking models sequential logic correctly.

7. The Golden Rules

The complete discipline, from decades of RTL practice:

  1. Use = (blocking) for combinational logic in always @(*). Ordered evaluation is what combinational chains need.
  2. Use <= (non-blocking) for sequential logic in always @(posedge clk). Deferred, together-update is what registers need.
  3. Do not mix = and <= in the same always block. Pick the one that matches the block's type; mixing causes confusion and bugs.
  4. Do not assign the same variable from more than one always block. Two blocks writing one signal is a multi-driver race (§7 below).

These rules are not negotiable in production RTL — they are the difference between code that works and code that races. Following them, your combinational logic evaluates correctly and your registers update like real flip-flops.

8. Races From Breaking the Rules

Violating the rules produces race conditions — non-deterministic behaviour that depends on simulation ordering:

  • Blocking in sequential logic collapses shift chains (§5) and can make one register see another's new value within the same edge, creating order-dependent results.
  • The same signal assigned from two always blocks is a race: which block "wins" depends on simulation order, and different simulators (or runs) can disagree.
  • Reading a blocking-assigned signal in another block at the same time races between the write and the read.

Non-blocking assignments are race-free for sequential logic precisely because the read (Active) and write (NBA) are separated — every reader sees the old value, so the result is deterministic regardless of block ordering. This race-freedom is a second reason the golden rule mandates non-blocking for sequential logic. Why violating the rule makes a result simulator-dependent — and the full taxonomy of races — is drilled in Chapter 19.4 Race Conditions & Determinism.

9. Worked Examples

9.1 Example 1 — a register and a shift register (<=)

seq-nonblocking.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // simple register
   always @(posedge clk) q <= d;
 
   // 3-stage shift register — non-blocking, updates together
   always @(posedge clk) begin
       s1 <= in;
       s2 <= s1;
       s3 <= s2;
   end

Non-blocking for all sequential logic: the register captures d on the edge, the shift register advances one stage per clock. This is the everyday sequential pattern.

9.2 Example 2 — a combinational chain (=)

comb-blocking.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // combinational: compute an intermediate, then use it
   always @(*) begin
       sum   = a + b;           // blocking — sum updated now
       carry = sum[8];          // uses the NEW sum → correct
       y     = carry ? 8'hFF : sum[7:0];
   end

Blocking for combinational logic: each statement uses the just-computed value of the previous one, so the chain (sumcarryy) evaluates correctly in order. Non-blocking here would read stale values and need extra simulation steps to settle — = is correct for combinational.

9.3 Example 3 — the rule applied across a module

rule-applied.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // combinational next-state logic: blocking
   always @(*) begin
       next_count = count + 1;
       wrap       = (next_count == LIMIT);
   end
 
   // sequential state register: non-blocking
   always @(posedge clk or negedge rst_n)
       if (!rst_n) count <= 0;
       else        count <= next_count;

The canonical two-block structure: a combinational always @(*) (blocking =) computes the next value, and a sequential always @(posedge clk) (non-blocking <=) registers it. This separation — combinational logic with =, the register with <= — is the backbone of clean RTL and FSM design.

10. Industry Perspective

  • The golden rule is universal. "= for combinational, <= for sequential" is taught to every RTL engineer and enforced by every team — it is the single most-stated rule in digital design.
  • Lint enforces it. Tools flag blocking assignments in clocked blocks, non-blocking in combinational blocks, mixed assignments in one block, and multi-block writes to one signal — the exact violations this page covers.
  • It is the top interview question. The shift-register demonstration, the Active/NBA explanation, and "why <= for sequential" are asked in virtually every RTL interview, because they test whether a candidate truly understands simulation semantics.
  • Breaking it causes the worst bugs. Race conditions and sim/synth mismatches from wrong assignment style are subtle, intermittent, and hard to find — which is why the rule is drilled so hard.

11. Common Mistakes

  1. Blocking (=) in a clocked block — collapses shift registers and creates order-dependent results; use <= (§5, DebugLab 1).
  2. Non-blocking (<=) in a combinational block — reads stale intermediates, needs extra deltas; use = (§9, DebugLab 2).
  3. Mixing = and <= in one block — confusing and bug-prone; pick one per block (§7, DebugLab 3).
  4. Assigning one signal from two always blocks — a race; one driver per signal (§8).
  5. Believing it's a style choice — it determines correctness, not aesthetics (§7).

12. Debugging Lab

Three blocking/non-blocking debug post-mortems

13. Interview Q&A

14. Exercises

Exercise 1 — Predict the shift register

For always @(posedge clk) begin q1 ? in; q2 ? q1; end with the assignment operator filled in, give the behaviour for (a) blocking =; (b) non-blocking <=.

Exercise 2 — Pick the operator

For each, choose = or <=: (a) a combinational adder in always @(*); (b) a register in always @(posedge clk); (c) an intermediate value computed then used in a combinational block; (d) a counter.

Exercise 3 — Fix the assignments

Each has the wrong operator. Identify and fix it.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
always @(posedge clk) begin a = b; c = a; end       // shift register intent
always @(*) begin t <= x & y; z <= t | w; end        // combinational chain

Exercise 4 — Explain the regions

(a) In which region is a non-blocking right-hand side read? (b) In which region does its left-hand-side update apply? (c) Why does this make q2 <= q1 read the old q1?

15. Summary

Blocking (=) versus non-blocking (<=) is the most important rule in Verilog:

  • Blocking = — immediate, ordered; each statement sees the previous one's result. For combinational logic (always @(*)).
  • Non-blocking <= — deferred, parallel; all right-hand sides read old values, all updates apply together. For sequential logic (always @(posedge clk)).
  • The scheduling regions — non-blocking right-hand sides read in the Active region (old values), left-hand sides update in the NBA region (together) — make <= model flip-flops correctly.
  • The shift-register proof<= gives a correct 3-stage shift; = collapses it to one flop.

The golden rules:

  • = for combinational, <= for sequential — not a style choice, a correctness rule.
  • Don't mix = and <= in one block.
  • One signal, one always driver — multiple writers race.

The discipline this page instils: combinational logic uses blocking =; clocked logic uses non-blocking <= — and your shift registers shift, your registers update like real flip-flops, and your RTL is race-free.

You now hold the single most important rule in behavioural modeling. The next page covers the timing controls inside procedural blocks: Chapter 14.4 Timing Controls drills delay (#), event (@), and level (wait) controls — how procedural statements are timed and synchronized.