Files
PeakRDL-BusDecoder/tests/cocotb/common/apb4_master.py
Copilot 4dc61d24ca Add cocotb testbench for validating generated bus decoder RTL across APB3, APB4, and AXI4-Lite interfaces (#9)
* Initial plan

* Add cocotb test infrastructure and testbenches for APB3, APB4, and AXI4-Lite

Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>

* Add integration tests, examples, and documentation for cocotb testbenches

Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>

* Address code review feedback: use relative imports and update installation docs

Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>

* Add implementation summary document

Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>

* Merge cocotb dependencies into test group

Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>

* Add optional cocotb simulation workflow with Icarus Verilog

Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>
2025-10-23 23:46:51 -07:00

124 lines
3.3 KiB
Python

"""APB4 Master Bus Functional Model for cocotb."""
import cocotb
from cocotb.triggers import RisingEdge, Timer
class APB4Master:
"""APB4 Master Bus Functional Model."""
def __init__(self, dut, name, clock):
"""
Initialize APB4 Master.
Args:
dut: The device under test
name: Signal name prefix (e.g., 's_apb')
clock: Clock signal to use for synchronization
"""
self.dut = dut
self.clock = clock
self.name = name
# Get signals
self.psel = getattr(dut, f"{name}_PSEL")
self.penable = getattr(dut, f"{name}_PENABLE")
self.pwrite = getattr(dut, f"{name}_PWRITE")
self.paddr = getattr(dut, f"{name}_PADDR")
self.pwdata = getattr(dut, f"{name}_PWDATA")
self.pstrb = getattr(dut, f"{name}_PSTRB")
self.pprot = getattr(dut, f"{name}_PPROT")
self.prdata = getattr(dut, f"{name}_PRDATA")
self.pready = getattr(dut, f"{name}_PREADY")
self.pslverr = getattr(dut, f"{name}_PSLVERR")
def reset(self):
"""Reset the bus to idle state."""
self.psel.value = 0
self.penable.value = 0
self.pwrite.value = 0
self.paddr.value = 0
self.pwdata.value = 0
self.pstrb.value = 0
self.pprot.value = 0
async def write(self, addr, data, strb=None):
"""
Perform APB4 write transaction.
Args:
addr: Address to write to
data: Data to write
strb: Byte strobe mask (default: all bytes enabled)
Returns:
True if write succeeded, False if error
"""
# Calculate strobe if not provided
if strb is None:
data_width_bytes = len(self.pwdata) // 8
strb = (1 << data_width_bytes) - 1
# Setup phase
await RisingEdge(self.clock)
self.psel.value = 1
self.penable.value = 0
self.pwrite.value = 1
self.paddr.value = addr
self.pwdata.value = data
self.pstrb.value = strb
self.pprot.value = 0
# Access phase
await RisingEdge(self.clock)
self.penable.value = 1
# Wait for ready
while True:
await RisingEdge(self.clock)
if self.pready.value == 1:
error = self.pslverr.value == 1
break
# Return to idle
self.psel.value = 0
self.penable.value = 0
return not error
async def read(self, addr):
"""
Perform APB4 read transaction.
Args:
addr: Address to read from
Returns:
Tuple of (data, error) where error is True if read failed
"""
# Setup phase
await RisingEdge(self.clock)
self.psel.value = 1
self.penable.value = 0
self.pwrite.value = 0
self.paddr.value = addr
self.pprot.value = 0
# Access phase
await RisingEdge(self.clock)
self.penable.value = 1
# Wait for ready
while True:
await RisingEdge(self.clock)
if self.pready.value == 1:
data = self.prdata.value.integer
error = self.pslverr.value == 1
break
# Return to idle
self.psel.value = 0
self.penable.value = 0
return data, error