Simulation
The source code for this example can be found in examples/simple_verilog
In the previous section Getting Started, we saw how to download SoCMake inside our project.
Let's try to run a simple Verilog testbench now with Icarus Verilog and/or Verilator.
For this step make sure you have Iverilog and/or Verilator installed on your system.
Lets create a simple verilog testbench file:
The same example is also available with VHDL, you can find the source code here, you can follow the same instructions than in this tutorial if you choose to do it with vhdl.
tb.v
The following testbench will just print something to the stdout.
module tb;
reg [4:0] a, b;
wire [4:0] o;
adder adder_i (
.NUM1(a),
.NUM2(b),
.SUM(o)
);
initial begin
a = 5;
b = 10;
#1;
$display("Hello world, from SoCMake build system\n");
$display("%d + %d = %d", a, b, o);
$finish();
end
endmodule
CMakeLists.txt
The following CMakeLists.txt will create a library, add sources and create verilator and iverilog Makefile targets.
cmake_minimum_required(VERSION 3.27)
project(simple_verilog_example CXX C)
include("../../SoCMakeConfig.cmake")
option_enum(SIMULATOR "Which simulator to use" "iverilog;vivado_sim;questa;xcelium;vcs;verilator;all" "iverilog")
if(SIMULATOR STREQUAL "all")
set(ALL_SIMS TRUE)
endif()
add_ip(tb
DESCRIPTION "Simple verilog testbench")
ip_sources(${IP} VERILOG
tb.v)
add_subdirectory(adder)
ip_link(${IP} adder)
if(SIMULATOR STREQUAL "iverilog" OR ALL_SIMS)
iverilog(${IP})
endif()
if(SIMULATOR STREQUAL "questa" OR ALL_SIMS)
questasim(${IP})
endif()
if(SIMULATOR STREQUAL "vivado_sim" OR ALL_SIMS)
vivado_sim(${IP})
endif()
if(SIMULATOR STREQUAL "xcelium" OR ALL_SIMS)
xcelium(${IP})
endif()
if(SIMULATOR STREQUAL "verilator" OR ALL_SIMS)
verilator(${IP} MAIN VERILATOR_ARGS --timing)
endif()
if(SIMULATOR STREQUAL "vcs" OR ALL_SIMS)
vcs(${IP})
endif()
help()
add_ip()
We are creating an IP library called tb using add_ip() function.
Function add_ip takes the following arguments:
- First argument is positional NAME of the defined library
- VENDOR - name of the vendor (your company, organization, ...)
- LIBRARY - name of the library which the IP is part of
- VERSION - Version number of the IP
The library that will be created will hold a name <VENDOR>__<LIBRARY>__<NAME>__<VERSION>, there will be also a CMake alias library that is created with the name <VENDOR>::<LIBRARY>::<NAME>::<VERSION>.
In this a library called cern::ip::tb::0.0.1 is created.
This function also sets IP variable in the current scope from where it was called. The IP will hold the non alias full library name.
ip_sources()
To add design sources to the IP library, we can use ip_sources() function.
The function takes
- First argument is positional NAME of the library to add sources to.
- Second argument is positional TYPE and represents the file type to be added.
- Third argument is positional SOURCES and is a list of source files to be added
Using just the name of the library tb is possible only if the add_ip() call is in the same CMakeLists.txt (subdirectory), and it was the last library added.
In other cases you can use:
${IP}same astb, should be in same subdirectory and last library added- Full name
cern__ip__tb__0.0.1(always works, from any subdirectory) - Alias libray name
cern::ip::tb::0.0.1(always works, from any subdirectory)
iverilog()
This function will add a target to compile the Icarus Verilog testbench.
The name of the created Makefile target will be: ${IP}_iverilog in this case : cern__ip__tb__0.0.1_iverilog.
verilator()
This function will add a target to compile the Verilator testbench.
In this case because it is a Verilog only testbench, we are passing MAIN argument, to let Verilator create a main.cpp file for us.
For a custom C++ testbench checkout the next example.
The name of the created Makefile target will be: ${IP}_verilate in this case : cern__ip__tb__0.0.1_verilate.
Running the example
Video example
Checkout the video example below to see how to run the simulation.
Video demonstration
Instructions
To run the example we need to create a build directory as always:
mkdir build
cd build
Then we generate Makefiles with:
cmake ../
Now we have Makefile in the build directory, which contain generated targets.
The targets we are interested in are:
cern__ip__tb__0.0.1_iverilogcern__ip__tb__0.0.1_verilate
To run the simulation we can do:
make cern__ip__tb__0.0.1_iverilog # Compile with Icarus Verilog`
make cern__ip__tb__0.0.1_verilate -j$(nproc) # Compile with Verilator`
We can compile Verilator testbench with maximum threads with -j$(nproc) flag.
Once we execute one or both of these targets we will have the testbenches compiled as executables and available to run as a normal executables.
The executables will be present in ${PROJECT_BINARY_DIR} in this case build directory.
cern__ip__tb__0.0.1_ivcern__ip__tb__0.0.1_verilator_tb
Running with other tools
As you can see in the CMakeLists.txt, it's possible to use options to also try with other EDA tools that are compatible with SoCMake and that you would have on your machine.
The steps are similar except that when generating Makefiles, you should use the SIMULATOR argument when generating Makefiles (example for xcelium) :
cmake -DSIMULATOR="xcelium" ../
Keep in mind that as we are using the function help(), you can take a look at the following generated target, help_target and help_options, they contains information that are useful to know which argument to give when calling make or which options you want to choose if you have to regenerate Makefiles.