19. Introduction to Simulation Semantics
Verilog simulation operates through a sophisticated event-driven scheduling mechanism. Understanding timing regions is crucial for writing race-free code, avoiding simulation-synthesis mismatches, and creating reliable hardware descriptions.
What are Timing Regions?
Timing regions (also called scheduling regions or event queues) are distinct phases within a simulation time step where different types of events are processed. The Verilog simulator maintains separate queues for different event types and processes them in a strictly defined order.
Why Understanding Timing Regions Matters
-
Race Condition Avoidance: Prevent order-dependent behavior
-
Blocking vs Non-Blocking: Understand why NBA prevents races
-
Proper Testbench Writing: Know when signals update
-
Debug Simulation Issues: Understand event ordering
-
Synthesizable Code: Write hardware-equivalent code
Simulation Time Step
A time step is the smallest unit of simulation time. All events scheduled at the same time are processed through the timing regions in order. Once all regions are processed for a given time, simulation advances to the next time with scheduled events.
The Five Timing Regions
According to IEEE 1364 Verilog standard, simulation divides each time step into five distinct regions:
.png)
.png)
19.1. Active Region (Region 1)
The Active Region is where most simulation activity occurs. It processes events that execute immediately at the current simulation time.
Events in Active Region
-
Blocking assignments (=): Execute and update immediately
-
Continuous assignments (assign): Evaluate when RHS changes
-
Primitive outputs: Gate-level model updates
-
$display: Print current values
-
RHS of NBAs: Evaluate right-hand side (store for later)
Active Region Example
Verilog
Output:
Time=0 a=0 b=0 out=0
Time=10 a=1 b=1 c=1 out=1
Active Region Characteristics
-
Immediate execution: Events execute as soon as encountered
-
Order-dependent: Execution order matters for blocking
-
Iterative: Can trigger more active events (loops)
-
Race-prone: Multiple blocking assignments can race
19.2. Inactive Region (Region 2)
The Inactive Region processes events with explicit #0 (zero-delay) timing control. These events execute at current time but AFTER all active events complete.
Purpose of #0 Delay
The #0 delay doesn't advance simulation time but defers execution to the inactive region. This is useful for:
-
Ensuring other active events complete first
-
Creating deterministic ordering
-
Breaking combinational loops in simulation
-
Sampling stable values in testbenches
Inactive Region Examples
Verilog
Timing Diagram: Active vs Inactive
EXECUTION ORDER AT TIME T=10:
Time T=10
│
├─► ACTIVE REGION
│ │
│ ├─ a = 1'b1; // Executes first
│ ├─ c = a & b; // Sees new 'a'
│ └─ $display(...); // Shows updated values
│
├─► INACTIVE REGION
│ │
│ └─ #0 d = a; // Executes after active
│
├─► NBA REGION (if any)
│
└─► Time advances to next event
19.3. NBA Region (Region 3)
The NBA (Non-Blocking Assignment) region is where all non-blocking assignments (<=) update their left-hand sides. This is the KEY to avoiding race conditions in sequential logic.
Two-Step Process
Non-blocking assignments execute in two stages:
-
Step 1 (Active Region): Evaluate RHS, store result
-
Step 2 (NBA Region): Update LHS with stored result
CRITICAL: ALL non-blocking assignments update SIMULTANEOUSLY in the NBA region. This prevents race conditions.
Why NBA Prevents Races
Verilog
NBA Execution Timeline
NON-BLOCKING ASSIGNMENT TIMELINE:
@posedge clk (Time T)
│
├─► ACTIVE REGION
│ │ // Evaluate all RHS expressions
│ ├─ Evaluate: data_in → store result A
│ ├─ Evaluate: shift_reg[0] (OLD) → store result B
│ └─ Evaluate: shift_reg[1] (OLD) → store result C
│
├─► INACTIVE REGION (if any #0)
│
├─► NBA REGION ← SIMULTANEOUS UPDATES
│ │
│ ├─ shift_reg[0] ← result A ┐
│ ├─ shift_reg[1] ← result B ├─ All at same instant
│ └─ shift_reg[2] ← result C ┘
│
└─► MONITOR REGION
$strobe sees final values after NBA
Industry Example: Pipeline Stage
Verilog
19.4. Posponed Region (Region 4)
The Monitor Region executes $monitor and $strobe statements. These execute AFTER all updates are complete, ensuring they display final values.
$display vs $strobe vs $monitor
.png)
Practical Example
Verilog
Output:
[$display] T=10: q=0 ← Active region, q not updated yet
[$strobe] T=10: q=1 ← Monitor region, q updated
Race Conditions and Solutions
Race conditions occur when the order of execution affects the result, making simulation non-deterministic.
Understanding timing regions is key to avoiding races.
Common Race Scenarios
Race #1: Write-Write Race
Verilog
Race #2: Read-Write Race
Verilog
Golden Rules to Avoid Races
-
Rule 1: Use NBA (<=) for all sequential logic
-
Rule 2: Use blocking (=) for combinational logic
-
Rule 3: Don't mix NBA and blocking in same always block
-
Rule 4: Don't write same variable from multiple always blocks
-
Rule 5: Use $strobe in testbenches to see final values
Complete Execution Flow
Detailed Example with All Regions
Verilog
Execution Order Diagram
COMPLETE EXECUTION AT @posedge clk:
1. ACTIVE REGION
├─ Evaluate: assign w = a & b (if a or b changed)
├─ Execute: c = a (blocking, immediate)
├─ Execute: $display Active (immediate print)
├─ Evaluate RHS: b → store for d (NBA step 1)
├─ Evaluate RHS: c → store for e (NBA step 1)
└─ Schedule: #0 $display for inactive
└─ Schedule: $strobe for monitor
2. INACTIVE REGION
└─ Execute: $display Inactive (d,e still old)
3. NBA REGION ← SIMULTANEOUS
├─ Update: d ← stored value
└─ Update: e ← stored value
4. MONITOR REGION
└─ Execute: $strobe Monitor (d,e updated)
Best Practices
Coding Guidelines
.png)
