Skip to content

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?

why-does-this-work.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // 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

two-phases.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   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.
   end

The 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 flow
Non-blocking assignment — evaluate in Active, update in NBAActive: evaluateRHScapture current values, schedule updateActive drainsall reads complete firstNBA: apply LHSupdateall <= updates land together
A non-blocking assignment reads its right-hand side in the Active region and defers the left-hand side update to the NBA region. Because every read completes (Active drains) before any update is applied (NBA), all non-blocking assignments in the instant see the old values and update together.

4. Why Updates Last → the Swap

The classic demonstration is the swap:

swap.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // 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 swap

Both right-hand sides are read in Active — capturing the old b and the old abefore either update is applied in NBA. The values cannot interfere, so the swap is exact. Contrast blocking:

swap-blocking-fails.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // 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
   end

With 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:

shift-register.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   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 flow
Shift register — all sample in Active, all update in NBAActive: every flop samples its DActive: everyflop samples its…old d, old q1, old q2NBA: every flopupdates its Qtogether, from sampled values→ one-stage shift, any block order→ one-stageshift, any block…deterministic
Because all flops sample their inputs in Active and all update their outputs in NBA, each stage advances by exactly one regardless of the order the blocks are evaluated. This order-independence is determinism — and it mirrors how real flip-flops sample then update.

6. 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.

delta-cycle.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   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:

comb-nonblocking-bug.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // 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

  1. Using <= in combinational always @(*). It defers the update to NBA, so dependents see stale values for a delta and the result mismatches blocking/gate references (§7, DebugLab).
  2. Thinking <= updates immediately. Its RHS is read in Active, but the LHS update lands in NBA — one delta later (§3, §6).
  3. 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).
  4. 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.
  5. 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 <= in always @(*) 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.