A three-stage shift register chains q1, q2, and q3. One version uses blocking =, and the other uses non-blocking <=.
A 1 is driven into the input, and the outputs are compared after each edge. The full code sits under Testbench below.
Slot k is the picture just after clock edge k, and edge 0 is the first edge, with d still low. The chain has three stages, so a real shift register needs three edges to carry the 1 to q3.
Slot 1: d is 1 for the first time at an edge. The blocking version copies it through all three variables inside one edge, so q3_b is 1 at once.
Slots 1 to 3: the non-blocking version moves the 1 one stage per edge: q1_n, then q2_n, then q3_n.
Slot 3: both versions end at 1, but the blocking one got there two edges earlier, because its three flops collapsed into one.
A blocking assignment updates its variable immediately. The next statement already sees the new value.
A non-blocking assignment evaluates its right-hand side when the statement runs and schedules the left-hand update in the NBA region at the same timestamp. With consistent clocked coding and stable inputs, every flop then samples the pre-edge value, and all outputs change together after it.
Where | Use | Why |
|---|---|---|
Clocked ( | `<=` non-blocking | All flops sample old values, then update together, matching real flops and avoiding races between blocks. |
Combinational blocks ( | `=` blocking | Later statements should see earlier results, like a chain of gates evaluated in order. |
Never | Assign the same signal with both | Lint flags it and synthesis tools may reject it. The simulated result no longer describes one clear piece of hardware. |
Avoid (style rule) | Mix | Statements in one block run in written order, so this is deterministic. But a blocking temporary in a clocked block must stay local: if another process reads it, the result depends on scheduling and races. |
// bad-example: two processes depend on active-region execution order
module blocking_process_race (input wire clk, d, output reg q);
reg shared;
always @(posedge clk) shared = d;
always @(posedge clk) q = shared;
endmoduleBefore edge: shared=0, q=0, d=1 | First process | Second process | q after edge |
|---|---|---|---|
Writer first | shared becomes 1 | Reader copies new shared | 1 |
Reader first | Reader copies old shared=0 | Writer then makes shared=1 | 0 |
Both schedules are permitted; neither is a stable design contract. With shared <= d and q <= shared, both right-hand sides evaluate before these NBA updates, so q takes the old shared=0 in this disciplined example. By contrast, a=d; b=a; in one process always executes in that written order: it deterministically collapses a pipeline, not an interprocess scheduling race. The test below demonstrates that separate, deterministic effect.
a <= b; b <= a;. What does it do?It swaps a and b every clock edge, because both right-hand sides are evaluated before either update takes effect. With blocking assignments (a = b; b = a;) both variables would end up equal to the old b.
One testbench runs the same shift register with blocking and non-blocking assignments.
module tb_blocking;
logic clk = 0, d = 0;
logic q1_b, q2_b, q3_b; // blocking version
logic q1_n, q2_n, q3_n; // non-blocking version
int errors = 0;
always @(posedge clk) begin // BAD for sequential logic
q1_b = d; q2_b = q1_b; q3_b = q2_b;
end
always @(posedge clk) begin // correct
q1_n <= d; q2_n <= q1_n; q3_n <= q2_n;
end
always #5 clk = ~clk;
initial begin
q1_n = 0; q2_n = 0; q3_n = 0;
#1 d = 1;
@(posedge clk); #1;
$display("edge 1 blocking=%b%b%b non-blocking=%b%b%b", q1_b, q2_b, q3_b, q1_n, q2_n, q3_n);
if ({q1_b, q2_b, q3_b} !== 3'b111) errors++; // collapsed in ONE edge
if ({q1_n, q2_n, q3_n} !== 3'b100) errors++; // shifted one stage
@(posedge clk); #1;
$display("edge 2 blocking=%b%b%b non-blocking=%b%b%b", q1_b, q2_b, q3_b, q1_n, q2_n, q3_n);
if ({q1_n, q2_n, q3_n} !== 3'b110) errors++;
@(posedge clk); #1;
$display("edge 3 blocking=%b%b%b non-blocking=%b%b%b", q1_b, q2_b, q3_b, q1_n, q2_n, q3_n);
if ({q1_n, q2_n, q3_n} !== 3'b111) errors++;
if (errors == 0) $display("PASS: blocking collapses the pipeline; non-blocking shifts");
$finish;
end
endmoduleedge 1 blocking=111 non-blocking=100
edge 2 blocking=111 non-blocking=110
edge 3 blocking=111 non-blocking=111