Module bfm

This document contains technical documentation for the bfm module.

axi_bfm_pkg.vhd

Types and functions for the AXI BFMs.

axi_lite_master.vhd

component axi_lite_master is
  generic (
    bus_handle : bus_master_t
  );
  port (
    clk : in std_logic;
    --# {{}}
    axi_lite_m2s : out axi_lite_m2s_t;
    axi_lite_s2m : in axi_lite_s2m_t
  );
end component;

Wrapper around VUnit BFM that uses convenient record types for the AXI-Lite signals.

Instantiates the VUnit axi_lite_master verification component, which creates AXI-Lite read/write transactions. Is used by performing VUnit VC calls, such as read_bus, or by using the register convenience methods in reg_operations_pkg.vhd.

axi_lite_read_slave.vhd

component axi_lite_read_slave is
  generic (
    axi_slave : axi_slave_t;
    data_width : positive
  );
  port (
    clk : in std_logic;
    --# {{}}
    axi_lite_read_m2s : in axi_lite_read_m2s_t;
    axi_lite_read_s2m : out axi_lite_read_s2m_t
  );
end component;

Wrapper around VUnit BFM that uses convenient record types for the AXI-Lite signals.

Instantiates the VUnit axi_read_slave verification component, which acts as an AXI slave and reads data from the VUnit memory model.

axi_lite_slave.vhd

component axi_lite_slave is
  generic (
    axi_read_slave : axi_slave_t;
    axi_write_slave : axi_slave_t;
    data_width : positive
  );
  port (
    clk : in std_logic;
    --# {{}}
    axi_lite_read_m2s : in axi_lite_read_m2s_t;
    axi_lite_read_s2m : out axi_lite_read_s2m_t;
    --# {{}}
    axi_lite_write_m2s : in axi_lite_write_m2s_t;
    axi_lite_write_s2m : out axi_lite_write_s2m_t
  );
end component;

AXI-Lite slave BFM that will perform read/write operations on the VUnit memory model.

This entity instantiates axi_lite_read_slave.vhd and/or axi_lite_write_slave.vhd based on what generics are provided.

axi_lite_write_slave.vhd

component axi_lite_write_slave is
  generic (
    axi_slave : axi_slave_t;
    data_width : positive
  );
  port (
    clk : in std_logic;
    --# {{}}
    axi_lite_write_m2s : in axi_lite_write_m2s_t;
    axi_lite_write_s2m : out axi_lite_write_s2m_t
  );
end component;

Wrapper around VUnit BFM that uses convenient record types for the AXI-Lite signals.

Instantiates the VUnit axi_read_slave verification component, which acts as an AXI slave and writes data to the VUnit memory model.

axi_master.vhd

component axi_master is
  generic (
    bus_handle : bus_master_t
  );
  port (
    clk : in std_logic;
    --# {{}}
    axi_read_m2s : out axi_read_m2s_t;
    axi_read_s2m : in axi_read_s2m_t;
    --# {{}}
    axi_write_m2s : out axi_write_m2s_t;
    axi_write_s2m : in axi_write_s2m_t
  );
end component;

Wrapper around VUnit BFM that uses convenient record types for the AXI signals.

Instantiates the VUnit axi_lite_master verification component, which creates AXI-Lite read/write transactions. The AXI-Lite interface are then connected to the “full” AXI interface of this module. The fact that the BFM uses AXI-Lite means that there is no burst support. This wrapper is typically only used for register read/write operations on the chip top level, where the register bus is still AXI.

It is used by performing VUnit VC calls, such as read_bus, or by using the register convenience methods in reg_operations_pkg.vhd.

axi_read_master.vhd

