Skip to main content

apb modules for cocotb

Project description

APB interface modules for Cocotb

Build Status PyPI version Downloads

GitHub repository: https://github.com/daxzio/cocotbext-apb

Introduction

APB simulation models for cocotb.

The APB protocol is cover in these documents APB Protocol Specification and APB Architecture Specification

Installation

Installation from pip (release version, stable):

$ pip install cocotbext-apb

Installation from git (latest development version, potentially unstable):

$ pip install https://github.com/daxzio/cocotbext-apb/archive/main.zip

Installation for active development:

$ git clone https://github.com/daxzio/cocotbext-apb
$ pip install -e cocotbext-apb

Documentation and usage examples

See the tests directory for complete testbenches using these modules.

APB Write

APB Write

APB Read

APB Read

APB Bus

The APBBus is used to map to a APB interface on the dut. Class methods from_entity and from_prefix are provided to facilitate signal default name matching.

Required:

  • psel
  • pwrite
  • paddr
  • pwdata
  • pready
  • prdata

Optional:

  • pstrb
  • pprot
  • pslverr

APB Master

The ApbMaster class implement a APB driver and is capable of generating read and write operations against APB slaves.

The master automatically handles data wider than the bus width by splitting transactions into multiple sequential APB accesses at consecutive addresses. This allows seamless transfers of wide data values across narrower APB interfaces.

To use these modules, import the one you need and connect it to the DUT:

from cocotbext.apb import ApbMaster, ApbBus

bus = ApbBus.from_prefix(dut, "s_apb")
apb_driver = ApbMaster(bus, dut.clk)

The first argument to the constructor accepts an ApbBus object. These objects are containers for the interface signals and include class methods to automate connections.

Once the module is instantiated, read and write operations can be initiated in a couple of different ways.

ApbMasterconstructor parameters

  • bus: ApbBus object containing APB interface signals
  • clock: clock signal
  • timeout_max: Maximum clock cycles to wait for pready signal before timing out (optional, default 1000). Set to -1 to disable timeout.
  • reset: reset signal (optional)
  • reset_active_level: reset active level (optional, default True)

Additional optional arguments for ApbMaster

  • seednum: For random testing a seed can be supplied, default None, random seed.

Address Mapping

The ApbMaster supports address mapping through the addrmap attribute, which allows using string names instead of numeric addresses in read and write operations. This makes testbenches more readable and maintainable by using register names instead of hardcoded addresses.

The addrmap attribute is a dictionary that maps register names (strings) to their corresponding addresses (integers). When a string is passed as the addr parameter to read(), write(), read_nowait(), write_nowait(), or poll() methods, the master will automatically look up the address in the addrmap dictionary.

Indexed Register Access:

For registers that are arrays or need indexed access, you can use either:

  1. String format with bracket notation: "STATUS[0]", "STATUS[1]", etc.
  2. The index parameter: read("STATUS", data, index=0), write("STATUS", data, index=1), etc.

Both methods add an offset to the base register address equal to index * wbytes (where wbytes is the bus width in bytes). The index parameter is particularly useful when using a variable index in loops.

Example:

from cocotbext.apb import ApbMaster, ApbBus

bus = ApbBus.from_prefix(dut, "s_apb")
apb_driver = ApbMaster(bus, dut.clk)

apb_driver.addrmap = {
    'STATUS'    : 0x00,
    'BUSY'      : 0x04,
    'CONFIG'    : 0x08,
    'INTERRUPT' : 0x0c,
}

# Use string names instead of numeric addresses
await apb_driver.write('STATUS', 0x12)
await apb_driver.read('CONFIG')

# Indexed access using string format
await apb_driver.read('STATUS[0]', 0x12)
await apb_driver.read('STATUS[1]', 0x34)

# Indexed access using index parameter (useful with variables)
for i in range(4):
    await apb_driver.write('STATUS', data[i], index=i)
    await apb_driver.read('STATUS', expected[i], index=i)

