In short: Use non-blocking (<=) in clocked always blocks to model flip-flops, and blocking (=) in combinational blocks — mixing them wrongly causes race conditions and simulation-vs-synthesis mismatches.
Few topics trip up RTL designers as reliably as the difference between blocking and non-blocking assignments in Verilog. The syntax difference is a single character — = versus <= — but the semantics reach deep into how an event-driven simulator (Synopsys VCS, Cadence Xcelium, Siemens Questa) schedules updates. Get it wrong and your gate-level netlist behaves differently from your RTL, or your testbench passes while the silicon fails. This guide explains the scheduling model and the golden rules used in production design flows.
Two assignment operators, two intents
A blocking assignment (a = b;) executes immediately and in order. The statement "blocks" the ones after it until its right-hand side (RHS) is evaluated and its left-hand side (LHS) is updated. It behaves like a sequential statement in C.
A non-blocking assignment (a <= b;) samples all RHS values at the current time step, then defers the LHS update to the end of the time step. Every non-blocking assignment in a block sees the old values of its inputs, which is exactly how real edge-triggered flip-flops behave: they all sample the data present just before the clock edge.
Blocking = "compute and update now." Non-blocking = "sample now, update later." That one sentence resolves 90% of beginner confusion.
The Verilog event scheduler and regions
The IEEE 1364 (and 1800 for SystemVerilog) scheduling model divides each simulation time step into ordered regions. The two that matter here are the Active region and the NBA (non-blocking assignment) region. Blocking assignments and RHS evaluations happen in the Active region. The LHS updates of non-blocking assignments are queued and applied later, in the NBA region, after every Active event at that time has drained.
Why a shift register needs non-blocking
Consider three flops forming a shift register on posedge clk:
always @(posedge clk) begin q1 <= d; q2 <= q1; q3 <= q2; end
With non-blocking assignments, all three RHS values are sampled first using the pre-edge values, so data advances one stage per clock — a correct 3-stage shift register. Now swap to blocking:
always @(posedge clk) begin q1 = d; // q1 updated immediately q2 = q1; // sees the NEW q1 q3 = q2; // sees the NEW q2 end
Here d races straight through to q3 in a single clock: the block collapses to a single flop feeding two buffers. Simulation shows one flop; synthesis in Synopsys Design Compiler may infer something different depending on optimization, producing a classic RTL-vs-gate mismatch.
Why combinational logic needs blocking
For pure combinational logic you want the intermediate values to propagate within the same evaluation, so blocking is correct:
always @(*) begin sum = a ^ b ^ cin; carry = (a & b) | (cin & (a ^ b)); end
If you used non-blocking here and a downstream statement depended on an earlier result, it would read the stale value and require an extra delta cycle to settle — harmless for a simple adder but a frequent source of simulation slowdowns and subtle glitches in larger combinational chains. For more on structuring these processes, see our Verilog always block guide.
The animated race: blocking vs non-blocking
<= the bit moves exactly one flop per clock — the behaviour of real hardware.Golden rules (what senior designers enforce)
- Sequential logic (
always @(posedge clk)): use only non-blocking<=. - Combinational logic (
always @(*)): use only blocking=. - Never mix blocking and non-blocking to the same variable in one block.
- Do not drive the same variable from more than one
alwaysblock. - Latch/level-sensitive logic follows the combinational rule — blocking.
These five rules come straight from Cliff Cummings' well-known SNUG guidance and are baked into most corporate RTL linting decks (Siemens, Synopsys SpyGlass). Teams building verified flows — like the design-services group at Avecas — treat a violation as a blocking lint error, not a warning.
| Aspect | Blocking = | Non-blocking <= |
|---|---|---|
| Update timing | Immediate, in order | Deferred to NBA region |
| RHS sampling | Current (possibly updated) | Pre-edge values |
| Models | Combinational / latch | Edge-triggered flop |
| Typical bug | Accidental flop collapse | Extra delta-cycle delay |
A single = inside a clocked block is one of the most common causes of a "passes in sim, fails on FPGA" bug report filed against junior engineers.
Want to drill these rules with real waveform labs on Questa and VCS? Our CourseTron RTL & FPGA track walks you from assignment semantics to timing closure with hands-on projects.
Reading waveforms to diagnose it
When you suspect a blocking/non-blocking bug, dump VCD or FSDB and look at the edge. If a register value changes on the same edge as its input and skips the expected one-cycle latency, you almost certainly used blocking where you needed non-blocking. For deeper debug technique, pair this with our Verilog testbench essentials.
FAQ
Can I use non-blocking in a combinational always block?
Functionally it can produce the right result after extra delta cycles, but it is bad practice, slows simulation, and confuses linters. Stick to blocking for combinational logic.
Why do all my flops sample old values with non-blocking?
Because every RHS is evaluated in the Active region before any LHS update is applied in the NBA region. All non-blocking statements therefore see the values present at the start of the time step — exactly like hardware flip-flops sampling just before the clock edge.
Is this the same in SystemVerilog?
Yes. IEEE 1800 keeps the same Active/NBA scheduling. SystemVerilog adds always_ff and always_comb, which let tools enforce the golden rules automatically — a strong reason to use them.
Does the single-character difference really matter for synthesis?
Absolutely. The operator changes which logic the tool infers. A blocking assignment in a clocked block can collapse flops or create unintended combinational paths, yielding a netlist that no longer matches your intended behaviour.