1 # This file is Copyright (c) 2019 Florent Kermarrec <florent@enjoy-digital.fr>
9 from litex
.soc
.interconnect
.axi
import *
10 from litex
.soc
.interconnect
import wishbone
12 # Software Models ----------------------------------------------------------------------------------
15 def __init__(self
, addr
, type=BURST_FIXED
, len=0, size
=0):
23 for i
in range(self
.len + 1):
24 if self
.type == BURST_INCR
:
25 offset
= i
*2**(self
.size
)
26 r
+= [Beat(self
.addr
+ offset
)]
27 elif self
.type == BURST_WRAP
:
28 offset
= (i
*2**(self
.size
))%((2**self
.size
)*(self
.len + 1))
29 r
+= [Beat(self
.addr
+ offset
)]
31 r
+= [Beat(self
.addr
)]
36 def __init__(self
, addr
):
41 def __init__(self
, addr
, data
, id, **kwargs
):
42 Burst
.__init
__(self
, addr
, **kwargs
)
54 # TestAXI ------------------------------------------------------------------------------------------
56 class TestAXI(unittest
.TestCase
):
57 def test_burst2beat(self
):
58 def bursts_generator(ax
, bursts
, valid_rand
=50):
59 prng
= random
.Random(42)
62 yield ax
.addr
.eq(burst
.addr
)
63 yield ax
.burst
.eq(burst
.type)
64 yield ax
.len.eq(burst
.len)
65 yield ax
.size
.eq(burst
.size
)
66 while (yield ax
.ready
) == 0:
69 while prng
.randrange(100) < valid_rand
:
74 def beats_checker(ax
, beats
, ready_rand
=50):
77 prng
= random
.Random(42)
79 while ((yield ax
.valid
) and (yield ax
.ready
)) == 0:
80 if prng
.randrange(100) > ready_rand
:
85 ax_addr
= (yield ax
.addr
)
86 if ax_addr
!= beat
.addr
:
91 ax_burst
= stream
.Endpoint(ax_description(32, 32))
92 ax_beat
= stream
.Endpoint(ax_description(32, 32))
93 dut
= AXIBurst2Beat(ax_burst
, ax_beat
)
95 # generate dut input (bursts)
96 prng
= random
.Random(42)
99 bursts
.append(Burst(prng
.randrange(2**32), BURST_FIXED
, prng
.randrange(255), log2_int(32//8)))
100 bursts
.append(Burst(prng
.randrange(2**32), BURST_INCR
, prng
.randrange(255), log2_int(32//8)))
101 bursts
.append(Burst(4, BURST_WRAP
, 4-1, log2_int(2)))
103 # generate expected dut output (beats for reference)
106 beats
+= burst
.to_beats()
110 bursts_generator(ax_burst
, bursts
),
111 beats_checker(ax_beat
, beats
)
113 run_simulation(dut
, generators
)
114 self
.assertEqual(self
.errors
, 0)
117 def _test_axi2wishbone(self
,
118 naccesses
=16, simultaneous_writes_reads
=False,
119 # random: 0: min (no random), 100: max.
121 id_rand_enable
= False,
122 len_rand_enable
= False,
123 data_rand_enable
= False,
124 # flow valid randomness
129 # flow ready randomness
135 def writes_cmd_generator(axi_port
, writes
):
136 prng
= random
.Random(42)
138 while prng
.randrange(100) < aw_valid_random
:
141 yield axi_port
.aw
.valid
.eq(1)
142 yield axi_port
.aw
.addr
.eq(write
.addr
<<2)
143 yield axi_port
.aw
.burst
.eq(write
.type)
144 yield axi_port
.aw
.len.eq(write
.len)
145 yield axi_port
.aw
.size
.eq(write
.size
)
146 yield axi_port
.aw
.id.eq(write
.id)
148 while (yield axi_port
.aw
.ready
) == 0:
150 yield axi_port
.aw
.valid
.eq(0)
152 def writes_data_generator(axi_port
, writes
):
153 prng
= random
.Random(42)
154 yield axi_port
.w
.strb
.eq(2**(len(axi_port
.w
.data
)//8) - 1)
156 for i
, data
in enumerate(write
.data
):
157 while prng
.randrange(100) < w_valid_random
:
160 yield axi_port
.w
.valid
.eq(1)
161 if (i
== (len(write
.data
) - 1)):
162 yield axi_port
.w
.last
.eq(1)
164 yield axi_port
.w
.last
.eq(0)
165 yield axi_port
.w
.data
.eq(data
)
167 while (yield axi_port
.w
.ready
) == 0:
169 yield axi_port
.w
.valid
.eq(0)
170 axi_port
.reads_enable
= True
172 def writes_response_generator(axi_port
, writes
):
173 prng
= random
.Random(42)
174 self
.writes_id_errors
= 0
177 yield axi_port
.b
.ready
.eq(0)
179 while (yield axi_port
.b
.valid
) == 0:
181 while prng
.randrange(100) < b_ready_random
:
183 yield axi_port
.b
.ready
.eq(1)
185 if (yield axi_port
.b
.id) != write
.id:
186 self
.writes_id_errors
+= 1
188 def reads_cmd_generator(axi_port
, reads
):
189 prng
= random
.Random(42)
190 while not axi_port
.reads_enable
:
193 while prng
.randrange(100) < ar_valid_random
:
196 yield axi_port
.ar
.valid
.eq(1)
197 yield axi_port
.ar
.addr
.eq(read
.addr
<<2)
198 yield axi_port
.ar
.burst
.eq(read
.type)
199 yield axi_port
.ar
.len.eq(read
.len)
200 yield axi_port
.ar
.size
.eq(read
.size
)
201 yield axi_port
.ar
.id.eq(read
.id)
203 while (yield axi_port
.ar
.ready
) == 0:
205 yield axi_port
.ar
.valid
.eq(0)
207 def reads_response_data_generator(axi_port
, reads
):
208 prng
= random
.Random(42)
209 self
.reads_data_errors
= 0
210 self
.reads_id_errors
= 0
211 self
.reads_last_errors
= 0
212 while not axi_port
.reads_enable
:
215 for i
, data
in enumerate(read
.data
):
216 # wait data / response
217 yield axi_port
.r
.ready
.eq(0)
219 while (yield axi_port
.r
.valid
) == 0:
221 while prng
.randrange(100) < r_ready_random
:
223 yield axi_port
.r
.ready
.eq(1)
225 if (yield axi_port
.r
.data
) != data
:
226 self
.reads_data_errors
+= 1
227 if (yield axi_port
.r
.id) != read
.id:
228 self
.reads_id_errors
+= 1
229 if i
== (len(read
.data
) - 1):
230 if (yield axi_port
.r
.last
) != 1:
231 self
.reads_last_errors
+= 1
233 if (yield axi_port
.r
.last
) != 0:
234 self
.reads_last_errors
+= 1
239 self
.axi
= AXIInterface(data_width
=32, address_width
=32, id_width
=8)
240 self
.wishbone
= wishbone
.Interface(data_width
=32)
242 axi2wishbone
= AXI2Wishbone(self
.axi
, self
.wishbone
)
243 self
.submodules
+= axi2wishbone
245 wishbone_mem
= wishbone
.SRAM(1024, bus
=self
.wishbone
)
246 self
.submodules
+= wishbone_mem
250 # generate writes/reads
251 prng
= random
.Random(42)
254 for i
in range(naccesses
):
255 _id
= prng
.randrange(2**8) if id_rand_enable
else i
256 _len
= prng
.randrange(32) if len_rand_enable
else i
257 _data
= [prng
.randrange(2**32) if data_rand_enable
else j
for j
in range(_len
+ 1)]
258 writes
.append(Write(offset
, _data
, _id
, type=BURST_INCR
, len=_len
, size
=log2_int(32//8)))
260 # dummy reads to ensure datas have been written before the effective reads start.
261 dummy_reads
= [Read(1023, [0], 0, type=BURST_FIXED
, len=0, size
=log2_int(32//8)) for _
in range(32)]
265 if simultaneous_writes_reads
:
266 dut
.axi
.reads_enable
= True
268 dut
.axi
.reads_enable
= False # will be set by writes_data_generator
270 writes_cmd_generator(dut
.axi
, writes
),
271 writes_data_generator(dut
.axi
, writes
),
272 writes_response_generator(dut
.axi
, writes
),
273 reads_cmd_generator(dut
.axi
, reads
),
274 reads_response_data_generator(dut
.axi
, reads
)
276 run_simulation(dut
, generators
)
277 self
.assertEqual(self
.writes_id_errors
, 0)
278 self
.assertEqual(self
.reads_data_errors
, 0)
279 self
.assertEqual(self
.reads_id_errors
, 0)
280 self
.assertEqual(self
.reads_last_errors
, 0)
282 # test with no randomness
283 def test_axi2wishbone_writes_then_reads_no_random(self
):
284 self
._test
_axi
2wishbone
(simultaneous_writes_reads
=False)
286 # test randomness one parameter at a time
287 def test_axi2wishbone_writes_then_reads_random_bursts(self
):
288 self
._test
_axi
2wishbone
(
289 simultaneous_writes_reads
= False,
290 id_rand_enable
= True,
291 len_rand_enable
= True,
292 data_rand_enable
= True)
294 def test_axi2wishbone_random_w_ready(self
):
295 self
._test
_axi
2wishbone
(w_ready_random
=90)
297 def test_axi2wishbone_random_b_ready(self
):
298 self
._test
_axi
2wishbone
(b_ready_random
=90)
300 def test_axi2wishbone_random_r_ready(self
):
301 self
._test
_axi
2wishbone
(r_ready_random
=90)
303 def test_axi2wishbone_random_aw_valid(self
):
304 self
._test
_axi
2wishbone
(aw_valid_random
=90)
306 def test_axi2wishbone_random_w_valid(self
):
307 self
._test
_axi
2wishbone
(w_valid_random
=90)
309 def test_axi2wishbone_random_ar_valid(self
):
310 self
._test
_axi
2wishbone
(ar_valid_random
=90)
312 def test_axi2wishbone_random_r_valid(self
):
313 self
._test
_axi
2wishbone
(r_valid_random
=90)
315 # now let's stress things a bit... :)
316 def test_axi2wishbone_random_all(self
):
317 self
._test
_axi
2wishbone
(
318 simultaneous_writes_reads
= False,
319 id_rand_enable
= True,
320 len_rand_enable
= True,
321 aw_valid_random
= 50,
325 ar_valid_random
= 90,