AVL-APB Completer
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.resetAction to be taken on bus reset. By default all completion signals are set to 0.Driver.quiesceAction to be taken between transactions. By default all completion signals are set to 0.CplDriver.driveAction 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 theSequenceItemattributes.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()