How to Simulate Verilog with Icarus Verilog
Goal: Load, compile, and simulate a Verilog/SystemVerilog design using the open-source Icarus Verilog simulator.
Note
This flow needs no Vivado or Xilinx license. It handles Verilog and SystemVerilog sources only; VHDL is a hard error (see Troubleshooting below). For a VHDL design, use How to Simulate VHDL with GHDL instead.
Prerequisites
Before running the simulation flow, ensure the following are in place:
Icarus Verilog 12.0 or newer, with
iverilog,iverilog-vpi, andvvpall on PATH. The version and tool checks run inmake load_source_code, before any source is loaded.gtkwaveinstalled for waveform viewing.For Rogue co-simulation designs only:
gcc,pkg-config,libzmq4.1.0 or newer, and a surf version that providessimlink/iverilog/.Project
Makefileincludessystem_iverilog.mk.
Makefile Setup
Add the following to your project Makefile:
include $(TOP_DIR)/submodules/ruckus/system_iverilog.mk
Set any knobs before the include line, the same way GHDLFLAGS is set
for the GHDL flow:
export VERILOG_INCDIRS = $(PROJ_DIR)/rtl/include
export VERILOG_DEFINES = SIM_SPEED_UP
export SIM_TOP = MyTb
include $(TOP_DIR)/submodules/ruckus/system_iverilog.mk
Switching simulators means changing only this include line to
system_verilator.mk; every other knob is shared between the two flows.
Loading Sources
A project’s ruckus.tcl tree is loaded the same way as every other ruckus
backend, through loadSource and loadRuckusTcl:
source $::env(RUCKUS_PROC_TCL)
# A package that other files in this directory depend on
loadSource -path "$::DIR_PATH/rtl/MyPkg.sv"
# The rest of the RTL sources in this directory
loadSource -dir "$::DIR_PATH/rtl"
# Testbench-only sources
loadSource -sim_only -dir "$::DIR_PATH/tb"
The loader (shared/verilog_proc.tcl) applies the following rules:
.vand.svfiles are compiled in load order: the orderloadSource/loadRuckusTclcalls occur across the whole recursive load, not alphabetical or directory order.loadSource -dirloads the directory’s.v/.sv/.vh/.svhfiles sorted by name, non-recursively.Duplicate files (loaded twice through different paths) are dropped by real path.
.vhand.svhfiles are never compiled; each loaded header’s directory is instead added as an include directory automatically.-libis accepted and ignored, since Verilog has no libraries.-sim_onlyis accepted and stripped; both flows compile everything in one filelist.Any
.vhd/.vhdlfile anywhere in the loaded tree is a hard error:load_source_codelists every offending path and exits before writing a filelist. If the VHDL came from surf,loadRuckusTcl $::env(MODULES)/surf/simlinkinstead of loading all of surf.No
BuildInfoPkg.vhd/BUILD_INFO_Cgeneric is generated: that mechanism is VHDL-only and would trip the rule above.
Icarus needs a package compiled before the files that use it: load the
package with -path before the -dir that references it. Loading
them in the wrong order surfaces as a syntax error at the import line
(see Troubleshooting). Verilator tolerates either order.
Steps
Load the source file list:
make load_source_codeChecks the Icarus version floor and the
iverilog-vpi/vvptools, loads the project’sruckus.tcltree, and writes the ordered, deduplicated filelist to$(OUT_DIR)/$(PROJECT).f.Build:
make buildRuns
iverilog $(IVERILOG_FLAGS) -I<dir>... -D<def>... -s $(SIM_TOP) -o $(SIM_TOP).vvp -c $(PROJECT).fin$(OUT_DIR). When a Rogue SimLink leaf is in the filelist, this step also builds the SimLink VPI module first (see Rogue Co-Simulation below).Run:
make tbRuns
vvp $(VVP_FLAGS) [-M$(OUT_DIR) -mRogueSimLink] $(SIM_TOP).vvp [-fst] $(SIM_PLUSARGS)in$(OUT_DIR).make tbdepends onbuild, which depends onload_source_code, which depends ondir(which itself depends onclean), so a baremake tbalways runs the whole chain from a clean$(OUT_DIR), exactly like the GHDL flow.View a waveform:
make gtkwaveSets
WAVES=1, runsmake tb, and opens$(OUT_DIR)/$(PROJECT).fstin GTKWave.Print environment variables:
make test
Clean the build directory:
make clean
Waveforms
The testbench owns waveform dumping; neither tool dumps anything on its own:
initial begin
$dumpfile("MyTb.fst");
$dumpvars;
end
WAVES=1 adds -fst to the vvp run, so the dump above is written in
FST format. make gtkwave sets WAVES=1 for you and opens the .fst
file. Without -fst, vvp still honors $dumpfile/$dumpvars but
writes plain VCD text under the .fst filename.
Rogue Co-Simulation
A design that instantiates surf’s flat SimLink wrappers, RogueTcpStreamWrap,
RogueTcpMemoryWrap, RogueSideBandWrap (loaded via loadRuckusTcl
$::env(MODULES)/surf/simlink; port and parameter contract documented as
plain text in surf’s simlink/sv/README.md, since it lives in another
repository), gets Rogue co-simulation automatically:
make builddetectsRogueTcpStream.sv,RogueTcpMemory.sv, orRogueSideBand.svin the filelist. If none is present, the rest of this section is skipped entirely and no libzmq check runs.When a leaf is detected,
make buildchecksgcc,pkg-config, andlibzmq>= 4.1.0, then runsmakein-tree in surf’ssimlink/iverilog/, copies the resultingRogueSimLink.vpiinto$(OUT_DIR), and runsmake cleanin the surf tree.make tbadds-M$(OUT_DIR) -mRogueSimLinkto thevvpcommand line so the VPI module loads at run time.No environment setup (no
setup_env.sh, noLD_LIBRARY_PATH) is needed; the module is found through-M$(OUT_DIR).A Rogue peer (a PyRogue client) connects to ports
NandN + 1of each wrapper’sPORT_NUM_Gonce the simulation is running.
Key Variables
Variable |
Default |
Description |
|---|---|---|
|
Top module name simulated by |
|
|
Flags passed to |
|
|
Flags passed to |
|
(empty) |
Whitespace-separated list of extra include directories, each added as
|
|
(empty) |
Whitespace-separated list of |
|
(empty) |
Plusargs appended verbatim after the |
|
(empty) |
Set to |
|
|
Backend selector read by |
|
|
Git dirty-state check bypassed by default. |
Note
Parameter overrides go through the -P<top>.<name>=<value> escape
hatch in IVERILOG_FLAGS rather than a dedicated variable:
export IVERILOG_FLAGS = -g2012 -PMyTb.WIDTH_G=16
Troubleshooting
- “VerilogCheckVersion: ruckus requires iverilog 12.0 or newer”
The Icarus Verilog on PATH is older than the enforced floor. There is no bypass variable; install Icarus Verilog 12.0 or newer.
- “VerilogCheckTool: … not found in PATH”
One of
iverilog,iverilog-vpi, orvvpis missing. Install Icarus Verilog and confirm all three binaries are on PATH.
“libzmq package was not found”
libzmq package was not found Please make sure that you have libzmq installed or have sourced the necessary rogue setup scriptsOnly appears for a design containing a Rogue SimLink leaf. Install the
libzmqdevelopment package sopkg-configcan find it.
- “VerilogWriteFilelist: VHDL sources are not supported …”
A
.vhd/.vhdlfile is somewhere in the loaded tree. Remove it, or if it came from surf,loadRuckusTcl $::env(MODULES)/surf/simlinkinstead of loading all of surf.- “VerilogWriteFilelist: no .v or .sv sources were loaded”
The loaded tree has no compilable sources; check that
loadSource/loadRuckusTclcalls actually resolve to a directory containing.v/.svfiles.- “syntax error” at an ``import`` line
A SystemVerilog package was loaded after the file that imports it. Move the package’s
loadSource -pathcall before theloadSource -dircall that references it (see Loading Sources above).- Waveform file is empty or missing
Confirm the testbench calls
$dumpfile/$dumpvarsand thatWAVES=1was set (make gtkwavesets it for you).make tbwithoutWAVES=1still writes a file under the.fstname, but as plain VCD text rather than FST.