tests: add tb for axi4l_reg_map [WIP, 1 test broken]

This commit is contained in:
Phil
2026-06-17 17:23:23 +03:00
parent 600fba5d3e
commit 8818dd7aac

View File

@ -0,0 +1,164 @@
# SPDX-License-Identifier: MIT
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge
from cocotbext.axi import AxiLiteBus, AxiLiteMaster
OKAY = 0
SLVERR = 2
REG0_CTRL = 0x00
REG1_STATUS = 0x04
REG2_CONFIG = 0x08
REG_INVALID = 0x0C # N_REGS=3, so index 3 is bad
def _resp_code(resp):
"""Return integer AXI response code from a cocotbext-axi response object."""
if resp is None:
return OKAY
value = getattr(resp, "resp", OKAY)
# common guards
try:
return int(value)
except TypeError:
return int(value.integer)
def _read_data(resp):
return int.from_bytes(bytes(resp.data), "little")
class TB:
def __init__(self, dut):
self.dut = dut
self.axil = AxiLiteMaster(AxiLiteBus.from_prefix(dut, "s_axil"), dut.clk, dut.rst)
async def reset(self):
self.dut.rst.value = 1
self.dut.busy_i.value = 0
self.dut.error_code_i.value = 0
for _ in range(5):
await RisingEdge(self.dut.clk)
self.dut.rst.value = 0
for _ in range(5):
await RisingEdge(self.dut.clk)
# shortcuts sorta
async def write32(self, addr, value, expected_resp=OKAY):
resp = await self.axil.write(addr, value.to_bytes(4, "little"))
assert _resp_code(resp) == expected_resp, (
f"write addr=0x{addr:08x}: expected resp {expected_resp}, got {_resp_code(resp)}"
)
return resp
async def read32(self, addr, expected_resp=OKAY):
resp = await self.axil.read(addr, 4)
assert _resp_code(resp) == expected_resp, (
f"read addr=0x{addr:08x}: expected resp {expected_resp}, got {_resp_code(resp)}"
)
return _read_data(resp)
@cocotb.test()
async def test_reset_and_basic_reads(dut):
"""Check reset values and RO status path via reg_i."""
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
tb = TB(dut)
await tb.reset()
# REG0 reset: W1C sticky bit set. W1S reads as 0, RW reads from reg_o
assert await tb.read32(REG0_CTRL) == 0x00000004
# REG2 is a normal RW config register with non-zero reset value
assert await tb.read32(REG2_CONFIG) == 0x12345678
# Drive external status and read it through RO bits
dut.busy_i.value = 1
dut.error_code_i.value = 0x5A
await RisingEdge(dut.clk)
assert await tb.read32(REG1_STATUS) == 0x00005A01
@cocotb.test()
async def test_rw_and_w1c_bits(dut):
"""Check RW bit update and W1C sticky clear behavior."""
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
tb = TB(dut)
await tb.reset()
# Initial REG0: bit2 W1C is set, bit1 RW is clear, bit0 W1S reads as 0
assert await tb.read32(REG0_CTRL) == 0x00000004
# Set RW bit1 while preserving W1C bit2
await tb.write32(REG0_CTRL, 0x00000006)
assert await tb.read32(REG0_CTRL) == 0x00000006
# Write 1 to W1C bit2, keep RW bit1 set
await tb.write32(REG0_CTRL, 0x00000006)
assert await tb.read32(REG0_CTRL) == 0x00000002
# Write zero clears the RW bit because REG0 is a normal word write for RW fields
await tb.write32(REG0_CTRL, 0x00000000)
assert await tb.read32(REG0_CTRL) == 0x00000000
# Full RW register write/read
await tb.write32(REG2_CONFIG, 0xDEADBEEF)
assert await tb.read32(REG2_CONFIG) == 0xDEADBEEF
@cocotb.test()
async def test_w1s_seen_on_reg_o_probe(dut):
"""Check that a W1S write creates a short-lived internal reg_o bit."""
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
tb = TB(dut)
await tb.reset()
seen = False
async def monitor_w1s_bit():
nonlocal seen
for _ in range(20):
await RisingEdge(dut.clk)
if int(dut.reg0_o.value) & 0x1:
seen = True
mon = cocotb.start_soon(monitor_w1s_bit())
await tb.write32(REG0_CTRL, 0x00000001)
await mon
assert seen, "W1S bit was never observed on reg0_o[0]"
assert await tb.read32(REG0_CTRL) == 0x00000004 # W1S reads as 0, W1C reset bit remains set
@cocotb.test()
async def test_write_to_ro_returns_slverr(dut):
"""Writing a RO bit should return SLVERR and should not change status."""
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
tb = TB(dut)
await tb.reset()
dut.busy_i.value = 1
dut.error_code_i.value = 0x33
await RisingEdge(dut.clk)
await tb.write32(REG1_STATUS, 0x00000101, expected_resp=SLVERR)
assert await tb.read32(REG1_STATUS) == 0x00003301
@cocotb.test()
async def test_invalid_address_returns_slverr(dut):
"""N_REGS=3, so address 0x0C selects index 3 and must fail."""
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
tb = TB(dut)
await tb.reset()
await tb.write32(REG_INVALID, 0x11223344, expected_resp=SLVERR)
assert await tb.read32(REG_INVALID, expected_resp=SLVERR) == 0x00000000