Verilog is a hardware description language and there is no requirement for designers to simulate their RTL designs to be able to convert them into logic gates. So why do we need to simulate?
Simulation is a technique of applying different input stimulus to the design at different times to check if the RTL code behaves the intended way. Essentially, simulation is a well-followed technique to verify the robustness of the design. It is also similar to how a fabricated chip will be used in the real world and how it reacts to different inputs. For example, the design above represents a positive edge detector with inputs clock and signal which are evaluated at periodic intervals to find the output pe as shown.
There are several EDA companies that develop simulators capable of figuring out the outputs for various inputs to the design. Verilog is defined in terms of a discrete event execution model and different simulators are free to use different algorithms to provide the user with a consistent set of results. The Verilog code is divided into multiple processes and threads and may be evaluated at different times in the course of a simulation.
module tb; reg clk; reg sig; always #5 clk = ~clk; // Process loops after every 5ns initial begin // Process starts at time 0ns sig = 0; #5 clk = 0; // Assign clk to 0 at time 5ns #15 sig = 1; // Assign sig to 1 at time 20ns (#5 + #15) #20 sig = 0; // Assign sig to 0 at time 40ns (#5 + #15 + #20) #15 sig = 1; // Assign sig to 1 at time 55ns (#5 + #15 + #20 + #15) #10 sig = 0; // Assign sig to 0 at time 65ns (#5 + #15 + #20 + #15 + #10) #20 $finish; // Finish simulation at time 85ns end endmodule
Simulations allow us to dump design and testbench signals into a waveform that can be graphically represented to analyze and debug functionality of the RTL design. The waveform shown below is obtained from an EDA tool and shows the progress of each signal with respect to time and is same as the timing diagram shown before.
Every change in the value of a variable or net is called an update event. And processes are sensitive to update events such that these processes are evaluated whenever the update event happens and is called an evaluation event. Because of the possibility of having multiple processes being evaluated arbitrarily, the order of changes has to be tracked in something called as an event queue. Naturally, they are ordered by the simulation time. Placement of a new event on the queue is called scheduling. Simulation time is used to refer to the time value maintained by the simulator to model the actual time it would take for the circuit being simulated. The time values for the example above are shown in nanoseconds
ns in the timing diagram.
module des; wire abc; wire a, b, c; assign abc = a & b | c; // abc is updated via the assign statement (process) whenever a, b or c change -> update event endmodule
The Verilog event queue is logically divided into five regions, and events can be added to any of them. However, it can be removed only from the active region.
|Active||Occur at the current simulation time, and can be processed in any order|
|Inactive||Occur at the current simulation time, but is processed after all active events are done|
|Nonblocking||Evaluated at some previous time, but assignment is done in the current simulation time after active and inactive events are done|
|Monitor||Processed after all the active, inactive and non-blocking events are done|
|Future||Occur at some future simulation time|
A simulation cycle is where all active events are processed. The standard guarantees a certain scheduling order except for a few cases and. For example, statements inside a begin-end block will only be executed in the order in which they appear.
module tb; reg [3:0] a; reg [3:0] b; initial begin // Statements are executed one after the other at appropriate simulation times a = 5; // At time 0ns, a is assigned 5 b = 2; // In the same simulation step (time 0ns), b is assigned 2 #10 a = 7; // When simulation advances to 10ns, a is assigned 7 end endmodule
The event queue defines that assignment to b should happen after assignment to a.