Verilog design and testbench typically have many lines of code comprising of always
or initial
blocks, continuous assignments and other procedural statements which become active at different times in the course of a simulation.
Event Types
- Update Events: Triggered by changes in signal values.
- Evaluation Events: Occur when processes like
always
orassign
blocks are evaluated due to update events in any arbitrary order.
Since these events can happen at different times, they are better managed and ensured of their correct order of execution by scheduling them into event queues that are arranged by simulation time.
module tb;
reg a, b, c;
wire d;
// 'always' is a process that gets evaluated when either 'a' or 'b' is updated.
// When 'a' or 'b' changes in value it is called an 'update event'. When 'always'
// block is triggered because of a change in 'a' or 'b' it is called an evaluation
// event
always @ (a or b) begin
c = a & b;
end
// Here 'assign' is a process which is evaluated when either 'a' or 'b' or 'c'
// gets updated
assign d = a | b ^ c;
endmodule
Event Queue
A simulation step can be segmented into four different regions. An active event queue is just a set of processes that need to execute at the current time which can result in more processes to be scheduled into active or other event queues. Events can be added to any of the regions, but always removed from the active region. When all events in the active queue for the current time step has been executed, the simulator advances time to the next time step and executes its active queue.
Active Region
Contains events that are currently being processed. These events can be executed in any order. All active events occur at the current simulation time.
Inactive Region
Holds normally scheduled events that are set to execute after all active events have been processed. Events in this region are evaluated after the active region is complete.
Nonblocking Assignment (NBA) Region
This region handles nonblocking assignments made during the active region, ensuring they are assigned after all active and inactive events have been processed.
Monitor Region
Processes monitoring tasks such as $monitor
and $strobe
, which report values after all other events have been executed.
Example
module tb;
reg x, y, z
initial begin
#1 x = 1;
y = 1;
#1 z = 0;
end
endmodule
Simulation starts at time 0, and the first statement is scheduled to be executed when simulation time reaches 1 time unit at which it assigns x and y to 1. This is the active queue for the current time which is 1 time unit. Simulator then schedules the next statement after 1 more time unit at which z is assigned 0.
Event Processing Flow
When the simulator advances through time, it follows this general flow:
- Execute all events in the active region.
- Move any remaining events from the inactive region to the active region for processing.
- Process nonblocking assignments from the NBA region.
- Finally, handle any monitoring tasks in the monitor region.
What makes simulation nondeterministic ?
There can be race conditions during simulation that end up giving different outputs for the same design and testbench. One of the reasons for nondeterministic behavior is because active events can be removed from the queue and processed in any order.