1 # SPDX-License-Identifier: LGPL-3-or-later
2 # Copyright 2022 Jacob Lifshay
4 # Funded by NLnet Assure Programme 2021-02-052, https://nlnet.nl/assure part
5 # of Horizon 2020 EU Programme 957073.
8 from nmigen
.hdl
.ast
import AnyConst
, Assert
, Signal
, Const
, unsigned
9 from nmigen
.hdl
.dsl
import Module
10 from nmutil
.formaltest
import FHDLTestCase
11 from nmigen_gf
.hdl
.cldivrem
import (CLDivRemFSMStage
, CLDivRemInputData
,
12 CLDivRemOutputData
, CLDivRemShape
, CLDivRemState
,
13 equal_leading_zero_count_reference
,
14 EqualLeadingZeroCount
)
15 from nmigen
.sim
import Delay
, Tick
16 from nmutil
.sim_util
import do_sim
, hash_256
17 from nmigen_gf
.reference
.cldivrem
import cldivrem
20 class TestEqualLeadingZeroCount(FHDLTestCase
):
21 def tst(self
, width
, full
):
22 dut
= EqualLeadingZeroCount(width
)
23 self
.assertEqual(dut
.a
.shape(), unsigned(width
))
24 self
.assertEqual(dut
.b
.shape(), unsigned(width
))
25 self
.assertEqual(dut
.out
.shape(), unsigned(1))
28 assert isinstance(a
, int)
29 assert isinstance(b
, int)
30 expected
= a
.bit_length() == b
.bit_length()
31 with self
.subTest(a
=hex(a
), b
=hex(b
),
33 reference
= equal_leading_zero_count_reference(a
, b
, width
)
34 with self
.subTest(reference
=reference
):
35 self
.assertEqual(expected
, reference
)
37 with self
.subTest(a
=hex(a
), b
=hex(b
),
43 with self
.subTest(out
=out
):
44 self
.assertEqual(expected
, out
)
48 for a
in range(1 << width
):
49 for b
in range(1 << width
):
53 a
= hash_256(f
"eqlzc input a {i}")
54 a
= Const
.normalize(a
, dut
.a
.shape())
55 b
= hash_256(f
"eqlzc input b {i}")
56 b
= Const
.normalize(b
, dut
.b
.shape())
59 with
do_sim(self
, dut
, [dut
.a
, dut
.b
, dut
.out
]) as sim
:
60 sim
.add_process(process
)
63 def tst_formal(self
, width
):
64 dut
= EqualLeadingZeroCount(width
)
66 m
.submodules
.dut
= dut
67 m
.d
.comb
+= dut
.a
.eq(AnyConst(width
))
68 m
.d
.comb
+= dut
.b
.eq(AnyConst(width
))
69 # use a bunch of Value.matches() and boolean logic rather than a
70 # giant Switch()/If() to avoid
71 # https://github.com/YosysHQ/yosys/issues/3268
73 for leading_zeros
in range(width
+ 1):
74 pattern
= '0' * leading_zeros
+ '1' + '-' * width
75 pattern
= pattern
[0:width
]
76 a_has_count
= Signal(name
=f
"a_has_{leading_zeros}")
77 b_has_count
= Signal(name
=f
"b_has_{leading_zeros}")
79 a_has_count
.eq(dut
.a
.matches(pattern
)),
80 b_has_count
.eq(dut
.b
.matches(pattern
)),
82 expected_v |
= a_has_count
& b_has_count
84 m
.d
.comb
+= expected
.eq(expected_v
)
85 m
.d
.comb
+= Assert(dut
.out
== expected
)
89 self
.tst(64, full
=False)
92 self
.tst(8, full
=False)
95 self
.tst(3, full
=True)
97 def test_formal_64(self
):
100 def test_formal_8(self
):
103 def test_formal_3(self
):
107 class TestCLDivRemComb(FHDLTestCase
):
108 def tst(self
, shape
, full
):
109 assert isinstance(shape
, CLDivRemShape
)
111 n_in
= Signal(shape
.n_width
)
112 d_in
= Signal(shape
.width
)
113 states
: "list[CLDivRemState]" = []
114 for i
in shape
.step_range
:
115 states
.append(CLDivRemState(shape
, name
=f
"state_{i}"))
117 states
[i
].set_to_initial(m
, n
=n_in
, d
=d_in
)
119 states
[i
].set_to_next(m
, states
[i
- 1])
122 assert isinstance(n
, int)
123 assert isinstance(d
, int)
124 max_width
= max(shape
.width
, shape
.n_width
)
126 expected_q
, expected_r
= cldivrem(n
, d
, width
=max_width
)
128 expected_q
= expected_r
= 0
129 with self
.subTest(n
=hex(n
), d
=hex(d
),
130 expected_q
=hex(expected_q
),
131 expected_r
=hex(expected_r
)):
135 for i
in shape
.step_range
:
136 with self
.subTest(i
=i
):
137 done
= yield states
[i
].done
138 step
= yield states
[i
].step
139 self
.assertEqual(done
, i
>= shape
.done_step
)
140 self
.assertEqual(step
, i
)
141 q
= yield states
[-1].q
142 r
= yield states
[-1].r
143 with self
.subTest(q
=hex(q
), r
=hex(r
)):
144 # only check results when inputs are valid
145 if d
!= 0 and (expected_q
>> shape
.width
) == 0:
146 self
.assertEqual(q
, expected_q
)
147 self
.assertEqual(r
, expected_r
)
151 for n
in range(1 << shape
.n_width
):
152 for d
in range(1 << shape
.width
):
153 yield from case(n
, d
)
156 n
= hash_256(f
"cldivrem comb n {i}")
157 n
= Const
.normalize(n
, unsigned(shape
.n_width
))
158 d
= hash_256(f
"cldivrem comb d {i}")
159 d
= Const
.normalize(d
, unsigned(shape
.width
))
160 yield from case(n
, d
)
161 with
do_sim(self
, m
, [n_in
, d_in
, states
[-1].q
, states
[-1].r
]) as sim
:
162 sim
.add_process(process
)
166 self
.tst(CLDivRemShape(width
=4, n_width
=4), full
=True)
168 def test_8_by_4(self
):
169 self
.tst(CLDivRemShape(width
=4, n_width
=8), full
=True)
172 class TestCLDivRemFSM(FHDLTestCase
):
173 def tst(self
, shape
, full
, steps_per_clock
):
174 assert isinstance(shape
, CLDivRemShape
)
175 assert isinstance(steps_per_clock
, int) and steps_per_clock
>= 1
177 dut
= CLDivRemFSMStage(pspec
, shape
, steps_per_clock
=steps_per_clock
)
178 i_data
: CLDivRemInputData
= dut
.p
.i_data
179 o_data
: CLDivRemOutputData
= dut
.n
.o_data
180 self
.assertEqual(i_data
.n
.shape(), unsigned(shape
.n_width
))
181 self
.assertEqual(i_data
.d
.shape(), unsigned(shape
.width
))
182 self
.assertEqual(o_data
.q
.shape(), unsigned(shape
.width
))
183 self
.assertEqual(o_data
.r
.shape(), unsigned(shape
.width
))
186 assert isinstance(n
, int)
187 assert isinstance(d
, int)
188 max_width
= max(shape
.width
, shape
.n_width
)
190 expected_q
, expected_r
= cldivrem(n
, d
, width
=max_width
)
192 expected_q
= expected_r
= 0
193 with self
.subTest(n
=hex(n
), d
=hex(d
),
194 expected_q
=hex(expected_q
),
195 expected_r
=hex(expected_r
)):
196 yield dut
.p
.i_valid
.eq(0)
200 yield dut
.p
.i_valid
.eq(1)
202 valid
= yield dut
.n
.o_valid
203 ready
= yield dut
.p
.o_ready
205 self
.assertFalse(valid
)
206 self
.assertTrue(ready
)
208 yield i_data
.n
.eq(-1)
209 yield i_data
.d
.eq(-1)
210 yield dut
.p
.i_valid
.eq(0)
211 for i
in range(steps_per_clock
* 2, shape
.done_step
,
214 valid
= yield dut
.n
.o_valid
215 ready
= yield dut
.p
.o_ready
217 self
.assertFalse(valid
)
218 self
.assertFalse(ready
)
221 valid
= yield dut
.n
.o_valid
222 ready
= yield dut
.p
.o_ready
224 self
.assertTrue(valid
)
225 self
.assertFalse(ready
)
228 with self
.subTest(q
=hex(q
), r
=hex(r
)):
229 # only check results when inputs are valid
230 if d
!= 0 and (expected_q
>> shape
.width
) == 0:
231 self
.assertEqual(q
, expected_q
)
232 self
.assertEqual(r
, expected_r
)
233 yield dut
.n
.i_ready
.eq(1)
236 valid
= yield dut
.n
.o_valid
237 ready
= yield dut
.p
.o_ready
239 self
.assertFalse(valid
)
240 self
.assertTrue(ready
)
241 yield dut
.n
.i_ready
.eq(0)
245 for n
in range(1 << shape
.n_width
):
246 for d
in range(1 << shape
.width
):
247 yield from case(n
, d
)
250 n
= hash_256(f
"cldivrem fsm n {i}")
251 n
= Const
.normalize(n
, unsigned(shape
.n_width
))
252 d
= hash_256(f
"cldivrem fsm d {i}")
253 d
= Const
.normalize(d
, unsigned(shape
.width
))
254 yield from case(n
, d
)
256 with
do_sim(self
, dut
, list(dut
.ports())) as sim
:
257 sim
.add_process(process
)
261 def test_4_step_1(self
):
262 self
.tst(CLDivRemShape(width
=4, n_width
=4),
266 def test_4_step_2(self
):
267 self
.tst(CLDivRemShape(width
=4, n_width
=4),
271 def test_4_step_3(self
):
272 self
.tst(CLDivRemShape(width
=4, n_width
=4),
277 if __name__
== "__main__":