2 # SPDX-License-Identifier: LGPL-2.1-or-later
3 # See Notices.txt for copyright information
5 from nmigen
import Signal
, Module
, Elaboratable
6 from nmigen
.back
.pysim
import Simulator
, Delay
, Tick
, Passive
7 from nmigen
.cli
import verilog
, rtlil
9 from ieee754
.part
.partsig
import PartitionedSignal
10 from ieee754
.part_mux
.part_mux
import PMux
12 from random
import randint
18 return map(''.join
, itertools
.product('01', repeat
=k
))
21 def create_ilang(dut
, traces
, test_name
):
22 vl
= rtlil
.convert(dut
, ports
=traces
)
23 with
open("%s.il" % test_name
, "w") as f
:
27 def create_simulator(module
, traces
, test_name
):
28 create_ilang(module
, traces
, test_name
)
29 return Simulator(module
,
30 vcd_file
=open(test_name
+ ".vcd", "w"),
31 gtkw_file
=open(test_name
+ ".gtkw", "w"),
34 class TestAddMod(Elaboratable
):
35 def __init__(self
, width
, partpoints
):
36 self
.partpoints
= partpoints
37 self
.a
= PartitionedSignal(partpoints
, width
)
38 self
.b
= PartitionedSignal(partpoints
, width
)
39 self
.add_output
= Signal(width
)
40 self
.sub_output
= Signal(width
)
41 self
.eq_output
= Signal(len(partpoints
)+1)
42 self
.gt_output
= Signal(len(partpoints
)+1)
43 self
.ge_output
= Signal(len(partpoints
)+1)
44 self
.ne_output
= Signal(len(partpoints
)+1)
45 self
.lt_output
= Signal(len(partpoints
)+1)
46 self
.le_output
= Signal(len(partpoints
)+1)
47 self
.mux_sel
= Signal(len(partpoints
)+1)
48 self
.mux_out
= Signal(width
)
49 self
.carry_in
= Signal(len(partpoints
)+1)
50 self
.add_carry_out
= Signal(len(partpoints
)+1)
51 self
.sub_carry_out
= Signal(len(partpoints
)+1)
52 self
.neg_output
= Signal(width
)
54 def elaborate(self
, platform
):
58 m
.d
.comb
+= self
.lt_output
.eq(self
.a
< self
.b
)
59 m
.d
.comb
+= self
.ne_output
.eq(self
.a
!= self
.b
)
60 m
.d
.comb
+= self
.le_output
.eq(self
.a
<= self
.b
)
61 m
.d
.comb
+= self
.gt_output
.eq(self
.a
> self
.b
)
62 m
.d
.comb
+= self
.eq_output
.eq(self
.a
== self
.b
)
63 m
.d
.comb
+= self
.ge_output
.eq(self
.a
>= self
.b
)
65 add_out
, add_carry
= self
.a
.add_op(self
.a
, self
.b
,
67 m
.d
.comb
+= self
.add_output
.eq(add_out
)
68 m
.d
.comb
+= self
.add_carry_out
.eq(add_carry
)
69 sub_out
, sub_carry
= self
.a
.sub_op(self
.a
, self
.b
,
71 m
.d
.comb
+= self
.sub_output
.eq(sub_out
)
72 m
.d
.comb
+= self
.sub_carry_out
.eq(add_carry
)
73 m
.d
.comb
+= self
.neg_output
.eq(-self
.a
)
74 ppts
= self
.partpoints
75 m
.d
.comb
+= self
.mux_out
.eq(PMux(m
, ppts
, self
.mux_sel
, self
.a
, self
.b
))
80 class TestPartitionPoints(unittest
.TestCase
):
83 part_mask
= Signal(4) # divide into 4-bits
84 module
= TestAddMod(width
, part_mask
)
86 sim
= create_simulator(module
,
95 def test_add_fn(carry_in
, a
, b
, mask
):
96 lsb
= mask
& ~
(mask
-1) if carry_in
else 0
97 return mask
& ((a
& mask
) + (b
& mask
) + lsb
)
99 def test_sub_fn(carry_in
, a
, b
, mask
):
100 lsb
= mask
& ~
(mask
-1) if carry_in
else 0
101 return mask
& ((a
& mask
) + (~b
& mask
) + lsb
)
103 def test_neg_fn(carry_in
, a
, b
, mask
):
104 lsb
= mask
& ~
(mask
-1) if carry_in
else 0
105 return mask
& ((a
& mask
) + (~
0 & mask
))
107 def test_op(msg_prefix
, carry
, test_fn
, mod_attr
, *mask_list
):
110 a
, b
= randint(0, 1<<16), randint(0, 1<<16)
111 rand_data
.append((a
, b
))
112 for a
, b
in [(0x0000, 0x0000),
118 (0x0000, 0xFFFF)] + rand_data
:
121 carry_sig
= 0xf if carry
else 0
122 yield module
.carry_in
.eq(carry_sig
)
125 for i
, mask
in enumerate(mask_list
):
126 y |
= test_fn(carry
, a
, b
, mask
)
127 outval
= (yield getattr(module
, "%s_output" % mod_attr
))
128 # TODO: get (and test) carry output as well
129 print(a
, b
, outval
, carry
)
130 msg
= f
"{msg_prefix}: 0x{a:X} + 0x{b:X}" + \
131 f
" => 0x{y:X} != 0x{outval:X}"
132 self
.assertEqual(y
, outval
, msg
)
134 for (test_fn
, mod_attr
) in ((test_add_fn
, "add"),
135 (test_sub_fn
, "sub"),
136 (test_neg_fn
, "neg"),
138 yield part_mask
.eq(0)
139 yield from test_op("16-bit", 1, test_fn
, mod_attr
, 0xFFFF)
140 yield from test_op("16-bit", 0, test_fn
, mod_attr
, 0xFFFF)
141 yield part_mask
.eq(0b10)
142 yield from test_op("8-bit", 0, test_fn
, mod_attr
,
144 yield from test_op("8-bit", 1, test_fn
, mod_attr
,
146 yield part_mask
.eq(0b1111)
147 yield from test_op("4-bit", 0, test_fn
, mod_attr
,
148 0xF000, 0x0F00, 0x00F0, 0x000F)
149 yield from test_op("4-bit", 1, test_fn
, mod_attr
,
150 0xF000, 0x0F00, 0x00F0, 0x000F)
152 def test_ne_fn(a
, b
, mask
):
153 return (a
& mask
) != (b
& mask
)
155 def test_lt_fn(a
, b
, mask
):
156 return (a
& mask
) < (b
& mask
)
158 def test_le_fn(a
, b
, mask
):
159 return (a
& mask
) <= (b
& mask
)
161 def test_eq_fn(a
, b
, mask
):
162 return (a
& mask
) == (b
& mask
)
164 def test_gt_fn(a
, b
, mask
):
165 return (a
& mask
) > (b
& mask
)
167 def test_ge_fn(a
, b
, mask
):
168 return (a
& mask
) >= (b
& mask
)
170 def test_binop(msg_prefix
, test_fn
, mod_attr
, *maskbit_list
):
171 for a
, b
in [(0x0000, 0x0000),
184 # convert to mask_list
186 for mb
in maskbit_list
:
193 # do the partitioned tests
194 for i
, mask
in enumerate(mask_list
):
195 if test_fn(a
, b
, mask
):
196 # OR y with the lowest set bit in the mask
199 outval
= (yield getattr(module
, "%s_output" % mod_attr
))
200 msg
= f
"{msg_prefix}: {mod_attr} 0x{a:X} == 0x{b:X}" + \
201 f
" => 0x{y:X} != 0x{outval:X}, masklist %s"
202 print ((msg
% str(maskbit_list
)).format(locals()))
203 self
.assertEqual(y
, outval
, msg
% str(maskbit_list
))
205 for (test_fn
, mod_attr
) in ((test_eq_fn
, "eq"),
212 yield part_mask
.eq(0)
213 yield from test_binop("16-bit", test_fn
, mod_attr
, 0b1111)
214 yield part_mask
.eq(0b10)
215 yield from test_binop("8-bit", test_fn
, mod_attr
,
217 yield part_mask
.eq(0b1111)
218 yield from test_binop("4-bit", test_fn
, mod_attr
,
219 0b1000, 0b0100, 0b0010, 0b0001)
221 def test_muxop(msg_prefix
, *maskbit_list
):
222 for a
, b
in [(0x0000, 0x0000),
229 # convert to mask_list
231 for mb
in maskbit_list
:
238 # TODO: sel needs to go through permutations of mask_list
239 for p
in perms(len(mask_list
)):
243 for i
, v
in enumerate(p
):
245 sel |
= maskbit_list
[i
]
246 selmask |
= mask_list
[i
]
250 yield module
.mux_sel
.eq(sel
)
253 # do the partitioned tests
254 for i
, mask
in enumerate(mask_list
):
260 outval
= (yield module
.mux_out
)
261 msg
= f
"{msg_prefix}: mux " + \
262 f
"0x{sel:X} ? 0x{a:X} : 0x{b:X}" + \
263 f
" => 0x{y:X} != 0x{outval:X}, masklist %s"
264 #print ((msg % str(maskbit_list)).format(locals()))
265 self
.assertEqual(y
, outval
, msg
% str(maskbit_list
))
267 yield part_mask
.eq(0)
268 yield from test_muxop("16-bit", 0b1111)
269 yield part_mask
.eq(0b10)
270 yield from test_muxop("8-bit", 0b1100, 0b0011)
271 yield part_mask
.eq(0b1111)
272 yield from test_muxop("4-bit", 0b1000, 0b0100, 0b0010, 0b0001)
274 sim
.add_process(async_process
)
277 if __name__
== '__main__':