Worked examples — Blocking vs non-blocking assignments
The scenario matrix
Every worked example below fills one cell of this table. Together they cover all of them.
| # | Cell (case class) | Example that covers it |
|---|---|---|
| C1 | Sequential swap — <= vs = side by side |
Example 1 |
| C2 | Shift register / pipeline — order-independence of <= |
Example 2 |
| C3 | Combinational chain — = is required |
Example 3 |
| C4 | Degenerate input: self-assign / initial x (unknown) values |
Example 4 |
| C5 | Mixed operators on same var — the illegal/ambiguous case | Example 5 |
| C6 | Two always blocks driving related signals — race condition | Example 6 |
| C7 | Limiting case: a counter over many cycles (= vs <= divergence over time) |
Example 7 |
| C8 | Real-world word problem: traffic-light-style state + output register | Example 8 |
| C9 | Exam twist: reordered lines + a read of a just-written signal | Example 9 |
We trace numeric values everywhere so simulation outcomes are concrete, not hand-wavy.
Example 1 — The swap (cell C1)
Forecast: guess the two (a, b) pairs before reading on.
Version NB (non-blocking):
- Active region — sample all RHS from old values. Why this step?
<=reads pre-edge values. Olda = 3, oldb = 7. So we stash:a's pending value ,b's pending value . - NBA region — write both at once. Why this step? All stashed writes land together. Result:
a = 7,b = 3. Swapped.✔

Version B (blocking):
a = b→abecomes7immediately. Why this step?=updates now, before line 2.b = a→ butais already7, sob = 7. Why this step? Line 2 sees the freshly-writtena.- Final:
a = 7,b = 7. No swap.✘
Recall Verify (sanity check)
Non-blocking preserves the pair as a set: {3,7} before, {7,3} after — a genuine permutation. Blocking loses the value 3 entirely (information destroyed) — a tell-tale sign the swap failed.
Example 2 — Shift register, and why order can't matter (cell C2)
Forecast: does reordering change (q1, q2, q3)?
- Sample every RHS from old values (Active region). Why? Every
<=reads pre-edge. Stash:q3 ← q2_old = 1,q2 ← q1_old = 0,q1 ← d = 1. - Write together (NBA region). Result:
q1 = 1,q2 = 0,q3 = 1. - Reorder proof. Why unchanged? Step 1 read all three RHS before any LHS was written. Shuffling the lines shuffles nothing that step 1 depends on — the old values are fixed. Same hardware, same result.✔

Contrast with blocking (q1=d; q2=q1; q3=q2;): q1 becomes 1, then q2 = q1 = 1, then q3 = q2 = 1. In one clock d reached q3. Why wrong? Immediate updates let the value ripple through — you built one flip-flop, not a three-stage pipeline.
Recall Verify
After three clocks of the correct <= version, a 1 injected at d walks q1 → q2 → q3. That "one stage per clock" delay is exactly the definition of a shift register; the blocking version delivers it in zero extra clocks (collapsed).
Example 3 — Combinational chain where = is mandatory (cell C3)
Forecast: what is y? What would <= give instead?
tmp = a & b→1 & 1 = 1, applied now. Why blocking? This is pure data-flow (combinational logic);tmpmust be ready beforeyreads it, like a signal rippling through gates.y = tmp | c→1 | 0 = 1. Why? Line 2 must see the freshtmp. Correct answery = 1.✔- If we used
<=:tmp <= a&bsamples but does not update in this pass, so line 2 reads the oldtmp→ staley, and the tool may infer an unwanted latch → a sim/synthesis mismatch.
Recall Verify
Boolean truth: with a=b=1, c=0, . Blocking gives 1; non-blocking (reading stale tmp = 0 on the very first evaluation) would give 0 — provably wrong.
Example 4 — Degenerate inputs: self-assign & unknown x (cell C4)
Forecast: does r ever become known? Any difference between = and <= here?
r <= r, first edge. Sample RHS = oldr=x. Write backx. Why no change? A register assigned only its own old value is a hold; unknown stays unknown. It never resolves to0or1on its own.- Second edge: identical — still
x. This is the degenerate "does nothing but hold" flop. - Blocking
r = r: readsr, writes the samerimmediately — also a no-op, samex. Why identical here? With a single self-referential statement there is no later line to observe the difference, so=and<=coincide. - Lesson on degeneracy: the blocking/non-blocking distinction only becomes visible when one statement's result is read by another statement in the same block. Self-assign has no such reader.
Recall Verify
x under any bitwise identity op is x. So both versions leave r = x for all cycles — a fixed point. The two operators are indistinguishable in this degenerate single-statement case.
Example 5 — Mixing = and <= on the same variable (cell C5)
Forecast: guess the final n.
n = n + 1(blocking) →nbecomes3immediately. Why?=updates now.n <= n + 10(non-blocking) samples RHS using the currentn = 3→ stashes13, applied in the NBA region.- NBA region:
n = 13. So the blocking write of3is overwritten. Finaln = 13. - Why forbidden? The outcome depends on a subtle read/write ordering across two scheduler regions. A different tool or a reordered line changes the answer → classic ambiguity. Golden Rule 3: never mix operators on one variable.
Recall Verify
Trace: start 2 → blocking makes 3 → NBA RHS sampled as → NBA writes 13. Final = 13. The intermediate 3 is transient and unobservable, which is exactly why this style is dangerous.
Example 6 — Two always blocks, one race (cell C6)
Forecast: does splitting the swap across two blocks break it?
- Both blocks trigger on the same edge. Each
<=samples its RHS from old values in the Active region: Block 1 stashesa ← 9, Block 2 stashesb ← 5. Why safe here? Non-blocking sampling reads pre-edge values regardless of which block ran first. - NBA region writes both:
a = 9,b = 5. Correct swap — no race.✔ - The race version: if instead both used blocking (
a = b;in one block,b = a;in another), the result depends on which block the simulator runs first — an undefined ordering across separate blocks. That is a true race condition. - Caveat (Golden Rule 4): even the correct
<=version drives two different variables from two blocks — fine. Driving the same variable from two blocks is what's illegal.

