Direct Usage of iVerilog
How We Used to Do Things
Please Log In for full access to the web site.
Note that this link will take you to an external site (https://shimmer.mit.edu) to authenticate, and then you will be redirected back to this page.
For Fall 2024, we'll typically be using cocotb to run iVerilog, so that we can make use of our Python testbenches. However, iVerilog can also be run directly, so long as you have a testbench written in SystemVerilog. The SystemVerilog we typically write is intended for synthesis, describing a hardware design; however, Verilog also has the capacity to specify simulations. Below, you can see an example of such a SystemVerilog testbench, get it running, and see the waveforms generated by running the test.
Prior to following the instructions here, make sure you have both iVerilog and GTKWave installed!
Simulation
Make a folder, and create the following files inside it:
example.sv
: A file that will contain a very simple module for us to testexample_tb.sv
: A testbench for example.sv, where we'll have some code to test the module inexample.sv
Copy the following code into example.sv
:
`timescale 1ns / 1ps
`default_nettype none
// a simple module that will increment c_out on the clock edge if a_in < b_in
// otherwise, c_out doesn't change.
module example (
input wire clk_in,
input wire rst_in,
input wire [5:0] a_in,
input wire [5:0] b_in,
output logic [4:0] c_out);
always_ff @(posedge clk_in) begin
if (rst_in) begin
// reset to 0
c_out <= 0;
end else begin
c_out <= (a_in > b_in) ? c_out : c_out + 1;
end
end
endmodule
`default_nettype wire
Copy the following code into example_tb.sv
:
`timescale 1ns / 1ps
`default_nettype none
module example_tb();
logic clk_in;
logic rst_in;
logic [5:0] a_in, b_in;
logic [4:0] c_out
example uut( .clk_in(clk_in),
.rst_in(rst_in),
.a_in(a_in),
.b_in(b_in),
.c_out(c_out));
always begin
#5; //every 5 ns switch...so period of clock is 10 ns...100 MHz clock
clk_in = !clk_in;
end
//initial block...this is our test simulation
initial begin
$dumpfile("example.vcd"); //file to store value change dump (vcd)
$dumpvars(0,example_tb); //store everything at the current level and below
$display("Starting Sim"); //print nice message at start
clk_in = 0; //0 is generally a safe value to initialize with and not specify size
rst_in = 0;
a_in = 0;
b_in = 0;
#10
rst_in = 1; //always good to reset
#10
rst_in = 0;
$display("a_in b_in c_out");
for (integer i=0; i<256; i = i+1)begin
for (integer j=0; j<256; j= j+1)begin
a_in = i;
b_in = j;
#10; //wait
$display("%8b %8b %5c",a_in, b_in, c_out); //print values C-style formatting
end
end
$display("Finishing Sim"); //print nice message at end
$finish;
end
endmodule
`default_nettype wire
Once your source files are ready, we'll use this command to run the simulation:
iverilog -g2012 -o example.out example_tb.sv example.sv
Let's break down what each component means:
iverilog
calls the program-g2012
tells iVerilog to read the source files as SystemVerilog (specifically the SystemVerilog defined in IEEE 1800-2012)-o example.out
places the simulation output in the fileexample.out
example_tb.sv
andexample.sv
, are the source files that contain what we want to simulate. If you had other source files, they'd go here too.
When you run this command, you should get an error message like this:
example_tb.sv:10: syntax error
example_tb.sv:15: error: invalid module item.
This is an example of how/when/where a compile-time-error in your Verilog would show up. In this case, line 8 in example_tb.sv
is missing a semicolon at the end:
logic [4:0] c_out
Fix that, then rerun the simulation with the same command. If nothing shows up, good! This means the compiler ran with no errors. Inside your folder there should now a file called example.out
, which is a compiled version of your testbench.
When we run the iverilog
command, we used iVerilog to compile your Verilog testbench to an executable file. Now, We'll now want to run that executable file inside vvp
, which is the Verilog runtime that comes bundled with iVerilog. When we do this, we simulate the module, logging the values of the internal signals along the way. Go ahead and try this with:
vvp example.out
You should see tons of values fly by - these are the output of the $display
statements we made in the testbench. These statements are super useful for debugging, and we can view our results as waveforms too.
Viewing Waveforms
In addition to the text output from our simulation, we can also view the signals in the design as waveforms. Our simulation actually already generated the Value Change Dump (VCD) file needed for this, as you can see from the following lines in the testbench:
// store the simulation output as a Value Change Dump (VCD) file
$dumpfile("example.vcd");
// store everything at the current level and below
$dumpvars(0, example_tb);
This tells the simulator to store the value of every signal in the simulation at every time step. This goes into a .vcd
file, which is a standard digital signal file format that's viewable with a waveform viewer. There's multiple ones out there, but we recommend GTKWave since it is cross-platform and is relatively suck-free. 1 If you're on Windows or Linux, from a terminal you can just run:
gtkwave example.vcd
This will likely not work for people on MacOS - for some reason GTKWave doesn't like being called from the command line. Assuming it actually installed, you can probably get away with launching it from your Launchpad, and then opening .vcd
file from there.
Regardless, once you've loaded this vcd file, you can add your signals to the waveform viewer by highlighting which modules and signals you care about, clicking "Insert" and then, moving around/zooming, etc. If you later make a change to your code, you can then just simply:
- Recompile with
iverilog -g2012 -o example.out example_tb.sv example.sv
- Run simulation with
vvp example.out
- Reload the waveform from the file menu in GTKWave. Easy peasy.