JanusHDL

The UVVM demo projects

Tutorial · 20 minutes

UVVM is a verification library for VHDL. It comes with demo testbenches for its own example designs and verification components: a UART, an interrupt controller, a scoreboard, Ethernet and more. They are a good way to see a real verification environment run. This tutorial runs all eight of them with JanusHDL.

1. Get UVVM

JanusHDL does not include UVVM; you get it from its own project, so you always have the version you choose. With git:

git clone https://github.com/UVVM/UVVM.git
cd UVVM

Or download the zip from the project page on GitHub and unpack it. The file lists below were tested with the UVVM release of April 2026 (commit 90d56e9). A later release may add or move files; if a file is missing, the error names it.

2. Get the file lists

A JanusHDL file list names the source files of a design, each under its library, in the order they are analyzed. Download the one for the demo you want, or all eight, into the UVVM folder, the one that holds uvvm_util. The paths inside are relative to that folder.

DemoWhat it showsFile listTop
IRQCAn interrupt controller, tested with plain UVVM utility calls: no verification components.uvvm_irqc_demo.fbitvis_irqc.irqc_demo_tb
UARTA UART, driven by verification components (VVCs) for its bus and its serial lines.uvvm_uart_demo.fbitvis_uart.uart_vvc_demo_tb
Error injectionFaults injected into a signal between two components, on purpose.uvvm_ei_demo.fbitvis_vip_error_injection.ei_demo_tb
ScoreboardA scoreboard comparing what the UART sends with what was expected.uvvm_sb_demo.fbitvis_vip_scoreboard.sb_uart_sbi_demo_tb
UART VIPThe UART verification component on its own, with its monitor.uvvm_vip_uart_demo.fbitvis_vip_uart.uvvm_demo_tb
AssertionsUVVM's assertion checks on a running design.uvvm_assertions_demo.fuvvm_assertions.uvvm_assertions_demo_tb
CoverageConstrained random stimulus and functional coverage on the UART.uvvm_cr_fc_demo.fbitvis_uart.cr_fc_demo_tb
EthernetEthernet frames over a GMII interface, checked by a scoreboard. The longest run.uvvm_ethernet_demo.fbitvis_vip_ethernet.ethernet_sbi_gmii_demo_tb

Open one in a text editor: it is plain text, a library line followed by that library's files, then the next library. You can write your own the same way.

3. Run a demo

From the UVVM folder:

jhdl --compat -f uvvm_uart_demo.f --top bitvis_uart.uart_vvc_demo_tb

The top is given as library.entity, because the testbench is not in the library work. JanusHDL analyzes the files, elaborates the testbench and runs it, all in one command. --compat turns on compatibility mode, which UVVM needs.

Why --compat

Since VHDL-2002, the standard allows a shared variable only of a protected type, so that two processes cannot update it at the same time. UVVM keeps a few settings in plain shared variables. JanusHDL reports what the standard forbids as an error, so without --compat the analysis stops, and the first error says why and what to do:

error[unprotected-shared-variable]: a shared variable must be of a protected type, not T_LOG_DESTINATION
  --> uvvm_util/src/string_methods_pkg.vhd:31:3
   |
31 |   shared variable shared_default_log_destination : t_log_destination := C_DEFAULT_LOG_DESTINATION;
   |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = help: wrap the value in a protected type, or analyze the file as VHDL-93 (--std 93); or run with --compat, which accepts it and simulates it as written
   = LRM 6.4.2.4

The packages that declare such variables then fail to analyze, and every unit that uses them says so, so the output goes on for a while and ends with analysis failed; the first error is the one to read. (On Windows, paths show with \.)

With --compat, each of them is a warning instead, 42 for the UART demo, and UVVM runs as written:

warning[unprotected-shared-variable]: a shared variable must be of a protected type, not T_LOG_DESTINATION
  --> uvvm_util/src/string_methods_pkg.vhd:31:3
   |
31 |   shared variable shared_default_log_destination : t_log_destination := C_DEFAULT_LOG_DESTINATION;
   |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = help: accepted in compatibility mode (--compat) and simulated as written; to make it legal: wrap the value in a protected type, or analyze the file as VHDL-93 (--std 93)
   = LRM 6.4.2.4

The warnings do not affect the run. To leave them out as well:

jhdl --compat -f uvvm_uart_demo.f --top bitvis_uart.uart_vvc_demo_tb --allow unprotected-shared-variable

In the window, tick Compatibility mode in the Run dialog (Simulate → Run simulation…); it is remembered for the file list.

The run

