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 alg_op
is Operation
.UDivRem
:
166 for dividend
in dividends
:
167 for divisor
in divisors
:
168 yield from generate_test_case(core_config
,
173 for radicand
in radicands
:
174 yield from generate_test_case(core_config
,
180 class DivPipeCoreTestPipeline(Elaboratable
):
181 def __init__(self
, core_config
, sync
):
182 self
.setup_stage
= DivPipeCoreSetupStage(core_config
)
183 self
.calculate_stages
= [
184 DivPipeCoreCalculateStage(core_config
, stage_index
)
185 for stage_index
in range(core_config
.n_stages
)]
186 self
.final_stage
= DivPipeCoreFinalStage(core_config
)
187 self
.interstage_signals
= [
188 DivPipeCoreInterstageData(core_config
, reset_less
=True)
189 for i
in range(core_config
.n_stages
+ 1)]
190 self
.i
= DivPipeCoreInputData(core_config
, reset_less
=True)
191 self
.o
= DivPipeCoreOutputData(core_config
, reset_less
=True)
194 def elaborate(self
, platform
):
196 stages
= [self
.setup_stage
, *self
.calculate_stages
, self
.final_stage
]
197 stage_inputs
= [self
.i
, *self
.interstage_signals
]
198 stage_outputs
= [*self
.interstage_signals
, self
.o
]
199 for stage
, input, output
in zip(stages
, stage_inputs
, stage_outputs
):
200 stage
.setup(m
, input)
201 assignments
= output
.eq(stage
.process(input))
203 m
.d
.sync
+= assignments
205 m
.d
.comb
+= assignments
210 # for interstage_signal in self.interstage_signals:
211 # yield from interstage_signal
215 class TestDivPipeCore(unittest
.TestCase
):
216 def handle_config(self
,
220 if test_cases
is None:
221 test_cases
= get_test_cases(core_config
)
222 test_cases
= list(test_cases
)
223 base_name
= f
"test_div_pipe_core_bit_width_{core_config.bit_width}"
224 base_name
+= f
"_fract_width_{core_config.fract_width}"
225 base_name
+= f
"_radix_{1 << core_config.log2_radix}"
228 with self
.subTest(part
="synthesize"):
229 dut
= DivPipeCoreTestPipeline(core_config
, sync
)
230 vl
= rtlil
.convert(dut
, ports
=[*dut
.i
, *dut
.o
])
231 with
open(f
"{base_name}.il", "w") as f
:
233 dut
= DivPipeCoreTestPipeline(core_config
, sync
)
235 vcd_file
=open(f
"{base_name}.vcd", "w"),
236 gtkw_file
=open(f
"{base_name}.gtkw", "w"),
237 traces
=[*dut
.traces()]) as sim
:
238 def generate_process():
239 for test_case
in test_cases
:
241 yield dut
.i
.dividend
.eq(test_case
.dividend
)
242 yield dut
.i
.divisor_radicand
.eq(test_case
.divisor_radicand
)
243 yield dut
.i
.operation
.eq(int(test_case
.core_op
))
247 # sync with generator
250 for _
in range(core_config
.n_stages
):
254 # now synched with generator
255 for test_case
in test_cases
:
258 quotient_root
= (yield dut
.o
.quotient_root
)
259 remainder
= (yield dut
.o
.remainder
)
260 with self
.subTest(test_case
=str(test_case
)):
261 self
.assertEqual(quotient_root
,
262 test_case
.quotient_root
,
264 self
.assertEqual(remainder
, test_case
.remainder
,
267 sim
.add_sync_process(generate_process
)
268 sim
.add_sync_process(check_process
)
271 def test_bit_width_2_fract_width_1_radix_2_comb(self
):
272 self
.handle_config(DivPipeCoreConfig(bit_width
=2,
277 def test_bit_width_2_fract_width_1_radix_2(self
):
278 self
.handle_config(DivPipeCoreConfig(bit_width
=2,
282 def test_bit_width_8_fract_width_4_radix_2_comb(self
):
283 self
.handle_config(DivPipeCoreConfig(bit_width
=8,
288 def test_bit_width_8_fract_width_4_radix_2(self
):
289 self
.handle_config(DivPipeCoreConfig(bit_width
=8,
293 @unittest.skip("really slow")
294 def test_bit_width_32_fract_width_24_radix_8_comb(self
):
295 self
.handle_config(DivPipeCoreConfig(bit_width
=32,
300 @unittest.skip("really slow")
301 def test_bit_width_32_fract_width_24_radix_8(self
):
302 self
.handle_config(DivPipeCoreConfig(bit_width
=32,
306 @unittest.skip("really slow")
307 def test_bit_width_32_fract_width_28_radix_8_comb(self
):
308 self
.handle_config(DivPipeCoreConfig(bit_width
=32,
313 @unittest.skip("really slow")
314 def test_bit_width_32_fract_width_28_radix_8(self
):
315 self
.handle_config(DivPipeCoreConfig(bit_width
=32,
319 # FIXME: add more test_* functions
322 if __name__
== '__main__':