component axi_read_master is
  generic (
    addr_width : positive;
    id_width : natural;
    data_width : positive;
    -- Push jobs  to this queue. Each job pushed will result in an
    -- AR transaction.
    job_queue : queue_t;
    -- Push reference data  to this queue.
    -- Each element should be an unsigned byte. Little endian byte order is assumed.
    -- The data returned on the R channel will be checked against this data.
    reference_data_queue : queue_t;
    -- Stall configuration for the AR channel master
    ar_stall_config : stall_config_t;
    -- Stall configuration for the R channel slave
    r_stall_config : stall_config_t;
    -- Random seed for handshaking stall/jitter.
    -- Set to something unique in order to vary the random sequence.
    seed : natural;
    -- Suffix for the VUnit logger name. Can be used to differentiate between multiple instances.
    logger_name_suffix : string
  );
  port (
    clk : in std_logic;
    --# {{}}
    axi_read_m2s : out axi_read_m2s_t;
    axi_read_s2m : in axi_read_s2m_t;
    --# {{}}
    num_bursts_checked : out natural
  );
end component;

BFM that creates AXI read transactions and checkers based on a simple interface. AR transactions will be created based on jobs (axi_master_bfm_job_t) that the user pushes to the job_queue. The data returned on the R channel will be checked against the integer_array_t data pushed by the user to the reference_data_queue. The returned RID will be checked that it is the same as the corresponding ARID.

The RID check is based on the assumption that R transactions are returned in the same order as AR transactions are sent. Also the job address is assumed to be aligned with the bus data width.

The byte length of the transactions (as set in the job, as well as indicated by the length of the data arrays) does not need to be aligned with the data width of the bus. If unaligned, the last AXI beat will not have all byte lanes checked against reference data.

axi_read_slave.vhd

component axi_read_slave is
  generic (
    axi_slave : axi_slave_t;
    data_width : positive;
    -- Note that the VUnit BFM creates and integer_vector_ptr of length 2**id_width, so a large
    -- value for id_width might crash your simulator.
    id_width : natural range 0 to axi_id_sz
  );
  port (
    clk : in std_logic;
    --# {{}}
    axi_read_m2s : in axi_read_m2s_t;
    axi_read_s2m : out axi_read_s2m_t
  );
end component;

Wrapper around VUnit BFM that uses convenient record types for the AXI signals.

Instantiates the VUnit axi_read_slave verification component, which acts as an AXI slave and reads data from the VUnit memory model.

axi_slave.vhd

component axi_slave is
  generic (
    axi_read_slave : axi_slave_t;
    axi_write_slave : axi_slave_t;
    data_width : positive;
    -- Note that the VUnit BFM creates and integer_vector_ptr of length 2**id_width, so a large
    -- value for id_width might crash your simulator.
    id_width : natural range 0 to axi_id_sz;
    w_fifo_depth : natural
  );
  port (
    clk : in std_logic;
    --# {{}}
    axi_read_m2s : in axi_read_m2s_t;
    axi_read_s2m : out axi_read_s2m_t;
    --# {{}}
    axi_write_m2s : in axi_write_m2s_t;
    axi_write_s2m : out axi_write_s2m_t
  );
end component;

AXI slave BFM that will perform read/write operations on the VUnit memory model.

This entity instantiates axi_read_slave.vhd and/or axi_write_slave.vhd based on what generics are provided.

axi_slave_pkg.vhd

Package with an init value for the axi_slave_t type.

axi_stream_master.vhd

component axi_stream_master is
  generic (
    data_width : positive;
    -- Push data  to this queue.
    -- The integer arrays will be deallocated after this BFM is done with them.
    data_queue : queue_t;
    stall_config : stall_config_t;
    -- Random seed for handshaking stall/jitter.
    -- Set to something unique in order to vary the random sequence.
    seed : natural;
    -- Suffix for the VUnit logger name.
    logger_name_suffix : string;
    -- The 'strobe' is usually a "byte strobe", but the strobe unit width can be modified for cases
    -- when the strobe lanes are wider than bytes.
    strobe_unit_width : positive;
    -- When 'valid' is zero, the associated output ports will be driven with this value.
    -- This is to avoid a DUT sampling the values in the wrong clock cycle.
    drive_invalid_value : std_logic
  );
  port (
    clk : in std_logic;
    --# {{}}
    ready : in std_logic;
    valid : out std_logic;
    last : out std_logic;
    data : out std_logic_vector;
    strobe : out std_logic_vector
  );
end component;

BFM for sending data on an AXI stream interface.

Data is pushed as a VUnit integer_array to a VUnit queue. Each element in the integer_array should be an unsigned byte. Little endian byte order is assumed.

