|
4 | 4 | import logging |
5 | 5 | from math import log2 |
6 | 6 | import random |
| 7 | +from typing import Optional |
7 | 8 |
|
8 | 9 | from bus2csr import bytes2int, compare_values, int2dword |
9 | 10 | from interface import I3CTopTestInterface |
@@ -194,8 +195,7 @@ async def test_basic_burst_read(dut): |
194 | 195 |
|
195 | 196 | @cocotb.test() |
196 | 197 | 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 |
199 | 199 |
|
200 | 200 | # inner registers that are problematic to validate |
201 | 201 | # e.g. have hwclr fields that don't yield 0 |
@@ -224,66 +224,72 @@ async def test_basic_burst_write(dut): |
224 | 224 |
|
225 | 225 | tb = await initialize(dut, timeout=500) |
226 | 226 |
|
| 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 | + |
227 | 246 | # Registers that hang the bus due to additional requirements when accessing them |
228 | | - access_exceptions = [ |
| 247 | + access_exceptions = sorted([ |
229 | 248 | tb.reg_map.PIOCONTROL.RESPONSE_PORT.base_addr, |
230 | 249 | tb.reg_map.PIOCONTROL.TX_DATA_PORT.base_addr, |
231 | 250 | tb.reg_map.PIOCONTROL.IBI_PORT.base_addr, |
232 | 251 | tb.reg_map.I3C_EC.TTI.TX_DATA_PORT.base_addr, |
233 | 252 | tb.reg_map.I3C_EC.TTI.RX_DATA_PORT.base_addr, |
234 | 253 | 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." |
240 | 262 | ) |
241 | 263 |
|
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 | | - |
256 | 264 | burst_lens = { |
257 | 265 | 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 | + ] |
259 | 270 | } |
260 | 271 |
|
261 | 272 | for awburst, awlens in burst_lens.items(): |
262 | 273 | for awlen, awsize, awlock, awuser in product( |
263 | 274 | awlens, (0, 1, 2), AxiLockType, (0, 0xAAAAAAAA, 0x55555555) |
264 | 275 | ): |
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: |
268 | 281 | continue |
269 | 282 |
|
270 | | - start = None |
271 | | - size = 2 ** awsize |
272 | 283 | # Try to randomize start address 1000 times, if didn't succeed then |
273 | 284 | # it's probably impossible to randomize such burst on a given reg |
274 | 285 | # map with given exceptions |
275 | 286 | for _ in range(0, 1000): |
276 | | - start_addr = random.choice(legal_addr) |
| 287 | + start_addr = random.choice(range(0, MAX_END_ADDR, 4)) |
277 | 288 | 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): |
284 | 290 | 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) |
287 | 293 |
|
288 | 294 | if awburst is AxiBurstType.FIXED: |
289 | 295 | wdata = write_map[start_addr:start_addr+size] * (awlen + 1) |
|
0 commit comments