Blocking vs non-blocking assignments
WHY does this distinction even exist?
WHAT each does (the scheduling rule):
Blocking = |
Non-blocking <= |
|
|---|---|---|
| Evaluation of RHS | immediately | sampled at start of time step |
| Update of LHS | immediately, before next stmt | deferred to end of time step |
| Later statements see the new value? | Yes | No (they see the old value) |
| Models | combinational logic / sequential software-like steps | edge-triggered flip-flops |
HOW the scheduler actually runs it (first principles)
Verilog simulation at each time step has regions. Simplified:
- Active region — evaluate & apply blocking assignments; evaluate the RHS of non-blocking assignments and stash the result.
- NBA (non-blocking) region — apply all the stashed non-blocking updates.

Worked Example 1 — Swap two registers (the classic)
always @(posedge clk) begin
a <= b;
b <= a;
endWhat happens? At the edge, RHS sampled first: a's new value = old b, b's new value = old a. Then both update. Result: values swap. ✔
Why this step? Because <= uses old a in line 2, not the value line 1 just assigned.
Now with blocking:
always @(posedge clk) begin
a = b; // a becomes old b
b = a; // a is ALREADY b, so b = b -> NO swap!
endWhy this step? = applies line 1 immediately, so line 2 sees the new a. Both end up equal to old b. This synthesizes a single flop plus a wire — not a swap.
Worked Example 2 — Pipeline / shift register
Goal: q1 -> q2 -> q3 shift each clock.
always @(posedge clk) begin
q3 <= q2;
q2 <= q1;
q1 <= d;
endWhy this works regardless of order? All RHS (q2, q1, d) are sampled from pre-edge values, then applied. So q3 gets old q2, etc. — a true 3-stage shift register. Reordering the three lines gives the same hardware.
With blocking = (wrong for this):
q1 = d; q2 = q1; q3 = q2; // q3 = q2 = q1 = d ALL in one clock -> collapses!Why this fails? Immediate updates ripple d all the way to q3 in a single cycle → you built one flip-flop, not three.
Worked Example 3 — Combinational logic (blocking is correct)
always @(*) begin
tmp = a & b; // intermediate
y = tmp | c; // uses just-computed tmp
endWhy blocking here? This is a data-flow: tmp must be ready before y uses it, exactly like ripple through gates. Using <= would make y use the old tmp, producing wrong/latched behaviour in simulation.
The Golden Rules (80/20 — memorize these)
Common Mistakes (Steel-manned)
Recall Feynman: explain to a 12-year-old
Imagine a class doing a partner-swap dance. Blocking = the teacher tells kid A "go stand where B is," and A instantly moves; then tells B "go where A is" — but A already moved, so B copies A's new spot. They both end up in the same place — no swap! Non-blocking = the teacher takes a photo first, then says "everyone, move to where your partner was in the photo." Now they truly swap, because everyone looked at the old picture. Clocked chips work like the photo version: at each tick, all flip-flops look at the "before" picture and then jump together.
Active Recall
What does the = (blocking) operator do inside an always block?
What does <= (non-blocking) do?
Which assignment type for clocked/sequential logic?
<=.Which assignment type for combinational logic (always @(*))?
=.Why does a<=b; b<=a; swap correctly?
Why does blocking a=b; b=a; fail to swap?
a is updated immediately, so line 2 uses the new a; both become old b.Does the order of non-blocking statements affect the result?
What hardware bug comes from using = in a shift register?
Golden rule linking clock and operator (mnemonic)?
<=.Why can mixing = and <= for one variable cause problems?
Connections
- Always blocks and sensitivity lists
- Combinational vs Sequential logic
- D Flip-Flops and Registers
- Race conditions in RTL simulation
- Synthesis vs Simulation mismatch
- Verilog data types (reg vs wire)
- Pipelining and shift registers
Concept Map
Hinglish (regional understanding)
Intuition Hinglish mein samjho
Dekho, Verilog ke always block ke andar do tarah ke assignment hote hain. Blocking (=) normal software line jaisa chalta hai — ek statement pehle poora hota hai, uska result turant update ho jaata hai, tabhi agli line chalti hai. Non-blocking (<=) thoda alag hai: pehle saari RHS (right hand side) values ko ek saath "photo" ki tarah sample kar leta hai (edge se pehle wali values), phir end me sabhi LHS ko ek saath update karta hai. Isliye baad wali line ko purani value hi dikhti hai.
Yeh chhota sa difference hardware me bada farak laata hai. Jab tum clocked logic likhte ho (always @(posedge clk)), tumhe flip-flops chahiye jo edge par ek saath, purani values lekar update hote hain — iske liye <= use karo. Classic example: a<=b; b<=a; sahi se swap karta hai, kyunki dono old values use karte hain. Agar = lagao (a=b; b=a;) to swap fail — dono b ban jaate hain, kyunki line 1 turant apply ho gaya.
Ulta, combinational logic (always @(*)) me tumhe data flow chahiye — pehle tmp banao, phir tmp use karke y. Yahan = sahi hai, kyunki turant update chahiye. Yaad rakhne ka trick: "Clock? Non-blocK" — clock wala block me <=, aur combo (combinational) me =.
Sabse important rule: kabhi ek hi variable ke liye = aur <= mix mat karo, aur ek variable ko do alag always blocks se assign mat karo — warna race condition aur simulation vs synthesis mismatch ho jaayega. Bas yeh 80/20 rules pakad lo, exam aur real chip design dono me kaam aayenge.