SystemVerilog event scheduling is the set of rules that decides the order in which a simulator executes everything that happens at the same simulation time. Each time step is divided into regions; statements are placed into regions according to their type, and the simulator empties the regions in a fixed order. Understanding the regions explains race conditions, why non-blocking assignments behave as they do, and how a testbench samples and drives signals without racing the design.
Why regions are needed
Hardware is concurrent, but a simulator runs one thing at a time. When a clock edge arrives, dozens of always blocks, continuous assignments, assertions and testbench processes are all ready to run “now”. Without a defined order, results would depend on the simulator, which is what the scheduling regions prevent.
The time-step regions
The IEEE 1800 standard defines a sequence of regions per time step. The important ones, in execution order:
| Region | What runs here |
|---|---|
| Preponed | Sampling of values for concurrent assertions and clocking-block inputs, before anything in the time step changes |
| Active | Blocking assignments, continuous assignments, $display, evaluation of right-hand sides of non-blocking assignments, primitive and gate evaluation |
| Inactive | Processes delayed with #0 (to be avoided) |
| NBA (non-blocking assignment) | Updates of the left-hand sides of non-blocking assignments |
| Observed | Evaluation of concurrent assertions |
| Reactive | Testbench code in program blocks, clocking-block output drives, assertion action blocks |
| Re-inactive | #0 delays in the reactive region |
| Re-NBA | Non-blocking assignments made in the reactive region |
| Postponed | $strobe, $monitor; final values of the time step |
The Active, Inactive and NBA regions loop: if an NBA update triggers another process, the simulator returns to the Active region, and repeats until nothing is left. Then it moves on to Observed, Reactive and Postponed, and finally advances time.
What this explains
Non-blocking assignments model flip-flops
In always @(posedge clk) q <= d; the right-hand side is evaluated in the Active region and the update happens in the NBA region, after every other Active-region process has read the old value. All flip-flops therefore sample before any of them changes, which is exactly how a register bank behaves. Using blocking assignments for registers breaks this and causes order-dependent results.
Races between testbench and design
If a testbench drives a DUT input with a blocking assignment at posedge clk, and the DUT samples that input at the same posedge clk, both are in the Active region and the order is undefined. Fixes: drive with non-blocking assignments, use a clocking block (which samples in Preponed and drives in Reactive), or put the testbench in a program block so it runs in the Reactive region after the design has settled.
$display vs $strobe vs $monitor
$display prints immediately in the Active region, so it may show pre-NBA values. $strobe and $monitor run in the Postponed region and show the final values of the time step.
Concurrent assertions sample stable values
Assertion expressions are sampled in the Preponed region, before the time step’s changes, so they see the values that were stable at the clock edge regardless of how the design and testbench are written.
Practical rules
- Use non-blocking assignments for all clocked registers and blocking for combinational logic.
- Drive and sample DUT signals from the testbench through clocking blocks, or at least with non-blocking assignments and a small skew.
- Do not use
#0to “fix” ordering; it hides races rather than removing them. - Use
$strobewhen you need end-of-time-step values in a log. - Check
!==rather than!=in self-checking code so X values are caught.
Learn verification properly
Scheduling semantics, clocking blocks, assertions and race-free testbench construction are part of the SystemVerilog module in our ASIC design verification course. See also SystemVerilog assertions and the Verilog testbench tutorial.
Frequently asked questions
What is the event scheduler in SystemVerilog?
The part of the simulator that orders all activity within one simulation time step into regions (Preponed, Active, NBA, Observed, Reactive, Postponed and others) and executes them in a defined order.
Why do non-blocking assignments update in a separate region?
So that all right-hand sides are evaluated with old values before any left-hand side changes, which models how flip-flops sample simultaneously on a clock edge.
What is the difference between the Active and Reactive regions?
Active holds design (module) code; Reactive holds testbench code from program blocks and clocking-block drives, so the testbench runs after the design settles.
What is a race condition in simulation?
A situation where two processes in the same region read and write the same signal in the same time step, so the result depends on execution order.
Should I use #0 delays to fix ordering?
No. They move a process to the Inactive region and hide the race instead of removing it. Use non-blocking assignments, clocking blocks or program blocks.
Share your question in comments or talk to our mentor team for batch guidance.
Ask the Admin Team
Drop your basic question in comments: eligibility, prerequisites, tools, fee range, and placement support.
Our team reviews and responds regularly.
