Testing your design

Thanks to community member Tholin for originally writing this article.

To make absolutely sure that your hardware design is functional as you want it, it is possible to write Verilog unit tests. This guide will show you how to write a simple test for your hardware design, and create a GitHub actions pipeline to automatically run your tests every time you push to your repository.

This document refers to the example adder design included in the Verilog template.

The test can run in a GitHub action

Check the GitHub actions, the test should be green and passing. You can check the logs of a recent test to see what happens.

Even if you develop tests on your PC, it’s a good idea to keep the action enabled. If it ever fails you’ll receive an email.

Running locally

There is a good chance you already have most of what you need to get started if you’ve developed in Verilog before (for an FPGA, for instance). But just in case you don’t:

The first thing you need is a Verilog simulator. You can either install the full OSS CAD Suite (recommended), or you can install just the required packages:

sudo apt install iverilog verilator
pip3 install cocotb pytest

You should install pytest even if using the full CAD Suite, as it enables cocotb to print more verbose error messages if a test fails, helping you track down the issue faster.

Testbench module

The testbench tb.v file instantiates the example project and wires it up. You’ll want to change the instantiation so it matches the name of your module.

Replace tt_um_example user_project with the actual name of your top level module.

Makefile

We use a Makefile to run the tests. You need to edit the Makefile to include all your source files.

To do that, go to the line starting with VERILOG_SOURCES, and expand it to list all your files. Separate entries by spaces. Paths are relative to the directory the makefile is in (which should be ‘src’). If you only have a single verilog file, you only need one additional entry: $(PWD)/my_custom_verilog.v

Writing your first test

Now, you can actually get started writing tests. Have a look at the example.

It:

  • starts a clock,
  • enables the design,
  • sets inputs to 0,
  • resets it,
  • sets the inputs to 20 and 30,
  • asserts the output is 50

You can use the name of any of the wires defined in tb.v as values to set or read. assert is, in this case, a statement that will fail the test if the expression following it does not evaluate to True. A delay is also inserted in-between setting the input, and checking the output.

Print debug messages using dut._log.info("test")

Viewing the waveforms generated by your test

After you’ve run make, you should also have a tb.vcd file. You can open this with GTKWave, which is included in the OSS CAD Suite:

gtkwave tb.vcd

This can be very helpful to debug your tests and see your design in operation.

If you get stuck, let us know in the #verification channel of the discord chat.

Gate Level testing

The simulations we’ve covered above are all pre synthesis. A simulator reads the HDL design and simulates it.

It’s well worth running the same test on the post synthesis netlist. This post synthesis netlist is called a Gate Level netlist, because it includes all the actual standard cells (gates) used by your design. Gate Level testing can expose some bugs or issues that weren’t exposed by HDL simulation.

You can have a look at yours by downloading the GDS.zip from the actions page of your design and then looking at the file: runs/wokwi/results/final/verilog/gl/<your design name>.v

This Gate Level netlist snippet just shows 2 of the ~240 standard cells of an example design:

sky130_fd_sc_hd__and4_1 _319_ (.A(\second_counter[7] ),
    .B(\second_counter[9] ),
    .C(\second_counter[10] ),
    .D(\second_counter[12] ),
    .VGND(VGND),
    .VNB(VGND),
    .VPB(VPWR),
    .VPWR(VPWR),
    .X(_145_));
 sky130_fd_sc_hd__dfxtp_2 _320_ (.CLK(clknet_2_0__leaf_clk),
    .D(_007_),
    .VGND(VGND),
    .VNB(VGND),
    .VPB(VPWR),
    .VPWR(VPWR),
    .Q(\seg7.counter[0] ));

You can see the standard cells also have power ports, so one thing that has to change is the design must be powered. That happens automatically for you when the test is run as part of the GDS action.

Whenever the GDS action is triggered, your testbench will be run as a Gate Level test automatically!

If you get stuck, let us know in the #github-actions channel of the discord chat.