GVI generates glue code that allows to run Verilog modules inside of VHDL testbenches. Have a look at the examples to see how it can be used. In order to run the m-labs-lm32, serv, ibex, hazard3, toy_SoC or wr-cores examples, git submodules have to be activated (git submodule init; git submodule update;)
- examples/vhd_v_counter: Run a Verilog implementation of a counter with a VHDL implementation of the same counter in the same testbench.
- examples/two_modules: Two Verilog modules used at the same time. They may have multiple clock ports.
- examples/m-labs-lm32: Run an instance of the lm32 cpu.
- examples/serv: Run an instance of the serv risc-v cpu.
- examples/ibex: Run an instance of a more performant risc-v cpu.
- examples/hazard3: Run an instance of yet another risc-v cpu.
- examples/toy_SoC: A slightly more complex example with a simple SoC. Three RISC-V CPUs (uRV, picorv32, serv with MDU) are available. They have a custom wishbone wrapper for data and instruction bus and all CPUs runs the exact same firmware stored in memory that calculates digits of PI and writes these digits onto a pseudo UART device that ends up in a text file in the simulation directory (
cpu_output.txt). more details are here - examples/vhdl_verilog_mixed: Demonstrate a fully mixed language design. VHDL implementation, Verilog implementation, and Verilog instantiating VHDL entity running together in the same testbench. This is possible because GHDL can convert VHDL code into Verilog code using its synthesis capabilities (only tested with GHDL version 4).
GHDL can call into C APIs using VHPIDIRECT. Verilator can generate a C++ class (the verilated module) from a Verilog module, and C++ can create C APIs using extern "C". In order to use a verilated module from GHDL, two pieces of code are needed:
- C++ code that provides a C API for the verilated module.
- VHDL code with an entity that has the same interface as the targeted Verilog module, with an architecture that calls the C API for the verilated module.
GVI calls Verilator, compiles the verilated module, and generates these two pieces of code plus some text files containing compiler/linker flags that are needed to integrate everything with GHDL.
With GVI, using Verilog modules in a VHDL simulation requires only a few lines in a Makefile.
gvi [-p] [-vv <verilator-version>] -v <verilog-source> -t <top-module> { -c <clk-port> } { -I <verilog-include-path> } { -G <verilator-parameter>=<value> } { -o <verilator-option> } [-g] [-n] [<system-verilog-source> ...]
-v and -t are required for normal operation (unless -u or -h is given, or -g is used together with -G to only print a generics hash, see below).
| Option | Argument | Description |
|---|---|---|
-v |
<verilog-source> |
The Verilog file that contains the top module (and, typically, everything it depends on). Passed to verilator as the main source file. |
-t |
<top-module> |
Name of the top-level Verilog module to wrap. This must match a module <name> ... in the sources, and is passed to verilator --top-module. |
-c |
<clk-port> |
Name of a clock input port of the top module. May be given multiple times for designs with several clock domains; the generated VHDL entity re-evaluates the module whenever any of the listed clocks changes (wait until clk_a'event or clk_b'event ...). If -c is omitted entirely, GVI tries to autodetect clock ports by looking for input ports whose name contains clk or clock (but not _en, to avoid matching clock-enable signals). At least one clock port (given or detected) is required, or GVI aborts with an error. |
-I |
<verilog-include-path> |
Adds a Verilog/SystemVerilog include directory, forwarded to verilator as -I<path>. Repeatable; needed whenever the design uses `include with headers outside the source file's own directory. |
-G |
<name>=<value> |
Sets a Verilog module parameter, forwarded to verilator as -G<name>=<value>. Repeatable, one -G per parameter. This is the recommended way to pass parameters (as opposed to -o -G..., see -o below) because it also participates in -g's generics hash. |
-o |
<verilator-option> |
Passes an arbitrary extra option straight through to the verilator invocation, unmodified. Repeatable. Use this for anything GVI doesn't have a dedicated flag for, e.g. -o -Wno-fatal or even -o -Gportsize=8 (functionally equivalent to -G portsize=8, but such parameters are not included in the -g hash since GVI doesn't parse the contents of -o). |
-g |
(none) | Appends a short hash of all -G generics to the generated module/entity name (e.g. counter_v_a1b2c3d4). This is needed when the same Verilog module is instantiated multiple times in the same design with different parameter values, since each parameterization otherwise produces identically-named VHDL entities/packages that would collide. If -g and one or more -G options are given but no -v/-t (top module/source), GVI just prints the resulting hash string to stdout and exits — handy for computing the expected entity name from a Makefile without re-running the full generation. |
-n |
(none) | Disables waveform tracing. No .vcd dump file is produced by the verilated model, and the trace-related Verilator/GHDL glue code is omitted. Use this to speed up simulation when waveforms aren't needed. |
-vv |
<verilator-version> |
Tells GVI which Verilator version is installed (default: 5.012). This only affects whether verilated_threads.o is linked into the generated .flags file (needed for Verilator 5.x, not for 4.x). Examples typically pass `` `verilator --version |
-p |
(none) | Use pkg-config verilator --cflags to find the Verilator include paths needed to compile the generated glue code. If omitted (the default), GVI instead runs which verilator and derives the include paths from the executable's install prefix (<prefix>/share/verilator/include[/vltstd]). Use -p if your Verilator installation ships a working pkg-config file and the default detection fails. |
-u |
(none) | Runs GVI's internal unit tests (self-test of the token/port parsing logic) and exits. Does not require -v/-t. |
-h |
(none) | Prints the usage string and exits. |
| (positional) | <system-verilog-source> |
Any bare argument (not starting with -) is treated as an additional Verilog/SystemVerilog source file, appended to the verilator call before the main -v source. As soon as at least one such file is given, GVI also passes -sv to Verilator to enable SystemVerilog parsing. Useful when the design is split across several files that all need to be handed to Verilator (see examples/ibex). |
For a top module <top> (and, if -g is used, hash suffix <hash>), GVI creates:
.gvi/<top><hash>/— Verilator's own build directory (--Mdir), containing the verilated C++ model, its Makefile, and compiled objects..gvi/<top><hash>/<top><hash>_wrapper.vhd— the generated VHDL package + entity that GHDL analyzes and elaborates against your testbench..gvi/<top><hash>/<top><hash>_wrapper_c.cpp/_wrapper_c.o— the C++ glue code exposing a C API (extern "C") for the verilated model, and its compiled object..gvi/<top><hash>/<top><hash>_wrapper.flags— linker flags (-Wl,...) needed to link the module-specific object files and the Verilator-generated archive into the GHDL simulation binary..gvi/common.cpp/.gvi/common.oand.gvi/common.flags— code and flags shared by all verilated modules in a project (the simulation-time helpersmain_time/sc_time_stamp, plusverilated.o,verilated_vcd_c.o, and, for Verilator 5.x,verilated_threads.o). These are only generated/compiled once per.gvidirectory and reused across modules.
A typical Makefile rule looks like:
.gvi/counter_v/counter_v_wrapper.vhd: gvi counter_v.v
./gvi -vv $(VERILATOR_VERSION) -v counter_v.v -t counter_v -c clk_i
testbench: .gvi/counter_v/counter_v_wrapper.vhd counter_vhd.vhd testbench.vhd
ghdl -a $(GHDLFLAGS) $+
ghdl -m $(GHDLFLAGS) \
$(shell cat .gvi/counter_v/counter_v_wrapper.flags) \
$(shell cat .gvi/common.flags) \
testbenchi.e. the .flags files are cat'd straight into the ghdl -m (link) step. See examples/vhd_v_counter for the complete, minimal version of this pattern, and examples/two_modules or examples/toy_SoC for projects that verilate several modules (and therefore link several .flags files) into one testbench.
If the same Verilog module is used with different generic/parameter values in one design (e.g. two FIFOs of different depth), give each instantiation its own -G ... values plus -g:
./gvi -g -G portsize=8 -v fifo.v -t fifo # generates entity fifo_<hash8>
./gvi -g -G portsize=16 -v fifo.v -t fifo # generates entity fifo_<hash16>
Without -g, both calls would generate an identically-named VHDL package/entity (fifo), which GHDL cannot analyze twice into the same library. See examples/two_modules for a worked example, including the difference between -G name=value (included in the -g hash) and -o -Gname=value (forwarded to Verilator identically, but not part of the hash).
- Only input and output ports of verilog modules are supported. inout is not possible yet.
- GVI is developed and tested only with ghdl-gcc (GHDL with GCC backend)
- It is possible to use Verilog modules from VHDL, not the other way around. This limitation can be overcome by automatically converting VHDL code to Verilog using GHDL sythesis feature (see examples/vhdl_verilog_mixed).
- Module parameters from the Verilog module are not translated into VHDL generics, they have to be specified when calling GVI.
- Ports declared as unpacked arrays of vectors (e.g.
output [31:0] foo [7:0], which Verilator renders asVL_OUT32((&foo)[8],31,0)in its generated header) are not recognized as ports and are silently skipped; only plain scalar/vector ports are supported. - Ports wider than 32 bits are automatically split into multiple 32-bit "limbs" on the C++/VHDL boundary (suffixed
_gvi_lw0,_gvi_lw1, ...); this is handled transparently by GVI, but shows up in the generated code and can be useful to know when debugging a wide port.