AVL-APB Completer

Inheritance diagram of avl_apb._cdriver

The completer side of the AVL-APB agent does not follow the standard AVL / UVM structure of sequence, sequencer and driver.

As the completer is responsive the overhead of interacting between a monitor, sequence and driver is overly complicated and not required.

Instead the completer is implemented as a single driver that implements the legal protocol for the bus via 3 user defined tasks:

  • CplDriver.reset Action to be taken on bus reset. By default all completion signals are set to 0.

  • Driver.quiesce Action to be taken between transactions. By default all completion signals are set to 0.

  • CplDriver.drive Action of driving.

A single function is used to decide how to complete the request:

This function is called with the an argument of the item to be completed, the request side of the protocol has already populated the item in order to make a decision.

Each CplDriver must be configured with and index CplDriver.idx that corresponds to the psel signal of the AMBA interface. This index is assiged atomatically by the avl_apb._cdriver.CplDriver class based on the number of completers in the agent.

3 drivers are provided:

  • CplDriver - The default driver that completes the request by driving the response signals (protocol only - data values are left 0).

  • CplRandomDriver - Fully randomize the completion based on the SequenceItem attributes.

  • CplMemoryDriver - Completes the request behaving as a memory device.

Rate Control

The request driver is responsible for rate control. By setting the rate_limit variable in the CplDriver class, using a lambda function that returns a value between 0.0 and 1.0 the user can control the rate of driving the pready signal, if supported.

avl.Factory.set_variable("*.agent.cdrv.rate_limit", lambda: 0.1)

Memory Completion Driver

The memory completion driver must be configured to support one or more address ranges. This is done by setting the ranges variable to a list of address ranges. The ranges are defined as tuples of (start, end) addresses.

avl.Factory.set_variable("*.agent.cdrv.ranges", [(0x0000, 0xFFFF), (0x1000, 0x1FFF)])

If the requests is accessing a region supported by the memory driver:

  • Writes will update the memory. Strobes are taken into account, if supported.

  • Reads will return the value stored in the memory.

If the request is accessing a region not supported by the memory driver, the driver will:

  • Ignore writes.

  • Randomize completion read data.

  • Return pslverr if supported.

The is easy to override in the CplMemoryDriver.get_next_item method.

Example

# Copyright 2024 Apheleia
#
# Description:
# Apheleia attributes example


import avl
import avl_apb
import cocotb


class example_env(avl.Env):

    def __init__(self, name, parent):
        super().__init__(name, parent)
        self.hdl = avl.Factory.get_variable(f"{self.get_full_name()}.hdl", None)
        self.clk = avl.Factory.get_variable(f"{self.get_full_name()}.clk", None)
        self.rst_n = avl.Factory.get_variable(f"{self.get_full_name()}.rst_n", None)
        self.agent = avl_apb.Agent("agent", self)

    async def run_phase(self):
        self.raise_objection()

        cocotb.start_soon(self.timeout(1, units="ms"))
        cocotb.start_soon(self.clock(self.clk, 100))
        await self.async_reset(self.rst_n, duration=100, units="ns", active_high=False)

        self.drop_objection()

@cocotb.test
async def test(dut):
    """
    Example APB4 interface
        - Single psel
        - 1000 items in the request sequence with a rate limit of 0.1
            - 2 memory ranges (0x0000, 0x00FF) and (0xFF00, 0xFFFF)
        - pready with rate limit of 0.8
        - pslverr for paddr not in (0x0000, 0x00FF)
        - random wdata
        - memory based rdata
        - random pstrb
        - random pprot

    :param dut: The DUT instance
    :return: None
    """
    avl.Factory.set_variable("*.clk", dut.clk)
    avl.Factory.set_variable("*.rst_n", dut.rst_n)
    avl.Factory.set_variable("*.hdl", dut.apb_if)
    avl.Factory.set_variable("*.agent.cfg.has_requester", True)
    avl.Factory.set_variable("*.agent.cfg.num_completer", 1)
    avl.Factory.set_variable("*.agent.cfg.has_monitor", True)
    avl.Factory.set_variable("*.agent.cfg.has_coverage", True)
    avl.Factory.set_variable("*.agent.cfg.has_bandwidth", True)
    avl.Factory.set_variable("*.agent.cfg.has_trace", True)
    avl.Factory.set_variable("*.agent.rsqr.rseq.n_items", 100)
    avl.Factory.set_variable("*.agent.rdrv.rate_limit", lambda : 0.1)
    avl.Factory.set_variable("*.agent.cdrv.rate_limit", lambda : 0.8)

    # Set the ranges for the request sequence
    req_ranges = {
        (0, 0x0000, 0x00FF): 0.9,
        (0, 0xFF00, 0xFFFF): 0.1,
    }
    avl.Factory.set_variable("*.agent.rsqr.rseq.ranges", req_ranges)

    # Set the range for the completer driver
    avl.Factory.set_variable("*.agent.cdrv.ranges", [(0x0000, 0x00FF)])

    avl.Factory.set_override_by_type(avl_apb.CplDriver, avl_apb.CplMemoryDriver)
    e = example_env("env", None)
    await e.start()