Verilog · Chapter 19.2 · Timing Regions
The NBA Region in Verilog — Why Non-Blocking Updates Last
This is the mechanical heart of the chapter and the precise reason non-blocking assignments behave the way they do. A non-blocking assignment is processed in two phases across two regions. Its right-hand side is evaluated in the Active region, but its left-hand side update is deferred to the NBA region, which runs after all Active work has drained. That single deferral is what non-blocking updates last really means, and it is why a swap, a shift register, and every clocked design behave correctly no matter what order the simulator happens to run the blocks in, because every right-hand side is read before any left-hand side is written. It is also why using non-blocking assignments in combinational logic is a mistake, since the same deferral injects a delta-cycle delay that makes dependent logic see stale values. This page drills the two phases, the determinism they give, and what a delta cycle is.
Advanced13 min readVerilogEvent RegionsNBANon-BlockingDelta Cycle
Chapter 19 · Section 19.2 · Timing Regions
1. The Engineering Problem
You were told to use <= for sequential logic, and it works — a shift register shifts exactly one stage per clock, no matter how the blocks are arranged. But why? And why does the same <=, used in combinational logic, quietly cause trouble?
// A shift register — three flops. Written with non-blocking:
always @(posedge clk) q1 <= d;
always @(posedge clk) q2 <= q1;
always @(posedge clk) q3 <= q2;
// Each cycle: q1←d, q2←(old q1), q3←(old q2). A clean one-stage shift —
// and it works in ANY block order. Why is it order-independent?
//
// Yet the SAME operator in combinational logic misbehaves:
// always @(*) y <= a & b; // <- injects a delta delay; dependents see
// // a stale y. Combinational must use '='.The answer to both questions is one region: NBA. A non-blocking assignment does not update when it runs — it schedules its update for the NBA region, which the simulator processes only after the Active region is completely drained. Understanding that deferral explains the shift register, the swap, and the combinational pitfall all at once.
A non-blocking assignment evaluates its right-hand side in the Active region but applies its left-hand side update in the NBA region, which runs after all Active work. So every
<=reads before any<=writes — which makes clocked logic order-independent (deterministic), and makes<=in combinational logic a delta-cycle-delayed mistake.
2. Mental Model — Read in Active, Write in NBA: Sample-Then-Update
3. The Two Phases of a Non-Blocking Assignment
always @(posedge clk) begin
q <= d;
// Phase 1 (ACTIVE): read d's current value, schedule "q ← that value".
// Phase 2 (NBA): apply the update — q becomes the captured value.
endThe right-hand side is frozen at evaluation time (in Active) and carried to the NBA region, where it is written. Nothing between the read and the write — other Active events, other blocks — can change what gets written, because the value was already captured. This is the core mechanism; everything else in this page is a consequence of it.
Visual A — the two phases
Non-blocking assignment — evaluate in Active, update in NBA
data flow4. Why Updates Last → the Swap
The classic demonstration is the swap:
// Non-blocking — WORKS:
always @(posedge clk) begin
a <= b; // Active: read OLD b, schedule a←b
b <= a; // Active: read OLD a, schedule b←a
end // NBA: a←old b, b←old a → clean swapBoth right-hand sides are read in Active — capturing the old b and the old a — before either update is applied in NBA. The values cannot interfere, so the swap is exact. Contrast blocking:
// Blocking — FAILS:
always @(posedge clk) begin
a = b; // a immediately becomes b (Active)
b = a; // b becomes the NEW a (= b) — both end up b
endWith blocking, a = b updates a immediately, so b = a reads the new a. "Updates last" — the NBA deferral — is what makes the swap a swap.
5. Why It Models a Register — and Gives Determinism
The shift register is the swap's payoff at scale:
always @(posedge clk) q1 <= d; // read old d
always @(posedge clk) q2 <= q1; // read old q1
always @(posedge clk) q3 <= q2; // read old q2
// NBA: q1←old d, q2←old q1, q3←old q2. Each stage advances by ONE.Every block reads its source in Active (the old value), and all updates land together in NBA. So q2 gets q1's value as it was at the clock edge, not as it becomes this cycle — a true one-stage shift. Crucially, this holds no matter what order the simulator runs the three blocks in, because the reads all happen before the writes. That order-independence is determinism: the result is the same on every simulator. It is also exactly how three real flip-flops behave — each samples its D at the edge and updates its Q together — which is why <= is the correct model for sequential logic.
Visual B — sample-then-update gives an order-independent shift
Shift register — all sample in Active, all update in NBA
data flow6. Delta Cycles
The NBA region is not necessarily the end of the instant. When NBA updates apply, they can wake new events — a combinational always @(*) sensitive to an updated signal, for instance — which are scheduled back into a fresh Active region at the same simulation time. The simulator loops: Active → NBA → (new Active) → NBA → … until nothing new is scheduled. Each loop is a delta cycle — a step of zero simulation time.
always @(posedge clk) q <= d; // NBA updates q...
always @(*) y = q; // ...which wakes this block in a NEW
// delta cycle (same time), updating y.The takeaway: a non-blocking assignment makes its value visible one delta cycle later than a blocking one — same simulation time, but a scheduling step apart. This "delta delay" is harmless (and correct) for sequential logic, but it is the root of the combinational problem in §7.
7. The Flip Side — Why <= Is Wrong in Combinational Logic
The deferral that makes sequential logic deterministic makes combinational logic stale. A <= in an always @(*) defers its update to NBA, so any logic that reads the result in the same instant sees the old value until a later delta cycle:
// Combinational, written WRONG with non-blocking:
always @(*) partial <= a + b; // update deferred to NBA
always @(*) total <= partial + c; // reads OLD partial (NBA hasn't run)
// At the instant a/b/c change: 'total' is computed from the STALE partial.
// It only corrects on a LATER delta cycle, after partial's NBA update
// re-triggers the second block. Transiently wrong; extra delta cycles;
// mismatches the blocking (and gate-level) version.With blocking =, partial updates immediately in Active, the dependent block (re-triggered) reads the new partial at once, and the result settles in one evaluation — matching real combinational logic, which has no delta delay. This is the mechanism behind the universal rule:
Blocking
=for combinational logic; non-blocking<=for sequential logic. Combinational logic must update now (Active); sequential logic must update last (NBA). The NBA region is why each choice is correct.
8. Common Mistakes
- Using
<=in combinationalalways @(*). It defers the update to NBA, so dependents see stale values for a delta and the result mismatches blocking/gate references (§7, DebugLab). - Thinking
<=updates immediately. Its RHS is read in Active, but the LHS update lands in NBA — one delta later (§3, §6). - Assuming non-blocking fixes every race. It removes read-before-write ordering across blocks, but two
<=to the same variable from different blocks is still a multiple-driver race (§5 caveat). - Mixing
=and<=in one block. It blends Active and NBA semantics on related signals and produces hard-to-reason results — keep a block all-blocking or all-non-blocking. - Forgetting the RHS is frozen at evaluation. Nothing after the Active read can change what NBA writes (§3).
9. Debugging Lab
The combinational logic that was a delta cycle behind
10. Interview Q&A
11. Exercises
Exercise 1 — Trace the swap
Walk a <= b; b <= a; through Active and NBA with starting a = 1, b = 2. State the values after NBA, and contrast with the blocking version.
Exercise 2 — Old or new?
In always @(posedge clk) begin q2 <= q1; q1 <= d; end, does q2 get the old or new q1? Explain via Active/NBA, and note whether the statement order matters.
Exercise 3 — Spot the delta lag
Two always @(*) blocks use <= in a combinational chain. Describe what a same-instant reader of the final output sees, and why blocking fixes it.
Exercise 4 — Why a register?
Explain how the Active/NBA split mirrors a real flip-flop's behaviour at a clock edge.
12. Summary
The NBA region is why non-blocking assignments update last:
- Two phases — a
<=evaluates its RHS in Active and applies its LHS update in NBA, which runs after all Active work. - Reads before writes — every
<=reads the old value before any update lands, so the swap and shift register work and are order-independent (deterministic) — exactly like real flip-flops (sample then update). - Delta cycles — an NBA update can wake new Active events at the same time;
<=makes its value visible one delta later. - Combinational must be blocking — that same delta deferral makes
<=inalways @(*)read stale and mismatch references; combinational logic must update now (=), sequential last (<=).
The crux to keep: <= reads in Active, writes in NBA — update-last is determinism for registers and a delta-delay bug for combinational logic.
The next sub-topic covers the region after NBA, where settled values are read: Chapter 19.3 The Postponed Region — $strobe & $monitor — race-free sampling of post-update values.
Related Tutorials
- The Active Region & Evaluation Order — Chapter 19.1; where the
<=RHS is read and races are born. - Blocking and Non-Blocking Assignments — Chapter 14.3; the rule this page mechanically explains.
- The Postponed Region — $strobe & $monitor — Chapter 19.3; the region after NBA, for sampling settled values.
- Race Conditions & Simulation Determinism — Chapter 19.4; what happens when the NBA-region discipline is broken — the next link in the chain.
- Timing Regions — Chapter 19 overview; the full stratified event queue.