As part of my efforts to create “perfect” test benches I am to make sure the test benches work when compiled with different simulators. This approach is to expose language errors and any bugs in one of the simulators. Hence, I’ve tried to compile my test bench with Verilator and the process wasn’t as easy as I expected it to be. In this post I’ll give an overview of Verilator focusing on how it’s different from Icarus Verilog.
The main difference stems from the fact that Verilator is only as a compiler. All it does is convert the Verilog designs into code in C++ or other optional languages. This code complies only with synthesis rules of Verilog. Following that, the user is required to create a C++ sort of top module using Verilator related libraries and then compile this file along with the earlier produced C++ design files using any C++ compiler like GNU’s g++ This produces a binary file which is run to perform the simulation.
The C++ top module or wrapper is very similar to a test bench and performs many of it’s tasks such as:
- Instantiating the Top Module and specifying it’s name for compilation
- Creating signals such as clock and reset and advancing the time of simulation and deciding how long it runs.
- Dumping VCD Waveforms.
- Specifying when to stop and finish the simulation
On the other hand Icarus Verilog is different in the sense that it does all of the above tasks in one step using only Verilog files as input and producing the simulation excutable as output which can be run by VVP command for example.
The Verilator Manual contains all what you need to know about Verilator and even some examples about writing the C++ top module and the compilation process flow for Verilator. Moreover, you can refer to this amazing documentation of the Verilated libraries. You can find a lot of information about the C++ classes and functions that can be used in your top module. If you want the full documentation then get the Verilator source code and build it’s doxygen help files. Last but not least this presentation contains a good overview about Verilator.
There are more differences between Icarus Verilog and Verilator which I would like to highlight:
- Verilator doesn’t support many non-synthesizable verilog constructs such as #delay or @(posedge clk). Hence if you need to build a sophisticated test bench and compile it with Verilator you’ll have to use only synthesizable code and forget about using any non synthesizable delays or waits that can make your life easier.
- Since #delays are not supported the most common way of creating a clock with an always #delay can’t be used and the clock need to be created in the C++ top module.
- Verilator doesn’t support VCD dump commands ($dumpfile & $dumpvars). You have to generate the VCD dump from the C++ top module.
- Verilator is much more stricter than Icarus Verilog on verilog constructs. It will generate warnings about any assignment where the RHS is not equal to the LHS. Icarus Ignores many of those warnings unless they are actual errors.
- Verilator will highlight any mixing of blocking and non blocking assignments and or out of place usage.
To Sum up, while Verilator is not as easy to use as Icarus Verilog and supports only synthesizable constructs, it does highlight many small issues in the code that might cause death by Verilog! as explained in this profound paper about blocking and non blocking assignments.