Methods

  • enable_logging(): Enable debug logging
  • disable_logging(): Disable debug logging
  • enable_backpressure(seednum=None): Enable random delays on the interface
  • disable_backpressure(): Disable random delays on the interface
  • wait(): blocking wait until all outstanding operations complete
  • write(addr, data, strb=-1, prot=ApbProt.NONSECURE, error_expected=False, device=0, length=-1, index=-1): write data (bytes or int), to addr (int or string when addrmap is configured), wait for result. If an slverr is experienced a critical warning will be issued by default, but will reduced this to an info warning if error_expected=True. If data is wider than the bus width, it will automatically be split into multiple sequential APB write accesses at consecutive addresses. The optional length parameter can override the automatic length calculation, should be a multiple of the number of bytes in the wdata bus. The optional device parameter specifies the slave index to target. The optional index parameter adds an offset to the address equal to index * wbytes, useful for accessing indexed registers (alternative to using string format like "STATUS[0]").
  • write_nowait(addr, data, strb=-1, prot=ApbProt.NONSECURE, error_expected=False, device=0, length=-1, index=-1): write data (bytes or int), to addr (int or string when addrmap is configured), submit to queue. If an slverr is experienced a critical warning will be issued by default, but will reduced this to an info warning if error_expected=True. If data is wider than the bus width, it will automatically be split into multiple sequential APB write accesses at consecutive addresses. The optional length parameter can override the automatic length calculation, should be a multiple of the number of bytes in the wdata bus. The optional device parameter specifies the slave index to target. The optional index parameter adds an offset to the address equal to index * wbytes, useful for accessing indexed registers (alternative to using string format like "STATUS[0]").
  • read(addr, data=bytes(), prot=ApbProt.NONSECURE, error_expected=False, device=0, index=-1, length=-1): read bytes, at addr (int or string when addrmap is configured), if data supplied check for match, wait for result. If an slverr is experienced a critical warning will be issued by default, but will reduced this to an info warning if error_expected=True. If data is wider than the bus width, it will automatically be split into multiple sequential APB read accesses at consecutive addresses. The optional length parameter can override the automatic length calculation, should be a multiple of the number of bytes in the wdata bus. The optional device parameter specifies the slave index to target. The optional index parameter adds an offset to the address equal to index * wbytes, useful for accessing indexed registers (alternative to using string format like "STATUS[0]").
  • read_nowait(addr, data=bytes(), prot=ApbProt.NONSECURE, error_expected=False, device=0, index=-1, length=-1): read bytes, at addr (int or string when addrmap is configured), if data supplied check for match, submit to queue. If an slverr is experienced a critical warning will be issued by default, but will reduced this to an info warning if error_expected=True. If data is wider than the bus width, it will automatically be split into multiple sequential APB read accesses at consecutive addresses. The optional length parameter can override the automatic length calculation, should be a multiple of the number of bytes in the wdata bus. The optional device parameter specifies the slave index to target. The optional index parameter adds an offset to the address equal to index * wbytes, useful for accessing indexed registers (alternative to using string format like "STATUS[0]").
  • poll(addr, data=bytes(), device=0): poll address, at addr (int or string when addrmap is configured), until data at address matches data. The optional device parameter specifies the slave index to target.

Example:

# Write to slave 0
await tb.intf.write(0x100, 0xDEADBEEF, device=0)

# Read from slave 1
val = await tb.intf.read(0x200, device=1)

APB Monitor

The ApbMonitor class tracks APB bus transactions and verifies signal synchronization.

Usage

from cocotbext.apb import ApbMonitor
monitor = ApbMonitor(bus, dut.clk)

Methods

  • enable_check_sync(): Enable checking that signal changes are aligned with the clock edge, default.
  • disable_check_sync(): Disable the synchronous signal check.

Multi-Device Support

The ApbMaster supports multiple slave devices on the same bus instance. To use this feature:

  1. Signal Connection:

    • psel must be a vector (e.g., [1:0] for 2 slaves).
    • prdata must be a concatenated vector of all slave read data outputs (e.g., [63:0] for 2 slaves with 32-bit data width).
  2. Access:

    • Use the device parameter in read, write, read_nowait, and write_nowait methods to specify the target slave index (integer).
    • The ApbMaster will assert the corresponding bit in psel (1 << device) and slice the prdata appropriately.

Example:

# Write to slave 0
await tb.intf.write(0x100, 0xDEADBEEF, device=0)

# Read from slave 1
val = await tb.intf.read(0x200, device=1)

APB slave

The ApbSlave classe implement an APB slaves and is capable of completing read and write operations from upstream APB masters. This modules can either be used to perform memory reads and writes on a MemoryInterface on behalf of the DUT, or they can be extended to implement customized functionality.

To use these modules, import the one you need and connect it to the DUT:

from cocotbext.apb import ApbBus, ApbSlave, MemoryRegion

apb_slave = ApbSlave(ApbBus.from_prefix(dut, "m_apb"), dut.clk, dut.rst)
region = MemoryRegion(2**apb_slave.read_if.address_width)
apb_slave.target = region

The first argument to the constructor accepts an ApbBus object. These objects are containers for the interface signals and include class methods to automate connections.

It is also possible to extend these modules; operation can be customized by overriding the internal _read() and _write() methods. See ApbRam for an example.

ApbSlave constructor parameters

  • bus: ApbBus object containing APB interface signals
  • clock: clock signal
  • reset: reset signal (optional)
  • reset_active_level: reset active level (optional, default True)
  • target: target region (optional, default None)

ApbSlave editable attibutes

It is possible to set area of addressable memory to be treated a priviledged address space or instruction address space. If an APB master tries to access these regions, but has not set the correct prot value, NONSECURE for example, the ApbSlave will issue a slverr duting the pready phase of it response.

The ApbSlave has two attributes that can be edited by the user to allocate addresses and/or address ranges to the priviledged or instruction space.

  • privileged_addrs
  • instruction_addrs

Both attributes are arrays, and each element can be a single address, or a two element list, with a low address to a high address:

