3.5.4 · D4HDL & Digital Design Flow

Exercises — Blocking vs non-blocking assignments

2,500 words11 min readBack to topic

Two words we will use constantly, both defined on the parent page — repeated here so you never guess:

A reminder of the mental model we lean on the whole page:

Figure — Blocking vs non-blocking assignments

The blue camera-flash is non-blocking: photograph everybody's old position first, then everyone jumps at once. The magenta domino is blocking: line 1 moves, then line 2 sees that already-moved value.


Level 1 — Recognition

Exercise 1.1

Which operator (= or <=) belongs in a clocked block, and which in a combinational block? Fill the blanks:

  • always @(posedge clk) → use ____
  • always @(*) → use ____
Recall Solution 1.1
  • always @(posedge clk)<= (non-blocking). Clocked flip-flops all update together on the edge using the pre-edge picture.
  • always @(*)= (blocking). Combinational logic is a data-flow: each intermediate must be ready before the next line uses it.

Mnemonic from the parent: "Clock? Non-blocK."

Exercise 1.2

In the snippet below, mark each assignment as B (blocking) or N (non-blocking):

x =  a;
y <= b;
z <= c;
Recall Solution 1.2
  • x = a;B
  • y <= b;N
  • z <= c;N

The single = is blocking; <= (which looks like a "delay arrow") is non-blocking.


Level 2 — Application (trace the values)

Assume before the clock edge: a = 3, b = 7.

Exercise 2.1

always @(posedge clk) begin
    a <= b;
    b <= a;
end

What are a and b after the edge?

Recall Solution 2.1

Non-blocking: sample all RHS first using pre-edge values → RHS of line 1 is old b = 7, RHS of line 2 is old a = 3. Then apply both.

  • a = 7, b = 3. The values swap. ✔

Exercise 2.2

Same start (a = 3, b = 7), but now blocking:

always @(posedge clk) begin
    a = b;
    b = a;
end

What are a and b after the edge?

Recall Solution 2.2

Blocking updates immediately, in order:

  • Line 1: a = ba becomes 7.
  • Line 2: b = a → but a is already 7, so b = 7.
  • Result: a = 7, b = 7. No swap — both hold old b. ✘

Exercise 2.3

Start: a = 2, b = 5, c = 0. Trace this single-pass combinational block:

always @(*) begin
    c = a + b;   // blocking
    a = 10;      // blocking
end

What is c right after the block runs once?

Recall Solution 2.3

Blocking, in order:

  • c = a + b = 2 + 5 = 7 (uses the current a = 2).
  • a = 10 happens after — it does not retro-change c.
  • So c = 7, and a = 10. (In real combinational hardware, changing a afterward re-triggers @(*) and re-runs, but within this single pass c = 7.)

Level 3 — Analysis (find the bug)

Exercise 3.1

This is meant to be a 3-stage shift register (d → q1 → q2 → q3), one hop per clock. It doesn't work. What actually gets built, and why?

always @(posedge clk) begin
    q1 = d;
    q2 = q1;
    q3 = q2;
end
Recall Solution 3.1

Blocking makes each line apply immediately, so d ripples through all three in one edge:

  • q1 = d
  • q2 = q1 → now = d
  • q3 = q2 → now = d After one clock, q1 = q2 = q3 = d. All three collapse to the same value — the synthesizer builds effectively one flip-flop plus wires, not three stages. See the Pipelining and shift registers note for the intended structure. Fix: use <=, which samples pre-edge values so each stage receives its neighbour's old value.

Exercise 3.2

Combinational block. Why does using <= here break it?

always @(*) begin
    tmp <= a & b;
    y   <= tmp | c;
end
Recall Solution 3.2

With <=, tmp is not updated until the end of the pass, so line 2 uses the stale (previous) tmp, not the freshly-computed a & b. The simulator models a wrong/latched value, and it disagrees with the synthesized gate netlist → a classic Synthesis vs Simulation mismatch. The tool may even infer an unintended latch on tmp. Fix: combinational data-flow needs =, so tmp is ready before y reads it.

Exercise 3.3

Two always blocks both drive q. What is wrong?

always @(posedge clk) q <= d;
always @(posedge clk) q <= e;
Recall Solution 3.3

q is assigned from two different clocked blocks. Both fire on the same edge; the two non-blocking updates to the same LHS collide with no defined ordering → a race condition. The final value of q is simulator-dependent, and synthesis will complain about multiple drivers. Fix (Golden Rule 4): each variable is driven by exactly one always block. Merge the logic (e.g. q <= sel ? e : d;).


