Blocking and Non-Blocking assignment in Verilog
Introduction¶
In Verilog, assignment operators determine how values are updated inside procedural blocks.
 Two assignment styles — blocking (=) and non-blocking (<=) — define the timing and execution order of assignments.
Even though they look similar, the difference between = and <= can drastically change simulation results and hardware behavior.
 This guide explains both types in detail, including how they work, where to use them, and common mistakes that cause unexpected results.
What Are Blocking and Non-Blocking Assignments?¶
Procedural assignments in Verilog appear inside always or initial blocks.
 They can be blocking (=) or non-blocking (<=).
| Type | Symbol | Execution | Typical Use | 
|---|---|---|---|
| Blocking assignment | = |  Executes immediately in sequence | Combinational logic | 
| Non-blocking assignment | <= |  Executes concurrently at end of time step | Sequential logic (flip-flops) | 
Blocking Assignment (=)¶
 Behavior¶
A blocking assignment acts like a traditional software assignment — the statement blocks execution until it completes.
Example¶
always @(*) begin
  x = a & b;
  y = x | c;
end
Execution flow:
- x gets a & b.
 - Only after that, y gets x | c.
 
It’s like sequential statements in C or Python. In combinational logic, blocking assignments are often used to model signals that depend on earlier computed values. It is like cascade of and-gate followed by or-gate in the above example.
Non-Blocking Assignment (<=)¶
A non-blocking assignment schedules the new value to update after the current simulation time step, allowing all right-hand sides to be evaluated before any updates occur.
always @(*) begin
  x <= a & b;
  y <= x | c;
end
Here, both x and y are updated concurrently on the same clock edge:
xgets the old value ofa & bygets the old value ofx | c
Non-blocking assignments model flip-flops or registers, which all update together on a clock edge.
When to Use Which¶
| Situation | Recommended Assignment | Reason | 
|---|---|---|
| Combinational logic | = (blocking) | Sequential evaluation of logic | 
| Sequential (clocked) logic | <= (non-blocking) | Parallel update of registers | 
| Testbench / simulation variables | Either | Use = for clarity; <= rarely needed | 
In Summary, use blocking (=) for combinational logic, non-blocking (<=) for sequential logic.
Mixing Blocking and Non-Blocking¶
It’s legal to mix them, but doing so in the same always block often leads to race conditions.
Not-recommended practice¶
In a single always block, both blocking and non-blocking statements can be used, though not-recommended.
always @(posedge clk) begin
  sum = a + b;  // blocking
  out <= sum;   // non-blocking
end
Here, how the above code works :
aupdates immediately (=),outgets oldsum(not predictable in synthesis).
Uncertain simulation and synthesis behaviour.
Recommended practice¶
always @(*) begin
  sum = a + b;  // blocking (combinational)
end
always @(posedge clk) begin
  out <= sum;   // non-blocking (sequential)
end
This cleanly separates combinational and sequential parts.
Scheduling and Event Queue¶
To understand simulation order, know the Verilog event queue:
- Active region : Blocking assignments (=) execute immediately.
 - Non-blocking assignment (NBA) region : <= updates scheduled after all active events.
 - Post-poned region : Waveform dump, $monitor, etc.
 
So non-blocking updates happen after all blocking ones complete at that simulation time.
initial begin
  a = 0;
  b = 0;
  #5 a = 1;
  b <= 1;
end
At time 5:
- a = 1 happens now (blocking)
 - b <= 1 happens after the current time step (non-blocking)
 
Common Mistakes and Simulation Mismatches¶
| Mistake | Description | Result | 
|---|---|---|
| Using = in clocked logic | Updates in sequence, not parallel | Wrong pipeline timing | 
| Using <= in combinational logic | All signals update later | Possible unintended latches | 
| Mixing = and <= in one block | Race conditions | Non-deterministic results | 
| Missing sensitivity list in @(*) | Not updating properly | Simulation mismatch | 
| No reset for registers | Uninitialized state | X propagation in simulation | 
Example — Race Condition¶
reg a, b, clk;
always @(posedge clk)
  a = b;
always @(posedge clk)
  b = a;
Both blocks use blocking assignments on the same clock. In simulation, the order of execution is undefined — one may execute before the other.
In hardware, both flip-flops sample the old values simultaneously. Fix by using non-blocking assignments:
always @(posedge clk)
  a <= b; // changed to non-blocking assignment
always @(posedge clk)
  b <= a; // changed to non-blocking assignment
Now simulation matches real hardware behavior.
Advanced Example — Pipeline Delay¶
Let’s model a 3-stage pipeline.
Recommended (Non-blocking)¶
always @(posedge clk) begin
  stage1 <= in_data;
  stage2 <= stage1;
  stage3 <= stage2;
end
Not-recommended (Blocking)¶
always @(posedge clk) begin
  stage1 = in_data;
  stage2 = stage1;
  stage3 = stage2;
end
The blocking version collapses all stages into one — simulation gives the illusion that data moves through instantly, which is not synthesizable hardware behavior.
Synthesizable Examples¶
Example 1: Counter¶
always @(posedge clk or posedge rst) begin
  if (rst)
    count <= 0;
  else
    count <= count + 1;
end
Example 2: ALU¶
always @(*) begin
  case(op)
    2'b00: y = a + b;
    2'b01: y = a - b;
    2'b10: y = a & b;
    2'b11: y = a | b;
  endcase
end
Guidelines for Clean RTL Code¶
Do¶
- Use = in combinational logic 
always @(*). - Use <= in sequential logic 
always @(posedge clk). - Separate combinational and sequential blocks.
 - Provide resets for registers.
 
Don't¶
- Mix 
=and<=in the same block. - Use 
=in flip-flop code. - Forget 
@(*)in combinationalalwaysblocks. - Rely on simulation order — synthesis ignores statement order in concurrent logic.
 
| Feature | Blocking (=) | Non-Blocking (<=) | 
|---|---|---|
| Execution | Sequential (immediate) | Concurrent (scheduled) | 
| Typical use | Combinational logic | Sequential (clocked) logic | 
| Simulation order | One after another | All update after current time step | 
| Hardware mapping | Wires, gates | Flip-flops, registers | 
| Safe to mix in same block? | No | No | 
| Synthesis behavior | Follows order | Parallel register updates | 
| Used for testbenches? | Often | Rarely | 
Example: Correcting a Bug¶
Buggy code:
always @(posedge clk)
  sum = a + b;
Fix:
always @(posedge clk)
  sum <= a + b;
Now the register updates only on clock edge, matching real flip-flop timing.