How to Simulate Verilog with Verilator ======================================== **Goal:** Load, compile, and simulate a Verilog/SystemVerilog design using the open-source Verilator 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: - Verilator 5.020 or newer, on PATH. The ``--binary``/``--timing`` flags this flow relies on require timing support, which is not present in older releases. The version check runs 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/verilator/``. - Project ``Makefile`` includes ``system_verilator.mk``. Makefile Setup -------------- Add the following to your project ``Makefile``: .. code-block:: makefile include $(TOP_DIR)/submodules/ruckus/system_verilator.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_verilator.mk Switching simulators means changing only this include line to ``system_iverilog.mk`` (see :doc:`iverilog_simulation`); 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``, using the same shared loader as the Icarus Verilog flow: .. 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. Verilator tolerates either package/user order (unlike Icarus, see :doc:`iverilog_simulation`). - ``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. Steps ----- 1. **Load the source file list:** .. code-block:: bash make load_source_code Checks the Verilator version floor, 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 ``verilator $(VERILATOR_FLAGS) [--trace-fst] +incdir+... +define+... --top-module $(SIM_TOP) --Mdir $(OUT_DIR) -o V$(SIM_TOP) -f $(PROJECT).f [/libRogueSimLinkDpi.so -LDFLAGS "-Wl,-rpath,$(OUT_DIR) "]`` in ``$(OUT_DIR)``. When a Rogue SimLink leaf is in the filelist, this step also builds and links the SimLink DPI library first (see Rogue Co-Simulation below). 3. **Run:** .. code-block:: bash make tb Runs ``./V$(SIM_TOP) $(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; Verilator does not dump anything on its own: .. code-block:: verilog initial begin $dumpfile("MyTb.fst"); $dumpvars; end ``WAVES=1`` adds ``--trace-fst`` to the ``verilator`` build, so the dump above is written in FST format. ``make gtkwave`` sets ``WAVES=1`` for you and opens the ``.fst`` file. .. note:: Verilator's FST writer needs the ``lz4`` (and ``zlib``) development headers installed. Without them, ``make build WAVES=1`` stops with ``fatal error: lz4.h: No such file or directory`` (see Troubleshooting). 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/verilator/``, copies the resulting ``libRogueSimLinkDpi.so`` into ``$(OUT_DIR)``, and runs ``make clean`` in the surf tree. - Unlike the Icarus flow, which loads its VPI module at ``vvp`` run time, the ``verilator`` build links ``libRogueSimLinkDpi.so`` directly, staged from ``$(OUT_DIR)``, with ``-LDFLAGS "-Wl,-rpath,$(OUT_DIR) "`` so the resulting ``V$(SIM_TOP)`` binary finds the library and libzmq at run time with no additional setup. - No environment setup (no ``setup_env.sh``, no ``LD_LIBRARY_PATH``) is needed; the rpath resolves the library. - 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 ``V$(SIM_TOP)``. * - :envvar:`VERILATOR_FLAGS` - ``--binary --timing -j 0`` - Flags passed to ``verilator``. Override before the include line to change build behavior. * - :envvar:`VERILOG_INCDIRS` - (empty) - Whitespace-separated list of extra include directories, each added as ``+incdir+``. * - :envvar:`VERILOG_DEFINES` - (empty) - Whitespace-separated list of ``NAME`` or ``NAME=VAL`` words, each added as ``+define+``. Values containing spaces are not supported. * - :envvar:`SIM_PLUSARGS` - (empty) - Plusargs appended verbatim after ``V$(SIM_TOP)`` on the run command line. * - :envvar:`WAVES` - (empty) - Set to ``1`` to add ``--trace-fst`` to the ``verilator`` build. * - :envvar:`RUCKUS_SIM_BACKEND` - ``verilator`` - 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 ``-G=`` escape hatch in :envvar:`VERILATOR_FLAGS` rather than a dedicated variable: .. code-block:: makefile export VERILATOR_FLAGS = --binary --timing -j 0 -GWIDTH_G=16 Troubleshooting --------------- **"VerilogCheckVersion: ruckus requires verilator 5.020 or newer"** The Verilator on PATH is older than the enforced floor (the ``--binary`` and ``--timing`` flags this flow relies on require 5.020 or newer). There is no bypass variable; install Verilator 5.020 or newer. **"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. **Fatal lint warnings stop the build** Verilator's default lint warnings are fatal. Add ``-Wno-fatal`` to ``VERILATOR_FLAGS`` to continue past them (fix the warnings when practical instead of suppressing them permanently). **"%Warning-TIMESCALEMOD"** Mixing timescaled and non-timescaled modules in the same design raises this warning (fatal by default, see above). surf SimLink sources carry no timescale. Add ``--timescale 1ns/1ps`` to ``VERILATOR_FLAGS``. **"fatal error: lz4.h: No such file or directory"** Verilator's FST waveform writer needs the ``lz4`` (and ``zlib``) development headers. Install them, or avoid ``WAVES=1``/``make gtkwave`` if tracing is not needed.