Design for Simulations
This page is an introduction of the Simulation feature on the WebIDE, with some introductory examples of using and designing simulation testbench files.
Last updated
This page is an introduction of the Simulation feature on the WebIDE, with some introductory examples of using and designing simulation testbench files.
Last updated
Click here for:
Simulation in the context of digital systems and Verilog design is an important and highly useful process. It involves creating a virtual model of a digital circuit to test and analyze its behavior and responses before implementing it on an actual hardware.
Simulation is an useful technique because it allows hardware (digital) designers to verify the functionality and performance of their designs under different conditions without the expense of physically building the circuit first. By simulating, designers can identify and rectify errors, analyze internal modular behaviors, optimize overall design performance, and ensure reliability, thereby reducing development time, cost, and the likelihood of failure in the actual hardware.
To write a testbench, we need to understand the inputs, outputs, functionality and timing requirements of the module under test, or MUT.
For example, in Chapter 3 we designed a one-bit full-adder, module named as full_adder which has an internal structure as given below:
This is the module we want to test & simulate, which means that it requires 3 excitation inputs to observe the output response. Since this is a combinational circuit, there is no specific timing requirements.
To start off testbench design, we will create a module called fulladder_tb ( ), with three inputs and two outputs as given:
Note that the inputs are set as reg and outputs are wire.
You also noticed we have this line at the top:
In Verilog, the timescale
directive is used to specify the time unit and time precision for the simulation of the design. This directive is important for modeling delays and specifying how the simulator interprets time literals in the Verilog code.
For this above timescale 1ns / 1ps
directive, it means:
1ns
: This is the time unit. It tells the simulator that each time unit in the simulation will be interpreted as 1ns. So if you write timescale 5ns / 1ps instead, it means a delay of 5ns in the simulation.
1ps
: This is the time precision. It sets the smallest time increment that the simulator can resolve. In this case, 1ps is the smallest time increment for the simulation. Obviously the precision should be the same or smaller than the unit.
For both time unit and time precision, only use numbers in power of 10, such as 1, 10, 100; avoid using other integers or decimal numbers. This is because simulation time in Verilog is handled in decimal units only.
Inside the testbench, instantiate the MUT and connect the testbench vectors (reg for inputs, wire for outputs) to the inputs and outputs of the MUT.
Write an initial block to apply test vectors to the MUT's inputs and simulate its behavior.
Verilog provides an array of tasks and functions specifically tailored to enhance the verification process, enabling thorough testing and efficient debugging within simulation frameworks. You will see the senarios of some of these functions in later examples.
$display
: Used for displaying information in the simulation. It automatically moves to a new line after the output.
$write
: Similar to $display
but does not automatically append a newline at the end. It is used for displaying information in the simulation without moving to a new line.
$strobe
: Similar to $display
, this function is used for displaying information, but it outputs the values at the end of the simulation time step, which can be useful for debugging.
$monitor
: This task continuously displays information whenever a change occurs in the variables specified in its argument list. It's typically used for ongoing observation of variables.
$stop
: Temporarily stops the simulation and allows for interactive debugging.
$finish
: Terminates the simulation run completely.
$time
: Returns the current simulation time.
$random
: Returns a random number each time it is called. This can be useful for generating random test vectors.
$readmemb
: Reads binary data from a file into a memory array. It's often used to initialize memory contents in a simulation.
Ensure the Module Under Test is completed, this means you need to properly design the module first and save it.
In general, the testbench file is named xxx_tb to indicate it is a testbench. For example, the MUT is named full_adder, therefore we call this testbench as: fulladder_tb. But you can name whatever way you preferred. Make sure you set this file as "simulation file".
Here we need to write the testbench. Since we have done this in STEP 4: Stimulus Initialization, so we will copy & paste the code here. Click Save. Note that you must complete STEP 2 before Save the simulation file.
Simulation is implemented on IDE computations therefore you DO NOT need logic synthesis, pin assignment and FPGA mapping. Click Simulation, choose the testbench file fulladder_tb, and input simulation time.
If you testbench is incorrect, you may expect longer running time, and the run.log will indicate the line of code that results the error. For example, this log says "the design unit was not found", this is because I intentionally instantiate a wrong name "full_adderWrong".
In code 3.7, we wrote a divider_integer module to divider the base frequency by 12,000,000 times to generate 1Hz clock.
So we design a testbench as given below. Note that while instantiate the MUT, we reduce the divisor to N = 10 to save some computational power of the IDE simulation tool.
To run the simulation, we got the result as shown below. However, the parameters cnt and clkout have no signals shown.
This is because we need to initialize the register for cnt. To do so, we add an initialization for cnt to have it starting from 0.
Now we save the modified MUT, and run the simulation again, this is the result we have:
Here is the testbench we designed:
Notice that this time we used:
to instruct the simulation tracking the LEDs registers and plot them against clock.
Do not forget that we also need to initialize other registers such as cnt and state. We have also changed CNT_NUM to 10 for quicker simulation purposes. Here is the modified MUT code.
Here is the output simulation result. You can scroll in to examine the detailed signal changing in all variables (registers)
Now let us try another example. This time we will design a testbench for the LEDchaser module implemented in Chapter 5.