* Initial plan * Fix max_decode_depth to properly control decoder hierarchy and port generation Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com> * Fix test that relied on old depth behavior Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com> * Update documentation for max_decode_depth parameter Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com> * fix format * Add variable_depth RDL file and smoke tests for max_decode_depth parameter Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com> * Add variable depth tests for APB3 and AXI4-Lite CPUIFs Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com> * fix * fix * bump --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>
212 lines
6.3 KiB
Python
212 lines
6.3 KiB
Python
"""APB3 smoke tests for variable depth design testing max_decode_depth parameter."""
|
|
|
|
import cocotb
|
|
from cocotb.triggers import Timer
|
|
|
|
|
|
class _Apb3SlaveShim:
|
|
def __init__(self, dut):
|
|
prefix = "s_apb"
|
|
self.PSEL = getattr(dut, f"{prefix}_PSEL")
|
|
self.PENABLE = getattr(dut, f"{prefix}_PENABLE")
|
|
self.PWRITE = getattr(dut, f"{prefix}_PWRITE")
|
|
self.PADDR = getattr(dut, f"{prefix}_PADDR")
|
|
self.PWDATA = getattr(dut, f"{prefix}_PWDATA")
|
|
self.PRDATA = getattr(dut, f"{prefix}_PRDATA")
|
|
self.PREADY = getattr(dut, f"{prefix}_PREADY")
|
|
self.PSLVERR = getattr(dut, f"{prefix}_PSLVERR")
|
|
|
|
|
|
class _Apb3MasterShim:
|
|
def __init__(self, dut, base: str):
|
|
self.PSEL = getattr(dut, f"{base}_PSEL")
|
|
self.PENABLE = getattr(dut, f"{base}_PENABLE")
|
|
self.PWRITE = getattr(dut, f"{base}_PWRITE")
|
|
self.PADDR = getattr(dut, f"{base}_PADDR")
|
|
self.PWDATA = getattr(dut, f"{base}_PWDATA")
|
|
self.PRDATA = getattr(dut, f"{base}_PRDATA")
|
|
self.PREADY = getattr(dut, f"{base}_PREADY")
|
|
self.PSLVERR = getattr(dut, f"{base}_PSLVERR")
|
|
|
|
|
|
def _apb3_slave(dut):
|
|
return getattr(dut, "s_apb", None) or _Apb3SlaveShim(dut)
|
|
|
|
|
|
def _apb3_master(dut, base: str):
|
|
return getattr(dut, base, None) or _Apb3MasterShim(dut, base)
|
|
|
|
|
|
@cocotb.test()
|
|
async def test_depth_1(dut):
|
|
"""Test max_decode_depth=1 - should have interface for inner1 only."""
|
|
s_apb = _apb3_slave(dut)
|
|
|
|
# At depth 1, we should have m_apb_inner1 but not deeper interfaces
|
|
inner1 = _apb3_master(dut, "m_apb_inner1")
|
|
|
|
# Default slave side inputs
|
|
s_apb.PSEL.value = 0
|
|
s_apb.PENABLE.value = 0
|
|
s_apb.PWRITE.value = 0
|
|
s_apb.PADDR.value = 0
|
|
s_apb.PWDATA.value = 0
|
|
|
|
inner1.PRDATA.value = 0
|
|
inner1.PREADY.value = 0
|
|
inner1.PSLVERR.value = 0
|
|
|
|
await Timer(1, units="ns")
|
|
|
|
# Write to address 0x0 (should select inner1)
|
|
inner1.PREADY.value = 1
|
|
s_apb.PADDR.value = 0x0
|
|
s_apb.PWDATA.value = 0x12345678
|
|
s_apb.PWRITE.value = 1
|
|
s_apb.PSEL.value = 1
|
|
s_apb.PENABLE.value = 1
|
|
|
|
await Timer(1, units="ns")
|
|
|
|
assert int(inner1.PSEL.value) == 1, "inner1 must be selected"
|
|
assert int(inner1.PWRITE.value) == 1, "Write should propagate"
|
|
assert int(s_apb.PREADY.value) == 1, "Ready should mirror master"
|
|
|
|
|
|
@cocotb.test()
|
|
async def test_depth_2(dut):
|
|
"""Test max_decode_depth=2 - should have interfaces for reg1 and inner2."""
|
|
s_apb = _apb3_slave(dut)
|
|
|
|
# At depth 2, we should have m_apb_reg1 and m_apb_inner2
|
|
reg1 = _apb3_master(dut, "m_apb_reg1")
|
|
inner2 = _apb3_master(dut, "m_apb_inner2")
|
|
|
|
# Default slave side inputs
|
|
s_apb.PSEL.value = 0
|
|
s_apb.PENABLE.value = 0
|
|
s_apb.PWRITE.value = 0
|
|
s_apb.PADDR.value = 0
|
|
s_apb.PWDATA.value = 0
|
|
|
|
reg1.PRDATA.value = 0
|
|
reg1.PREADY.value = 0
|
|
reg1.PSLVERR.value = 0
|
|
|
|
inner2.PRDATA.value = 0
|
|
inner2.PREADY.value = 0
|
|
inner2.PSLVERR.value = 0
|
|
|
|
await Timer(1, units="ns")
|
|
|
|
# Write to address 0x0 (should select reg1)
|
|
reg1.PREADY.value = 1
|
|
s_apb.PADDR.value = 0x0
|
|
s_apb.PWDATA.value = 0xABCDEF01
|
|
s_apb.PWRITE.value = 1
|
|
s_apb.PSEL.value = 1
|
|
s_apb.PENABLE.value = 1
|
|
|
|
await Timer(1, units="ns")
|
|
|
|
assert int(reg1.PSEL.value) == 1, "reg1 must be selected for address 0x0"
|
|
assert int(inner2.PSEL.value) == 0, "inner2 should not be selected"
|
|
|
|
# Reset
|
|
s_apb.PSEL.value = 0
|
|
s_apb.PENABLE.value = 0
|
|
reg1.PREADY.value = 0
|
|
await Timer(1, units="ns")
|
|
|
|
# Write to address 0x10 (should select inner2)
|
|
inner2.PREADY.value = 1
|
|
s_apb.PADDR.value = 0x10
|
|
s_apb.PWDATA.value = 0x23456789
|
|
s_apb.PWRITE.value = 1
|
|
s_apb.PSEL.value = 1
|
|
s_apb.PENABLE.value = 1
|
|
|
|
await Timer(1, units="ns")
|
|
|
|
assert int(inner2.PSEL.value) == 1, "inner2 must be selected for address 0x10"
|
|
assert int(reg1.PSEL.value) == 0, "reg1 should not be selected"
|
|
|
|
|
|
@cocotb.test()
|
|
async def test_depth_0(dut):
|
|
"""Test max_decode_depth=0 - should have interfaces for all leaf registers."""
|
|
s_apb = _apb3_slave(dut)
|
|
|
|
# At depth 0, we should have all leaf registers: reg1, reg2, reg2b
|
|
reg1 = _apb3_master(dut, "m_apb_reg1")
|
|
reg2 = _apb3_master(dut, "m_apb_reg2")
|
|
reg2b = _apb3_master(dut, "m_apb_reg2b")
|
|
|
|
# Default slave side inputs
|
|
s_apb.PSEL.value = 0
|
|
s_apb.PENABLE.value = 0
|
|
s_apb.PWRITE.value = 0
|
|
s_apb.PADDR.value = 0
|
|
s_apb.PWDATA.value = 0
|
|
|
|
for master in [reg1, reg2, reg2b]:
|
|
master.PRDATA.value = 0
|
|
master.PREADY.value = 0
|
|
master.PSLVERR.value = 0
|
|
|
|
await Timer(1, units="ns")
|
|
|
|
# Write to address 0x0 (should select reg1)
|
|
reg1.PREADY.value = 1
|
|
s_apb.PADDR.value = 0x0
|
|
s_apb.PWDATA.value = 0x11111111
|
|
s_apb.PWRITE.value = 1
|
|
s_apb.PSEL.value = 1
|
|
s_apb.PENABLE.value = 1
|
|
|
|
await Timer(1, units="ns")
|
|
|
|
assert int(reg1.PSEL.value) == 1, "reg1 must be selected for address 0x0"
|
|
assert int(reg2.PSEL.value) == 0, "reg2 should not be selected"
|
|
assert int(reg2b.PSEL.value) == 0, "reg2b should not be selected"
|
|
|
|
# Reset
|
|
s_apb.PSEL.value = 0
|
|
s_apb.PENABLE.value = 0
|
|
reg1.PREADY.value = 0
|
|
await Timer(1, units="ns")
|
|
|
|
# Write to address 0x10 (should select reg2)
|
|
reg2.PREADY.value = 1
|
|
s_apb.PADDR.value = 0x10
|
|
s_apb.PWDATA.value = 0x22222222
|
|
s_apb.PWRITE.value = 1
|
|
s_apb.PSEL.value = 1
|
|
s_apb.PENABLE.value = 1
|
|
|
|
await Timer(1, units="ns")
|
|
|
|
assert int(reg2.PSEL.value) == 1, "reg2 must be selected for address 0x10"
|
|
assert int(reg1.PSEL.value) == 0, "reg1 should not be selected"
|
|
assert int(reg2b.PSEL.value) == 0, "reg2b should not be selected"
|
|
|
|
# Reset
|
|
s_apb.PSEL.value = 0
|
|
s_apb.PENABLE.value = 0
|
|
reg2.PREADY.value = 0
|
|
await Timer(1, units="ns")
|
|
|
|
# Write to address 0x14 (should select reg2b)
|
|
reg2b.PREADY.value = 1
|
|
s_apb.PADDR.value = 0x14
|
|
s_apb.PWDATA.value = 0x33333333
|
|
s_apb.PWRITE.value = 1
|
|
s_apb.PSEL.value = 1
|
|
s_apb.PENABLE.value = 1
|
|
|
|
await Timer(1, units="ns")
|
|
|
|
assert int(reg2b.PSEL.value) == 1, "reg2b must be selected for address 0x14"
|
|
assert int(reg1.PSEL.value) == 0, "reg1 should not be selected"
|
|
assert int(reg2.PSEL.value) == 0, "reg2 should not be selected"
|