The interviewer asks for the blocking vs non-blocking rule, then follows with a two-rank shift: two registers fed in series from one input, and a swap as a follow-up. The question tests whether the rule is a habit or a slogan.
What is really under test is cycle discipline. The candidate must predict simulation behaviour from assignment order and say which form synthesizes to the intended registers.
Use non-blocking assignments (<=) in clocked always_ff blocks and blocking assignments (=) in combinational always_comb blocks. The reason is scheduling, not style.
An NBA evaluates its right-hand side when the statement executes and schedules its destination update for the NBA region. With inputs stable before the edge, one writer per register and no blocking communication between clocked processes, this models parallel registers reading old state. It does not literally evaluate before the clock edge.
Context | Use | Why |
|---|---|---|
Clocked | Non-blocking | Parallel old-state model under stable inputs and disciplined single ownership. |
Combinational | Blocking | Immediate update; later statements see earlier ones, like gates. |
Temporary inside | Blocking for a combinational intermediate read in the same pass | Safe only when the temporary is written before it is read and never spans an edge. |
The comparison below feeds both styles the same rising input. Within each single process the result is deterministic, not a simulator race: a agrees while b differs because of statement ordering.
Slot 0: d is 0x10. Both a outputs read 0x10. b_nba still reads 0x00 while b_blk already reads 0x10.
Slot 1: d is 0x20. b_nba reads 0x10 (the old a) while b_blk reads 0x20 (the new a).
Slot 2: d is 0x30. b_nba reads 0x20 while b_blk reads 0x30.
Slot 3: d is 0x40. b_nba reads 0x30 while b_blk reads 0x40.
Slot 4: d is 0x50. b_nba reads 0x40 while b_blk reads 0x50.
Slot 5: d is 0x60. b_nba reads 0x50 while b_blk reads 0x60.
The NBA pair describes two register ranks. In a = d; b = a;, both registers capture d at the same edge; synthesis can merge equivalent registers. Simulation and synthesis agree on that behavior, but it is not the intended two-rank shift register.
What does a = b; b = a; in one clocked block mean? Both assigned values become the pre-edge b: the second statement reads the newly assigned a. Synthesis preserves that dependency; it does not infer an NBA-style swap. To exchange old values use a <= b; b <= a;. A separate race risk occurs when multiple clocked processes exchange values using blocking assignments and observe execution-order-dependent updates.
When is a blocking assignment inside clocked logic acceptable? For a combinational temporary written and read within the same pass, before any non-blocking update. The temporary never holds state across an edge.
NBA is not universal order independence. In one process, q <= a; q <= b; leaves q with b after NBA; reversing the statements leaves a. Also, a testbench driving d with blocking assignment at posedge races a DUT reading d in the active region, even if the DUT uses NBA. Drive away from the accepting edge and observe after NBA, as below.
A weaker candidate says blocking logic runs faster. Assignment type sets scheduling and data dependencies, not a guaranteed speed. Those dependencies can change inferred register ranks and allow register merging.
Another says non-blocking belongs everywhere for safety. In combinational blocks it delays updates to the end of the pass, which mismatches gate behaviour and can hold stale values across branches.
The blocking pair agrees with its own assignment semantics but disagrees with the intended two-rank reference.
d rise from 0x10 to 0x20. Which b output takes the value 0x20 at that edge, and why?The blocking b. It reads the already-updated a. The NBA b reads the pre-edge a, so it takes 0x10 and reaches 0x20 one edge later.
Both shift pairs plus the testbench that sweeps every 8-bit input and checks each against its reference.
module shift_nba (
input logic clk, rst_n,
input logic [7:0] d,
output logic [7:0] a, b
);
always_ff @(posedge clk or negedge rst_n)
if (!rst_n) begin a <= 8'h00; b <= 8'h00; end
else begin a <= d; b <= a; end // parallel update: b takes the OLD a
endmodule
module shift_block (
input logic clk, rst_n,
input logic [7:0] d,
output logic [7:0] a, b
);
always_ff @(posedge clk or negedge rst_n)
if (!rst_n) begin a = 8'h00; b = 8'h00; end
else begin a = d; b = a; end // immediate update: b takes the NEW a
endmodule
Bench run with Icarus Verilog 13.0 (2026-10-16): prints PASS. It sweeps every 8-bit input against each reference. Passing this simulation shows only the checked behaviour of each model, not a synthesis mismatch or a mapped implementation.