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:
always @(posedge clk) begin
q1 = d; // blocking
q2 = q1; // blocking
q3 = q2; // blocking
endIt 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:
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
3. Blocking Assignment (=)
A blocking assignment evaluates its right-hand side and updates its left-hand side immediately, before the next statement executes:
always @(*) begin
temp = a & b; // temp updated NOW
y = temp | c; // uses the NEW temp → correct combinational chain
endThe 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:
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 stepWithin 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:
// 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 flow6. 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 flow7. The Golden Rules
The complete discipline, from decades of RTL practice:
- Use
=(blocking) for combinational logic inalways @(*). Ordered evaluation is what combinational chains need. - Use
<=(non-blocking) for sequential logic inalways @(posedge clk). Deferred, together-update is what registers need. - Do not mix
=and<=in the samealwaysblock. Pick the one that matches the block's type; mixing causes confusion and bugs. - Do not assign the same variable from more than one
alwaysblock. 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
alwaysblocks 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 (<=)
// 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;
endNon-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 (=)
// 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];
endBlocking for combinational logic: each statement uses the just-computed value of the previous one, so the chain (sum → carry → y) 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
// 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
- Blocking (
=) in a clocked block — collapses shift registers and creates order-dependent results; use<=(§5, DebugLab 1). - Non-blocking (
<=) in a combinational block — reads stale intermediates, needs extra deltas; use=(§9, DebugLab 2). - Mixing
=and<=in one block — confusing and bug-prone; pick one per block (§7, DebugLab 3). - Assigning one signal from two
alwaysblocks — a race; one driver per signal (§8). - 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.
always @(posedge clk) begin a = b; c = a; end // shift register intent
always @(*) begin t <= x & y; z <= t | w; end // combinational chainExercise 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
alwaysdriver — 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.
Related Tutorials
- always & initial Blocks — Chapter 14.1; the combinational vs sequential blocks these assignments live in.
- The NBA Region — Why Non-Blocking Updates Last — Chapter 19.2; the simulator mechanism (Active → NBA) behind this rule.
- Race Conditions & Simulation Determinism — Chapter 19.4; why breaking this rule makes a result simulator-dependent.
- $display vs $monitor vs $strobe vs $write — Chapter 8.1; the Active/NBA scheduling regions, seen from the display family.
- reg — Chapter 5.2.1; the registers non-blocking assignments build, and "reg is not a register."
- Dataflow Advanced Techniques — Chapter 13.3; the sequential logic that requires these clocked blocks.