2 # SPDX-License-Identifier: LGPL-2.1-or-later
3 # See Notices.txt for copyright information
5 from ieee754
.div_rem_sqrt_rsqrt
.core
import (DivPipeCoreConfig
,
7 DivPipeCoreCalculateStage
, DivPipeCoreFinalStage
,
8 DivPipeCoreOperation
, DivPipeCoreInputData
,
9 DivPipeCoreInterstageData
, DivPipeCoreOutputData
)
10 from ieee754
.div_rem_sqrt_rsqrt
.algorithm
import (FixedUDivRemSqrtRSqrt
,
11 Fixed
, Operation
, div_rem
,
12 fixed_sqrt
, fixed_rsqrt
)
14 from nmigen
import Module
, Elaboratable
, Signal
15 from nmigen
.hdl
.ir
import Fragment
16 from nmigen
.back
import rtlil
17 from nmigen
.back
.pysim
import Simulator
, Delay
, Tick
18 from itertools
import chain
21 def show_fixed(bits
, fract_width
, bit_width
):
22 fixed
= Fixed
.from_bits(bits
, fract_width
, bit_width
, False)
23 return f
"{str(fixed)}:{repr(fixed)}"
26 def get_core_op(alg_op
):
27 if alg_op
is Operation
.UDivRem
:
28 return DivPipeCoreOperation
.UDivRem
29 if alg_op
is Operation
.SqrtRem
:
30 return DivPipeCoreOperation
.SqrtRem
31 assert alg_op
is Operation
.RSqrtRem
32 return DivPipeCoreOperation
.RSqrtRem
36 __test__
= False # make pytest ignore class
45 self
.dividend
= dividend
46 self
.divisor_radicand
= divisor_radicand
48 self
.quotient_root
= quotient_root
49 self
.remainder
= remainder
50 self
.core_config
= core_config
54 return get_core_op(self
.alg_op
)
57 bit_width
= self
.core_config
.bit_width
58 fract_width
= self
.core_config
.fract_width
59 dividend_str
= show_fixed(self
.dividend
,
61 bit_width
+ fract_width
)
62 divisor_radicand_str
= show_fixed(self
.divisor_radicand
,
65 quotient_root_str
= show_fixed(self
.quotient_root
,
68 remainder_str
= show_fixed(self
.remainder
,
71 return f
"{{dividend={dividend_str}, " \
72 + f
"divisor_radicand={divisor_radicand_str}, " \
73 + f
"op={self.alg_op.name}, " \
74 + f
"quotient_root={quotient_root_str}, " \
75 + f
"remainder={remainder_str}, " \
76 + f
"config={self.core_config}}}"
79 def generate_test_case(core_config
, dividend
, divisor_radicand
, alg_op
):
80 bit_width
= core_config
.bit_width
81 fract_width
= core_config
.fract_width
82 obj
= FixedUDivRemSqrtRSqrt(dividend
,
87 core_config
.log2_radix
)
89 yield TestCaseData(dividend
,
97 def shifted_ints(total_bits
, int_bits
):
98 """ Generate a sequence like a generalized binary version of A037124.
100 See https://oeis.org/A037124
102 Generates the sequence of all non-negative integers ``n`` in ascending
103 order with no repeats where ``n < (1 << total_bits) and n == (v << i)``
104 where ``i`` is a non-negative integer and ``v`` is a non-negative
105 integer less than ``1 << int_bits``.
108 while n
< (1 << total_bits
):
110 if n
< (1 << int_bits
):
113 n
+= 1 << (n
.bit_length() - int_bits
)
116 def partitioned_ints(bit_width
):
117 """ Get ints with all 1s on one side and 0s on the other. """
118 for i
in range(bit_width
):
119 yield (-1 << i
) & ((1 << bit_width
) - 1)
120 yield (1 << (i
+ 1)) - 1
123 class TestShiftedInts(unittest
.TestCase
):
128 0x004, 0x005, 0x006, 0x007,
129 0x008, 0x009, 0x00A, 0x00B, 0x00C, 0x00D, 0x00E, 0x00F,
130 0x010, 0x012, 0x014, 0x016, 0x018, 0x01A, 0x01C, 0x01E,
131 0x020, 0x024, 0x028, 0x02C, 0x030, 0x034, 0x038, 0x03C,
132 0x040, 0x048, 0x050, 0x058, 0x060, 0x068, 0x070, 0x078,
133 0x080, 0x090, 0x0A0, 0x0B0, 0x0C0, 0x0D0, 0x0E0, 0x0F0,
134 0x100, 0x120, 0x140, 0x160, 0x180, 0x1A0, 0x1C0, 0x1E0,
135 0x200, 0x240, 0x280, 0x2C0, 0x300, 0x340, 0x380, 0x3C0,
136 0x400, 0x480, 0x500, 0x580, 0x600, 0x680, 0x700, 0x780,
137 0x800, 0x900, 0xA00, 0xB00, 0xC00, 0xD00, 0xE00, 0xF00]
138 self
.assertEqual(list(shifted_ints(12, 4)), expected
)
141 def get_test_cases(core_config
,
145 if dividends
is None:
146 dividend_width
= core_config
.bit_width
+ core_config
.fract_width
147 dividends
= [*shifted_ints(dividend_width
,
148 max(3, core_config
.log2_radix
)),
149 *partitioned_ints(dividend_width
)]
151 assert isinstance(dividends
, list)
153 divisors
= [*shifted_ints(core_config
.bit_width
,
154 max(3, core_config
.log2_radix
)),
155 *partitioned_ints(core_config
.bit_width
)]
157 assert isinstance(divisors
, list)
158 if radicands
is None:
159 radicands
= [*shifted_ints(core_config
.bit_width
, 5),
160 *partitioned_ints(core_config
.bit_width
)]
162 assert isinstance(radicands
, list)
164 for alg_op
in reversed(Operation
): # put UDivRem at end
165 if get_core_op(alg_op
) not in core_config
.supported
:
167 if alg_op
is Operation
.UDivRem
:
168 for dividend
in dividends
:
169 for divisor
in divisors
:
170 yield from generate_test_case(core_config
,
175 for radicand
in radicands
:
176 yield from generate_test_case(core_config
,
182 class DivPipeCoreTestPipeline(Elaboratable
):
183 def __init__(self
, core_config
, sync
):
184 self
.setup_stage
= DivPipeCoreSetupStage(core_config
)
185 self
.calculate_stages
= [
186 DivPipeCoreCalculateStage(core_config
, stage_index
)
187 for stage_index
in range(core_config
.n_stages
)]
188 self
.final_stage
= DivPipeCoreFinalStage(core_config
)
189 self
.interstage_signals
= [
190 DivPipeCoreInterstageData(core_config
, reset_less
=True)
191 for i
in range(core_config
.n_stages
+ 1)]
192 self
.i
= DivPipeCoreInputData(core_config
, reset_less
=True)
193 self
.o
= DivPipeCoreOutputData(core_config
, reset_less
=True)
196 def elaborate(self
, platform
):
198 stages
= [self
.setup_stage
, *self
.calculate_stages
, self
.final_stage
]
199 stage_inputs
= [self
.i
, *self
.interstage_signals
]
200 stage_outputs
= [*self
.interstage_signals
, self
.o
]
201 for stage
, input, output
in zip(stages
, stage_inputs
, stage_outputs
):
202 stage
.setup(m
, input)
203 assignments
= output
.eq(stage
.process(input))
205 m
.d
.sync
+= assignments
207 m
.d
.comb
+= assignments
212 # for interstage_signal in self.interstage_signals:
213 # yield from interstage_signal
217 class TestDivPipeCore(unittest
.TestCase
):
218 def handle_config(self
,
222 if test_cases
is None:
223 test_cases
= get_test_cases(core_config
)
224 test_cases
= list(test_cases
)
225 base_name
= f
"test_div_pipe_core_bit_width_{core_config.bit_width}"
226 base_name
+= f
"_fract_width_{core_config.fract_width}"
227 base_name
+= f
"_radix_{1 << core_config.log2_radix}"
230 if core_config
.supported
!= frozenset(DivPipeCoreOperation
):
232 DivPipeCoreOperation
.UDivRem
: "div",
233 DivPipeCoreOperation
.SqrtRem
: "sqrt",
234 DivPipeCoreOperation
.RSqrtRem
: "rsqrt",
236 # loop using iter(DivPipeCoreOperation) to maintain order
237 for op
in DivPipeCoreOperation
:
238 if op
in core_config
.supported
:
239 base_name
+= f
"_{name_map[op]}"
242 with self
.subTest(part
="synthesize"):
243 dut
= DivPipeCoreTestPipeline(core_config
, sync
)
244 vl
= rtlil
.convert(dut
, ports
=[*dut
.i
, *dut
.o
])
245 with
open(f
"{base_name}.il", "w") as f
:
247 dut
= DivPipeCoreTestPipeline(core_config
, sync
)
249 with sim
.write_vcd(vcd_file
=open(f
"{base_name}.vcd", "w"),
250 gtkw_file
=open(f
"{base_name}.gtkw", "w"),
251 traces
=[*dut
.traces()]):
252 def generate_process():
253 for test_case
in test_cases
:
256 yield dut
.i
.dividend
.eq(test_case
.dividend
)
257 yield dut
.i
.divisor_radicand
.eq(test_case
.divisor_radicand
)
258 yield dut
.i
.operation
.eq(int(test_case
.core_op
))
265 # sync with generator
268 for _
in range(core_config
.n_stages
):
274 # now synched with generator
275 for test_case
in test_cases
:
281 quotient_root
= (yield dut
.o
.quotient_root
)
282 remainder
= (yield dut
.o
.remainder
)
283 with self
.subTest(test_case
=str(test_case
)):
284 self
.assertEqual(quotient_root
,
285 test_case
.quotient_root
,
287 self
.assertEqual(remainder
, test_case
.remainder
,
291 sim
.add_process(generate_process
)
292 sim
.add_process(check_process
)
295 def test_bit_width_2_fract_width_1_radix_2_comb(self
):
296 self
.handle_config(DivPipeCoreConfig(bit_width
=2,
301 def test_bit_width_2_fract_width_1_radix_2(self
):
302 self
.handle_config(DivPipeCoreConfig(bit_width
=2,
306 def test_bit_width_8_fract_width_4_radix_2_comb(self
):
307 self
.handle_config(DivPipeCoreConfig(bit_width
=8,
312 def test_bit_width_8_fract_width_4_radix_2(self
):
313 self
.handle_config(DivPipeCoreConfig(bit_width
=8,
317 def test_bit_width_8_fract_width_4_radix_4_comb(self
):
318 self
.handle_config(DivPipeCoreConfig(bit_width
=8,
323 def test_bit_width_8_fract_width_4_radix_4(self
):
324 self
.handle_config(DivPipeCoreConfig(bit_width
=8,
328 def test_bit_width_8_fract_width_4_radix_4_div_only(self
):
329 supported
= (DivPipeCoreOperation
.UDivRem
,)
330 self
.handle_config(DivPipeCoreConfig(bit_width
=8,
333 supported
=supported
))
335 def test_bit_width_8_fract_width_4_radix_4_comb_div_only(self
):
336 supported
= (DivPipeCoreOperation
.UDivRem
,)
337 self
.handle_config(DivPipeCoreConfig(bit_width
=8,
340 supported
=supported
),
343 @unittest.skip("really slow")
344 def test_bit_width_32_fract_width_24_radix_8_comb(self
):
345 self
.handle_config(DivPipeCoreConfig(bit_width
=32,
350 @unittest.skip("really slow")
351 def test_bit_width_32_fract_width_24_radix_8(self
):
352 self
.handle_config(DivPipeCoreConfig(bit_width
=32,
356 @unittest.skip("really slow")
357 def test_bit_width_32_fract_width_28_radix_8_comb(self
):
358 self
.handle_config(DivPipeCoreConfig(bit_width
=32,
363 @unittest.skip("really slow")
364 def test_bit_width_32_fract_width_28_radix_8(self
):
365 self
.handle_config(DivPipeCoreConfig(bit_width
=32,
369 # FIXME: add more test_* functions
372 if __name__
== '__main__':