Some time ago, while preparing for an earlier article on VUnit and UVM, a warning message in a VUnit run caught my attention.
Compiling into lib: vhdl_adder_tb.sv passed
WARNING VCP2515 "Undefined module: vhdl_adder was used. Port connection rules will not be checked
at such instantiations." ".../vhdl_adder_tb.sv" 20 6
Compiling into lib: vhdl_adder.vhd passed
The tests themselves were running nicely, but hey! VUnit compiles the testbench before the relevant design under test (DUT) and the compiler complains that the DUT is not available when compiling the testbench. Wasn’t VUnit supposed to figure out the compile order by itself?
Guiding VUnit’s compile order
The answer is that indeed, VUnit is supposed to figure out the compile order. But determining a viable compile order from a set of HDL files is far from trivial, so I wasn’t too surprised (or even disappointed) – tests were running, so the initial goal was achieved.
But then I got curious whether I could guide VUnit to find the
right compile order. I had already noticed that even if I was adding
the HDL files in the right order, VUnit would change the order –
incorrectly in this case. Soon I found that VUnit has
add_dependency_on()
methods to enforce dependencies between HDL
files. Sigasi Visual HDL (SVH) can export a compile
order. So the
idea came to use the compile order from SVH – which
is correct for our project – and set a dependency in VUnit
between each pair of successive files in Sigasi’s compile order. As
VUnit tests are driven by a Python script, the implementation was not
all that difficult:
last_file = None
with open('compilation_order.csv') as csv_file:
# Read the CSV file from Sigasi Visual HDL, line by line
# Each line contains the library name (not needed here) and the filename.
csv_reader = csv.reader(csv_file, skipinitialspace=True)
for library_name, file_name in csv_reader:
this_file = vu.get_source_file(file_name)
if last_file is not None:
print("Add dependency of " + file_name + " on " + last_file.name)
this_file.add_dependency_on(last_file)
last_file = this_file
The result was an unpleasant surprise. Suddenly I was seeing circular dependencies between files!
ERROR - Found circular dependency:
/home/wmeeus/git/uvm-in-vunit-tutorial-simple-adder/vhdl_adder_tb.sv ->
/home/wmeeus/git/uvm-in-vunit-tutorial-simple-adder/simpleadder_top_tb.sv ->
/home/wmeeus/git/uvm-in-vunit-tutorial-simple-adder/vhdl_adder_tb.sv
So clearly, VUnit still does its own dependency analysis in addition to the added dependencies from the script. But there shouldn’t haven been a dependency between these testbenches to begin with…
As it turned out, there was a problem with the SystemVerilog testbenches themselves: a SystemVerilog package was included in both testbenches, rather than being compiled by itself.
`include "simpleadder_pkg.sv"
`include "simpleadder_if.sv"
`include "vunit_defines.svh"
`include "uvm_macros.svh"
module simpleadder_top_tb;
// ...
Once that was fixed (i.e.: `include "simpleadder_pkg.sv"
removed
from testbenches and added to the project as a separate file), VUnit
worked like a charm. With the added dependencies, the compile
order was exactly as expected. But best of all, we didn’t even
need to add dependencies any more. With the code fixed, VUnit can
determine a correct compile order without assistance.
Managing VUnit source files
In the process of resolving the compile order, another VUnit method
caught my attention: add_source_files_from_csv()
. Hey, SVH
writes the compile order as a CSV file, in the correct format for use
by VUnit…
So far we’ve been maintaining a list of design files in the VUnit
run.py
script by hand. Wildcards in the list may help to
automatically include new design files as they are added to the
project, but excluding files is difficult. As shown in an earlier
article,
VUnit can be made to read the list of design files from SVH’s .project
file,
but that requires additional python code in run.py
.
So now we have an alternative: have SVH write out the
compile order,
and read the generated CSV file in run.py
. As a fallback, you can
still maintain a static list, e.g. to run VUnit tests outside SVH,
or for importing your project into SVH, when the CSV file hasn’t been generated yet.
if os.path.exists("compilation_order.csv"):
print("Using compilation_order.csv for input files")
vu.add_source_files_from_csv("compilation_order.csv")
else:
print("Using static list in run.py for input files")
# Create library 'lib'
lib = vu.add_library("lib")
# Add design files to library
lib.add_source_files([
"simpleadder_pkg.sv",
"simpleadder.v", "simpleadder_top_tb.sv",
"vhdl_adder.vhd", "vhdl_adder_tb.sv"
])
Conclusion
In this article, we’ve seen that VUnit sometimes fails to determine the compile order of the design files. It is possible to add dependencies between files to guide VUnit’s dependency analysis. The compile order, generated by SVH, can be used to force VUnit to use a particular compile order. In our case, adding dependencies helped us find and fix a design flaw, after which VUnit could determine the compile order correctly.
The compile order, exported from SVH, can also be used
directly as a list of source files for VUnit. This may reduce the
amount of maintenance to the VUnit run.py
script.
See also
- Using VUnit in a GitLab CI Verification Environment (blog post)
- Running UVM tests in VUnit (blog post)
- Using VUnit for Real (blog post)
- How to configure VUnit using the Sigasi VS Code extension (knowledge)
- VUnit projects in Sigasi Visual HDL (knowledge)