tb.ram.privileged_addrs =  [[0x1000, 0x1fff], 0x3000]
tb.ram.instruction_addrs = [[0x2000, 0x2fff], 0x4000]

If there is a read or a write with an address in this space, and the prot from the master does not match, it will report the type of error, as a warning, and assert slverr. The access will also be unsuccessful, the write will not occur and a read will result in all zeros being returned.

APB Write Error

APB RAM

The ApbRam class implements APB RAMs and is capable of completing read and write operations from upstream APB masters. These modules are extensions of the corresponding ApbSlave module. Internally, SparseMemory is used to support emulating very large memories.

To use these modules, import and connect it to the DUT:

from cocotbext.apb import ApbBus, ApbRam

apb_ram = ApbRam(ApbBus.from_prefix(dut, "m_apb"), dut.clk, dut.rst, size=2**32)

The first argument to the constructor accepts an ApbBus object. These objects are containers for the interface signals and include class methods to automate connections.

Once the module is instantiated, the memory contents can be accessed in a couple of different ways. First, the mmap object can be accessed directly via the mem attribute. Second, read(), write(), and various word-access wrappers are available. Hex dump helper methods are also provided for debugging. For example:

apb_ram.write(0x0000, b'test')
data = apb_ram.read(0x0000, 4)
apb_ram.hexdump(0x0000, 4, prefix="RAM")

Multi-port memories can be constructed by passing the mem object of the first instance to the other instances. For example, here is how to create a four-port RAM:

apb_ram_p1 = ApbRam(ApbBus.from_prefix(dut, "m00_apb"), dut.clk, dut.rst, size=2**32)
apb_ram_p2 = ApbRam(ApbBus.from_prefix(dut, "m01_apb"), dut.clk, dut.rst, mem=apb_ram_p1.mem)
apb_ram_p3 = ApbRam(ApbBus.from_prefix(dut, "m02_apb"), dut.clk, dut.rst, mem=apb_ram_p1.mem)
apb_ram_p4 = ApbRam(ApbBus.from_prefix(dut, "m03_apb"), dut.clk, dut.rst, mem=apb_ram_p1.mem)

ApbRam and ApbLiteRam constructor parameters

  • bus: ApbBus object containing APB interface signals
  • clock: clock signal
  • reset: reset signal (optional)
  • reset_active_level: reset active level (optional, default True)
  • size: memory size in bytes (optional, default 2**32)
  • mem: mmap or SparseMemory backing object to use (optional, overrides size)

Attributes:

  • mem: directly access shared mmap or SparseMemory backing object

Methods

  • read(address, length): read length bytes, starting at address
  • read_words(address, count, byteorder='little', ws=2): read count ws-byte words, starting at address
  • read_dwords(address, count, byteorder='little'): read count 4-byte dwords, starting at address
  • read_qwords(address, count, byteorder='little'): read count 8-byte qwords, starting at address
  • read_byte(address): read single byte at address
  • read_word(address, byteorder='little', ws=2): read single ws-byte word at address
  • read_dword(address, byteorder='little'): read single 4-byte dword at address
  • read_qword(address, byteorder='little'): read single 8-byte qword at address
  • write(address, data): write data (bytes), starting at address
  • write_words(address, data, byteorder='little', ws=2): write data (ws-byte words), starting at address
  • write_dwords(address, data, byteorder='little'): write data (4-byte dwords), starting at address
  • write_qwords(address, data, byteorder='little'): write data (8-byte qwords), starting at address
  • write_byte(address, data): write single byte at address
  • write_word(address, data, byteorder='little', ws=2): write single ws-byte word at address
  • write_dword(address, data, byteorder='little'): write single 4-byte dword at address
  • write_qword(address, data, byteorder='little'): write single 8-byte qword at address
  • hexdump(address, length, prefix=''): print hex dump of length bytes starting from address, prefix lines with optional prefix
  • hexdump_line(address, length, prefix=''): return hex dump (list of str) of length bytes starting from address, prefix lines with optional prefix
  • hexdump_str(address, length, prefix=''): return hex dump (str) of length bytes starting from address, prefix lines with optional prefix

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cocotbext_apb-0.11.0.tar.gz (22.6 kB view details)

Uploaded Source

File details

Details for the file cocotbext_apb-0.11.0.tar.gz.

File metadata

  • Download URL: cocotbext_apb-0.11.0.tar.gz
  • Upload date:
  • Size: 22.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for cocotbext_apb-0.11.0.tar.gz
Algorithm Hash digest
SHA256 2dd720ea08d2abb9fa96c36055ac692c72bb22f8c7a3225fb536f1f1fc84e903
MD5 2e202a6d5b3ee82b045d4f5fa37360cf
BLAKE2b-256 543096da637a120f76699b3404b9383d80be50d5ae4d136b252e74026e793cca

See more details on using hashes here.

Provenance

The following attestation bundles were made for cocotbext_apb-0.11.0.tar.gz:

Publisher: test_checkin.yml on daxzio/cocotbext-apb

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page