VUnit is invoked by a user-defined project specified in a Python script. At minimum, a VUnit project consists of a set of HDL source files mapped to libraries. The project serves as single point of entry for compiling and running all tests within an HDL project. VUnit provides automatic scanning for unit tests (test benches), automatic determination of compilation order, and incremental recompilation of modified sources.
The top level Python script is typically named
This script is the entry point for executing VUnit.
The script defines the location for each HDL source
file in the project, the library that each source file belongs to, any
external (pre-compiled) libraries, and special settings that may be required
in order to compile or simulate the project source files. The VUnit
Python interface is used to create and manipulate the VUnit
project within the
Since the VUnit project is defined by a Python script, the full functionality of Python is available to create dynamic rules to specify the files that should be included in the project. For example, HDL files may be recursively included from a directory structure based on a filename pattern. Other Python packages or modules may be imported in order to setup the project.
Once the files for a project have been included, the command line interface can then be used to perform a variety of actions on the project. For example, listing all tests discovered, or running individual tests matching a wildcard pattern. The Python interface also supports running a test bench or test for many different combinations of generic values.
from vunit import VUnit # Create VUnit instance by parsing command line arguments vu = VUnit.from_argv() # Optionally add VUnit's builtin HDL utilities for checking, logging, communication... # See http://vunit.github.io/hdl_libraries.html. vu.add_vhdl_builtins() # or # vu.add_verilog_builtins() # Create library 'lib' lib = vu.add_library("lib") # Add all files ending in .vhd in current working directory to library lib.add_source_files("*.vhd") # Run vunit function vu.main()
There are many example projects demonstrating the usage and capabilities of VUnit.
VUnit supports many simulators. Read about how they are detected and how to choose which one to use here.
VHDL Test Benches#
Example code available at vhdl/user_guide.
In its simplest form a VUnit VHDL test bench looks like this:
library vunit_lib; context vunit_lib.vunit_context; entity tb_example is generic (runner_cfg : string); end entity; architecture tb of tb_example is begin main : process begin test_runner_setup(runner, runner_cfg); report "Hello world!"; test_runner_cleanup(runner); -- Simulation ends here end process; end architecture;
tb_example.vhd a single test case named
lib.tb_example.all is created.
This example also outlines what you have to do with existing testbenches to
make them VUnit compatible. Include the VUnit context, add the
generic, and wrap your existing code in your main controlling process with
the calls to
test_runner_cleanup. Remember to
remove your testbench termination code, for example calls to
end of simulation asserts, or similar. A VUnit testbench must be terminated
test_runner_cleanup call. The procedures described here are part
of the VUnit run library. More information on this library can be found in its
It is also possible to put multiple tests in a single test bench that are each run in individual, independent, simulations. Putting multiple tests in the same test bench is a good way to share a common test environment.
library vunit_lib; context vunit_lib.vunit_context; entity tb_example_many is generic (runner_cfg : string); end entity; architecture tb of tb_example_many is begin main : process begin test_runner_setup(runner, runner_cfg); while test_suite loop if run("test_pass") then report "This will pass"; elsif run("test_fail") then assert false report "It fails"; end if; end loop; test_runner_cleanup(runner); end process; end architecture;
run() calls, two test cases are created:
SystemVerilog Test Benches#
Example code available at verilog/user_guide.
In its simplest form a VUnit SystemVerilog test bench looks like this:
// You do not need to worry about adding vunit_defines.svh to your // include path, VUnit will automatically do that for you if VUnit is // correctly installed (and your python run-script is correct). `include "vunit_defines.svh" module tb_example; `TEST_SUITE begin // Note: Do not place any code here (unless you are debugging // VUnit internals). `TEST_SUITE_SETUP begin // Here you will typically place things that are common to // all tests, such as asserting the reset signal and starting // the clock(s). $display("Running test suite setup code"); end `TEST_CASE_SETUP begin // By default VUnit will run each test separately. However, // advanced users may want to run tests consecutively rather // than in separate instances of the HDL-simulator. In that // case the code placed in a TEST_CASE_SETUP block should // restore the unit under test to the state expected by the // test cases below. In many cases this block would only // assert/deassert the reset signal for a couple of // clock-cycles. // // When trying out VUnit for the first time this section // should probably be left empty. $display("Running test case setup code"); end `TEST_CASE("Test that a successful test case passes") begin $display("This test case is expected to pass"); `CHECK_EQUAL(1, 1); end `TEST_CASE("Test that a failing test case actually fails") begin $display("This test case is expected to fail"); `CHECK_EQUAL(0, 1, "You may also optionally add a diagnostic message to CHECK_EQUAL"); // Note: A test case will also be marked as failing if the // simulator stops for other reasons before the end of the // TEST_SUITE block is reached. This means that you don't // need to use CHECK_EQUAL if the testbench you want to // convert to VUnit already contains code that for example // calls $stop if an error-condition is detected. end `TEST_CASE("Test that a test case that takes too long time fails with a timeout") begin $display("This test is expected to timeout because of the watch dog below."); #2ns; // end `TEST_CASE_CLEANUP begin // This section will run after the end of a test case. In // many cases this section will not be needed. $display("Cleaning up after a test case"); end `TEST_SUITE_CLEANUP begin // This section will run last before the TEST_SUITE block // exits. In many cases this section will not be needed. $display("Cleaning up after running the complete test suite"); end end; // The watchdog macro is optional, but recommended. If present, it // must not be placed inside any initial or always-block. `WATCHDOG(1ns); endmodule
`TEST_CASE() macros, three test cases are created:
lib.tb_example.Test that pass
lib.tb_example.Test that fail
lib.tb_example.Test that timeouts
Each test is run in an individual simulation. Putting multiple tests in the same test bench is a good way to share a common test environment.
Scanning for Test Benches#
VUnit will recognize a module or entity as a test bench and run it if
it has a
runner_cfg generic or parameter. A SystemVerilog test
bench using the
TEST_SUITE macro will have a
parameter created by the macro and thus match the criteria.
A warning will be given if:
The test bench entity or module name does not match the pattern
The name does match the above pattern but lacks a
runner_cfggeneric or parameter preventing it to be run by VUnit.
runner_cfg : string, used by VUnit to pass private information between Python and the HDL test runner.
output_path : string, path to the output directory of the current test; this is useful to create additional output files that can be checked after simulation by a post_check Python function.
tb_path : string, path to the directory containing the test bench; this is useful to read input data with a known location relative to the test bench location.
Optional generics/parameters are filled in automatically by VUnit if detected on the test bench.