
Race-Free Design (Software Race Conditions)
Why blocking vs non-blocking assignment order in HDL can cause simulation races — and how to avoid them.
Description
A software race in HDL happens when the simulated result depends on the order procedural assignments execute. The fix is disciplined assignment: non-blocking (<=) for sequential (clocked) logic and blocking (=) for combinational logic, so updates are deterministic and simulation matches synthesis.
- Blocking (=) executes immediately, affecting later statements in the block.
- If two clocked regs use blocking and read each other, the result depends on order.
- Non-blocking (<=) samples RHS first, updates all at edge end — order-independent.
- Mixing them in one block creates ambiguous behavior.
- Sim may differ from synthesized hardware.
- Sequential (posedge) blocks: non-blocking <= only.
- Combinational (@*) blocks: blocking = only.
- Don't assign the same variable in two blocks.
- Keep next-state logic separate.
- These rules make behavior deterministic.
At a glance
What
Order-dependent, nondeterministic behavior from mixing assignment types.
Why
Races cause sim/synthesis mismatch and intermittent bugs.
How
Non-blocking for clocked logic, blocking for combinational; never mix in one block.
Where
All sequential HDL.
When
Whenever multiple registers update on the same edge.
Think of it like…
Blocking is people updating a shared whiteboard one-by-one (order matters); non-blocking is everyone writing on sticky notes, then posting simultaneously (order-proof).
The race
- Blocking (=) executes immediately, affecting later statements in the block.
- If two clocked regs use blocking and read each other, the result depends on order.
- Non-blocking (<=) samples RHS first, updates all at edge end — order-independent.
- Mixing them in one block creates ambiguous behavior.
- Sim may differ from synthesized hardware.
The rules
- Sequential (posedge) blocks: non-blocking <= only.
- Combinational (@*) blocks: blocking = only.
- Don't assign the same variable in two blocks.
- Keep next-state logic separate.
- These rules make behavior deterministic.
Assignment discipline
| Block | Use |
|---|---|
| posedge clk | non-blocking <= |
| @(*) comb | blocking = |
| mixed | AVOID |
HDL — Verilog · VHDL · SystemVerilog
// CORRECT (non-blocking): true swap
always @(posedge clk) begin a <= b; b <= a; end
// WRONG (blocking): both end up = b
// always @(posedge clk) begin a = b; b = a; endRight vs wrong: swap two registers on a clock edge.
Real-world applications
The 5 Whys
- 1
Why races happen? Order-dependent blocking updates.
- 2
Why non-blocking for clocked? Simultaneous, order-proof updates.
- 3
Why blocking for comb? Matches dataflow evaluation.
- 4
Why not mix? Ambiguous, mismatched behavior.
- 5
Root cause: matching assignment type to logic type makes results deterministic.
Cheat sheet
Working principle
- Non-blocking for clocked logic, blocking for combinational; never mix in one block.
- Order-dependent, nondeterministic behavior from mixing assignment types.
Formulas & Boolean expressions
- Blocking (=) executes immediately, affecting later statements in the block.
- Non-blocking (<=) samples RHS first, updates all at edge end — order-independent.
- Sequential (posedge) blocks: non-blocking <= only.
- Combinational (@*) blocks: blocking = only.
- posedge clk = non-blocking <=
- @(*) comb = blocking =
- mixed = AVOID
Key facts
- Blocking (=) executes immediately, affecting later statements in the block.
- Sequential (posedge) blocks: non-blocking <= only.
Why it exists
- Root cause: matching assignment type to logic type makes results deterministic.