Then the testbench prints UVVM's log as it goes: each check, each transaction of the verification components, each alert. At the end, UVVM prints its summary of alerts, and the last lines look like this:

UVVM:      >> Simulation SUCCESS: No mismatch between counted and expected serious alerts
...
UVVM: ID_LOG_HDR                    370673.5 ns  TB seq.                        SIMULATION COMPLETED
...
simulation of bitvis_uart.uart_vvc_demo_tb ended at 370.6735 µs (stopped); analysis … ms, elaboration … ms, run … ms

The first line is UVVM's verdict. The last is JanusHDL's: where the simulation stopped (stopped: the testbench ended it), and how long analysis, elaboration and the run took on your machine. UVVM also writes its log to UVVM_Log.txt and its alerts to UVVM_Alert.txt in the current folder.

One demo, the UART VIP, sends bytes with parity and stop-bit errors on purpose. The UART design reports each with an assertion of severity error, so that run ends with exit code 1 although UVVM says SUCCESS: it expected those errors. The other seven end with exit code 0.

4. Faster the second time

Each run starts by analyzing the UVVM sources. Keep the analysis in a cache:

jhdl --compat -f uvvm_uart_demo.f --top bitvis_uart.uart_vvc_demo_tb --cache uvvm.cache

The next run with the same cache reads the analyzed libraries back instead of analyzing them again, as long as no source file changed and the options that affect analysis (here --compat) are the same; that takes a small fraction of the time analysis takes. The warnings are kept with it and shown again. Give each demo its own cache file: a cache holds the analysis of one list of files.

Native code is on by default: what qualifies is compiled to machine code while the simulation runs, and the rest runs in the interpreter, with the same results. --jit off runs everything in the interpreter.

5. Look at the waveforms

jhdl --compat -f uvvm_uart_demo.f --top bitvis_uart.uart_vvc_demo_tb --wave uart.jhw
jhdlw uart.jhw

In the viewer, open the hierarchy down to the UART and pick the signals of its bus and serial lines.

6. All eight at once

On Windows, a small batch file in the UVVM folder runs every demo, each in its own folder so their logs stay apart:

@echo off
for %%d in (irqc:bitvis_irqc.irqc_demo_tb
            uart:bitvis_uart.uart_vvc_demo_tb
            ei:bitvis_vip_error_injection.ei_demo_tb
            sb:bitvis_vip_scoreboard.sb_uart_sbi_demo_tb
            vip_uart:bitvis_vip_uart.uvvm_demo_tb
            assertions:uvvm_assertions.uvvm_assertions_demo_tb
            cr_fc:bitvis_uart.cr_fc_demo_tb
            ethernet:bitvis_vip_ethernet.ethernet_sbi_gmii_demo_tb) do call :run %%d
goto :eof
:run
for /f "tokens=1,2 delims=:" %%a in ("%1") do (
  mkdir runs\%%a 2>nul
  pushd runs\%%a
  jhdl --compat -f ..\..\uvvm_%%a_demo.f --top %%b --cache uvvm.cache --allow unprotected-shared-variable > out.txt 2>&1
  findstr /c:"Simulation SUCCESS" out.txt >nul && echo %%a: SUCCESS || echo %%a: FAILED, see runs\%%a\out.txt
  popd
)

On Linux, the same in a shell loop:

for d in irqc:bitvis_irqc.irqc_demo_tb uart:bitvis_uart.uart_vvc_demo_tb \
         ei:bitvis_vip_error_injection.ei_demo_tb sb:bitvis_vip_scoreboard.sb_uart_sbi_demo_tb \
         vip_uart:bitvis_vip_uart.uvvm_demo_tb assertions:uvvm_assertions.uvvm_assertions_demo_tb \
         cr_fc:bitvis_uart.cr_fc_demo_tb ethernet:bitvis_vip_ethernet.ethernet_sbi_gmii_demo_tb; do
  name=${d%%:*}; top=${d#*:}
  mkdir -p runs/$name
  (cd runs/$name && jhdl --compat -f ../../uvvm_${name}_demo.f --top $top --cache uvvm.cache \
     --allow unprotected-shared-variable > out.txt 2>&1)
  grep -q "Simulation SUCCESS" runs/$name/out.txt && echo "$name: SUCCESS" || echo "$name: FAILED"
done

Next

Use one of these file lists as the start of your own: keep the UVVM libraries, and add your design and testbench at the end, under their own library, and run it with --compat. In the window, File → Import a file list (.f) as a project… makes a project of it; turn on Compatibility mode in its settings. If a construct in your code is not supported yet, the error says which one.