Digital Design / Module 3: Verilog that means hardware
15. Blocking and non-blocking assignments
Understand the classic swap and the scheduler behind it.
See it move
The idea
= (blocking) takes effect immediately, so the next line sees the new value. <= (non-blocking) evaluates its right side now but updates the left side later, together with all the other non-blocking updates of that time step.
The classic swap
always @(posedge clk) begin
a <= b;
b <= a;
endBoth right sides are read first, then both updates happen. The values swap. This describes exactly what two flops do when each feeds the other: at the edge, both capture what was at their inputs just before it.
With = the first line destroys a before the second reads it, so both end up with the old value of b. The animation above steps through both.
The rule
- Clocked logic: use
<=. - Combinational logic: use
=. - Never mix them in one block.
These rules aren't about style. They make simulation match the hardware and remove race conditions.
Why order would otherwise matter
always @(posedge clk) begin
q1 = d; // wrong: blocking in a clocked block
q2 = q1;
endq2 receives d in the same step, so a two-stage collapses into one stage. With <=, it works in any order you write the lines.
Races between blocks
Two clocked blocks using = that read each other's outputs give different results depending on which block the simulator happens to run first. Different simulators can legally disagree. Non-blocking assignments remove this race, because all the reads finish before any update.
Common mistake
Thinking that <= adds delay. It doesn't add time; it only defers the update to later in the same time step. Combinational logic can also show extra simulation activity if written with <=, so keep to the rule.
Key ideas
- Non-blocking right-hand sides are sampled first, then updated together.
- Use <= in clocked blocks and = in combinational blocks.
- Mixing them creates simulation races.
Your exercise
Find the bug
This code has one deliberate bug. Read the symptom, then click the line you think is wrong.
The fix
q1 <= d;
q2 <= q1;Why it failed
With =, q1 takes its new value at once, and the next line reads that new value. So d passes through both stages in a single clock, and one flop disappears. With <=, both right-hand sides are read before any update, which is how two real flops behave.
Interview questions
coreWhat does a<=b; b<=a; do on a clock edge?
It swaps them, since both right-hand sides are sampled before either update.
basicIn a clocked block, a = 0 and b = 1. After an edge runs a = b; b = a; with blocking assignments, what are a and b?
Both are 1. The first line sets a to 1 immediately, and the second line reads that new a, so b also becomes 1. The old value of a is lost. With non-blocking assignments the two values would swap.
basicState the rule for choosing = or <=, and why it exists.
Use <= in clocked blocks and = in combinational blocks, and do not mix them in one block. Non-blocking matches how flops behave: all of them sample just before the edge and update together. Blocking matches combinational logic, where each line's result feeds the next. Following the rule makes simulation match the hardware and avoids races.
coreThis is meant to be a 3-stage shift register. What does it actually do?
always @(posedge clk) begin q1 = d; q2 = q1; q3 = q2; end
Because each line updates immediately, q1 takes d, then q2 takes the new q1, and q3 the new q2. After one edge all three equal d, so it behaves as one stage feeding three copies. Synthesis matches this: three flops all loading d, which a tool may merge into one. Change all three to <= and it becomes a real 3-stage shift register.
coreThe same shift register written in reverse order with blocking assignments works in simulation. Is it acceptable?
always @(posedge clk) begin q3 = q2; q2 = q1; q1 = d; end
It works within the block, because each line reads a register before it is overwritten, so a real shift happens. It is still poor practice. Its correctness depends on line order, which is easy to break during edits, and other always blocks that read these registers at the same edge can race with it. Non-blocking assignments work in any order and remove the race.
coren is 5 before a clock edge. What is n after this block runs?
always @(posedge clk) begin n <= n + 8'd1; n <= n + 8'd2; end
n is 7. Both right-hand sides are evaluated with the old n = 5, giving 6 and 7. The updates are applied in order at the end of the time step, so the later one wins. Multiple non-blocking assignments to the same signal in a block are legal, and the last one takes effect.
coreTwo clocked blocks use blocking assignments: one does a = b;, the other b = a;. What happens at the clock edge?
It is a race. Both blocks wake at the same edge, and the simulator may run them in either order. If the first runs first, both end up with old b; otherwise both end up with old a. Different simulators can legally disagree. With <= in both, all reads happen before any update and the values swap, which is what two real flops do.
advancedThis combinational block uses <=. Does it still give the right yy, and what is the cost?
always @(*) begin t <= a & b; yy <= t | c; end
It settles to the right answer, but in two passes. On the first pass yy is computed from the old t, because t's update is deferred. When t then changes, @(*) wakes the block again, and yy is corrected. In between, yy can briefly hold a wrong value within the same time step, and the extra passes slow simulation. With = the block computes yy correctly in one pass.
advancedA testbench changes d with always @(posedge clk) d = ~d; while the design does q <= d on the same edge. Why is this risky?
The testbench and the design both wake at the same edge. If the testbench block runs first, the design samples the new d; if it runs second, the old d. The order is not defined, so results can change between simulators or after small edits. Drive stimulus with <=, or change it away from the active edge, for example on the falling edge.