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 what is the 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. Simulation allows us to view the timing diagram of related signals to understand how the design description in Verilog actually behaves.

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, which will be touched upon later.
Example
The testbench called tb is a container to hold a design module. However, in this example we have not used any design instances. There are two variables or signals that can be assigned certain values at specific times. clk represents a clock which is generated within the testbench. This is done by the always
statement by alternating the clock's value after every 5ns. The initial
block contains a set of statements that assign different values to both the signals at different times.
module tb;
reg clk;
reg sig;
// Clock generation
// Process starts at time 0ns and loops after every 5ns
always #5 clk = ~clk;
// Initial block : Process starts at time 0ns
initial begin
// This system task will print out the signal values everytime they change
$monitor("Time = %0t clk = %0d sig = %0d", $time, clk, sig);
// Also called stimulus, we simply assign different values to the variables
// after some simulation "delay"
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
The simulator provides the following output after execution of the above testbench.
ncsim> run Time = 0 clk = x sig = 0 Time = 5 clk = 0 sig = 0 Time = 10 clk = 1 sig = 0 Time = 15 clk = 0 sig = 0 Time = 20 clk = 1 sig = 1 Time = 25 clk = 0 sig = 1 Time = 30 clk = 1 sig = 1 Time = 35 clk = 0 sig = 1 Time = 40 clk = 1 sig = 0 Time = 45 clk = 0 sig = 0 Time = 50 clk = 1 sig = 0 Time = 55 clk = 0 sig = 1 Time = 60 clk = 1 sig = 1 Time = 65 clk = 0 sig = 0 Time = 70 clk = 1 sig = 0 Time = 75 clk = 0 sig = 0 Time = 80 clk = 1 sig = 0 Simulation complete via $finish(1) at time 85 NS + 0
What is a simulation waveform ?
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
Refresh Verilog and see an example !
Regions in event queue
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.
Events | Description |
---|---|
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.