Verilator is a Verilog hardware description language (HDL) simulator that can compile synthesisable Verilog code into C++ or SystemC. It is designed primarily for high-performance simulations, and supports simple assertions and code-coverage analysis. It is released under GNU LGPL/Perl artistic licence. You can install it on Fedora 20 (x86_64) using the following:
[stextbox id=”grey”]$ sudo yum install verilator[/stextbox]
Consider a simple hello.v example.
[stextbox id=”grey”]
module hello;
initial begin
$display(“Hello!”);
$finish;
end
endmodule
[/stextbox]
A C++ wrapper file is written to test drive the hello module.
[stextbox id=”grey”]
#include “Vhello.h”
#include
int
main (int argc, char **argv, char **env)
{
Verilated::commandArgs(argc, argv);
Vhello* top = new Vhello;
while (!Verilated::gotFinish()) { top -> eval(); }
exit (0);
}
[/stextbox]
You can compile the above code with Verilator and generate required simulation files with –cc.
[stextbox id=”grey”]$ verilator –cc hello.v –exe main.cpp[/stextbox]
Before running main.cpp, you need to install gcc and gcc-C++ in your system.
This produces obj_dir with Makefiles and C++ code. These generated files can then be compiled using the following:
[stextbox id=”grey”]
$ cd obj_dir
$ make -j -f Vhello.mk Vhello
g++ -I. -MMD -I/usr/share/verilator/include \
-I/usr/share/verilator/include/vltstd -DVL_PRINTF=printf \
-DVM_TRACE=0 -DVM_COVERAGE=0 \
-c -o main.o ../main.cpp
g++ -I. -MMD -I/usr/share/verilator/include \
-I/usr/share/verilator/include/vltstd -DVL_PRINTF=printf \
-DVM_TRACE=0 -DVM_COVERAGE=0 \
-c -o verilated.o /usr/share/verilator/include/
verilated.cpp
/usr/bin/perl /usr/share/verilator/bin/verilator_includer \
Vhello.cpp > Vhello__ALLcls.cpp
/usr/bin/perl /usr/share/verilator/bin/verilator_includer \
Vhello__Syms.cpp > Vhello__ALLsup.cpp
g++ -I. -MMD -I/usr/share/verilator/include \
-I/usr/share/verilator/include/vltstd -DVL_PRINTF=printf \
-DVM_TRACE=0 -DVM_COVERAGE=0 \
-c -o Vhello__ALLsup.o Vhello__ALLsup.cpp
g++ -I. -MMD -I/usr/share/verilator/include \
-I/usr/share/verilator/include/vltstd -DVL_PRINTF=printf \
-DVM_TRACE=0 -DVM_COVERAGE=0 \
-c -o Vhello__ALLcls.o Vhello__ALLcls.cpp
Archiving Vhello__ALL.a …
ar r Vhello__ALL.a Vhello__ALLcls.o Vhello__ALLsup.o
ar: creating Vhello__ALL.a
ranlib Vhello__ALL.a
g++ main.o verilated.o Vhello__ALL.a -o Vhello -lm
-lstdc++ 2>&1 | c++filt
[/stextbox]
You can test the hello module with the following command:
[stextbox id=”grey”]
$ cd ..
$ ./obj_dir/Vhello
Hello!
– hello.v:5: Verilog $finish
[/stextbox]
Verilator accepts a number of arguments as options. -V lists the version of the software and provides a summary of configuration and environment settings. A pre-processing output of the code is produced with -E, without actually compiling or generating any code. This is illustrated below.
[stextbox id=”grey”]
$ verilator -cc hello.v -E
`line 1 “hello.v” 1
module hello;
`line 3 “hello.v” 0
initial begin
$display(“Hello!”);
$finish;
end
endmodule
`line 9 “hello.v” 2
[/stextbox]
-CFLAGS allows the user to override any C++ compiler flags during the build process. For example,
[stextbox id=”grey”]$ verilator -cc hello.v -CFLAGS -O0 –exe main.cpp[/stextbox]
The respective flags are passed to the compiler in the generated Makefiles. -O0 disables optimisation. The user can explicitly specify the level of optimisation with -On, where n is an integer. The highest level of optimisation is -O3.
Verilator can also produce SystemC output with -sc. SystemVerilog support also exists, and the relevant code can be generated with -sv.
If you want to analyse the intermediate steps in the compilation process, you can use –dump-tree:
[stextbox id=”grey”]
$ verilator –dump-tree –cc hello.v –exe main.cpp
dot -Tps -o ~/a.ps obj_dir/Vhello_01_linkcells.dot
dot -Tps -o ~/a.ps obj_dir/Vhello_21_task_call.dot
dot -Tps -o ~/a.ps obj_dir/Vhello_33_gate_simp.dot
dot -Tps -o ~/a.ps obj_dir/Vhello_34_gate_opt.dot
dot -Tps -o ~/a.ps obj_dir/Vhello_40_orderg_pre.dot
dot -Tps -o ~/a.ps obj_dir/Vhello_41_orderg_acyc.dot
dot -Tps -o ~/a.ps obj_dir/Vhello_42_orderg_order.dot
dot -Tps -o ~/a.ps obj_dir/Vhello_43_orderg_domain.dot
dot -Tps -o ~/a.ps obj_dir/Vhello_44_ordermv_simpl.dot
dot -Tps -o ~/a.ps obj_dir/Vhello_45_orderg_done.dot
[/stextbox]
–cdc performs a clock domain crossing (CDC) analysis, which can be invoked on an input module as follows:
[stextbox id=”grey”]$ verilator -cc input.v –cdc[/stextbox]
Verilator can check for warnings. Consider the following Verilog code:
[stextbox id=”grey”]
module test;
wire a, b, c;
and (x, b, c);
endmodule
[/stextbox]
An implicit warning is produced by Verilator as shown below:
[stextbox id=”grey”]
$ verilator -cc lint.v –exe main.cpp
%Warning-IMPLICIT: lint.v:4: Signal definition not found,
creating implicitly: x
%Warning-IMPLICIT: Use “/* verilator lint_off IMPLICIT
*/” and lint_on around source to disable this message.
%Error: Exiting due to 1 warning(s)
%Error: Command Failed verilator_bin -cc lint.v –exe main.cpp
[/stextbox]
Lint warnings can be disabled with -Wno-lint.
[stextbox id=”grey”]$ verilator -cc lint.v –exe main.cpp -Wno-lint
$[/stextbox]
Verilator can also produce a useful statistics file with –stats. Vhello_stats.txt file is created in objdir for hello.v module.
[stextbox id=”grey”]$ verilator –cc hello.v –exe main.cpp –stats[/stextbox]
There also exists –profile-cfuncs that adds profiling code to the generated C++ files. Tools like gprof [2] can be used on the generated output to analyse the input Verilog code.
[stextbox id=”grey”]$ verilator -cc hello.v –exe main.cpp –profile-cfuncs
[/stextbox]
The following is a half-adder example:
[stextbox id=”grey”]
module ha(a, b, sum, carry);
input a;
input b;
output sum;
output carry;
assign carry = a & b;
assign sum = a ^ b;
endmodule
[/stextbox]
The simulation to test the half-adder example is given by main.cpp file.
[stextbox id=”grey”]
#include “Vhalfadder.h”
#include
#include “verilated_vcd_c.h”
unsigned int main_time = 0;
double sc_time_stamp () {
return main_time;
}
int
main (int argc, char **argv, char **env)
{
Verilated::commandArgs(argc, argv);
Vhalfadder* top = new Vhalfadder;
Verilated::traceEverOn(true);
VerilatedVcdC* tfp = new VerilatedVcdC;
top->trace (tfp, 99);
tfp->open (“counter.vcd”);
top -> sum = 0;
top -> carry = 0;
top -> a = 0;
top -> b = 0;
while (main_time < 5 && !Verilated::gotFinish()) { if ((main_time % 4) == 0) { top -> a = 0;
top -> b = 0;
}
if ((main_time % 4) == 1) {
top -> a = 1;
top -> b = 0;
}
if ((main_time % 4) == 2) {
top -> a = 0;
top -> b = 1;
}
if ((main_time % 4) == 3) {
top -> a = 1;
top -> b = 1;
}
top -> eval();
if (tfp) tfp -> dump(main_time);
main_time ++;
}
top -> final();
if (tfp) tfp -> close();
delete top;
exit(0);
}
[/stextbox]
The while loop handles the different cases for various combinations of the input. Steps to compile, build and test the half-adder example can be automated in a Makefile.
[stextbox id=”grey”]
TARGET=halfadder
all:
verilator -cc $(TARGET).v –exe sim_main.cpp –trace
build:
make -j -C obj_dir -f V$(TARGET).mk V$(TARGET)
test:
./obj_dir/V$(TARGET)
clean:
rm -rf obj_dir *~ *.vcd
[/stextbox]
Sources can be compiled with Verilator using make.
[stextbox id=”grey”]
$ make
verilator -cc halfadder.v –exe_main.cpp –trace
[/stextbox]
The generated C++ code is then built using the following: