dff2d6ec847a7b7538db3b61f087b23d80328247
2 # SPDX-License-Identifier: LGPL-2.1-or-later
3 # See Notices.txt for copyright information
5 from nmigen
import Signal
, Module
, Elaboratable
, Mux
, Cat
, Shape
6 from nmigen
.back
.pysim
import Simulator
, Delay
, Settle
7 from nmigen
.cli
import rtlil
9 from ieee754
.part
.partsig
import PartitionedSignal
10 from ieee754
.part_mux
.part_mux
import PMux
12 from random
import randint
34 return map(''.join
, itertools
.product('01', repeat
=k
))
37 def create_ilang(dut
, traces
, test_name
):
38 vl
= rtlil
.convert(dut
, ports
=traces
)
39 with
open("%s.il" % test_name
, "w") as f
:
43 def create_simulator(module
, traces
, test_name
):
44 create_ilang(module
, traces
, test_name
)
45 return Simulator(module
)
48 # XXX this is for coriolis2 experimentation
49 class TestAddMod2(Elaboratable
):
50 def __init__(self
, width
, partpoints
):
51 self
.partpoints
= partpoints
52 self
.a
= PartitionedSignal(partpoints
, width
)
53 self
.b
= PartitionedSignal(partpoints
, width
)
54 self
.bsig
= Signal(width
)
55 self
.add_output
= Signal(width
)
56 self
.ls_output
= Signal(width
) # left shift
57 self
.ls_scal_output
= Signal(width
) # left shift
58 self
.rs_output
= Signal(width
) # right shift
59 self
.rs_scal_output
= Signal(width
) # right shift
60 self
.sub_output
= Signal(width
)
61 self
.eq_output
= Signal(len(partpoints
)+1)
62 self
.gt_output
= Signal(len(partpoints
)+1)
63 self
.ge_output
= Signal(len(partpoints
)+1)
64 self
.ne_output
= Signal(len(partpoints
)+1)
65 self
.lt_output
= Signal(len(partpoints
)+1)
66 self
.le_output
= Signal(len(partpoints
)+1)
67 self
.mux_sel2
= Signal(len(partpoints
)+1)
68 self
.mux_sel2
= PartitionedSignal(partpoints
, len(partpoints
))
69 self
.mux_out
= Signal(width
)
70 self
.mux2_out
= Signal(width
)
71 self
.carry_in
= Signal(len(partpoints
)+1)
72 self
.add_carry_out
= Signal(len(partpoints
)+1)
73 self
.sub_carry_out
= Signal(len(partpoints
)+1)
74 self
.neg_output
= Signal(width
)
76 def elaborate(self
, platform
):
82 self
.mux_sel2
.set_module(m
)
84 sync
+= self
.lt_output
.eq(self
.a
< self
.b
)
85 sync
+= self
.ne_output
.eq(self
.a
!= self
.b
)
86 sync
+= self
.le_output
.eq(self
.a
<= self
.b
)
87 sync
+= self
.gt_output
.eq(self
.a
> self
.b
)
88 sync
+= self
.eq_output
.eq(self
.a
== self
.b
)
89 sync
+= self
.ge_output
.eq(self
.a
>= self
.b
)
91 add_out
, add_carry
= self
.a
.add_op(self
.a
, self
.b
,
93 sync
+= self
.add_output
.eq(add_out
)
94 sync
+= self
.add_carry_out
.eq(add_carry
)
96 sub_out
, sub_carry
= self
.a
.sub_op(self
.a
, self
.b
,
98 sync
+= self
.sub_output
.eq(sub_out
)
99 sync
+= self
.sub_carry_out
.eq(sub_carry
)
101 sync
+= self
.neg_output
.eq(-self
.a
)
103 sync
+= self
.ls_output
.eq(self
.a
<< self
.b
)
104 sync
+= self
.rs_output
.eq(self
.a
>> self
.b
)
105 ppts
= self
.partpoints
106 sync
+= self
.mux_out
.eq(PMux(m
, ppts
, self
.mux_sel
, self
.a
, self
.b
))
107 sync
+= self
.mux_out2
.eq(Mux(self
.mux_sel2
, self
.a
, self
.b
))
109 comb
+= self
.bsig
.eq(self
.b
.lower())
110 sync
+= self
.ls_scal_output
.eq(self
.a
<< self
.bsig
)
111 sync
+= self
.rs_scal_output
.eq(self
.a
>> self
.bsig
)
116 class TestMuxMod(Elaboratable
):
117 def __init__(self
, width
, partpoints
):
118 self
.partpoints
= partpoints
119 self
.a
= PartitionedSignal(partpoints
, width
)
120 self
.b
= PartitionedSignal(partpoints
, width
)
121 self
.mux_sel
= Signal(len(partpoints
)+1)
122 self
.mux_sel2
= PartitionedSignal(partpoints
, len(partpoints
)+1)
123 self
.mux_out
= Signal(width
)
124 self
.mux_out2
= Signal(width
)
126 def elaborate(self
, platform
):
132 self
.mux_sel2
.set_module(m
)
133 ppts
= self
.partpoints
135 comb
+= self
.mux_out
.eq(PMux(m
, ppts
, self
.mux_sel
, self
.a
, self
.b
))
136 comb
+= self
.mux_out2
.eq(Mux(self
.mux_sel2
, self
.a
, self
.b
))
141 class TestCatMod(Elaboratable
):
142 def __init__(self
, width
, partpoints
):
143 self
.partpoints
= partpoints
144 self
.a
= PartitionedSignal(partpoints
, width
)
145 self
.b
= PartitionedSignal(partpoints
, width
*2)
146 self
.cat_sel
= Signal(len(partpoints
)+1)
147 self
.cat_out
= Signal(width
*3)
149 def elaborate(self
, platform
):
154 #self.cat_sel.set_module(m)
156 comb
+= self
.cat_out
.eq(Cat(self
.a
, self
.b
))
161 class TestAssMod(Elaboratable
):
162 def __init__(self
, width
, out_shape
, partpoints
, scalar
):
163 self
.partpoints
= partpoints
166 self
.a
= Signal(width
)
168 self
.a
= PartitionedSignal(partpoints
, width
)
169 self
.ass_out
= PartitionedSignal(partpoints
, out_shape
)
171 def elaborate(self
, platform
):
176 self
.ass_out
.set_module(m
)
178 comb
+= self
.ass_out
.eq(self
.a
)
183 class TestAddMod(Elaboratable
):
184 def __init__(self
, width
, partpoints
):
185 self
.partpoints
= partpoints
186 self
.a
= PartitionedSignal(partpoints
, width
)
187 self
.b
= PartitionedSignal(partpoints
, width
)
188 self
.bsig
= Signal(width
)
189 self
.add_output
= Signal(width
)
190 self
.ls_output
= Signal(width
) # left shift
191 self
.ls_scal_output
= Signal(width
) # left shift
192 self
.rs_output
= Signal(width
) # right shift
193 self
.rs_scal_output
= Signal(width
) # right shift
194 self
.sub_output
= Signal(width
)
195 self
.eq_output
= Signal(len(partpoints
)+1)
196 self
.gt_output
= Signal(len(partpoints
)+1)
197 self
.ge_output
= Signal(len(partpoints
)+1)
198 self
.ne_output
= Signal(len(partpoints
)+1)
199 self
.lt_output
= Signal(len(partpoints
)+1)
200 self
.le_output
= Signal(len(partpoints
)+1)
201 self
.carry_in
= Signal(len(partpoints
)+1)
202 self
.add_carry_out
= Signal(len(partpoints
)+1)
203 self
.sub_carry_out
= Signal(len(partpoints
)+1)
204 self
.neg_output
= Signal(width
)
205 self
.signed_output
= Signal(width
)
206 self
.xor_output
= Signal(len(partpoints
)+1)
207 self
.bool_output
= Signal(len(partpoints
)+1)
208 self
.all_output
= Signal(len(partpoints
)+1)
209 self
.any_output
= Signal(len(partpoints
)+1)
211 def elaborate(self
, platform
):
218 comb
+= self
.lt_output
.eq(self
.a
< self
.b
)
219 comb
+= self
.ne_output
.eq(self
.a
!= self
.b
)
220 comb
+= self
.le_output
.eq(self
.a
<= self
.b
)
221 comb
+= self
.gt_output
.eq(self
.a
> self
.b
)
222 comb
+= self
.eq_output
.eq(self
.a
== self
.b
)
223 comb
+= self
.ge_output
.eq(self
.a
>= self
.b
)
225 add_out
, add_carry
= self
.a
.add_op(self
.a
, self
.b
,
227 comb
+= self
.add_output
.eq(add_out
.sig
)
228 comb
+= self
.add_carry_out
.eq(add_carry
)
230 sub_out
, sub_carry
= self
.a
.sub_op(self
.a
, self
.b
,
232 comb
+= self
.sub_output
.eq(sub_out
.sig
)
233 comb
+= self
.sub_carry_out
.eq(sub_carry
)
234 # neg / signed / unsigned
235 comb
+= self
.neg_output
.eq((-self
.a
).sig
)
236 comb
+= self
.signed_output
.eq(self
.a
.as_signed())
237 # horizontal operators
238 comb
+= self
.xor_output
.eq(self
.a
.xor())
239 comb
+= self
.bool_output
.eq(self
.a
.bool())
240 comb
+= self
.all_output
.eq(self
.a
.all())
241 comb
+= self
.any_output
.eq(self
.a
.any())
243 comb
+= self
.ls_output
.eq(self
.a
<< self
.b
)
245 comb
+= self
.rs_output
.eq(self
.a
>> self
.b
)
246 ppts
= self
.partpoints
248 comb
+= self
.bsig
.eq(self
.b
.lower())
249 comb
+= self
.ls_scal_output
.eq(self
.a
<< self
.bsig
)
251 comb
+= self
.rs_scal_output
.eq(self
.a
>> self
.bsig
)
256 class TestMux(unittest
.TestCase
):
259 part_mask
= Signal(3) # divide into 4-bits
260 module
= TestMuxMod(width
, part_mask
)
262 test_name
= "part_sig_mux"
268 sim
= create_simulator(module
, traces
, test_name
)
272 def test_muxop(msg_prefix
, *maskbit_list
):
273 for a
, b
in [(0x0000, 0x0000),
280 # convert to mask_list
282 for mb
in maskbit_list
:
289 # TODO: sel needs to go through permutations of mask_list
290 for p
in perms(len(mask_list
)):
294 for i
, v
in enumerate(p
):
296 sel |
= maskbit_list
[i
]
297 selmask |
= mask_list
[i
]
299 yield module
.a
.lower().eq(a
)
300 yield module
.b
.lower().eq(b
)
301 yield module
.mux_sel
.eq(sel
)
302 yield module
.mux_sel2
.lower().eq(sel
)
305 # do the partitioned tests
306 for i
, mask
in enumerate(mask_list
):
312 outval
= (yield module
.mux_out
)
313 outval2
= (yield module
.mux_out2
)
314 msg
= f
"{msg_prefix}: mux " + \
315 f
"0x{sel:X} ? 0x{a:X} : 0x{b:X}" + \
316 f
" => 0x{y:X} != 0x{outval:X}, masklist %s"
317 # print ((msg % str(maskbit_list)).format(locals()))
318 self
.assertEqual(y
, outval
, msg
% str(maskbit_list
))
319 self
.assertEqual(y
, outval2
, msg
% str(maskbit_list
))
321 yield part_mask
.eq(0)
322 yield from test_muxop("16-bit", 0b1111)
323 yield part_mask
.eq(0b10)
324 yield from test_muxop("8-bit", 0b1100, 0b0011)
325 yield part_mask
.eq(0b1111)
326 yield from test_muxop("4-bit", 0b1000, 0b0100, 0b0010, 0b0001)
328 sim
.add_process(async_process
)
330 vcd_file
=open(test_name
+ ".vcd", "w"),
331 gtkw_file
=open(test_name
+ ".gtkw", "w"),
336 class TestCat(unittest
.TestCase
):
339 part_mask
= Signal(3) # divide into 4-bits
340 module
= TestCatMod(width
, part_mask
)
342 test_name
= "part_sig_cat"
347 sim
= create_simulator(module
, traces
, test_name
)
349 # annoying recursive import issue
350 from ieee754
.part_cat
.cat
import get_runlengths
354 def test_catop(msg_prefix
):
355 # define lengths of a/b test input
357 # pairs of test values a, b
358 for a
, b
in [(0x0000, 0x00000000),
359 (0xDCBA, 0x12345678),
360 (0xABCD, 0x01234567),
363 (0x1F1F, 0xF1F1F1F1),
364 (0x0000, 0xFFFFFFFF)]:
366 # convert a and b to partitions
367 apart
, bpart
= [], []
368 ajump
, bjump
= alen
// 4, blen
// 4
370 apart
.append((a
>> (ajump
*i
) & ((1<<ajump
)-1)))
371 bpart
.append((b
>> (bjump
*i
) & ((1<<bjump
)-1)))
373 print ("apart bpart", hex(a
), hex(b
),
374 list(map(hex, apart
)), list(map(hex, bpart
)))
376 yield module
.a
.lower().eq(a
)
377 yield module
.b
.lower().eq(b
)
381 # work out the runlengths for this mask.
382 # 0b011 returns [1,1,2] (for a mask of length 3)
383 mval
= yield part_mask
384 runlengths
= get_runlengths(mval
, 3)
391 print ("runlength", i
,
393 "apart", hex(apart
[ai
]),
401 print ("runlength", i
,
403 "bpart", hex(bpart
[bi
]),
411 outval
= (yield module
.cat_out
)
412 msg
= f
"{msg_prefix}: cat " + \
413 f
"0x{mval:X} 0x{a:X} : 0x{b:X}" + \
414 f
" => 0x{y:X} != 0x{outval:X}"
415 self
.assertEqual(y
, outval
, msg
)
417 yield part_mask
.eq(0)
418 yield from test_catop("16-bit")
419 yield part_mask
.eq(0b10)
420 yield from test_catop("8-bit")
421 yield part_mask
.eq(0b1111)
422 yield from test_catop("4-bit")
424 sim
.add_process(async_process
)
426 vcd_file
=open(test_name
+ ".vcd", "w"),
427 gtkw_file
=open(test_name
+ ".gtkw", "w"),
432 class TestAssign(unittest
.TestCase
):
433 def run_tst(self
, in_width
, out_width
, out_signed
, scalar
):
434 part_mask
= Signal(3) # divide into 4-bits
435 module
= TestAssMod(in_width
,
436 Shape(out_width
, out_signed
),
439 test_name
= "part_sig_ass_%d_%d_%s_%s" % (in_width
, out_width
,
440 "signed" if out_signed
else "unsigned",
441 "scalar" if scalar
else "partitioned")
444 module
.ass_out
.lower()]
446 traces
.append(module
.a
)
448 traces
.append(module
.a
.lower())
449 sim
= create_simulator(module
, traces
, test_name
)
451 # annoying recursive import issue
452 from ieee754
.part_cat
.cat
import get_runlengths
456 def test_assop(msg_prefix
):
457 # define lengths of a test input
461 randomvals
.append(randint(0, 65535))
477 # work out the runlengths for this mask.
478 # 0b011 returns [1,1,2] (for a mask of length 3)
479 mval
= yield part_mask
480 runlengths
= get_runlengths(mval
, 3)
482 print ("test a", hex(a
), "mask", bin(mval
), "widths",
484 "signed", out_signed
,
487 # convert a to runlengths sub-sections
492 subpart
= (a
>> (ajump
*ai
) & ((1<<(ajump
*i
))-1))
493 msb
= (subpart
>> ((ajump
*i
)-1)) # will contain the sign
494 apart
.append((subpart
, msb
))
495 print ("apart", ajump
*i
, hex(a
), hex(subpart
), msb
)
502 yield module
.a
.lower().eq(a
)
507 ojump
= out_width
// 4
508 for ai
, i
in enumerate(runlengths
):
509 # get "a" partition value
511 # do sign-extension if needed
513 if out_signed
and ojump
> ajump
:
515 signext
= (-1 << ajump
*i
) & ((1<<(ojump
*i
))-1)
519 av
&= ((1<<(ojump
*i
))-1)
520 print ("runlength", i
,
522 "apart", hex(av
), amsb
,
523 "signext", hex(signext
),
530 y
&= (1<<out_width
)-1
533 outval
= (yield module
.ass_out
.lower())
534 outval
&= (1<<out_width
)-1
535 msg
= f
"{msg_prefix}: assign " + \
536 f
"mask 0x{mval:X} input 0x{a:X}" + \
537 f
" => expected 0x{y:X} != actual 0x{outval:X}"
538 self
.assertEqual(y
, outval
, msg
)
540 # run the actual tests, here - 16/8/4 bit partitions
541 for (mask
, name
) in ((0, "16-bit"),
544 with self
.subTest(name
+ " " + test_name
):
545 yield part_mask
.eq(mask
)
547 yield from test_assop(name
)
549 sim
.add_process(async_process
)
551 vcd_file
=open(test_name
+ ".vcd", "w"),
552 gtkw_file
=open(test_name
+ ".gtkw", "w"),
557 for out_width
in [16, 24, 8]:
558 for sign
in [True, False]:
559 for scalar
in [True, False]:
560 self
.run_tst(16, out_width
, sign
, scalar
)
563 class TestPartitionedSignal(unittest
.TestCase
):
566 part_mask
= Signal(3) # divide into 4-bits
567 module
= TestAddMod(width
, part_mask
)
569 test_name
= "part_sig_add"
575 sim
= create_simulator(module
, traces
, test_name
)
579 def test_xor_fn(a
, mask
):
588 def test_bool_fn(a
, mask
):
592 def test_all_fn(a
, mask
):
593 # slightly different: all bits masked must be 1
597 def test_horizop(msg_prefix
, test_fn
, mod_attr
, *maskbit_list
):
600 randomvals
.append(randint(0, 65535))
619 with self
.subTest("%s %s %s" % (msg_prefix
,
620 test_fn
.__name
__, hex(a
))):
621 yield module
.a
.lower().eq(a
)
623 # convert to mask_list
625 for mb
in maskbit_list
:
632 # do the partitioned tests
633 for i
, mask
in enumerate(mask_list
):
635 # OR y with the lowest set bit in the mask
638 outval
= (yield getattr(module
, "%s_output" % mod_attr
))
639 msg
= f
"{msg_prefix}: {mod_attr} 0x{a:X} " + \
640 f
" => 0x{y:X} != 0x{outval:X}, masklist %s"
641 print((msg
% str(maskbit_list
)).format(locals()))
642 self
.assertEqual(y
, outval
, msg
% str(maskbit_list
))
644 for (test_fn
, mod_attr
) in ((test_xor_fn
, "xor"),
645 (test_all_fn
, "all"),
646 (test_bool_fn
, "any"), # same as bool
647 (test_bool_fn
, "bool"),
650 yield part_mask
.eq(0)
651 yield from test_horizop("16-bit", test_fn
, mod_attr
, 0b1111)
652 yield part_mask
.eq(0b10)
653 yield from test_horizop("8-bit", test_fn
, mod_attr
,
655 yield part_mask
.eq(0b1111)
656 yield from test_horizop("4-bit", test_fn
, mod_attr
,
657 0b1000, 0b0100, 0b0010, 0b0001)
659 def test_ls_scal_fn(carry_in
, a
, b
, mask
):
661 bits
= count_bits(mask
)
662 newb
= b
& ((bits
-1))
663 print ("%x %x %x bits %d trunc %x" % \
664 (a
, b
, mask
, bits
, newb
))
668 lsb
= mask
& ~
(mask
-1) if carry_in
else 0
669 sum = ((a
& mask
) << b
)
671 carry
= (sum & mask
) != sum
673 print("res", hex(a
), hex(b
), hex(sum), hex(mask
), hex(result
))
676 def test_rs_scal_fn(carry_in
, a
, b
, mask
):
678 bits
= count_bits(mask
)
679 newb
= b
& ((bits
-1))
680 print ("%x %x %x bits %d trunc %x" % \
681 (a
, b
, mask
, bits
, newb
))
685 lsb
= mask
& ~
(mask
-1) if carry_in
else 0
686 sum = ((a
& mask
) >> b
)
688 carry
= (sum & mask
) != sum
690 print("res", hex(a
), hex(b
), hex(sum), hex(mask
), hex(result
))
693 def test_ls_fn(carry_in
, a
, b
, mask
):
695 bits
= count_bits(mask
)
696 fz
= first_zero(mask
)
697 newb
= b
& ((bits
-1)<<fz
)
698 print ("%x %x %x bits %d zero %d trunc %x" % \
699 (a
, b
, mask
, bits
, fz
, newb
))
703 lsb
= mask
& ~
(mask
-1) if carry_in
else 0
706 sum = ((a
& mask
) << b
)
708 carry
= (sum & mask
) != sum
710 print("res", hex(a
), hex(b
), hex(sum), hex(mask
), hex(result
))
713 def test_rs_fn(carry_in
, a
, b
, mask
):
715 bits
= count_bits(mask
)
716 fz
= first_zero(mask
)
717 newb
= b
& ((bits
-1)<<fz
)
718 print ("%x %x %x bits %d zero %d trunc %x" % \
719 (a
, b
, mask
, bits
, fz
, newb
))
723 lsb
= mask
& ~
(mask
-1) if carry_in
else 0
726 sum = ((a
& mask
) >> b
)
728 carry
= (sum & mask
) != sum
730 print("res", hex(a
), hex(b
), hex(sum), hex(mask
), hex(result
))
733 def test_add_fn(carry_in
, a
, b
, mask
):
734 lsb
= mask
& ~
(mask
-1) if carry_in
else 0
735 sum = (a
& mask
) + (b
& mask
) + lsb
737 carry
= (sum & mask
) != sum
738 print(a
, b
, sum, mask
)
741 def test_sub_fn(carry_in
, a
, b
, mask
):
742 lsb
= mask
& ~
(mask
-1) if carry_in
else 0
743 sum = (a
& mask
) + (~b
& mask
) + lsb
745 carry
= (sum & mask
) != sum
748 def test_neg_fn(carry_in
, a
, b
, mask
):
749 lsb
= mask
& ~
(mask
- 1) # has only LSB of mask set
750 pos
= lsb
.bit_length() - 1 # find bit position
751 a
= (a
& mask
) >> pos
# shift it to the beginning
752 return ((-a
) << pos
) & mask
, 0 # negate and shift it back
754 def test_signed_fn(carry_in
, a
, b
, mask
):
757 def test_op(msg_prefix
, carry
, test_fn
, mod_attr
, *mask_list
):
760 a
, b
= randint(0, 1 << 16), randint(0, 1 << 16)
761 rand_data
.append((a
, b
))
762 for a
, b
in [(0x0000, 0x0000),
768 (0x0000, 0xFFFF)] + rand_data
:
769 yield module
.a
.lower().eq(a
)
770 yield module
.b
.lower().eq(b
)
771 carry_sig
= 0xf if carry
else 0
772 yield module
.carry_in
.eq(carry_sig
)
776 for i
, mask
in enumerate(mask_list
):
777 print ("i/mask", i
, hex(mask
))
778 res
, c
= test_fn(carry
, a
, b
, mask
)
780 lsb
= mask
& ~
(mask
- 1)
781 bit_set
= int(math
.log2(lsb
))
782 carry_result |
= c
<< int(bit_set
/4)
783 outval
= (yield getattr(module
, "%s_output" % mod_attr
))
784 # TODO: get (and test) carry output as well
785 print(a
, b
, outval
, carry
)
786 msg
= f
"{msg_prefix}: 0x{a:X} {mod_attr} 0x{b:X}" + \
787 f
" => 0x{y:X} != 0x{outval:X}"
788 self
.assertEqual(y
, outval
, msg
)
789 if hasattr(module
, "%s_carry_out" % mod_attr
):
790 c_outval
= (yield getattr(module
,
791 "%s_carry_out" % mod_attr
))
792 msg
= f
"{msg_prefix}: 0x{a:X} {mod_attr} 0x{b:X}" + \
793 f
" => 0x{carry_result:X} != 0x{c_outval:X}"
794 self
.assertEqual(carry_result
, c_outval
, msg
)
796 # run through series of operations with corresponding
797 # "helper" routines to reproduce the result (test_fn). the same
798 # a/b input is passed to *all* outputs, where the name of the
799 # output attribute (mod_attr) will contain the result to be
800 # compared against the expected output from test_fn
801 for (test_fn
, mod_attr
) in (
802 (test_ls_scal_fn
, "ls_scal"),
804 (test_rs_scal_fn
, "rs_scal"),
806 (test_add_fn
, "add"),
807 (test_sub_fn
, "sub"),
808 (test_neg_fn
, "neg"),
809 (test_signed_fn
, "signed"),
811 yield part_mask
.eq(0)
812 yield from test_op("16-bit", 1, test_fn
, mod_attr
, 0xFFFF)
813 yield from test_op("16-bit", 0, test_fn
, mod_attr
, 0xFFFF)
814 yield part_mask
.eq(0b10)
815 yield from test_op("8-bit", 0, test_fn
, mod_attr
,
817 yield from test_op("8-bit", 1, test_fn
, mod_attr
,
819 yield part_mask
.eq(0b1111)
820 yield from test_op("4-bit", 0, test_fn
, mod_attr
,
821 0xF000, 0x0F00, 0x00F0, 0x000F)
822 yield from test_op("4-bit", 1, test_fn
, mod_attr
,
823 0xF000, 0x0F00, 0x00F0, 0x000F)
825 def test_ne_fn(a
, b
, mask
):
826 return (a
& mask
) != (b
& mask
)
828 def test_lt_fn(a
, b
, mask
):
829 return (a
& mask
) < (b
& mask
)
831 def test_le_fn(a
, b
, mask
):
832 return (a
& mask
) <= (b
& mask
)
834 def test_eq_fn(a
, b
, mask
):
835 return (a
& mask
) == (b
& mask
)
837 def test_gt_fn(a
, b
, mask
):
838 return (a
& mask
) > (b
& mask
)
840 def test_ge_fn(a
, b
, mask
):
841 return (a
& mask
) >= (b
& mask
)
843 def test_binop(msg_prefix
, test_fn
, mod_attr
, *maskbit_list
):
844 for a
, b
in [(0x0000, 0x0000),
854 yield module
.a
.lower().eq(a
)
855 yield module
.b
.lower().eq(b
)
857 # convert to mask_list
859 for mb
in maskbit_list
:
866 # do the partitioned tests
867 for i
, mask
in enumerate(mask_list
):
868 if test_fn(a
, b
, mask
):
869 # OR y with the lowest set bit in the mask
872 outval
= (yield getattr(module
, "%s_output" % mod_attr
))
873 msg
= f
"{msg_prefix}: {mod_attr} 0x{a:X} == 0x{b:X}" + \
874 f
" => 0x{y:X} != 0x{outval:X}, masklist %s"
875 print((msg
% str(maskbit_list
)).format(locals()))
876 self
.assertEqual(y
, outval
, msg
% str(maskbit_list
))
878 for (test_fn
, mod_attr
) in ((test_eq_fn
, "eq"),
885 yield part_mask
.eq(0)
886 yield from test_binop("16-bit", test_fn
, mod_attr
, 0b1111)
887 yield part_mask
.eq(0b10)
888 yield from test_binop("8-bit", test_fn
, mod_attr
,
890 yield part_mask
.eq(0b1111)
891 yield from test_binop("4-bit", test_fn
, mod_attr
,
892 0b1000, 0b0100, 0b0010, 0b0001)
894 sim
.add_process(async_process
)
896 vcd_file
=open(test_name
+ ".vcd", "w"),
897 gtkw_file
=open(test_name
+ ".gtkw", "w"),
902 # TODO: adapt to PartitionedSignal. perhaps a different style?
904 from nmigen.tests.test_hdl_ast import SignedEnum
905 def test_matches(self)
907 self.assertRepr(s.matches(), "(const 1'd0)")
908 self.assertRepr(s.matches(1), """
909 (== (sig s) (const 1'd1))
911 self.assertRepr(s.matches(0, 1), """
912 (r| (cat (== (sig s) (const 1'd0)) (== (sig s) (const 1'd1))))
914 self.assertRepr(s.matches("10--"), """
915 (== (& (sig s) (const 4'd12)) (const 4'd8))
917 self.assertRepr(s.matches("1 0--"), """
918 (== (& (sig s) (const 4'd12)) (const 4'd8))
921 def test_matches_enum(self):
922 s = Signal(SignedEnum)
923 self.assertRepr(s.matches(SignedEnum.FOO), """
924 (== (sig s) (const 1'sd-1))
927 def test_matches_width_wrong(self):
929 with self.assertRaisesRegex(SyntaxError,
930 r"^Match pattern '--' must have the same width as "
931 r"match value \(which is 4\)$"):
933 with self.assertWarnsRegex(SyntaxWarning,
934 (r"^Match pattern '10110' is wider than match value "
935 r"\(which has width 4\); "
936 r"comparison will never be true$")):
939 def test_matches_bits_wrong(self):
941 with self.assertRaisesRegex(SyntaxError,
942 (r"^Match pattern 'abc' must consist of 0, 1, "
943 r"and - \(don't care\) bits, "
944 r"and may include whitespace$")):
947 def test_matches_pattern_wrong(self):
949 with self.assertRaisesRegex(SyntaxError,
950 r"^Match pattern must be an integer, a string, "
951 r"or an enumeration, not 1\.0$"):
955 if __name__
== '__main__':