Recall Verify
<= across two blocks: {5,9} → {9,5}, a clean permutation. Blocking across two blocks yields either {9,9} or {5,5} depending on run order — provably non-deterministic.
Example 7 — Limiting case: divergence over many cycles (cell C7)
Forecast: guess both values after 4 clocks.
- Counter A: each edge samples old
cnt, adds 1, writes once. Sequence:0 → 1 → 2 → 3 → 4. After 4 clocks,cnt = 4. Why? Exactly one increment per edge — the intended counter. - Counter B: per edge,
nxt = cnt+1thencnt = nxtthencnt = cnt+1→ two increments applied immediately each edge. Sequence:0 → 2 → 4 → 6 → 8. After 4 clocks,cnt = 8. - Limiting insight. Why care about "many cycles"? A one-cycle test might look "close enough," but the error compounds linearly: Counter B runs at double rate, drifting further every clock. The bug is invisible at cycle 0 and glaring at the limit.
Recall Verify
A after clocks ; B after clocks . At : A , B . The gap grows without bound.
Example 8 — Real-world word problem: state + output register (cell C8)
Forecast: after this edge, is light already 1, or still 0?
- Sample RHS from old values. Why
<=? This is clocked sequential logic; a registered output must reflect the pre-edge situation to stay glitch-free. Stashstate ← 1,light ← old state = 0. - NBA writes:
state = 1,light = 0. So the lamp lags the state by exactly one clock — precisely the intended "one clock later" behaviour. - Next edge (now old
state = 1):light <= statestashes1→light = 1. The STOP lamp lights one clock after the state changed. ✔ - Why not blocking?
light = stateafterstate = 1would show1the same cycle, defeating the registered-output timing and risking a decode glitch.
Recall Verify
Edge 1: (state,light): (0,0) → (1,0). Edge 2: (1,0) → (1,1). The output is a one-cycle delayed copy of the state — the hallmark of a registered output.
Example 9 — Exam twist: read a just-written signal (cell C9)
Forecast: after the edge, what are p and q? And after swapping lines?
- Sample all RHS from old values (Active region). Why?
<=never sees a same-cycle update. Oldp = 4. Stashp ← 5,q ← old p = 4. - NBA writes:
p = 5,q = 4. Soqgets the oldp, not the incremented one. - Swap the lines (
q <= p;first, thenp <= p + 1;): still samples oldp = 4for both. Result identical:p = 5,q = 4. Why? Order-independence of<=(Example 2's principle). - Common exam trap: students expect
q = 5because line 2 is "after" line 1. But<=breaks the software intuition — line 2 reads the pre-edgep.
Recall Verify
p_new = p_old + 1 = 5; q_new = p_old = 4, independent of line order. If a student answers q = 5 they've fallen for the blocking-style intuition.
Wrap-up: the matrix, filled
Active Recall
Value of n after Example 5's edge?
3.Counter B's value after 4 clocks (Example 7)?
(p, q) after Example 9's edge?
p = 5, q = 4; q reads the pre-edge p, unaffected by line order.After a WALK→STOP transition, when does the lamp light (Example 8)?
Connections
- Blocking vs non-blocking assignments (parent)
- Always blocks and sensitivity lists
- Combinational vs Sequential logic
- D Flip-Flops and Registers
- Race conditions in RTL simulation
- Synthesis vs Simulation mismatch
- Verilog data types (reg vs wire)
- Pipelining and shift registers