The byte length of the bursts (as indicated by the length of the data_queue arrays) does not need to be aligned with the data width of the bus. If unaligned, the last data beat will not have all byte lanes set to valid data and strobe.

axi_stream_slave.vhd

component axi_stream_slave is
  generic (
    -- Set to zero in order to disable 'id' check. In this case, nothing has to be pushed
    -- to 'reference_id_queue'.
    id_width : natural;
    data_width : positive;
    -- Push reference data  to this queue.
    -- The integer arrays will be deallocated after this BFM is done with them.
    reference_data_queue : queue_t;
    -- Push reference 'id' for each data burst to this queue. All data beats in a burst must have
    -- the same ID, and there may be no interleaving of data.
    -- If 'id_width' is zero, no check will be performed and nothing shall be pushed to
    -- this queue.
    reference_id_queue : queue_t;
    stall_config : stall_config_t;
    -- Random seed for handshaking stall/jitter.
    -- Set to something unique in order to vary the random sequence.
    seed : natural;
    -- Suffix for the VUnit logger name. Can be used to differentiate between multiple instances.
    logger_name_suffix : string;
    -- For protocol checking of the 'data' port.
    -- The VUnit axi_stream_protocol_checker does not allow any bit in tdata to be e.g. '-' or 'X'
    -- when tvalid is asserted. Even when that bit is strobed out by tstrb/tkeep.
    -- This often becomes a problem, since many implementations assign don't care to strobed out
    -- byte lanes as a way of minimizing LUT consumption.
    -- Assigning 'true' to this generic will workaround the check by assigning '0' to all bits that
    -- are not '1' or '0', and are in strobed out byte lanes.
    remove_strobed_out_invalid_data : boolean;
    -- The 'strobe' is usually a "byte strobe", but the strobe unit width can be modified for cases
    -- when the strobe lanes are wider than bytes.
    strobe_unit_width : positive;
    -- If true: Once asserted, 'ready' will not fall until valid has been asserted (i.e. a
    -- handshake has happened). Note that according to the AXI-Stream standard 'ready' may fall
    -- at any time . However, many modules are developed with this
    -- well-behavedness as a way of saving resources.
    well_behaved_stall : boolean;
    -- For busses that do not have the 'last' indicator, the check for 'last' on the last beat of
    -- data can be disabled.
    disable_last_check : boolean
  );
  port (
    clk : in std_logic;
    --
    ready : out std_logic;
    valid : in std_logic;
    last : in std_logic;
    id : in unsigned;
    data : in std_logic_vector;
    strobe : in std_logic_vector;
    --
    num_bursts_checked : out natural
  );
end component;

Verify data on an AXI stream interface.

Reference data is pushed as a VUnit integer_array to a VUnit queue. Each element in the integer_array should be an unsigned byte. Little endian byte order is assumed.

An optional expected ID is pushed as a natural to another queue by the user.

The byte length of the bursts (as indicated by the length of the reference_data_queue arrays) does not need to be aligned with the data width of the bus. If unaligned, the last beat will not have all data byte lanes checked against reference data.

axi_write_master.vhd

component axi_write_master is
  generic (
    addr_width : positive;
    id_width : natural;
    data_width : positive;
    -- Push jobs  to this queue. Each job pushed will result in an
    -- AW transaction and eventually a B check.
    job_queue : queue_t;
    -- Push data  to this queue. Each element should be an
    -- unsigned byte. Little endian byte order is assumed.
    data_queue : queue_t;
    -- Stall configuration for the AW channel master
    aw_stall_config : stall_config_t;
    -- Stall configuration for the W channel master
    w_stall_config : stall_config_t;
    -- Stall configuration for the B channel slave
    b_stall_config : stall_config_t;
    -- Random seed for handshaking stall/jitter.
    -- Set to something unique in order to vary the random sequence.
    seed : natural;
    -- Suffix for the VUnit logger name. Can be used to differentiate between multiple instances.
    logger_name_suffix : string;
    -- When this generic is set, WID will be assigned to same ID as corresponding AW transaction.
    -- Also means that W data will never be sent before an AW transaction
    set_axi3_w_id : boolean
  );
  port (
    clk : in std_logic;
    --# {{}}
    axi_write_m2s : out axi_write_m2s_t;
    axi_write_s2m : in axi_write_s2m_t
  );
