tests: add tb for axi4l_reg_map [WIP, 1 test broken]
This commit is contained in:
164
axi/tb/axil_reg_cocotb/test_axi4l_reg_map.py
Normal file
164
axi/tb/axil_reg_cocotb/test_axi4l_reg_map.py
Normal 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
|
||||||
Reference in New Issue
Block a user