In this post we will set up a minimal testbench to generate data using an AXI4-Stream Bus Functional Model from the UVVM Light library. After briefly introducing the UVVM framework we will discuss the testbench code and run the simulations, including the validation of the AXI Stream Ready/Valid handshake.
Contents
What is the UVVM?
The Universal VHDL Verification Methodology (UVVM) is a verification framework for digital designs written in VHDL. It provides a structure, utilities and reusable IP for creating advanced verification environments. Its goal is to provide an alternative to the Universal Verification Methodology (UVM), which is written in SystemVerilog and is widely used for ASIC verification.
The UVVM is targeted at FPGA developers that would like to increase their verification quality and productivity without undertaking the technical and financial cost of adopting the UVM. Moreover, as stated in their documentation, “the UVVM has two different complexity levels. The VVC Framework and VVCs for medium to advanced testbenches, and the Utility library and BFMs for simple usage – and as a basis for more advanced testbenches.”
Minimal Testbench with the UVVM Light
Though the UVVM Utility Library and BFMs (we’ll call it UVVM Light) is described as targeting simple use cases, it has the potential to substantially increase the quality and productivity of the verification process with a relatively low upfront effort. We will look at how to get the AXI4-Stream BFM up and running in a minimal testbench.
The code walkthrough that follows assumes that Questa or ModelSim are used, and that the ‘uvvm_light’ library has been compiled and linked to the simulator. The instructions for compiling the libraries in Questa/ModelSim are described in the UVVM Light repository.
We start our minimal testbench by including the necessary VHDL packages. Other than the usual packages, we’ll make sure to include those associated with the UVVM Utitlity Library and the BFM that we would like to use – in this case the AXI4-Stream BFM.
library ieee;
use ieee.std_logic_1164.all;
library std;
use std.env.all;
library uvvm_util;
context uvvm_util.uvvm_util_context;
use uvvm_util.axistream_bfm_pkg.all;
use std.textio.all;
use std.env.finish;
After including all the necessay packages we can begin describing our testbench entity. We’ll start by declaring our clock signal (which we will also drive using the UVVM) and our AXI4-Stream master interface, ‘m_axistream’. The ‘m_axistream’ interface is of type ‘t_axistream_if’ in the ‘axistream_bfm_pkg’ of the UVVM.
entity uvvm_axistream_bfm is
end uvvm_axistream_bfm;
architecture behavioral of uvvm_axistream_bfm is
-- Clock signal
signal clk : std_logic;
-- Record definition for the AXI4-Stream bus
subtype t_axistream is t_axistream_if (
tdata (7 downto 0),
tkeep (0 downto 0),
tuser (0 downto 0),
tstrb (0 downto 0),
tid (0 downto 0),
tdest (0 downto 0)
);
signal m_axistream : t_axistream;
We then declare and initialize the array that will hold the data that we will send over our AXI4-Stream interface. This array will be of type ‘t_slv_array’, which is also defined in the ‘axistream_bfm_pkg’ of the UVVM.
-- AXI4-Stream data
signal tdata_array : t_slv_array(0 to 15) (7 downto 0) := (
x"00", x"11", x"22", x"33",
x"44", x"55", x"66", x"77",
x"88", x"59", x"AA", x"BB",
x"CC", x"DD", x"EE", x"FF"
);
Now we add a signal to control the verbosity of the logs, with ‘shared_msg_id_panel’ being the default verbosity of the UVVM Utility library.
signal msg_id_panel : t_msg_id_panel := shared_msg_id_panel;
Finally, we declare a constant that will hold the interface configuration. The record that defines the interface configuration, as well as its default values, are defined in the ‘axistream_bfm_pkg’ of the UVVM.
-- Configure the AXI4-Stream BFM
constant C_AXISTREAM_BFM_CONFIG_DEFAULT : t_axistream_bfm_config := (
max_wait_cycles => 100,
max_wait_cycles_severity => ERROR,
clock_period => -1 ns,
clock_period_margin => 0 ns,
clock_margin_severity => TB_ERROR,
setup_time => -1 ns,
hold_time => -1 ns,
bfm_sync => SYNC_ON_CLOCK_ONLY,
match_strictness => MATCH_EXACT,
byte_endianness => LOWER_BYTE_LEFT,
valid_low_at_word_num => 0,
valid_low_multiple_random_prob => 0.5,
valid_low_duration => 0,
valid_low_max_random_duration => 5,
check_packet_length => false,
protocol_error_severity => ERROR,
ready_low_at_word_num => 0,
ready_low_multiple_random_prob => 0.5,
ready_low_duration => 0,
ready_low_max_random_duration => 5,
ready_default_value => '0',
id_for_bfm => ID_BFM
);
Now we are ready to describe the stimuli that our testbench will generate. Let’s go line by line:
begin
-- Generate the clock
clock_generator(clk, 10 ns);
-- Drive tready
m_axistream.tready <= '1';
stimuli : process
constant C_SCOPE : string := "STIMULI Process";
begin
-- Log setup
enable_log_msg(ALL_MESSAGES, "Logging all Messages", NON_QUIET, C_SCOPE);
log(ID_LOG_HDR, "SIMULATION START", C_SCOPE);
-- Send data over AXI4-Stream
axistream_transmit(tdata_array, "Send AXI4-Stream data", clk, m_axistream,
C_SCOPE, msg_id_panel, C_AXISTREAM_BFM_CONFIG_DEFAULT);
wait for 50 ns;
-- End of simulation
report_alert_counters(FINAL);
log(ID_LOG_HDR, "SIMULATION END", C_SCOPE);
std.env.stop;
wait;
end process stimuli;
end architecture behavioral;
- Line 4 generates the clock signal. This calls a UVVM procedure to which we pass the clock signal and the desired clock period.
- Line 7 asserts the ‘ready’ input from the master interface, so not backpressure is possible.
- Line 10 sets a name for the scope from which the log messages will be generated.
- Line 13 enables logging of all messages.
- Line 14 prints the ‘SIMULATION START’ message.
- Line 17 sends the data over the AXI4-Stream interface. Note that, as with all BFMs in the UVVM Light library, this is a blocking call. Non-blocking calls are only supported by UVVM VHDL Verification Components (VVCs).
- Line 21 prints a summary of all alerts found during the simulation.
- Line 22 logs the ‘SIMULATION END’ message.
- Line 23 stops the simulation.
Running the Simulation
We can use the following script to run our minimal testbench from ModelSim/Questa:
vlib work
vcom -2008 ./uvvm_axistream_bfm.vhd
vsim uvvm_axistream_bfm
add wave -position insertpoint sim:/uvvm_axistream_bfm/*
run -all
Here is the resulting transcript with the verbosity and messages that we selected. Though it might not seem that critical, having a robust system for managing simulation logs – including message severity, scope, and even consistent text formatting – can improve the quality of the verification process.
The resulting waveform shows the signal-level activity for our master interface. We can see that it sends out the data array that we passed to the ‘axistream_transmit’ procedure and drives the ‘tvalid’ and ‘tlast’ protocol signals at the appropriate times.
Modelling the Ready/Valid Handshake
The Ready/Valid handshake is perhaps the most important behavior to get right when implementing and simulating AXI4-Stream interfaces. For a UVVM Master BFM, we can set the behavior of the ‘tvalid’ signal in the ‘C_AXISTREAM_BFM_CONFIG_DEFAULT’ constant.
-- Configure the AXI4-Stream BFM
constant C_AXISTREAM_BFM_CONFIG_DEFAULT : t_axistream_bfm_config := (
max_wait_cycles => 100,
max_wait_cycles_severity => ERROR,
clock_period => -1 ns,
clock_period_margin => 0 ns,
clock_margin_severity => TB_ERROR,
setup_time => -1 ns,
hold_time => -1 ns,
bfm_sync => SYNC_ON_CLOCK_ONLY,
match_strictness => MATCH_EXACT,
byte_endianness => LOWER_BYTE_LEFT,
valid_low_at_word_num => C_MULTIPLE_RANDOM,
valid_low_multiple_random_prob => 0.5,
valid_low_duration => C_RANDOM,
valid_low_max_random_duration => 5,
check_packet_length => false,
protocol_error_severity => ERROR,
ready_low_at_word_num => 0,
ready_low_multiple_random_prob => 0.5,
ready_low_duration => 0,
ready_low_max_random_duration => 5,
ready_default_value => '0',
id_for_bfm => ID_BFM
);
By setting ‘valid_low_at_word_num’ to ‘C_MULTIPLE_RANDOM’ we enable the random deassertion of the ‘tvalid’ signal, with the probability of deassertion given by ‘valid_low_multiple_random_prob’ (by default 0.5). In a similar way, by setting ‘valid_low_duration’ to ‘C_RANDOM’ we tell the BFM to keep ‘tvalid’ low for random periods of up to ‘valid_low_max_random_duration’ clock cycles (by default 5).
We can now see in the output of the AXI4-Stream interface that the ‘tvalid’ signal is deasserted at random. While this would be enough to test many corner cases related to the ‘tvalid’ signal, it is also possible to configure the ‘C_AXISTREAM_BFM_CONFIG_DEFAULT’ constant so that ‘tvalid’ is deasserted at a specific location of the data array, for instance, with the first or the last data value.
This minimal testbench shows how easy it can be to get a verification environment up and running with the UVVM BFMs and Utility libraries, but it is by no means an exhaustive exploration of the UVVM Light features. We will look at some of those in future posts.
Cheers,
Isaac