end component;

BFM that creates AXI write transactions based on a simple interface. AW transactions will be created based on jobs (axi_master_bfm_job_t) that the user pushes to the job_queue. A W burst will be created based on the integer_array_t data pushed by the user to the data_queue. Each AW transaction will result in a check that the eventually returned BID is correct.

The job address is assumed to be aligned with bus data width.

The byte length of the transactions (as set in the job_queue records, as well as indicated by the length of the data_queue arrays) does not need to be aligned with the data width of the bus. If unaligned, the last AXI beat will have a strobe that is not ‘1’ for all byte lanes.

Note that data can be pushed to data_queue before the corresponding job is pushed. This data will be pushed to the AXI W channel straight away, unless in AXI3 mode.

axi_write_slave.vhd

component axi_write_slave is
  generic (
    axi_slave : axi_slave_t;
    data_width : positive;
    -- Note that the VUnit BFM creates and integer_vector_ptr of length 2**id_width, so a large
    -- value for id_width might crash your simulator.
    id_width : natural range 0 to axi_id_sz;
    -- Optionally add a FIFO to the W channel. Makes it possible to perform W transactions
    -- before AW transactions.
    w_fifo_depth : natural;
    -- For protocol checking of WDATA.
    -- The VUnit axi_stream_protocol_checker does not allow any bit in tdata to be e.g. '-' or 'X'
    -- when tvalid is asserted.
    -- This often becomes a problem, since many implementations assign don't care to strobed out
    -- byte lanes as a way of minimizing LUT consumption.
    -- Assigning 'true' to this generic will workaround the check by assigning '0' to all bits in
    -- WDATA that are not '1' or '0', and are in strobed out byte lanes in WSTRB.
    remove_strobed_out_invalid_data : boolean
  );
  port (
    clk : in std_logic;
    --# {{}}
    axi_write_m2s : in axi_write_m2s_t;
    axi_write_s2m : out axi_write_s2m_t;
    --# {{}}
    -- The number of bursts where data has been written to memory , and the
    -- B transaction has completed.
    num_bursts_done : out natural
  );
end component;

Wrapper around VUnit BFM that uses convenient record types for the AXI signals.

Instantiates the VUnit axi_read_slave verification component, which acts as an AXI slave and writes data to the VUnit memory model.

bfm_pkg.vhd

Types and convenience methods used to implement the BFMs.

handshake_master.vhd

component handshake_master is
  generic (
    stall_config : stall_config_t;
    -- Random seed for handshaking stall/jitter.
    -- Set to something unique in order to vary the random sequence.
    seed : natural;
    -- Suffix for the VUnit logger name. Can be used to differentiate between multiple instances.
    logger_name_suffix : string;
    -- Assign a non-zero value in order to use the 'data'/'strobe' ports for protocol checking
    data_width : natural;
    -- This can be used to essentially disable the
    --   "rule 4: Check failed for performance - tready active N clock cycles after tvalid."
    -- warning by setting a very high value for the limit.
    -- This warning is considered noise in most testbenches that exercise backpressure.
    -- Set to a lower value in order the enable the warning.
    rule_4_performance_check_max_waits : natural
  );
  port (
    clk : in std_logic;
    --# {{}}
    -- Set by testbench when there is data available to push
    data_is_valid : in std_logic;
    --# {{}}
    ready : in std_logic;
    valid : out std_logic;
    --# {{}}
    -- The signals below are optional to connect. Only used for protocol checking.
    last : in std_logic;
    -- Must set 'data_width' generic in order to use these ports.
    data : in std_logic_vector;
    strobe : in std_logic_vector
  );
end component;

Toggle the valid signal based on probabilities set via generics. This realizes a handshake master with jitter that is compliant with the AXI-Stream standard. According to the standard, valid may be lowered only after a transaction.

