Blocking vs Non-Blocking Assignments

The single most important concept in Register Transfer Level (RTL) design. Understanding the = vs <= distinction prevent race conditions.

The Great Divider

In Verilog, how you assign a value determines when that value updates. Mixing these up is the #1 cause of "simulation vs synthesis" mismatches.

Blocking (=)

Symbol: =

Behavior: Executes immediately. The next line of code cannot run until this one finishes.

Analogy: A Chef chopping vegetables. He cannot start cooking until he finishes chopping.

Use for: Combinational Logic

Non-Blocking (<=)

Symbol: <=

Behavior: Schedules assignment for the end of the time step. All lines execute in parallel.

Analogy: An Assembly Line. All workers perform their task on the current item simultaneously.

Use for: Sequential Logic (Flip-Flops)

The "Swap" Problem (Race Conditions)

Let's try to swap two registers, A and B. This perfectly illustrates why we need Non-Blocking assignments.

Bad Code (Blocking)


always @(posedge clk) begin
    a = b; // 'a' gets 'b' value IMMEDIATELY
    b = a; // 'b' gets the NEW 'a' value (which is just 'b'!)
end
// Result: A = B, B = B. The old value of A is lost forever.
                            

Good Code (Non-Blocking)


always @(posedge clk) begin
    a <= b; // Schedule 'a' to update at end of step
    b <= a; // Schedule 'b' to update at end of step (using OLD 'a')
end
// Result: A becomes old B, B becomes old A. Perfect Swap.
                            

Why did the "Bad Code" fail?

Because = blocks the execution flow. The first line updated a instantly, corrupting the source data for the second line. <= samples all Right-Hand Side (RHS) values first, and updates the Left-Hand Side (LHS) later.

Deep Dive: The Verilog Stratified Event Queue

Verilog simulators break a single "time step" into multiple regions to handle scheduling.

Simulating Time Step T=0

flowchart TD; Start(("Start T=0")) --> A1; subgraph Active ["Active Region"]; A1["Execute Blocking Assignments (=)"]; A2["Evaluate RHS of Non-Blocking"]; A3["Execute $display"]; end; A3 --> I1; subgraph Inactive ["Inactive Region"]; I1["#0 Delays"]; end; I1 --> N1; subgraph NBA ["NBA Region"]; N1["Update LHS of Non-Blocking (<=)"]; end; N1 --> M1; subgraph Monitor ["Postponed Region"]; M1["$monitor / $strobe"]; end; M1 --> Next(("Next T")); style Active fill:#ffcdd2,stroke:#d32f2f,stroke-width:2px; style NBA fill:#c8e6c9,stroke:#388e3c,stroke-width:2px;

Key Takeaway: Non-Blocking assignments (NBA) happen in the NBA Region, which is strictly after the Active region. This guarantees that all `always @(posedge clk)` blocks read the old values before any of them update.

The Golden Rules

Memorize This

  1. Use = for Combinational Logic always @(*)
  2. Use <= for Sequential Logic always @(posedge clk)
  3. NEVER mix = and <= in the same block.
  4. If you mix them, you will create race conditions that might work in simulation but fail in silicon.