14
14
import cocotb
15
15
from cocotb .triggers import ClockCycles , RisingEdge , Timer
16
16
17
+ VALID_I3C_ADDRESSES = (
18
+ [i for i in range (0x03 , 0x3E )]
19
+ + [i for i in range (0x3F , 0x5B )]
20
+ + [i for i in range (0x5C , 0x5E )]
21
+ + [i for i in range (0x5F , 0x6E )]
22
+ + [i for i in range (0x5F , 0x6E )]
23
+ + [i for i in range (0x6F , 0x76 )]
24
+ + [i for i in range (0x77 , 0x7A )]
25
+ + [0x7B , 0x7D ]
26
+ )
17
27
TARGET_ADDRESS = 0x5A
18
28
19
29
@@ -32,7 +42,7 @@ async def test_setup(dut, fclk=100.0, fbus=12.5):
32
42
"""
33
43
34
44
cocotb .log .setLevel (logging .INFO )
35
- cocotb .start_soon (timeout_task (200 ))
45
+ cocotb .start_soon (timeout_task (500000 ))
36
46
37
47
dut ._log .info (f"fclk = { fclk :.3f} MHz" )
38
48
dut ._log .info (f"fbus = { fbus :.3f} MHz" )
@@ -338,6 +348,78 @@ def compare(expected, received, lnt=None):
338
348
assert response .nack
339
349
340
350
351
+ @cocotb .test ()
352
+ async def test_i3c_target_read_to_multiple_targets (dut ):
353
+
354
+ # Setup
355
+ i3c_controller , i3c_target , tb = await test_setup (dut , fclk = 400 )
356
+
357
+ # Generates a randomized transfer and puts it into the TTI TX queue
358
+ async def make_transfer (min_len = 1 , max_len = 16 ):
359
+
360
+ length = random .randint (min_len , max_len )
361
+ data = [random .randint (0 , 255 ) for _ in range (length )]
362
+
363
+ dut ._log .info (f"Enqueueing transfer of length { length } " )
364
+
365
+ # Write data to TTI TX FIFO
366
+ for i in range ((length + 3 ) // 4 ):
367
+ word = data [4 * i ]
368
+ if 4 * i + 1 < length :
369
+ word |= data [4 * i + 1 ] << 8
370
+ if 4 * i + 2 < length :
371
+ word |= data [4 * i + 2 ] << 16
372
+ if 4 * i + 3 < length :
373
+ word |= data [4 * i + 3 ] << 24
374
+
375
+ await tb .write_csr (tb .reg_map .I3C_EC .TTI .TX_DATA_PORT .base_addr , int2dword (word ), 4 )
376
+
377
+ # Write the TX descriptor
378
+ await tb .write_csr (tb .reg_map .I3C_EC .TTI .TX_DESC_QUEUE_PORT .base_addr , int2dword (length ), 4 )
379
+
380
+ return data
381
+
382
+ def compare (expected , received , lnt = None ):
383
+ if lnt is None or lnt == len (expected ):
384
+ sfx = ""
385
+ else :
386
+ sfx = " ([" + " " .join ([f"{ d :02X} " for d in expected [lnt :]]) + "] skipped)"
387
+ expected = expected [:lnt ]
388
+
389
+ dut ._log .info ("Expected: [" + " " .join ([f"{ d :02X} " for d in expected ]) + "]" + sfx )
390
+ dut ._log .info ("Received: [" + " " .join ([f"{ d :02X} " for d in received ]) + "]" )
391
+ assert expected == received
392
+
393
+ # issue 40 random read transactions
394
+ # randomly choose to inicialize the FIFO or not
395
+ # if FIFO is not initialized the transation should be NACKed
396
+ for _ in range (40 ):
397
+ num_transfers = random .randint (3 , 10 )
398
+ addresses = []
399
+ num_transfers_to_our_target = random .randint (1 , num_transfers - 1 )
400
+ for _ in range (num_transfers_to_our_target ):
401
+ addresses .append (TARGET_ADDRESS )
402
+ while len (addresses ) < num_transfers :
403
+ addresses .append (random .choice (VALID_I3C_ADDRESSES ))
404
+ random .shuffle (addresses )
405
+ data_len_rsvd_stop_nack = []
406
+ for i , addr in enumerate (addresses ):
407
+ send_rsvd = random .choice ([True , False ]) if i == 0 else False
408
+ stop = i == num_transfers - 1
409
+ if addr == TARGET_ADDRESS :
410
+ tx_data = await make_transfer ()
411
+ data_len_rsvd_stop_nack .append ((tx_data , len (tx_data ), send_rsvd , stop , False ))
412
+ else :
413
+ data_len_rsvd_stop_nack .append ((None , random .randint (1 , 16 ), send_rsvd , stop , True ))
414
+
415
+ for address , (tx_data , length , rsvd , stop , nack ) in zip (addresses , data_len_rsvd_stop_nack ):
416
+ response = await i3c_controller .i3c_read (address , length , send_rsvd = rsvd , stop = stop )
417
+ assert nack == response .nack
418
+ if not nack :
419
+ rx_data = list (response .data )
420
+ compare (tx_data , rx_data )
421
+
422
+
341
423
@cocotb .test ()
342
424
async def test_i3c_target_ibi (dut ):
343
425
"""
0 commit comments