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 :doc:`ghdl_simulation` instead. Prerequisites ------------- Before running the simulation flow, ensure the following are in place: - Icarus Verilog 12.0 or newer, with ``iverilog``, ``iverilog-vpi``, and ``vvp`` all on PATH. The version and tool checks run in ``make load_source_code``, before any source is loaded. - ``gtkwave`` installed for waveform viewing. - For Rogue co-simulation designs only: ``gcc``, ``pkg-config``, ``libzmq`` 4.1.0 or newer, and a surf version that provides ``simlink/iverilog/``. - Project ``Makefile`` includes ``system_iverilog.mk``. Makefile Setup -------------- Add the following to your project ``Makefile``: .. code-block:: 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: .. code-block:: makefile 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``: .. code-block:: tcl 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: - ``.v`` and ``.sv`` files are compiled in **load order**: the order ``loadSource``/``loadRuckusTcl`` calls occur across the whole recursive load, not alphabetical or directory order. - ``loadSource -dir`` loads the directory's ``.v``/``.sv``/``.vh``/``.svh`` files sorted by name, non-recursively. - Duplicate files (loaded twice through different paths) are dropped by real path. - ``.vh`` and ``.svh`` files are never compiled; each loaded header's directory is instead added as an include directory automatically. - ``-lib`` is accepted and ignored, since Verilog has no libraries. - ``-sim_only`` is accepted and stripped; both flows compile everything in one filelist. - Any ``.vhd``/``.vhdl`` file anywhere in the loaded tree is a **hard error**: ``load_source_code`` lists every offending path and exits before writing a filelist. If the VHDL came from surf, ``loadRuckusTcl $::env(MODULES)/surf/simlink`` instead of loading all of surf. - No ``BuildInfoPkg.vhd``/``BUILD_INFO_C`` generic 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 ----- 1. **Load the source file list:** .. code-block:: bash make load_source_code Checks the Icarus version floor and the ``iverilog-vpi``/``vvp`` tools, loads the project's ``ruckus.tcl`` tree, and writes the ordered, deduplicated filelist to ``$(OUT_DIR)/$(PROJECT).f``. 2. **Build:** .. code-block:: bash make build Runs ``iverilog $(IVERILOG_FLAGS) -I... -D... -s $(SIM_TOP) -o $(SIM_TOP).vvp -c $(PROJECT).f`` in ``$(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). 3. **Run:** .. code-block:: bash make tb Runs ``vvp $(VVP_FLAGS) [-M$(OUT_DIR) -mRogueSimLink] $(SIM_TOP).vvp [-fst] $(SIM_PLUSARGS)`` in ``$(OUT_DIR)``. ``make tb`` depends on ``build``, which depends on ``load_source_code``, which depends on ``dir`` (which itself depends on ``clean``), so a bare ``make tb`` always runs the whole chain from a clean ``$(OUT_DIR)``, exactly like the GHDL flow. 4. **View a waveform:** .. code-block:: bash make gtkwave Sets ``WAVES=1``, runs ``make tb``, and opens ``$(OUT_DIR)/$(PROJECT).fst`` in GTKWave. 5. **Print environment variables:** .. code-block:: bash make test 6. **Clean the build directory:** .. code-block:: bash make clean Waveforms --------- The testbench owns waveform dumping; neither tool dumps anything on its own: .. code-block:: verilog 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 build`` detects ``RogueTcpStream.sv``, ``RogueTcpMemory.sv``, or ``RogueSideBand.sv`` in 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 build`` checks ``gcc``, ``pkg-config``, and ``libzmq`` >= 4.1.0, then runs ``make`` in-tree in surf's ``simlink/iverilog/``, copies the resulting ``RogueSimLink.vpi`` into ``$(OUT_DIR)``, and runs ``make clean`` in the surf tree. - ``make tb`` adds ``-M$(OUT_DIR) -mRogueSimLink`` to the ``vvp`` command line so the VPI module loads at run time. - No environment setup (no ``setup_env.sh``, no ``LD_LIBRARY_PATH``) is needed; the module is found through ``-M$(OUT_DIR)``. - A Rogue peer (a PyRogue client) connects to ports ``N`` and ``N + 1`` of each wrapper's ``PORT_NUM_G`` once the simulation is running. Key Variables ------------- .. list-table:: :header-rows: 1 :widths: 22 15 63 * - Variable - Default - Description * - :envvar:`SIM_TOP` - ``$(PROJECT)`` - Top module name simulated by ``vvp``. * - :envvar:`IVERILOG_FLAGS` - ``-g2012`` - Flags passed to ``iverilog``. Override before the include line to change the Verilog/SystemVerilog standard. * - :envvar:`VVP_FLAGS` - ``-n`` - Flags passed to ``vvp``. * - :envvar:`VERILOG_INCDIRS` - (empty) - Whitespace-separated list of extra include directories, each added as ``-I``. * - :envvar:`VERILOG_DEFINES` - (empty) - Whitespace-separated list of ``NAME`` or ``NAME=VAL`` words, each added as ``-D``. Values containing spaces are not supported. * - :envvar:`SIM_PLUSARGS` - (empty) - Plusargs appended verbatim after the ``.vvp`` file on the ``vvp`` command line. * - :envvar:`WAVES` - (empty) - Set to ``1`` to add ``-fst`` to the ``vvp`` run. * - :envvar:`RUCKUS_SIM_BACKEND` - ``iverilog`` - Backend selector read by ``surf/simlink/ruckus.tcl``. * - :envvar:`GIT_BYPASS` - ``1`` - Git dirty-state check bypassed by default. .. note:: Parameter overrides go through the ``-P.=`` escape hatch in :envvar:`IVERILOG_FLAGS` rather than a dedicated variable: .. code-block:: makefile 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``, or ``vvp`` is missing. Install Icarus Verilog and confirm all three binaries are on PATH. **"libzmq package was not found"** .. code-block:: text libzmq package was not found Please make sure that you have libzmq installed or have sourced the necessary rogue setup scripts Only appears for a design containing a Rogue SimLink leaf. Install the ``libzmq`` development package so ``pkg-config`` can find it. **"VerilogWriteFilelist: VHDL sources are not supported ..."** A ``.vhd``/``.vhdl`` file is somewhere in the loaded tree. Remove it, or if it came from surf, ``loadRuckusTcl $::env(MODULES)/surf/simlink`` instead of loading all of surf. **"VerilogWriteFilelist: no .v or .sv sources were loaded"** The loaded tree has no compilable sources; check that ``loadSource`` / ``loadRuckusTcl`` calls actually resolve to a directory containing ``.v``/``.sv`` files. **"syntax error" at an ``import`` line** A SystemVerilog package was loaded after the file that imports it. Move the package's ``loadSource -path`` call before the ``loadSource -dir`` call that references it (see Loading Sources above). **Waveform file is empty or missing** Confirm the testbench calls ``$dumpfile``/``$dumpvars`` and that ``WAVES=1`` was set (``make gtkwave`` sets it for you). ``make tb`` without ``WAVES=1`` still writes a file under the ``.fst`` name, but as plain VCD text rather than FST.