11# SPDX-License-Identifier: Apache-2.0
22"""Shared constants and helpers for I3C Target cocotb tests."""
33
4+ import random
5+
46import cocotb
57from cocotb .triggers import Timer
68
7- # Canonical I3C address list — single source of truth
9+ from ccc import CCC
10+
11+ # Canonical I3C address list -- single source of truth
812# 0x23 is excluded: it is hardcoded as the I3CTarget sim model address
913# and selecting it for DUT addresses causes bus contention.
1014VALID_I3C_ADDRESSES = (
@@ -28,3 +32,232 @@ def log_seed(dut):
2832 """Log the random seed for reproducibility."""
2933 seed = cocotb .plusargs .get ("seed" , None )
3034 dut ._log .info (f"Random seed: { seed or 'unknown (set via RANDOM_SEED plusarg)' } " )
35+
36+
37+ # =============================================================================
38+ # CCC helpers
39+ #
40+ # Generic wrappers around common CCC commands. Each function takes an
41+ # i3c_controller and target address(es) explicitly so they can be reused
42+ # from any test file.
43+ #
44+ # GET helpers return the raw data bytes (bytearray) and assert ACK.
45+ # SET helpers return True/False indicating whether the target ACKed.
46+ # All helpers log the CCC name, address, and returned data.
47+ # =============================================================================
48+
49+ # Valid event-enable bit patterns for ENEC / DISEC
50+ ENEC_DISEC_PATTERNS = [0x01 , 0x02 , 0x08 , 0x03 , 0x09 , 0x0A , 0x0B ]
51+
52+
53+ async def do_getstatus (i3c_controller , addr ):
54+ """Send directed GETSTATUS. Returns 2-byte status (bytearray)."""
55+ responses = await i3c_controller .i3c_ccc_read (
56+ ccc = CCC .DIRECT .GETSTATUS , addr = addr , count = 2 )
57+ ack , data = responses [0 ]
58+ assert ack , f"GETSTATUS NACK addr=0x{ addr :02X} "
59+ cocotb .log .info (f"GETSTATUS addr=0x{ addr :02X} -> { data .hex ()} " )
60+ return data
61+
62+
63+ async def do_getbcr (i3c_controller , addr ):
64+ """Send directed GETBCR. Returns 1-byte BCR (bytearray)."""
65+ responses = await i3c_controller .i3c_ccc_read (
66+ ccc = CCC .DIRECT .GETBCR , addr = addr , count = 1 )
67+ ack , data = responses [0 ]
68+ assert ack , f"GETBCR NACK addr=0x{ addr :02X} "
69+ cocotb .log .info (f"GETBCR addr=0x{ addr :02X} -> { data .hex ()} " )
70+ return data
71+
72+
73+ async def do_getdcr (i3c_controller , addr ):
74+ """Send directed GETDCR. Returns 1-byte DCR (bytearray)."""
75+ responses = await i3c_controller .i3c_ccc_read (
76+ ccc = CCC .DIRECT .GETDCR , addr = addr , count = 1 )
77+ ack , data = responses [0 ]
78+ assert ack , f"GETDCR NACK addr=0x{ addr :02X} "
79+ cocotb .log .info (f"GETDCR addr=0x{ addr :02X} -> { data .hex ()} " )
80+ return data
81+
82+
83+ async def do_getmwl (i3c_controller , addr ):
84+ """Send directed GETMWL. Returns 2-byte MWL (bytearray)."""
85+ responses = await i3c_controller .i3c_ccc_read (
86+ ccc = CCC .DIRECT .GETMWL , addr = addr , count = 2 )
87+ ack , data = responses [0 ]
88+ assert ack , f"GETMWL NACK addr=0x{ addr :02X} "
89+ cocotb .log .info (f"GETMWL addr=0x{ addr :02X} -> { data .hex ()} " )
90+ return data
91+
92+
93+ async def do_getmrl (i3c_controller , addr ):
94+ """Send directed GETMRL. Returns 3-byte MRL (bytearray)."""
95+ responses = await i3c_controller .i3c_ccc_read (
96+ ccc = CCC .DIRECT .GETMRL , addr = addr , count = 3 )
97+ ack , data = responses [0 ]
98+ assert ack , f"GETMRL NACK addr=0x{ addr :02X} "
99+ cocotb .log .info (f"GETMRL addr=0x{ addr :02X} -> { data .hex ()} " )
100+ return data
101+
102+
103+ async def do_getpid (i3c_controller , addr ):
104+ """Send directed GETPID. Returns 6-byte PID (bytearray)."""
105+ responses = await i3c_controller .i3c_ccc_read (
106+ ccc = CCC .DIRECT .GETPID , addr = addr , count = 6 )
107+ ack , data = responses [0 ]
108+ assert ack , f"GETPID NACK addr=0x{ addr :02X} "
109+ cocotb .log .info (f"GETPID addr=0x{ addr :02X} -> { data .hex ()} " )
110+ return data
111+
112+
113+ async def do_getcaps (i3c_controller , addr ):
114+ """Send directed GETCAPS (no defining byte, 3 bytes). Returns bytearray."""
115+ responses = await i3c_controller .i3c_ccc_read (
116+ ccc = CCC .DIRECT .GETCAPS , addr = addr , count = 3 )
117+ ack , data = responses [0 ]
118+ assert ack , f"GETCAPS NACK addr=0x{ addr :02X} "
119+ cocotb .log .info (f"GETCAPS addr=0x{ addr :02X} -> { data .hex ()} " )
120+ return data
121+
122+
123+ async def do_setmwl_direct (i3c_controller , addr , val = None ):
124+ """Send directed SETMWL. Returns True if ACKed, False otherwise."""
125+ if val is None :
126+ val = random .randint (0 , 0xFFFF )
127+ acks = await i3c_controller .i3c_ccc_write (
128+ ccc = CCC .DIRECT .SETMWL ,
129+ directed_data = [(addr , [(val >> 8 ) & 0xFF , val & 0xFF ])])
130+ ok = bool (acks and acks [0 ])
131+ cocotb .log .info (f"SETMWL direct addr=0x{ addr :02X} val=0x{ val :04X} ack={ ok } " )
132+ return ok
133+
134+
135+ async def do_setmrl_direct (i3c_controller , addr , val = None , ibil = None ):
136+ """Send directed SETMRL. Returns True if ACKed, False otherwise."""
137+ if val is None :
138+ val = random .randint (0 , 0xFFFF )
139+ if ibil is None :
140+ ibil = random .randint (0 , 0xFF )
141+ acks = await i3c_controller .i3c_ccc_write (
142+ ccc = CCC .DIRECT .SETMRL ,
143+ directed_data = [(addr , [(val >> 8 ) & 0xFF , val & 0xFF , ibil ])])
144+ ok = bool (acks and acks [0 ])
145+ cocotb .log .info (
146+ f"SETMRL direct addr=0x{ addr :02X} mrl=0x{ val :04X} "
147+ f"ibil=0x{ ibil :02X} ack={ ok } " )
148+ return ok
149+
150+
151+ async def do_enec_direct (i3c_controller , addr , pattern = None ):
152+ """Send directed ENEC. Returns True if ACKed, False otherwise."""
153+ if pattern is None :
154+ pattern = random .choice (ENEC_DISEC_PATTERNS )
155+ acks = await i3c_controller .i3c_ccc_write (
156+ ccc = CCC .DIRECT .ENEC , directed_data = [(addr , [pattern ])])
157+ ok = bool (acks and acks [0 ])
158+ cocotb .log .info (f"ENEC direct addr=0x{ addr :02X} pattern=0x{ pattern :02X} ack={ ok } " )
159+ return ok
160+
161+
162+ async def do_disec_direct (i3c_controller , addr , pattern = None ):
163+ """Send directed DISEC. Returns True if ACKed, False otherwise."""
164+ if pattern is None :
165+ pattern = random .choice (ENEC_DISEC_PATTERNS )
166+ acks = await i3c_controller .i3c_ccc_write (
167+ ccc = CCC .DIRECT .DISEC , directed_data = [(addr , [pattern ])])
168+ ok = bool (acks and acks [0 ])
169+ cocotb .log .info (
170+ f"DISEC direct addr=0x{ addr :02X} pattern=0x{ pattern :02X} ack={ ok } " )
171+ return ok
172+
173+
174+ async def do_rstact_direct (i3c_controller , addr , defining_byte = None ):
175+ """Send directed RSTACT. Returns True if ACKed, False otherwise."""
176+ if defining_byte is None :
177+ defining_byte = random .choice ([0x00 , 0x01 , 0x02 ])
178+ acks = await i3c_controller .i3c_ccc_write (
179+ ccc = CCC .DIRECT .RSTACT , defining_byte = defining_byte ,
180+ directed_data = [(addr , [])], stop = True )
181+ ok = bool (acks and acks [0 ])
182+ cocotb .log .info (
183+ f"RSTACT direct addr=0x{ addr :02X} db=0x{ defining_byte :02X} ack={ ok } " )
184+ return ok
185+
186+
187+ async def do_setmwl_bcast (i3c_controller , val = None ):
188+ """Send broadcast SETMWL. Returns True (broadcast has no per-target ACK)."""
189+ if val is None :
190+ val = random .randint (0 , 0xFFFF )
191+ await i3c_controller .i3c_ccc_write (
192+ ccc = CCC .BCAST .SETMWL ,
193+ broadcast_data = [(val >> 8 ) & 0xFF , val & 0xFF ])
194+ cocotb .log .info (f"SETMWL bcast val=0x{ val :04X} " )
195+ return True
196+
197+
198+ async def do_setmrl_bcast (i3c_controller , val = None , ibil = None ):
199+ """Send broadcast SETMRL. Returns True (broadcast has no per-target ACK)."""
200+ if val is None :
201+ val = random .randint (0 , 0xFFFF )
202+ if ibil is None :
203+ ibil = random .randint (0 , 0xFF )
204+ await i3c_controller .i3c_ccc_write (
205+ ccc = CCC .BCAST .SETMRL ,
206+ broadcast_data = [(val >> 8 ) & 0xFF , val & 0xFF , ibil ])
207+ cocotb .log .info (f"SETMRL bcast mrl=0x{ val :04X} ibil=0x{ ibil :02X} " )
208+ return True
209+
210+
211+ async def do_enec_bcast (i3c_controller , pattern = None ):
212+ """Send broadcast ENEC. Returns True (broadcast has no per-target ACK)."""
213+ if pattern is None :
214+ pattern = random .choice (ENEC_DISEC_PATTERNS )
215+ await i3c_controller .i3c_ccc_write (
216+ ccc = CCC .BCAST .ENEC , broadcast_data = [pattern ])
217+ cocotb .log .info (f"ENEC bcast pattern=0x{ pattern :02X} " )
218+ return True
219+
220+
221+ async def do_disec_bcast (i3c_controller , pattern = None ):
222+ """Send broadcast DISEC. Returns True (broadcast has no per-target ACK)."""
223+ if pattern is None :
224+ pattern = random .choice (ENEC_DISEC_PATTERNS )
225+ await i3c_controller .i3c_ccc_write (
226+ ccc = CCC .BCAST .DISEC , broadcast_data = [pattern ])
227+ cocotb .log .info (f"DISEC bcast pattern=0x{ pattern :02X} " )
228+ return True
229+
230+
231+ def build_ccc_stress_table (i3c_controller , targets ):
232+ """Build a weighted CCC command table for stress testing.
233+
234+ Returns a list of zero-argument coroutine functions. Each picks a random
235+ target from *targets* (for directed CCCs) and sends one CCC.
236+ SET CCCs appear twice for higher weight (more race opportunity).
237+ """
238+ get_table = [
239+ lambda c = i3c_controller , t = targets : do_getstatus (c , random .choice (t )),
240+ lambda c = i3c_controller , t = targets : do_getbcr (c , random .choice (t )),
241+ lambda c = i3c_controller , t = targets : do_getdcr (c , random .choice (t )),
242+ lambda c = i3c_controller , t = targets : do_getmwl (c , random .choice (t )),
243+ lambda c = i3c_controller , t = targets : do_getmrl (c , random .choice (t )),
244+ lambda c = i3c_controller , t = targets : do_getpid (c , random .choice (t )),
245+ lambda c = i3c_controller , t = targets : do_getcaps (c , random .choice (t )),
246+ ]
247+
248+ set_directed = [
249+ lambda c = i3c_controller , t = targets : do_setmwl_direct (c , random .choice (t )),
250+ lambda c = i3c_controller , t = targets : do_setmrl_direct (c , random .choice (t )),
251+ lambda c = i3c_controller , t = targets : do_enec_direct (c , random .choice (t )),
252+ lambda c = i3c_controller , t = targets : do_disec_direct (c , random .choice (t )),
253+ lambda c = i3c_controller , t = targets : do_rstact_direct (c , random .choice (t )),
254+ ]
255+
256+ set_bcast = [
257+ lambda c = i3c_controller : do_setmwl_bcast (c ),
258+ lambda c = i3c_controller : do_setmrl_bcast (c ),
259+ lambda c = i3c_controller : do_enec_bcast (c ),
260+ lambda c = i3c_controller : do_disec_bcast (c ),
261+ ]
262+
263+ return get_table + set_directed * 2 + set_bcast * 2
0 commit comments