This BFM can be more convenient to use than the VUnit axi_stream_master BFM in some cases. Specifically when the data is not an SLV, but instead a record. When using VUnit BFMs we would need to have conversion functions to and from SLV. When using this BFM instead for the handshaking, the data can be handled as records in the testbench with no conversion necessary. Using this simple BFM is also significantly faster. A drawback of this BFM is that the testbench code becomes more “RTL”-like compared to the VUnit BFM, which results in more “high level” code.

See the testbench tb_handshake_bfm for example usage.

This entity can also optionally perform protocol checking on the handshaking data interface. This will verify that the AXI-Stream standard is followed. Assign the last/data/strobe ports and set the correct data_width generic in order to use this.

handshake_slave.vhd

component handshake_slave is
  generic (
    stall_config : stall_config_t;
    -- Random seed for handshaking stall/jitter.
    -- Set to something unique in order to vary the random sequence.
    seed : natural;
    -- Suffix for the VUnit logger name. Can be used to differentiate between multiple instances.
    logger_name_suffix : string;
    -- Assign a non-zero value in order to use the 'id' port for protocol checking.
    id_width : natural;
    -- Assign a non-zero value in order to use the 'data'/'strobe' ports for protocol checking.
    data_width : natural;
    -- If true: Once asserted, 'ready' will not fall until valid has been asserted (i.e. a
    -- handshake has happened). Note that according to the AXI-Stream standard 'ready' may fall
    -- at any time . However, many modules are developed with this
    -- well-behavedness as a way of saving resources.
    well_behaved_stall : boolean;
    -- This can be used to essentially disable the
    --   "rule 4: Check failed for performance - tready active N clock cycles after tvalid."
    -- warning by setting a very high value for the limit.
    -- This warning is considered noise in most testbenches that exercise backpressure.
    -- Set to a lower value in order the enable the warning.
    rule_4_performance_check_max_waits : natural;
    -- For protocol checking of the 'data' port.
    -- The VUnit axi_stream_protocol_checker does not allow any bit in tdata to be e.g. '-' or 'X'
    -- when tvalid is asserted. Even when that bit is strobed out by tstrb/tkeep.
    -- This often becomes a problem, since many implementations assign don't care to strobed out
    -- byte lanes as a way of minimizing LUT consumption.
    -- Assigning 'true' to this generic will workaround the check by assigning '0' to all bits that
    -- are not '1' or '0', and are in strobed out byte lanes.
    remove_strobed_out_invalid_data : boolean
  );
  port (
    clk : in std_logic;
    --# {{}}
    -- Can be set to '0' by testbench when it is not yet ready to receive data
    data_is_ready : in std_logic;
    --# {{}}
    ready : out std_logic;
    -- Must be connected if 'well_behaved_stall' is true. Otherwise it is optional and
    -- only for protocol checking.
    valid : in std_logic;
    --# {{}}
    -- The signals below are optional to connect. Only used for protocol checking.
    last : in std_logic;
    -- Must set 'id_width' generic in order to use this.
    id : in unsigned;
    -- Must set 'data_width' generic in order to use these.
    data : in std_logic_vector;
    strobe : in std_logic_vector
  );
end component;

Toggle the ‘ready’ signal based on probabilities set via generics. This realizes a handshake slave with jitter that is compliant with the AXI-Stream standard. According to the standard, ‘ready’ can be lowered at any time, not just after a transaction.

This BFM can be more convenient to use than the VUnit ‘axi_stream_slave’ BFM in some cases. Specifically

  1. When the data is not an SLV, but instead a record. When using VUnit BFMs we would need to have conversion functions to and from SLV. When using this BFM instead for the handshaking, the data can be handled as records in the testbench with no conversion necessary.

  2. When full throughput in the slave is desirable. When using the VUnit BFM the pops must be queued up and “pop references” must be queued up in a separate queue before data is read. This is a lot of boilerplate code that is hard to read.

Using this simple BFM is also significantly faster. A drawback of this BFM is that the testbench code becomes more “RTL”-like compared to the VUnit BFM, which results in more “high level” code. See the testbench ‘tb_handshake_bfm’ for example usage.

This entity can also optionally perform protocol checking on the handshaking data interface. This will verify that the AXI-Stream standard is followed. Assign the valid/last/data/strobe ports and set the correct data_width generic in order to use this.