AVL-APB Configuration

AVL-APB is configured via the provided RTL interface.

The default interface does not contain any modports or clocking blocs to remain compatible with the majority of simulators.

If the user wishes to add directionality or timing to the interface, they can do so by modifying the avl_apb.sv file.

Connection the interface to the APB bus should be done with standard assign statements.

All signals are included, however, optional signals are disabled by default. To enable optional signals, the user must set the appropriate parameters in the interface. Where defined the same configuration naming conventions used in the AMBA specification have been followed.

Assertion checks for optional signals that are not included ensure they remain unchanged during simulation and therefore can be ignored the the user.

These assertion checks are deliberately implemented as initial blocks with $fatals in order to be supported on the widest range of simulators.

// Copyright 2025 Apheleia
//
// Description:
// Apheleia Verification Library APB Interface
// As defined in https://developer.arm.com/documentation/ihi0024/latest/

`define AVL_APB_IMPL_CHECK(cond, signal) \
if (``cond`` == 1) begin : ``signal``_cond \
    initial begin \
        #0.1; \
        @(``signal``) $fatal("%m: ``signal`` not supported in configuration");\
    end \
end : ``signal``_cond

interface apb_if #(parameter string CLASSIFICATION      = "APB",
                   parameter int    VERSION             = 2,
                   parameter int    PSEL_WIDTH          = 1,
                   parameter int    ADDR_WIDTH          = 32,
                   parameter int    DATA_WIDTH          = 32,
                   parameter bit    Protection_Support  = 0,
                   parameter bit    RME_Support         = 0,
                   parameter bit    Pstrb_Support       = 0,
                   parameter bit    Wakeup_Signal       = 0,
                   parameter int    USER_REQ_WIDTH      = 0,
                   parameter int    USER_DATA_WIDTH     = 0,
                   parameter int    USER_RESP_WIDTH     = 0)();

    localparam PSTRB_WIDTH = int'(DATA_WIDTH/8);

    logic                                                   pclk;
    logic                                                   presetn;
    logic [ADDR_WIDTH-1:0]                                  paddr;
    logic [2:0]                                             pprot;
    logic                                                   pnse;
    logic [PSEL_WIDTH-1:0]                                  psel;
    logic                                                   penable;
    logic                                                   pwrite;
    logic [DATA_WIDTH-1:0]                                  pwdata;
    logic [PSTRB_WIDTH-1:0]                                 pstrb;
    logic                                                   pready;
    logic [DATA_WIDTH-1:0]                                  prdata;
    logic                                                   pslverr;
    logic                                                   pwakeup;
    logic [USER_REQ_WIDTH  > 0 ? USER_REQ_WIDTH-1  : 0 : 0] pauser;
    logic [USER_DATA_WIDTH > 0 ? USER_DATA_WIDTH-1 : 0 : 0] pwuser;
    logic [USER_DATA_WIDTH > 0 ? USER_DATA_WIDTH-1 : 0 : 0] pruser;
    logic [USER_RESP_WIDTH > 0 ? USER_RESP_WIDTH-1 : 0 : 0] pbuser;

    generate

        `AVL_APB_IMPL_CHECK((VERSION < 3), pready)

        `AVL_APB_IMPL_CHECK((VERSION < 3), pslverr)

        `AVL_APB_IMPL_CHECK(((VERSION < 4) || (Protection_Support == 0)), pprot)

        `AVL_APB_IMPL_CHECK(((VERSION < 4) || (Pstrb_Support == 0)), pstrb)

        `AVL_APB_IMPL_CHECK(((VERSION < 5) || (RME_Support == 0)), pnse)

        `AVL_APB_IMPL_CHECK(((VERSION < 5) || (Wakeup_Signal == 0)), pwakeup)

        `AVL_APB_IMPL_CHECK(((VERSION < 5) || (USER_REQ_WIDTH == 0)), pauser)

        `AVL_APB_IMPL_CHECK(((VERSION < 5) || (USER_DATA_WIDTH == 0)), pwuser)

        `AVL_APB_IMPL_CHECK(((VERSION < 5) || (USER_DATA_WIDTH == 0)), pruser)

        `AVL_APB_IMPL_CHECK(((VERSION < 5) || (USER_RESP_WIDTH == 0)), pbuser)

    endgenerate

endinterface : apb_if

`undef AVL_APB_IMPL_CHECK

Integrating with a Build Environment

AVL-APB comes with a tools utility avl_apb._tools.get_verilog() to help integrate the library into your build environment.

This is exposed to the environment as the command line tool avl_apb_get_verilog which returns a list of all RTL files required for AVL_APB.

An example of integrating with the verilator build environment is shown below:

# HDL source files
VERILOG_SOURCES      += $(shell avl-apb-get-verilog)

# include cocotb's make rules to take care of the simulator setup
include $(shell cocotb-config --makefiles)/Makefile.sim

Connecting to the AVL Environment

The recommended way to connect to the AVL environment is via the factory.

avl.Factory.set_variable("*.hdl", dut.apb_if)

When the agent is created it will automatically use this factory setting to connect to the APB interface.

Parameterization

The AVL environment automatically picks up the parameters (version, features and width) from the RTL interface. This ensures the AVL environment and HDL environment are always in sync.

Once connected the agent creates and internal avl_apb.Interface object which is used to act as the physical interface to the APB bus and share the parameters with the rest of the environment.

This internal interface in generated to serve 2 purposes:

  1. It abstracts the simulator specific behaviour of the generate blocks. Some simulators flatten the generate blocks, while others do not.

  2. It provides Interface.get and Interface.set methods to access the HDL with knowledge of which signals are present based on the configuration of the bus.