Skip to content

Commit a578eab

Browse files
Improve AXI burst write test randomness
1 parent a1e8c57 commit a578eab

1 file changed

Lines changed: 43 additions & 37 deletions

File tree

verification/cocotb/top/lib_i3c_top/test_csr_access.py

Lines changed: 43 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import logging
55
from math import log2
66
import random
7+
from typing import Optional
78

89
from bus2csr import bytes2int, compare_values, int2dword
910
from interface import I3CTopTestInterface
@@ -194,8 +195,7 @@ async def test_basic_burst_read(dut):
194195

195196
@cocotb.test()
196197
async def test_basic_burst_write(dut):
197-
UPPER_START_ADDR_BOUNDARY = 0x200
198-
MAX_BURST_SIZE = (1 << 7) * 4 # Max INCR len multiplied by max AWSIZE
198+
MAX_END_ADDR = 1024
199199

200200
# inner registers that are problematic to validate
201201
# e.g. have hwclr fields that don't yield 0
@@ -224,66 +224,72 @@ async def test_basic_burst_write(dut):
224224

225225
tb = await initialize(dut, timeout=500)
226226

227+
def generate_random_csr_data() -> tuple[list[int], list[Optional[int]]]:
228+
i3ec = tb.reg_map.I3C_EC
229+
filtered_regs = (
230+
r for r in i3ec if r != "start_addr" and "base_addr" not in getattr(i3ec, r)
231+
)
232+
# an iterator over test data for all registers in all I3C_EC.* groups
233+
test_data = chain(*(csr_access_test_data(getattr(i3ec, r), validation_exceptions) for r in filtered_regs))
234+
235+
write_map = [0] * MAX_END_ADDR
236+
expect_map: list[Optional[int]] = [None] * MAX_END_ADDR # None values will not be verified
237+
238+
for reg_name, addr, wdata, exp_rd in test_data:
239+
for i, (wb, eb) in enumerate(zip(int2dword(wdata), int2dword(exp_rd))):
240+
write_map[addr+i] = wb
241+
if reg_name not in validation_exceptions:
242+
expect_map[addr+i] = eb
243+
244+
return write_map, expect_map
245+
227246
# Registers that hang the bus due to additional requirements when accessing them
228-
access_exceptions = [
247+
access_exceptions = sorted([
229248
tb.reg_map.PIOCONTROL.RESPONSE_PORT.base_addr,
230249
tb.reg_map.PIOCONTROL.TX_DATA_PORT.base_addr,
231250
tb.reg_map.PIOCONTROL.IBI_PORT.base_addr,
232251
tb.reg_map.I3C_EC.TTI.TX_DATA_PORT.base_addr,
233252
tb.reg_map.I3C_EC.TTI.RX_DATA_PORT.base_addr,
234253
tb.reg_map.I3C_EC.SECFWRECOVERYIF.INDIRECT_FIFO_DATA.base_addr,
235-
]
236-
237-
i3ec = tb.reg_map.I3C_EC
238-
filtered_regs = (
239-
r for r in i3ec if r != "start_addr" and "base_addr" not in getattr(i3ec, r)
254+
])
255+
max_access_size = max(a - b for a, b in zip(access_exceptions, [0, *access_exceptions]))
256+
257+
assert max_access_size >= 256, (
258+
"Max. access size is smaller than expected. The CSR map either got reordered"
259+
" or new access exceptions that partitioned the existing gaps even more were"
260+
" introduced. Make sure that this test still makes sense with the smaller"
261+
" maximum gap and adjust this assertion accordingly if so."
240262
)
241263

242-
# an iterator over test data for all registers in all I3C_EC.* groups
243-
test_data = chain(*(csr_access_test_data(getattr(i3ec, r), validation_exceptions) for r in filtered_regs))
244-
245-
legal_addr = list(range(0, UPPER_START_ADDR_BOUNDARY, 4))
246-
write_map = [0] * (MAX_BURST_SIZE + UPPER_START_ADDR_BOUNDARY)
247-
expect_map = [None] * (MAX_BURST_SIZE + UPPER_START_ADDR_BOUNDARY) # None values will not be verified
248-
249-
for reg_name, addr, wdata, exp_rd in test_data:
250-
if reg_name in validation_exceptions:
251-
continue
252-
for i, (wb, eb) in enumerate(zip(int2dword(wdata), int2dword(exp_rd))):
253-
write_map[addr+i] = wb
254-
expect_map[addr+i] = eb
255-
256264
burst_lens = {
257265
AxiBurstType.FIXED: range(15),
258-
AxiBurstType.INCR: (1 << i for i in range(8)), # toggles each bit
266+
AxiBurstType.INCR: [
267+
*(1 << i for i in range(8)), # make sure to toggle each bit
268+
*random.randbytes(10) # but also run through some random lengths
269+
]
259270
}
260271

261272
for awburst, awlens in burst_lens.items():
262273
for awlen, awsize, awlock, awuser in product(
263274
awlens, (0, 1, 2), AxiLockType, (0, 0xAAAAAAAA, 0x55555555)
264275
):
265-
# When AWSIZE == 2 and AWLEN == 128, total burst range is too big
266-
# for our address map
267-
if awsize == 2 and awlen == 128:
276+
write_map, expect_map = generate_random_csr_data()
277+
size = 2 ** awsize
278+
279+
# The burst must fit between gaps in access_exceptions
280+
if ((awlen + 1) * size) > max_access_size:
268281
continue
269282

270-
start = None
271-
size = 2 ** awsize
272283
# Try to randomize start address 1000 times, if didn't succeed then
273284
# it's probably impossible to randomize such burst on a given reg
274285
# map with given exceptions
275286
for _ in range(0, 1000):
276-
start_addr = random.choice(legal_addr)
287+
start_addr = random.choice(range(0, MAX_END_ADDR, 4))
277288
end_addr = start_addr + ((awlen + 1) * size)
278-
fail = False
279-
for addr in access_exceptions:
280-
if addr >= start_addr and addr <= end_addr:
281-
fail = True
282-
if not fail:
283-
start = start_addr
289+
if end_addr < MAX_END_ADDR and all(a < start_addr or a >= end_addr for a in access_exceptions):
284290
break
285-
286-
assert start is not None, "Failed to randomize start address for AWBURST: {}, AWLEN: {}, AWSIZE: {}, AWUSER: {}".format(awburst, awlen, awsize, awuser)
291+
else:
292+
assert False, "Failed to randomize start address for AWBURST: {}, AWLEN: {}, AWSIZE: {}, AWUSER: {}".format(awburst, awlen, awsize, awuser)
287293

288294
if awburst is AxiBurstType.FIXED:
289295
wdata = write_map[start_addr:start_addr+size] * (awlen + 1)

0 commit comments

Comments
 (0)