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
, Repl
6 from nmigen
.back
.pysim
import Simulator
, Delay
, Settle
7 from nmigen
.cli
import rtlil
9 from ieee754
.part
.partsig
import SimdSignal
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
= SimdSignal(partpoints
, width
)
53 self
.b
= SimdSignal(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
= SimdSignal(partpoints
, len(partpoints
))
69 self
.mux2_out
= Signal(width
)
70 self
.carry_in
= Signal(len(partpoints
)+1)
71 self
.add_carry_out
= Signal(len(partpoints
)+1)
72 self
.sub_carry_out
= Signal(len(partpoints
)+1)
73 self
.neg_output
= Signal(width
)
75 def elaborate(self
, platform
):
81 self
.mux_sel2
.set_module(m
)
83 sync
+= self
.lt_output
.eq(self
.a
< self
.b
)
84 sync
+= self
.ne_output
.eq(self
.a
!= self
.b
)
85 sync
+= self
.le_output
.eq(self
.a
<= self
.b
)
86 sync
+= self
.gt_output
.eq(self
.a
> self
.b
)
87 sync
+= self
.eq_output
.eq(self
.a
== self
.b
)
88 sync
+= self
.ge_output
.eq(self
.a
>= self
.b
)
90 add_out
, add_carry
= self
.a
.add_op(self
.a
, self
.b
,
92 sync
+= self
.add_output
.eq(add_out
)
93 sync
+= self
.add_carry_out
.eq(add_carry
)
95 sub_out
, sub_carry
= self
.a
.sub_op(self
.a
, self
.b
,
97 sync
+= self
.sub_output
.eq(sub_out
)
98 sync
+= self
.sub_carry_out
.eq(sub_carry
)
100 sync
+= self
.neg_output
.eq(-self
.a
)
102 sync
+= self
.ls_output
.eq(self
.a
<< self
.b
)
103 sync
+= self
.rs_output
.eq(self
.a
>> self
.b
)
104 ppts
= self
.partpoints
105 sync
+= self
.mux_out2
.eq(Mux(self
.mux_sel2
, self
.a
, self
.b
))
107 comb
+= self
.bsig
.eq(self
.b
.lower())
108 sync
+= self
.ls_scal_output
.eq(self
.a
<< self
.bsig
)
109 sync
+= self
.rs_scal_output
.eq(self
.a
>> self
.bsig
)
114 class TestMuxMod(Elaboratable
):
115 def __init__(self
, width
, partpoints
):
116 self
.partpoints
= partpoints
117 self
.a
= SimdSignal(partpoints
, width
)
118 self
.b
= SimdSignal(partpoints
, width
)
119 self
.mux_sel
= Signal(len(partpoints
)+1)
120 self
.mux_sel2
= SimdSignal(partpoints
, len(partpoints
)+1)
121 self
.mux_out2
= Signal(width
)
123 def elaborate(self
, platform
):
129 self
.mux_sel2
.set_module(m
)
130 ppts
= self
.partpoints
132 comb
+= self
.mux_out2
.eq(Mux(self
.mux_sel2
, self
.a
, self
.b
))
137 class TestCatMod(Elaboratable
):
138 def __init__(self
, width
, partpoints
):
139 self
.partpoints
= partpoints
140 self
.a
= SimdSignal(partpoints
, width
)
141 self
.b
= SimdSignal(partpoints
, width
*2)
142 self
.cat_out
= Signal(width
*3)
144 def elaborate(self
, platform
):
150 comb
+= self
.cat_out
.eq(Cat(self
.a
, self
.b
))
155 class TestReplMod(Elaboratable
):
156 def __init__(self
, width
, partpoints
):
157 self
.partpoints
= partpoints
158 self
.a
= SimdSignal(partpoints
, width
)
159 self
.repl_sel
= Signal(len(partpoints
)+1)
160 self
.repl_out
= Signal(width
*2)
162 def elaborate(self
, platform
):
167 comb
+= self
.repl_out
.eq(Repl(self
.a
, 2))
172 class TestAssMod(Elaboratable
):
173 def __init__(self
, width
, out_shape
, partpoints
, scalar
):
174 self
.partpoints
= partpoints
177 self
.a
= Signal(width
)
179 self
.a
= SimdSignal(partpoints
, width
)
180 self
.ass_out
= SimdSignal(partpoints
, out_shape
)
182 def elaborate(self
, platform
):
187 self
.ass_out
.set_module(m
)
189 comb
+= self
.ass_out
.eq(self
.a
)
194 class TestAddMod(Elaboratable
):
195 def __init__(self
, width
, partpoints
):
196 self
.partpoints
= partpoints
197 self
.a
= SimdSignal(partpoints
, width
)
198 self
.b
= SimdSignal(partpoints
, width
)
199 self
.bsig
= Signal(width
)
200 self
.add_output
= Signal(width
)
201 self
.ls_output
= Signal(width
) # left shift
202 self
.ls_scal_output
= Signal(width
) # left shift
203 self
.rs_output
= Signal(width
) # right shift
204 self
.rs_scal_output
= Signal(width
) # right shift
205 self
.sub_output
= Signal(width
)
206 self
.eq_output
= Signal(len(partpoints
)+1)
207 self
.gt_output
= Signal(len(partpoints
)+1)
208 self
.ge_output
= Signal(len(partpoints
)+1)
209 self
.ne_output
= Signal(len(partpoints
)+1)
210 self
.lt_output
= Signal(len(partpoints
)+1)
211 self
.le_output
= Signal(len(partpoints
)+1)
212 self
.carry_in
= Signal(len(partpoints
)+1)
213 self
.add_carry_out
= Signal(len(partpoints
)+1)
214 self
.sub_carry_out
= Signal(len(partpoints
)+1)
215 self
.neg_output
= Signal(width
)
216 self
.signed_output
= Signal(width
)
217 self
.xor_output
= Signal(len(partpoints
)+1)
218 self
.bool_output
= Signal(len(partpoints
)+1)
219 self
.all_output
= Signal(len(partpoints
)+1)
220 self
.any_output
= Signal(len(partpoints
)+1)
222 def elaborate(self
, platform
):
229 comb
+= self
.lt_output
.eq(self
.a
< self
.b
)
230 comb
+= self
.ne_output
.eq(self
.a
!= self
.b
)
231 comb
+= self
.le_output
.eq(self
.a
<= self
.b
)
232 comb
+= self
.gt_output
.eq(self
.a
> self
.b
)
233 comb
+= self
.eq_output
.eq(self
.a
== self
.b
)
234 comb
+= self
.ge_output
.eq(self
.a
>= self
.b
)
236 add_out
, add_carry
= self
.a
.add_op(self
.a
, self
.b
,
238 comb
+= self
.add_output
.eq(add_out
.sig
)
239 comb
+= self
.add_carry_out
.eq(add_carry
)
241 sub_out
, sub_carry
= self
.a
.sub_op(self
.a
, self
.b
,
243 comb
+= self
.sub_output
.eq(sub_out
.sig
)
244 comb
+= self
.sub_carry_out
.eq(sub_carry
)
245 # neg / signed / unsigned
246 comb
+= self
.neg_output
.eq((-self
.a
).sig
)
247 comb
+= self
.signed_output
.eq(self
.a
.as_signed())
248 # horizontal operators
249 comb
+= self
.xor_output
.eq(self
.a
.xor())
250 comb
+= self
.bool_output
.eq(self
.a
.bool())
251 comb
+= self
.all_output
.eq(self
.a
.all())
252 comb
+= self
.any_output
.eq(self
.a
.any())
254 comb
+= self
.ls_output
.eq(self
.a
<< self
.b
)
256 comb
+= self
.rs_output
.eq(self
.a
>> self
.b
)
257 ppts
= self
.partpoints
259 comb
+= self
.bsig
.eq(self
.b
.lower())
260 comb
+= self
.ls_scal_output
.eq(self
.a
<< self
.bsig
)
262 comb
+= self
.rs_scal_output
.eq(self
.a
>> self
.bsig
)
267 class TestMux(unittest
.TestCase
):
270 part_mask
= Signal(3) # divide into 4-bits
271 module
= TestMuxMod(width
, part_mask
)
273 test_name
= "part_sig_mux"
278 sim
= create_simulator(module
, traces
, test_name
)
282 def test_muxop(msg_prefix
, *maskbit_list
):
283 for a
, b
in [(0x0000, 0x0000),
290 # convert to mask_list
292 for mb
in maskbit_list
:
299 # TODO: sel needs to go through permutations of mask_list
300 for p
in perms(len(mask_list
)):
304 for i
, v
in enumerate(p
):
306 sel |
= maskbit_list
[i
]
307 selmask |
= mask_list
[i
]
309 yield module
.a
.lower().eq(a
)
310 yield module
.b
.lower().eq(b
)
311 yield module
.mux_sel
.eq(sel
)
312 yield module
.mux_sel2
.lower().eq(sel
)
315 # do the partitioned tests
316 for i
, mask
in enumerate(mask_list
):
322 outval2
= (yield module
.mux_out2
)
323 msg
= f
"{msg_prefix}: mux " + \
324 f
"0x{sel:X} ? 0x{a:X} : 0x{b:X}" + \
325 f
" => 0x{y:X} != 0x{outval2:X}, masklist %s"
326 # print ((msg % str(maskbit_list)).format(locals()))
327 self
.assertEqual(y
, outval2
, msg
% str(maskbit_list
))
329 yield part_mask
.eq(0)
330 yield from test_muxop("16-bit", 0b1111)
331 yield part_mask
.eq(0b10)
332 yield from test_muxop("8-bit", 0b1100, 0b0011)
333 yield part_mask
.eq(0b1111)
334 yield from test_muxop("4-bit", 0b1000, 0b0100, 0b0010, 0b0001)
336 sim
.add_process(async_process
)
338 vcd_file
=open(test_name
+ ".vcd", "w"),
339 gtkw_file
=open(test_name
+ ".gtkw", "w"),
344 class TestCat(unittest
.TestCase
):
347 part_mask
= Signal(3) # divide into 4-bits
348 module
= TestCatMod(width
, part_mask
)
350 test_name
= "part_sig_cat"
355 sim
= create_simulator(module
, traces
, test_name
)
357 # annoying recursive import issue
358 from ieee754
.part_cat
.cat
import get_runlengths
362 def test_catop(msg_prefix
):
363 # define lengths of a/b test input
365 # pairs of test values a, b
366 for a
, b
in [(0x0000, 0x00000000),
367 (0xDCBA, 0x12345678),
368 (0xABCD, 0x01234567),
371 (0x1F1F, 0xF1F1F1F1),
372 (0x0000, 0xFFFFFFFF)]:
374 # convert a and b to partitions
375 apart
, bpart
= [], []
376 ajump
, bjump
= alen
// 4, blen
// 4
378 apart
.append((a
>> (ajump
*i
) & ((1<<ajump
)-1)))
379 bpart
.append((b
>> (bjump
*i
) & ((1<<bjump
)-1)))
381 print ("apart bpart", hex(a
), hex(b
),
382 list(map(hex, apart
)), list(map(hex, bpart
)))
384 yield module
.a
.lower().eq(a
)
385 yield module
.b
.lower().eq(b
)
389 # work out the runlengths for this mask.
390 # 0b011 returns [1,1,2] (for a mask of length 3)
391 mval
= yield part_mask
392 runlengths
= get_runlengths(mval
, 3)
399 print ("runlength", i
,
401 "apart", hex(apart
[ai
]),
409 print ("runlength", i
,
411 "bpart", hex(bpart
[bi
]),
419 outval
= (yield module
.cat_out
)
420 msg
= f
"{msg_prefix}: cat " + \
421 f
"0x{mval:X} 0x{a:X} : 0x{b:X}" + \
422 f
" => 0x{y:X} != 0x{outval:X}"
423 self
.assertEqual(y
, outval
, msg
)
425 yield part_mask
.eq(0)
426 yield from test_catop("16-bit")
427 yield part_mask
.eq(0b10)
428 yield from test_catop("8-bit")
429 yield part_mask
.eq(0b1111)
430 yield from test_catop("4-bit")
432 sim
.add_process(async_process
)
434 vcd_file
=open(test_name
+ ".vcd", "w"),
435 gtkw_file
=open(test_name
+ ".gtkw", "w"),
440 class TestRepl(unittest
.TestCase
):
443 part_mask
= Signal(3) # divide into 4-bits
444 module
= TestReplMod(width
, part_mask
)
446 test_name
= "part_sig_repl"
450 sim
= create_simulator(module
, traces
, test_name
)
452 # annoying recursive import issue
453 from ieee754
.part_repl
.repl
import get_runlengths
457 def test_replop(msg_prefix
):
458 # define lengths of a/b test input
460 # pairs of test values a, b
461 for a
, b
in [(0x0000, 0x00000000),
462 (0xDCBA, 0x12345678),
463 (0xABCD, 0x01234567),
466 (0x1F1F, 0xF1F1F1F1),
467 (0x0000, 0xFFFFFFFF)]:
469 # convert a and b to partitions
470 apart
, bpart
= [], []
471 ajump
, bjump
= alen
// 4, blen
// 4
473 apart
.append((a
>> (ajump
*i
) & ((1<<ajump
)-1)))
474 bpart
.append((b
>> (bjump
*i
) & ((1<<bjump
)-1)))
476 print ("apart bpart", hex(a
), hex(b
),
477 list(map(hex, apart
)), list(map(hex, bpart
)))
479 yield module
.a
.lower().eq(a
)
483 # work out the runlengths for this mask.
484 # 0b011 returns [1,1,2] (for a mask of length 3)
485 mval
= yield part_mask
486 runlengths
= get_runlengths(mval
, 3)
493 print ("runlength", i
,
495 "apart", hex(apart
[ai
]),
503 print ("runlength", i
,
505 "bpart", hex(bpart
[bi
]),
513 outval
= (yield module
.repl_out
)
514 msg
= f
"{msg_prefix}: repl " + \
515 f
"0x{mval:X} 0x{a:X} : 0x{b:X}" + \
516 f
" => 0x{y:X} != 0x{outval:X}"
517 self
.assertEqual(y
, outval
, msg
)
519 yield part_mask
.eq(0)
520 yield from test_replop("16-bit")
521 yield part_mask
.eq(0b10)
522 yield from test_replop("8-bit")
523 yield part_mask
.eq(0b1111)
524 yield from test_replop("4-bit")
526 sim
.add_process(async_process
)
528 vcd_file
=open(test_name
+ ".vcd", "w"),
529 gtkw_file
=open(test_name
+ ".gtkw", "w"),
534 class TestAssign(unittest
.TestCase
):
535 def run_tst(self
, in_width
, out_width
, out_signed
, scalar
):
536 part_mask
= Signal(3) # divide into 4-bits
537 module
= TestAssMod(in_width
,
538 Shape(out_width
, out_signed
),
541 test_name
= "part_sig_ass_%d_%d_%s_%s" % (in_width
, out_width
,
542 "signed" if out_signed
else "unsigned",
543 "scalar" if scalar
else "partitioned")
546 module
.ass_out
.lower()]
548 traces
.append(module
.a
)
550 traces
.append(module
.a
.lower())
551 sim
= create_simulator(module
, traces
, test_name
)
553 # annoying recursive import issue
554 from ieee754
.part_cat
.cat
import get_runlengths
558 def test_assop(msg_prefix
):
559 # define lengths of a test input
563 randomvals
.append(randint(0, 65535))
579 # work out the runlengths for this mask.
580 # 0b011 returns [1,1,2] (for a mask of length 3)
581 mval
= yield part_mask
582 runlengths
= get_runlengths(mval
, 3)
584 print ("test a", hex(a
), "mask", bin(mval
), "widths",
586 "signed", out_signed
,
589 # convert a to runlengths sub-sections
594 subpart
= (a
>> (ajump
*ai
) & ((1<<(ajump
*i
))-1))
595 msb
= (subpart
>> ((ajump
*i
)-1)) # will contain the sign
596 apart
.append((subpart
, msb
))
597 print ("apart", ajump
*i
, hex(a
), hex(subpart
), msb
)
604 yield module
.a
.lower().eq(a
)
609 ojump
= out_width
// 4
610 for ai
, i
in enumerate(runlengths
):
611 # get "a" partition value
613 # do sign-extension if needed
615 if out_signed
and ojump
> ajump
:
617 signext
= (-1 << ajump
*i
) & ((1<<(ojump
*i
))-1)
621 av
&= ((1<<(ojump
*i
))-1)
622 print ("runlength", i
,
624 "apart", hex(av
), amsb
,
625 "signext", hex(signext
),
632 y
&= (1<<out_width
)-1
635 outval
= (yield module
.ass_out
.lower())
636 outval
&= (1<<out_width
)-1
637 msg
= f
"{msg_prefix}: assign " + \
638 f
"mask 0x{mval:X} input 0x{a:X}" + \
639 f
" => expected 0x{y:X} != actual 0x{outval:X}"
640 self
.assertEqual(y
, outval
, msg
)
642 # run the actual tests, here - 16/8/4 bit partitions
643 for (mask
, name
) in ((0, "16-bit"),
646 with self
.subTest(name
+ " " + test_name
):
647 yield part_mask
.eq(mask
)
649 yield from test_assop(name
)
651 sim
.add_process(async_process
)
653 vcd_file
=open(test_name
+ ".vcd", "w"),
654 gtkw_file
=open(test_name
+ ".gtkw", "w"),
659 for out_width
in [16, 24, 8]:
660 for sign
in [True, False]:
661 for scalar
in [True, False]:
662 self
.run_tst(16, out_width
, sign
, scalar
)
665 class TestSimdSignal(unittest
.TestCase
):
668 part_mask
= Signal(3) # divide into 4-bits
669 module
= TestAddMod(width
, part_mask
)
671 test_name
= "part_sig_add"
677 sim
= create_simulator(module
, traces
, test_name
)
681 def test_xor_fn(a
, mask
):
690 def test_bool_fn(a
, mask
):
694 def test_all_fn(a
, mask
):
695 # slightly different: all bits masked must be 1
699 def test_horizop(msg_prefix
, test_fn
, mod_attr
, *maskbit_list
):
702 randomvals
.append(randint(0, 65535))
721 with self
.subTest("%s %s %s" % (msg_prefix
,
722 test_fn
.__name
__, hex(a
))):
723 yield module
.a
.lower().eq(a
)
725 # convert to mask_list
727 for mb
in maskbit_list
:
734 # do the partitioned tests
735 for i
, mask
in enumerate(mask_list
):
737 # OR y with the lowest set bit in the mask
740 outval
= (yield getattr(module
, "%s_output" % mod_attr
))
741 msg
= f
"{msg_prefix}: {mod_attr} 0x{a:X} " + \
742 f
" => 0x{y:X} != 0x{outval:X}, masklist %s"
743 print((msg
% str(maskbit_list
)).format(locals()))
744 self
.assertEqual(y
, outval
, msg
% str(maskbit_list
))
746 for (test_fn
, mod_attr
) in ((test_xor_fn
, "xor"),
747 (test_all_fn
, "all"),
748 (test_bool_fn
, "any"), # same as bool
749 (test_bool_fn
, "bool"),
752 yield part_mask
.eq(0)
753 yield from test_horizop("16-bit", test_fn
, mod_attr
, 0b1111)
754 yield part_mask
.eq(0b10)
755 yield from test_horizop("8-bit", test_fn
, mod_attr
,
757 yield part_mask
.eq(0b1111)
758 yield from test_horizop("4-bit", test_fn
, mod_attr
,
759 0b1000, 0b0100, 0b0010, 0b0001)
761 def test_ls_scal_fn(carry_in
, a
, b
, mask
):
763 bits
= count_bits(mask
)
764 newb
= b
& ((bits
-1))
765 print ("%x %x %x bits %d trunc %x" % \
766 (a
, b
, mask
, bits
, newb
))
770 lsb
= mask
& ~
(mask
-1) if carry_in
else 0
771 sum = ((a
& mask
) << b
)
773 carry
= (sum & mask
) != sum
775 print("res", hex(a
), hex(b
), hex(sum), hex(mask
), hex(result
))
778 def test_rs_scal_fn(carry_in
, a
, b
, mask
):
780 bits
= count_bits(mask
)
781 newb
= b
& ((bits
-1))
782 print ("%x %x %x bits %d trunc %x" % \
783 (a
, b
, mask
, bits
, newb
))
787 lsb
= mask
& ~
(mask
-1) if carry_in
else 0
788 sum = ((a
& mask
) >> b
)
790 carry
= (sum & mask
) != sum
792 print("res", hex(a
), hex(b
), hex(sum), hex(mask
), hex(result
))
795 def test_ls_fn(carry_in
, a
, b
, mask
):
797 bits
= count_bits(mask
)
798 fz
= first_zero(mask
)
799 newb
= b
& ((bits
-1)<<fz
)
800 print ("%x %x %x bits %d zero %d trunc %x" % \
801 (a
, b
, mask
, bits
, fz
, newb
))
805 lsb
= mask
& ~
(mask
-1) if carry_in
else 0
808 sum = ((a
& mask
) << b
)
810 carry
= (sum & mask
) != sum
812 print("res", hex(a
), hex(b
), hex(sum), hex(mask
), hex(result
))
815 def test_rs_fn(carry_in
, a
, b
, mask
):
817 bits
= count_bits(mask
)
818 fz
= first_zero(mask
)
819 newb
= b
& ((bits
-1)<<fz
)
820 print ("%x %x %x bits %d zero %d trunc %x" % \
821 (a
, b
, mask
, bits
, fz
, newb
))
825 lsb
= mask
& ~
(mask
-1) if carry_in
else 0
828 sum = ((a
& mask
) >> b
)
830 carry
= (sum & mask
) != sum
832 print("res", hex(a
), hex(b
), hex(sum), hex(mask
), hex(result
))
835 def test_add_fn(carry_in
, a
, b
, mask
):
836 lsb
= mask
& ~
(mask
-1) if carry_in
else 0
837 sum = (a
& mask
) + (b
& mask
) + lsb
839 carry
= (sum & mask
) != sum
840 print(a
, b
, sum, mask
)
843 def test_sub_fn(carry_in
, a
, b
, mask
):
844 lsb
= mask
& ~
(mask
-1) if carry_in
else 0
845 sum = (a
& mask
) + (~b
& mask
) + lsb
847 carry
= (sum & mask
) != sum
850 def test_neg_fn(carry_in
, a
, b
, mask
):
851 lsb
= mask
& ~
(mask
- 1) # has only LSB of mask set
852 pos
= lsb
.bit_length() - 1 # find bit position
853 a
= (a
& mask
) >> pos
# shift it to the beginning
854 return ((-a
) << pos
) & mask
, 0 # negate and shift it back
856 def test_signed_fn(carry_in
, a
, b
, mask
):
859 def test_op(msg_prefix
, carry
, test_fn
, mod_attr
, *mask_list
):
862 a
, b
= randint(0, 1 << 16), randint(0, 1 << 16)
863 rand_data
.append((a
, b
))
864 for a
, b
in [(0x0000, 0x0000),
870 (0x0000, 0xFFFF)] + rand_data
:
871 yield module
.a
.lower().eq(a
)
872 yield module
.b
.lower().eq(b
)
873 carry_sig
= 0xf if carry
else 0
874 yield module
.carry_in
.eq(carry_sig
)
878 for i
, mask
in enumerate(mask_list
):
879 print ("i/mask", i
, hex(mask
))
880 res
, c
= test_fn(carry
, a
, b
, mask
)
882 lsb
= mask
& ~
(mask
- 1)
883 bit_set
= int(math
.log2(lsb
))
884 carry_result |
= c
<< int(bit_set
/4)
885 outval
= (yield getattr(module
, "%s_output" % mod_attr
))
886 # TODO: get (and test) carry output as well
887 print(a
, b
, outval
, carry
)
888 msg
= f
"{msg_prefix}: 0x{a:X} {mod_attr} 0x{b:X}" + \
889 f
" => 0x{y:X} != 0x{outval:X}"
890 self
.assertEqual(y
, outval
, msg
)
891 if hasattr(module
, "%s_carry_out" % mod_attr
):
892 c_outval
= (yield getattr(module
,
893 "%s_carry_out" % mod_attr
))
894 msg
= f
"{msg_prefix}: 0x{a:X} {mod_attr} 0x{b:X}" + \
895 f
" => 0x{carry_result:X} != 0x{c_outval:X}"
896 self
.assertEqual(carry_result
, c_outval
, msg
)
898 # run through series of operations with corresponding
899 # "helper" routines to reproduce the result (test_fn). the same
900 # a/b input is passed to *all* outputs, where the name of the
901 # output attribute (mod_attr) will contain the result to be
902 # compared against the expected output from test_fn
903 for (test_fn
, mod_attr
) in (
904 (test_ls_scal_fn
, "ls_scal"),
906 (test_rs_scal_fn
, "rs_scal"),
908 (test_add_fn
, "add"),
909 (test_sub_fn
, "sub"),
910 (test_neg_fn
, "neg"),
911 (test_signed_fn
, "signed"),
913 yield part_mask
.eq(0)
914 yield from test_op("16-bit", 1, test_fn
, mod_attr
, 0xFFFF)
915 yield from test_op("16-bit", 0, test_fn
, mod_attr
, 0xFFFF)
916 yield part_mask
.eq(0b10)
917 yield from test_op("8-bit", 0, test_fn
, mod_attr
,
919 yield from test_op("8-bit", 1, test_fn
, mod_attr
,
921 yield part_mask
.eq(0b1111)
922 yield from test_op("4-bit", 0, test_fn
, mod_attr
,
923 0xF000, 0x0F00, 0x00F0, 0x000F)
924 yield from test_op("4-bit", 1, test_fn
, mod_attr
,
925 0xF000, 0x0F00, 0x00F0, 0x000F)
927 def test_ne_fn(a
, b
, mask
):
928 return (a
& mask
) != (b
& mask
)
930 def test_lt_fn(a
, b
, mask
):
931 return (a
& mask
) < (b
& mask
)
933 def test_le_fn(a
, b
, mask
):
934 return (a
& mask
) <= (b
& mask
)
936 def test_eq_fn(a
, b
, mask
):
937 return (a
& mask
) == (b
& mask
)
939 def test_gt_fn(a
, b
, mask
):
940 return (a
& mask
) > (b
& mask
)
942 def test_ge_fn(a
, b
, mask
):
943 return (a
& mask
) >= (b
& mask
)
945 def test_binop(msg_prefix
, test_fn
, mod_attr
, *maskbit_list
):
946 for a
, b
in [(0x0000, 0x0000),
956 yield module
.a
.lower().eq(a
)
957 yield module
.b
.lower().eq(b
)
959 # convert to mask_list
961 for mb
in maskbit_list
:
968 # do the partitioned tests
969 for i
, mask
in enumerate(mask_list
):
970 if test_fn(a
, b
, mask
):
971 # OR y with the lowest set bit in the mask
974 outval
= (yield getattr(module
, "%s_output" % mod_attr
))
975 msg
= f
"{msg_prefix}: {mod_attr} 0x{a:X} == 0x{b:X}" + \
976 f
" => 0x{y:X} != 0x{outval:X}, masklist %s"
977 print((msg
% str(maskbit_list
)).format(locals()))
978 self
.assertEqual(y
, outval
, msg
% str(maskbit_list
))
980 for (test_fn
, mod_attr
) in ((test_eq_fn
, "eq"),
987 yield part_mask
.eq(0)
988 yield from test_binop("16-bit", test_fn
, mod_attr
, 0b1111)
989 yield part_mask
.eq(0b10)
990 yield from test_binop("8-bit", test_fn
, mod_attr
,
992 yield part_mask
.eq(0b1111)
993 yield from test_binop("4-bit", test_fn
, mod_attr
,
994 0b1000, 0b0100, 0b0010, 0b0001)
996 sim
.add_process(async_process
)
998 vcd_file
=open(test_name
+ ".vcd", "w"),
999 gtkw_file
=open(test_name
+ ".gtkw", "w"),
1004 # TODO: adapt to SimdSignal. perhaps a different style?
1006 from nmigen.tests.test_hdl_ast import SignedEnum
1007 def test_matches(self)
1009 self.assertRepr(s.matches(), "(const 1'd0)")
1010 self.assertRepr(s.matches(1), """
1011 (== (sig s) (const 1'd1))
1013 self.assertRepr(s.matches(0, 1), """
1014 (r| (cat (== (sig s) (const 1'd0)) (== (sig s) (const 1'd1))))
1016 self.assertRepr(s.matches("10--"), """
1017 (== (& (sig s) (const 4'd12)) (const 4'd8))
1019 self.assertRepr(s.matches("1 0--"), """
1020 (== (& (sig s) (const 4'd12)) (const 4'd8))
1023 def test_matches_enum(self):
1024 s = Signal(SignedEnum)
1025 self.assertRepr(s.matches(SignedEnum.FOO), """
1026 (== (sig s) (const 1'sd-1))
1029 def test_matches_width_wrong(self):
1031 with self.assertRaisesRegex(SyntaxError,
1032 r"^Match pattern '--' must have the same width as "
1033 r"match value \(which is 4\)$"):
1035 with self.assertWarnsRegex(SyntaxWarning,
1036 (r"^Match pattern '10110' is wider than match value "
1037 r"\(which has width 4\); "
1038 r"comparison will never be true$")):
1041 def test_matches_bits_wrong(self):
1043 with self.assertRaisesRegex(SyntaxError,
1044 (r"^Match pattern 'abc' must consist of 0, 1, "
1045 r"and - \(don't care\) bits, "
1046 r"and may include whitespace$")):
1049 def test_matches_pattern_wrong(self):
1051 with self.assertRaisesRegex(SyntaxError,
1052 r"^Match pattern must be an integer, a string, "
1053 r"or an enumeration, not 1\.0$"):
1057 if __name__
== '__main__':