1 # SPDX-License-Identifier: LGPL-3-or-later
2 # Copyright 2022 Jacob Lifshay
4 from functools
import reduce
7 from nmigen
.hdl
.ast
import AnyConst
, Assert
, Signal
, Const
, Array
, Shape
, Mux
8 from nmigen
.hdl
.dsl
import Module
9 from nmutil
.formaltest
import FHDLTestCase
10 from nmutil
.picker
import (BetterMultiPriorityPicker
, PriorityPicker
,
12 from nmutil
.sim_util
import write_il
15 class TestPriorityPicker(FHDLTestCase
):
16 def tst(self
, wid
, msb_mode
, reverse_i
, reverse_o
):
17 assert isinstance(wid
, int)
18 assert isinstance(msb_mode
, bool)
19 assert isinstance(reverse_i
, bool)
20 assert isinstance(reverse_o
, bool)
21 dut
= PriorityPicker(wid
=wid
, msb_mode
=msb_mode
, reverse_i
=reverse_i
,
23 self
.assertEqual(wid
, dut
.wid
)
24 self
.assertEqual(msb_mode
, dut
.msb_mode
)
25 self
.assertEqual(reverse_i
, dut
.reverse_i
)
26 self
.assertEqual(reverse_o
, dut
.reverse_o
)
27 self
.assertEqual(len(dut
.i
), wid
)
28 self
.assertEqual(len(dut
.o
), wid
)
29 self
.assertEqual(len(dut
.en_o
), 1)
31 m
.submodules
.dut
= dut
32 m
.d
.comb
+= dut
.i
.eq(AnyConst(wid
))
34 # assert dut.o only has zero or one bit set
35 m
.d
.comb
+= Assert((dut
.o
& (dut
.o
- 1)) == 0)
37 m
.d
.comb
+= Assert((dut
.o
!= 0) == dut
.en_o
)
39 unreversed_i
= Signal(wid
)
41 m
.d
.comb
+= unreversed_i
.eq(dut
.i
[::-1])
43 m
.d
.comb
+= unreversed_i
.eq(dut
.i
)
45 unreversed_o
= Signal(wid
)
47 m
.d
.comb
+= unreversed_o
.eq(dut
.o
[::-1])
49 m
.d
.comb
+= unreversed_o
.eq(dut
.o
)
51 expected_unreversed_o
= Signal(wid
)
53 found
= Const(False, 1)
54 for i
in reversed(range(wid
)) if msb_mode
else range(wid
):
55 m
.d
.comb
+= expected_unreversed_o
[i
].eq(unreversed_i
[i
] & ~found
)
56 found |
= unreversed_i
[i
]
58 m
.d
.comb
+= Assert(expected_unreversed_o
== unreversed_o
)
62 def test_1_msbm_f_revi_f_revo_f(self
):
63 self
.tst(wid
=1, msb_mode
=False, reverse_i
=False, reverse_o
=False)
65 def test_1_msbm_f_revi_f_revo_t(self
):
66 self
.tst(wid
=1, msb_mode
=False, reverse_i
=False, reverse_o
=True)
68 def test_1_msbm_f_revi_t_revo_f(self
):
69 self
.tst(wid
=1, msb_mode
=False, reverse_i
=True, reverse_o
=False)
71 def test_1_msbm_f_revi_t_revo_t(self
):
72 self
.tst(wid
=1, msb_mode
=False, reverse_i
=True, reverse_o
=True)
74 def test_1_msbm_t_revi_f_revo_f(self
):
75 self
.tst(wid
=1, msb_mode
=True, reverse_i
=False, reverse_o
=False)
77 def test_1_msbm_t_revi_f_revo_t(self
):
78 self
.tst(wid
=1, msb_mode
=True, reverse_i
=False, reverse_o
=True)
80 def test_1_msbm_t_revi_t_revo_f(self
):
81 self
.tst(wid
=1, msb_mode
=True, reverse_i
=True, reverse_o
=False)
83 def test_1_msbm_t_revi_t_revo_t(self
):
84 self
.tst(wid
=1, msb_mode
=True, reverse_i
=True, reverse_o
=True)
86 def test_2_msbm_f_revi_f_revo_f(self
):
87 self
.tst(wid
=2, msb_mode
=False, reverse_i
=False, reverse_o
=False)
89 def test_2_msbm_f_revi_f_revo_t(self
):
90 self
.tst(wid
=2, msb_mode
=False, reverse_i
=False, reverse_o
=True)
92 def test_2_msbm_f_revi_t_revo_f(self
):
93 self
.tst(wid
=2, msb_mode
=False, reverse_i
=True, reverse_o
=False)
95 def test_2_msbm_f_revi_t_revo_t(self
):
96 self
.tst(wid
=2, msb_mode
=False, reverse_i
=True, reverse_o
=True)
98 def test_2_msbm_t_revi_f_revo_f(self
):
99 self
.tst(wid
=2, msb_mode
=True, reverse_i
=False, reverse_o
=False)
101 def test_2_msbm_t_revi_f_revo_t(self
):
102 self
.tst(wid
=2, msb_mode
=True, reverse_i
=False, reverse_o
=True)
104 def test_2_msbm_t_revi_t_revo_f(self
):
105 self
.tst(wid
=2, msb_mode
=True, reverse_i
=True, reverse_o
=False)
107 def test_2_msbm_t_revi_t_revo_t(self
):
108 self
.tst(wid
=2, msb_mode
=True, reverse_i
=True, reverse_o
=True)
110 def test_3_msbm_f_revi_f_revo_f(self
):
111 self
.tst(wid
=3, msb_mode
=False, reverse_i
=False, reverse_o
=False)
113 def test_3_msbm_f_revi_f_revo_t(self
):
114 self
.tst(wid
=3, msb_mode
=False, reverse_i
=False, reverse_o
=True)
116 def test_3_msbm_f_revi_t_revo_f(self
):
117 self
.tst(wid
=3, msb_mode
=False, reverse_i
=True, reverse_o
=False)
119 def test_3_msbm_f_revi_t_revo_t(self
):
120 self
.tst(wid
=3, msb_mode
=False, reverse_i
=True, reverse_o
=True)
122 def test_3_msbm_t_revi_f_revo_f(self
):
123 self
.tst(wid
=3, msb_mode
=True, reverse_i
=False, reverse_o
=False)
125 def test_3_msbm_t_revi_f_revo_t(self
):
126 self
.tst(wid
=3, msb_mode
=True, reverse_i
=False, reverse_o
=True)
128 def test_3_msbm_t_revi_t_revo_f(self
):
129 self
.tst(wid
=3, msb_mode
=True, reverse_i
=True, reverse_o
=False)
131 def test_3_msbm_t_revi_t_revo_t(self
):
132 self
.tst(wid
=3, msb_mode
=True, reverse_i
=True, reverse_o
=True)
134 def test_4_msbm_f_revi_f_revo_f(self
):
135 self
.tst(wid
=4, msb_mode
=False, reverse_i
=False, reverse_o
=False)
137 def test_4_msbm_f_revi_f_revo_t(self
):
138 self
.tst(wid
=4, msb_mode
=False, reverse_i
=False, reverse_o
=True)
140 def test_4_msbm_f_revi_t_revo_f(self
):
141 self
.tst(wid
=4, msb_mode
=False, reverse_i
=True, reverse_o
=False)
143 def test_4_msbm_f_revi_t_revo_t(self
):
144 self
.tst(wid
=4, msb_mode
=False, reverse_i
=True, reverse_o
=True)
146 def test_4_msbm_t_revi_f_revo_f(self
):
147 self
.tst(wid
=4, msb_mode
=True, reverse_i
=False, reverse_o
=False)
149 def test_4_msbm_t_revi_f_revo_t(self
):
150 self
.tst(wid
=4, msb_mode
=True, reverse_i
=False, reverse_o
=True)
152 def test_4_msbm_t_revi_t_revo_f(self
):
153 self
.tst(wid
=4, msb_mode
=True, reverse_i
=True, reverse_o
=False)
155 def test_4_msbm_t_revi_t_revo_t(self
):
156 self
.tst(wid
=4, msb_mode
=True, reverse_i
=True, reverse_o
=True)
158 def test_8_msbm_f_revi_f_revo_f(self
):
159 self
.tst(wid
=8, msb_mode
=False, reverse_i
=False, reverse_o
=False)
161 def test_8_msbm_f_revi_f_revo_t(self
):
162 self
.tst(wid
=8, msb_mode
=False, reverse_i
=False, reverse_o
=True)
164 def test_8_msbm_f_revi_t_revo_f(self
):
165 self
.tst(wid
=8, msb_mode
=False, reverse_i
=True, reverse_o
=False)
167 def test_8_msbm_f_revi_t_revo_t(self
):
168 self
.tst(wid
=8, msb_mode
=False, reverse_i
=True, reverse_o
=True)
170 def test_8_msbm_t_revi_f_revo_f(self
):
171 self
.tst(wid
=8, msb_mode
=True, reverse_i
=False, reverse_o
=False)
173 def test_8_msbm_t_revi_f_revo_t(self
):
174 self
.tst(wid
=8, msb_mode
=True, reverse_i
=False, reverse_o
=True)
176 def test_8_msbm_t_revi_t_revo_f(self
):
177 self
.tst(wid
=8, msb_mode
=True, reverse_i
=True, reverse_o
=False)
179 def test_8_msbm_t_revi_t_revo_t(self
):
180 self
.tst(wid
=8, msb_mode
=True, reverse_i
=True, reverse_o
=True)
182 def test_32_msbm_f_revi_f_revo_f(self
):
183 self
.tst(wid
=32, msb_mode
=False, reverse_i
=False, reverse_o
=False)
185 def test_32_msbm_f_revi_f_revo_t(self
):
186 self
.tst(wid
=32, msb_mode
=False, reverse_i
=False, reverse_o
=True)
188 def test_32_msbm_f_revi_t_revo_f(self
):
189 self
.tst(wid
=32, msb_mode
=False, reverse_i
=True, reverse_o
=False)
191 def test_32_msbm_f_revi_t_revo_t(self
):
192 self
.tst(wid
=32, msb_mode
=False, reverse_i
=True, reverse_o
=True)
194 def test_32_msbm_t_revi_f_revo_f(self
):
195 self
.tst(wid
=32, msb_mode
=True, reverse_i
=False, reverse_o
=False)
197 def test_32_msbm_t_revi_f_revo_t(self
):
198 self
.tst(wid
=32, msb_mode
=True, reverse_i
=False, reverse_o
=True)
200 def test_32_msbm_t_revi_t_revo_f(self
):
201 self
.tst(wid
=32, msb_mode
=True, reverse_i
=True, reverse_o
=False)
203 def test_32_msbm_t_revi_t_revo_t(self
):
204 self
.tst(wid
=32, msb_mode
=True, reverse_i
=True, reverse_o
=True)
206 def test_64_msbm_f_revi_f_revo_f(self
):
207 self
.tst(wid
=64, msb_mode
=False, reverse_i
=False, reverse_o
=False)
209 def test_64_msbm_f_revi_f_revo_t(self
):
210 self
.tst(wid
=64, msb_mode
=False, reverse_i
=False, reverse_o
=True)
212 def test_64_msbm_f_revi_t_revo_f(self
):
213 self
.tst(wid
=64, msb_mode
=False, reverse_i
=True, reverse_o
=False)
215 def test_64_msbm_f_revi_t_revo_t(self
):
216 self
.tst(wid
=64, msb_mode
=False, reverse_i
=True, reverse_o
=True)
218 def test_64_msbm_t_revi_f_revo_f(self
):
219 self
.tst(wid
=64, msb_mode
=True, reverse_i
=False, reverse_o
=False)
221 def test_64_msbm_t_revi_f_revo_t(self
):
222 self
.tst(wid
=64, msb_mode
=True, reverse_i
=False, reverse_o
=True)
224 def test_64_msbm_t_revi_t_revo_f(self
):
225 self
.tst(wid
=64, msb_mode
=True, reverse_i
=True, reverse_o
=False)
227 def test_64_msbm_t_revi_t_revo_t(self
):
228 self
.tst(wid
=64, msb_mode
=True, reverse_i
=True, reverse_o
=True)
231 class TestMultiPriorityPicker(FHDLTestCase
):
232 def make_dut(self
, width
, levels
, indices
, multi_in
):
233 dut
= MultiPriorityPicker(wid
=width
, levels
=levels
, indices
=indices
,
235 self
.assertEqual(width
, dut
.wid
)
236 self
.assertEqual(levels
, dut
.levels
)
237 self
.assertEqual(indices
, dut
.indices
)
238 self
.assertEqual(multi_in
, dut
.multi_in
)
241 def tst(self
, *, width
, levels
, indices
, multi_in
):
242 assert isinstance(width
, int) and width
>= 1
243 assert isinstance(levels
, int) and 1 <= levels
<= width
244 assert isinstance(indices
, bool)
245 assert isinstance(multi_in
, bool)
246 dut
= self
.make_dut(width
=width
, levels
=levels
, indices
=indices
,
250 self
.assertIsInstance(dut
.i
, (Array
, list))
251 self
.assertEqual(len(dut
.i
), levels
)
253 self
.assertIsInstance(i
, Signal
)
254 self
.assertEqual(len(i
), width
)
255 expected_ports
.append(i
)
257 self
.assertIsInstance(dut
.i
, Signal
)
258 self
.assertEqual(len(dut
.i
), width
)
259 expected_ports
.append(dut
.i
)
261 self
.assertIsInstance(dut
.o
, (Array
, list))
262 self
.assertEqual(len(dut
.o
), levels
)
264 self
.assertIsInstance(o
, Signal
)
265 self
.assertEqual(len(o
), width
)
266 expected_ports
.append(o
)
268 self
.assertEqual(len(dut
.en_o
), levels
)
269 expected_ports
.append(dut
.en_o
)
272 self
.assertIsInstance(dut
.idx_o
, (Array
, list))
273 self
.assertEqual(len(dut
.idx_o
), levels
)
274 for idx_o
in dut
.idx_o
:
275 self
.assertIsInstance(idx_o
, Signal
)
276 expected_ports
.append(idx_o
)
278 self
.assertFalse(hasattr(dut
, "idx_o"))
280 self
.assertListEqual(expected_ports
, dut
.ports())
282 write_il(self
, dut
, ports
=dut
.ports())
285 m
.submodules
.dut
= dut
288 m
.d
.comb
+= i
.eq(AnyConst(width
))
290 m
.d
.comb
+= dut
.i
.eq(AnyConst(width
))
293 for o
, en_o
in zip(dut
.o
, dut
.en_o
):
294 # assert o only has zero or one bit set
295 m
.d
.comb
+= Assert((o
& (o
- 1)) == 0)
296 # assert o doesn't overlap any previous outputs
297 m
.d
.comb
+= Assert((o
& prev_set
) == 0)
300 m
.d
.comb
+= Assert((o
!= 0) == en_o
)
302 prev_set
= Const(0, width
)
303 priority_pickers
= [PriorityPicker(width
) for _
in range(levels
)]
304 for level
in range(levels
):
305 pp
= priority_pickers
[level
]
306 setattr(m
.submodules
, f
"pp_{level}", pp
)
307 inp
= dut
.i
[level
] if multi_in
else dut
.i
308 m
.d
.comb
+= pp
.i
.eq(inp
& ~prev_set
)
309 cur_set
= Signal(width
, name
=f
"cur_set_{level}")
310 m
.d
.comb
+= cur_set
.eq(prev_set | pp
.o
)
312 m
.d
.comb
+= Assert(pp
.o
== dut
.o
[level
])
313 expected_idx
= Signal(32, name
=f
"expected_idx_{level}")
314 number_of_prev_en_o_set
= reduce(
315 operator
.add
, (i
.en_o
for i
in priority_pickers
[:level
]), 0)
316 m
.d
.comb
+= expected_idx
.eq(number_of_prev_en_o_set
)
318 m
.d
.comb
+= Assert(expected_idx
== dut
.idx_o
[level
])
322 def test_4_levels_1_idxs_f_mi_f(self
):
323 self
.tst(width
=4, levels
=1, indices
=False, multi_in
=False)
325 def test_4_levels_1_idxs_f_mi_t(self
):
326 self
.tst(width
=4, levels
=1, indices
=False, multi_in
=True)
328 def test_4_levels_1_idxs_t_mi_f(self
):
329 self
.tst(width
=4, levels
=1, indices
=True, multi_in
=False)
331 def test_4_levels_1_idxs_t_mi_t(self
):
332 self
.tst(width
=4, levels
=1, indices
=True, multi_in
=True)
334 def test_4_levels_2_idxs_f_mi_f(self
):
335 self
.tst(width
=4, levels
=2, indices
=False, multi_in
=False)
337 def test_4_levels_2_idxs_f_mi_t(self
):
338 self
.tst(width
=4, levels
=2, indices
=False, multi_in
=True)
340 def test_4_levels_2_idxs_t_mi_f(self
):
341 self
.tst(width
=4, levels
=2, indices
=True, multi_in
=False)
343 def test_4_levels_2_idxs_t_mi_t(self
):
344 self
.tst(width
=4, levels
=2, indices
=True, multi_in
=True)
346 def test_4_levels_3_idxs_f_mi_f(self
):
347 self
.tst(width
=4, levels
=3, indices
=False, multi_in
=False)
349 def test_4_levels_3_idxs_f_mi_t(self
):
350 self
.tst(width
=4, levels
=3, indices
=False, multi_in
=True)
352 def test_4_levels_3_idxs_t_mi_f(self
):
353 self
.tst(width
=4, levels
=3, indices
=True, multi_in
=False)
355 def test_4_levels_3_idxs_t_mi_t(self
):
356 self
.tst(width
=4, levels
=3, indices
=True, multi_in
=True)
358 def test_4_levels_4_idxs_f_mi_f(self
):
359 self
.tst(width
=4, levels
=4, indices
=False, multi_in
=False)
361 def test_4_levels_4_idxs_f_mi_t(self
):
362 self
.tst(width
=4, levels
=4, indices
=False, multi_in
=True)
364 def test_4_levels_4_idxs_t_mi_f(self
):
365 self
.tst(width
=4, levels
=4, indices
=True, multi_in
=False)
367 def test_4_levels_4_idxs_t_mi_t(self
):
368 self
.tst(width
=4, levels
=4, indices
=True, multi_in
=True)
370 def test_8_levels_1_idxs_f_mi_f(self
):
371 self
.tst(width
=8, levels
=1, indices
=False, multi_in
=False)
373 def test_8_levels_1_idxs_f_mi_t(self
):
374 self
.tst(width
=8, levels
=1, indices
=False, multi_in
=True)
376 def test_8_levels_1_idxs_t_mi_f(self
):
377 self
.tst(width
=8, levels
=1, indices
=True, multi_in
=False)
379 def test_8_levels_1_idxs_t_mi_t(self
):
380 self
.tst(width
=8, levels
=1, indices
=True, multi_in
=True)
382 def test_8_levels_2_idxs_f_mi_f(self
):
383 self
.tst(width
=8, levels
=2, indices
=False, multi_in
=False)
385 def test_8_levels_2_idxs_f_mi_t(self
):
386 self
.tst(width
=8, levels
=2, indices
=False, multi_in
=True)
388 def test_8_levels_2_idxs_t_mi_f(self
):
389 self
.tst(width
=8, levels
=2, indices
=True, multi_in
=False)
391 def test_8_levels_2_idxs_t_mi_t(self
):
392 self
.tst(width
=8, levels
=2, indices
=True, multi_in
=True)
394 def test_8_levels_3_idxs_f_mi_f(self
):
395 self
.tst(width
=8, levels
=3, indices
=False, multi_in
=False)
397 def test_8_levels_3_idxs_f_mi_t(self
):
398 self
.tst(width
=8, levels
=3, indices
=False, multi_in
=True)
400 def test_8_levels_3_idxs_t_mi_f(self
):
401 self
.tst(width
=8, levels
=3, indices
=True, multi_in
=False)
403 def test_8_levels_3_idxs_t_mi_t(self
):
404 self
.tst(width
=8, levels
=3, indices
=True, multi_in
=True)
406 def test_8_levels_4_idxs_f_mi_f(self
):
407 self
.tst(width
=8, levels
=4, indices
=False, multi_in
=False)
409 def test_8_levels_4_idxs_f_mi_t(self
):
410 self
.tst(width
=8, levels
=4, indices
=False, multi_in
=True)
412 def test_8_levels_4_idxs_t_mi_f(self
):
413 self
.tst(width
=8, levels
=4, indices
=True, multi_in
=False)
415 def test_8_levels_4_idxs_t_mi_t(self
):
416 self
.tst(width
=8, levels
=4, indices
=True, multi_in
=True)
418 def test_8_levels_5_idxs_f_mi_f(self
):
419 self
.tst(width
=8, levels
=5, indices
=False, multi_in
=False)
421 def test_8_levels_5_idxs_f_mi_t(self
):
422 self
.tst(width
=8, levels
=5, indices
=False, multi_in
=True)
424 def test_8_levels_5_idxs_t_mi_f(self
):
425 self
.tst(width
=8, levels
=5, indices
=True, multi_in
=False)
427 def test_8_levels_5_idxs_t_mi_t(self
):
428 self
.tst(width
=8, levels
=5, indices
=True, multi_in
=True)
430 def test_8_levels_6_idxs_f_mi_f(self
):
431 self
.tst(width
=8, levels
=6, indices
=False, multi_in
=False)
433 def test_8_levels_6_idxs_f_mi_t(self
):
434 self
.tst(width
=8, levels
=6, indices
=False, multi_in
=True)
436 def test_8_levels_6_idxs_t_mi_f(self
):
437 self
.tst(width
=8, levels
=6, indices
=True, multi_in
=False)
439 def test_8_levels_6_idxs_t_mi_t(self
):
440 self
.tst(width
=8, levels
=6, indices
=True, multi_in
=True)
442 def test_8_levels_7_idxs_f_mi_f(self
):
443 self
.tst(width
=8, levels
=7, indices
=False, multi_in
=False)
445 def test_8_levels_7_idxs_f_mi_t(self
):
446 self
.tst(width
=8, levels
=7, indices
=False, multi_in
=True)
448 def test_8_levels_7_idxs_t_mi_f(self
):
449 self
.tst(width
=8, levels
=7, indices
=True, multi_in
=False)
451 def test_8_levels_7_idxs_t_mi_t(self
):
452 self
.tst(width
=8, levels
=7, indices
=True, multi_in
=True)
454 def test_8_levels_8_idxs_f_mi_f(self
):
455 self
.tst(width
=8, levels
=8, indices
=False, multi_in
=False)
457 def test_8_levels_8_idxs_f_mi_t(self
):
458 self
.tst(width
=8, levels
=8, indices
=False, multi_in
=True)
460 def test_8_levels_8_idxs_t_mi_f(self
):
461 self
.tst(width
=8, levels
=8, indices
=True, multi_in
=False)
463 def test_8_levels_8_idxs_t_mi_t(self
):
464 self
.tst(width
=8, levels
=8, indices
=True, multi_in
=True)
466 def test_16_levels_16_idxs_f_mi_f(self
):
467 self
.tst(width
=16, levels
=16, indices
=False, multi_in
=False)
470 class TestBetterMultiPriorityPicker(TestMultiPriorityPicker
):
471 def make_dut(self
, width
, levels
, indices
, multi_in
):
474 "multi_in are not supported by BetterMultiPriorityPicker")
477 "indices are not supported by BetterMultiPriorityPicker")
478 dut
= BetterMultiPriorityPicker(width
=width
, levels
=levels
)
479 self
.assertEqual(width
, dut
.width
)
480 self
.assertEqual(levels
, dut
.levels
)
484 if __name__
== "__main__":