Skip to main content

Simulation

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:

tb.v

The following testbench will just print something to the stdout.

tb.v
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.

CMakeLists.txt
cmake_minimum_required(VERSION 3.25)
project(simple_verilog_example NONE)

include("../../SoCMakeConfig.cmake")

option_enum(SIMULATOR "Which simulator to use" "iverilog;vivado_sim;questa;modelsim;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 SIMULATOR STREQUAL "modelsim" OR ALL_SIMS)
modelsim(${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
info

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 as tb, 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_iverilog
  • cern__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_iv
  • cern__ip__tb__0.0.1_verilator_tb