Journey into Open Source VHDL Verification Frameworks (Part 2)

Luca Colombini
5 min readNov 1, 2021

This article focuses on using the UVVM VHDL verification framework.
I will show how to use an Axi4 Bus Functional Model (BFM), provided by the UVVM library, to test a register map implemented using the Airhdl register maps generator (https://airhdl.com).

Adam Taylor already published an introductory article to UVVM. You can refer to it to see how to clone the UVVM Github repo and how to compile and run testbenches that are provided off-the-shelf in the repo by using open-source VHDL simulator (Github source code).

Let’s start by creating a register map on Airhdl. You will need to register for a free account.

This is the sample register map I am going to use in this demo:

Download the VHDL component and the VHDL package into a development folder, by pressing on the Download button. You will need to replicate this register map implementation. Anyway a copy of the source code is available in a Github repository.

Then open Modelsim (you can choose any flavor, Siemens-licenced or vendor-provided free editions).

Let’s first change the working directory to the one in which we have stored our register description source code:

  • GUI: File -> Change Directory …

or

  • Command line/ Transcript panel :
    `cd C:/devel/axi4-uvvm-test`

Since I am not going to show how to fully automate a verification environment, I will not use scripts, but I will base my example on a Modelsim project.

From the Modelsim GUI, select File->New -> Project…

Now we can add source files to the project:

  • axi4lite_tester_regs_pkg.vhd (VHDL Package)
  • axi4lite_tester_regs.vhd (VHDL Component)
  • axi4lite_tester_regs_tb.vhd (VHDL Testbench)

The final result should be:

The order of compilation is not rightat first, but we can change it manually or use Compile ->Compile Order … -> Auto generate button. As expected the compilation order is package (order 0), component(entity/architecture) (order 1) and testbench (order 2).

Everything compiles fine, but we have an almost empty testbench. The one provided by Airhdl is just a placeorder for our implementation.

The Airhdl testbench already includes a minimal Bus Functional Model in the form of two procedures. They are bus_write() and bus_read() and since they are declared in the test process, their scope allow them to driver the axi bus signals.

An example of a testbench implementation is given here.

Now it is time to compile the library source code into binary form, prior to be imported in our testbench.

Let’s clone the UVVM repo into a local folder. Let’s assume we are cloning the repo into the ‘C:/work’ folder on a Windows machine (just change the path according to your path syntax in your development environment).

Then let’s run the following command from the Modelsim TCL shell in the transcript window:

do C:/work/UVVM/script/compile_all.do C:/work/UVVM/script/ .

This command will instruct Modelsim to compile all of the UVVM components into our current directory.

The library panel will appear like here:

For our simple example, we will not need all of the libraries. Only uvvm_util and bitvis_vip_axilite will be needed. But we compile all libraries as we could develop further examples in the same folder in perspective.

Now let’s start modifying the testbench to add support for the UVVM library.

Firstly, check that the testbench file at least will be compiled for -2008 option: UVVM requires VHDL 2008 support.

Include the following two lines at the beginning of the testbench:

library uvvm_util;
context uvvm_util.uvvm_util_context;

This will allow us to call objects from the uvvm_util library.

The add the following two lines:

library bitvis_vip_axilite;
use bitvis_vip_axilite.axilite_bfm_pkg.all;

This will allow us to use Axi4 Lite BFM. The reference manual for the BFM can be found here.

The UVVM BFM declares a basic type, t_axilite_if, which is a record. It contains a record for each of the five channels defined in the AXI4 bus.
Since the AXI4 bus can have different sizes, either in terms of address range and data range, each channel record is then made of the individual signals with unconstrained arrays.
Unconstrained arrays impose us to declare the actual size at definition time or by means of subtype declarations. I will follow the second way.

Now we have some setup to do before being able to invoke the functions exported by the BFM.

  • Declare a subtype of t_axilite_if to assign address and data widths (let’s call it ST_AXILite_32). Here is the subtype declaration in the architecture declarative region:
  • Declare a signal of that subtype (let’s call it axilite_if)
axilite_if signal declaration
  • Assign AXI4 individual signals to the axilite_if, to allow BFM calls to actually drive DUT signals
  • Declare Local BFM overloads, as suggested by the UVVM AXILite BFM Quick reference guide.

At this point, all is ready to use BFM functions to perform write/read/check operations.

axilite_if must be initialize at the beginning of the test process. Then all the margin is performed by calling axilite_write() to write into a location or axilite_check() to check read value against a certain value.

The overall result is a synthetic print into the simulator transcript, like this:

Conclusions

An overview of generating a testbench based on UVVM BFM has been given.

By starting from an almost empty testbench example, an implementation has been demonstrated using basic bus access functions declared into the initial example. Then they have been substituted by calls to the UVVM AXilite BFM.

The final result is a clean log of the testbench operations, with print messages and checks directly provided by the library.

The final codebase is available on Github:

https://github.com/lukipedio/uvvm-journey-1

Enjoy!

--

--

Luca Colombini

FPGA designer with a passion for languages (both human and artificial)