Level 4 — Synthesis (write the code)

Exercise 4.1

Write an always block for a 4-stage shift register: input d, stages q1,q2,q3,q4, output is q4. One shift per rising clock edge.

Recall Solution 4.1

Clocked ⇒ non-blocking:

always @(posedge clk) begin
    q1 <= d;
    q2 <= q1;
    q3 <= q2;
    q4 <= q3;
end

Each stage grabs its neighbour's old value, so data advances exactly one stage per clock. See D Flip-Flops and Registers.

Exercise 4.2

Write a combinational block computing y = (a & b) | c using an intermediate signal p. Which operator, and why?

Recall Solution 4.2

Combinational ⇒ blocking =, and list all inputs (or use @(*)):

always @(*) begin
    p = a & b;   // ready first
    y = p | c;   // uses fresh p
end

= makes p available immediately for the next line, matching how a signal ripples through gates. Using <= would feed line 2 a stale p.

Exercise 4.3

A register count should increment by 1 every clock, resetting to 0 when rst is high (synchronous reset). Write it.

Recall Solution 4.3

Clocked ⇒ non-blocking; synchronous reset is inside the clocked block:

always @(posedge clk) begin
    if (rst)
        count <= 0;
    else
        count <= count + 1;
end

count <= count + 1 samples the old count, adds 1, and defers the update — exactly one increment per edge, no runaway.


Level 5 — Mastery (subtle scheduling)

Exercise 5.1

Before the edge: a = 1. This mixes operators for the same variable in one block. What value does a end up with, and what is the real problem?

always @(posedge clk) begin
    a = a + 1;   // blocking
    a <= a + 2;  // non-blocking, same variable
end
Recall Solution 5.1

Step through the scheduler:

  • Active region: a = a + 1 (blocking) applies immediately → a becomes 2.
  • Then the <= RHS is sampled using the current a = 2 → stashed value = 2 + 2 = 4.
  • NBA region: apply the stash → a = 4. Final a = 4. But this violates Golden Rule 3 — mixing = and <= for the same variable creates fragile, hard-to-read, tool-dependent behaviour. Never write this in real RTL; the answer is a trap, not a technique.

Exercise 5.2

Two clocked blocks, non-blocking, feeding each other (a proper cross-coupled pair). Before edge: x = 1, y = 0. What are x, y after one edge, and after two edges?

always @(posedge clk) x <= y;
always @(posedge clk) y <= x;
Recall Solution 5.2

Both blocks sample RHS from pre-edge values, then update in the NBA region. Different variables ⇒ no illegal double-drive.

  • After edge 1: sample old x = 1, y = 0 → new x = y = 0, new y = x = 1. So x = 0, y = 1 (they swapped).
  • After edge 2: sample x = 0, y = 1x = 1, y = 0 (swap back). They ping-pong every clock: (1,0) → (0,1) → (1,0) → …. This is the multi-block version of the swap example, and it works precisely because <= uses the old picture.

Exercise 5.3

Before the edge: sel = 1, a = 4, b = 9, out = 0. Trace this clocked block that mixes a blocking temporary with a non-blocking output (a common legal pattern — the temp is local scratch, not a flip-flop input elsewhere):

always @(posedge clk) begin
    tmp = sel ? a : b;  // blocking scratch
    out <= tmp + 1;     // non-blocking flip-flop
end

What is out after the edge?

Recall Solution 5.3
  • tmp = sel ? a : b blocking → sel = 1, so tmp = a = 4, applied immediately.
  • out <= tmp + 1: RHS sampled now using tmp = 4 → stash = 5; applied in NBA region.
  • Final out = 5. The blocking tmp is fine here because it is fully computed and consumed within the same pass before the deferred <= samples it. out is still a clean edge-triggered flip-flop.

Recall Ladder

Recall One-line answers to the whole ladder

L1: clock→<=, combinational→=. <= is assignment, not comparison. L2: <= swap works (old values); = swap fails (both = old b). L3: blocking shift register collapses to one flop; <= in combinational uses stale intermediates; two blocks on one var = race. L4: shift register / combinational chain / counter — correct operators as shown. L5: mixed operators are traceable but forbidden; cross-coupled <= ping-pongs; blocking scratch + <= output is legal.

Order among non-blocking statements is irrelevant?
Yes — all RHS are sampled before any LHS updates.
Which region applies the deferred non-blocking updates?
The NBA (non-blocking assignment) region, after the Active region.
Why is a=b; b=a; not a swap?
a updates immediately, so line 2 reads the new a; both become old b.

Connections