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 or assign 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:

  1. Execute all events in the active region.
  2. Move any remaining events from the inactive region to the active region for processing.
  3. Process nonblocking assignments from the NBA region.
  4. 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.