2 # SPDX-License-Identifier: LGPL-2.1-or-later
3 # See Notices.txt for copyright information
5 from .core
import (DivPipeCoreConfig
, DivPipeCoreSetupStage
,
6 DivPipeCoreCalculateStage
, DivPipeCoreFinalStage
,
7 DivPipeCoreOperation
, DivPipeCoreInputData
,
8 DivPipeCoreInterstageData
, DivPipeCoreOutputData
)
9 from .algorithm
import (FixedUDivRemSqrtRSqrt
, Fixed
, Operation
, div_rem
,
10 fixed_sqrt
, fixed_rsqrt
)
12 from nmigen
import Module
, Elaboratable
, Signal
13 from nmigen
.hdl
.ir
import Fragment
14 from nmigen
.back
import rtlil
15 from nmigen
.back
.pysim
import Simulator
, Delay
, Tick
16 from itertools
import chain
19 def show_fixed(bits
, fract_width
, bit_width
):
20 fixed
= Fixed
.from_bits(bits
, fract_width
, bit_width
, False)
21 return f
"{str(fixed)}:{repr(fixed)}"
24 def get_core_op(alg_op
):
25 if alg_op
is Operation
.UDivRem
:
26 return DivPipeCoreOperation
.UDivRem
27 if alg_op
is Operation
.SqrtRem
:
28 return DivPipeCoreOperation
.SqrtRem
29 assert alg_op
is Operation
.RSqrtRem
30 return DivPipeCoreOperation
.RSqrtRem
34 __test__
= False # make pytest ignore class
43 self
.dividend
= dividend
44 self
.divisor_radicand
= divisor_radicand
46 self
.quotient_root
= quotient_root
47 self
.remainder
= remainder
48 self
.core_config
= core_config
52 return get_core_op(self
.alg_op
)
55 bit_width
= self
.core_config
.bit_width
56 fract_width
= self
.core_config
.fract_width
57 dividend_str
= show_fixed(self
.dividend
,
59 bit_width
+ fract_width
)
60 divisor_radicand_str
= show_fixed(self
.divisor_radicand
,
63 quotient_root_str
= show_fixed(self
.quotient_root
,
66 remainder_str
= show_fixed(self
.remainder
,
69 return f
"{{dividend={dividend_str}, " \
70 + f
"divisor_radicand={divisor_radicand_str}, " \
71 + f
"op={self.alg_op.name}, " \
72 + f
"quotient_root={quotient_root_str}, " \
73 + f
"remainder={remainder_str}, " \
74 + f
"config={self.core_config}}}"
77 def generate_test_case(core_config
, dividend
, divisor_radicand
, alg_op
):
78 bit_width
= core_config
.bit_width
79 fract_width
= core_config
.fract_width
80 obj
= FixedUDivRemSqrtRSqrt(dividend
,
85 core_config
.log2_radix
)
87 yield TestCaseData(dividend
,
95 def shifted_ints(total_bits
, int_bits
):
96 """ Generate a sequence like a generalized binary version of A037124.
98 See https://oeis.org/A037124
100 Generates the sequence of all non-negative integers ``n`` in ascending
101 order with no repeats where ``n < (1 << total_bits) and n == (v << i)``
102 where ``i`` is a non-negative integer and ``v`` is a non-negative
103 integer less than ``1 << int_bits``.
106 while n
< (1 << total_bits
):
108 if n
< (1 << int_bits
):
111 n
+= 1 << (n
.bit_length() - int_bits
)
114 def partitioned_ints(bit_width
):
115 """ Get ints with all 1s on one side and 0s on the other. """
116 for i
in range(bit_width
):
117 yield (-1 << i
) & ((1 << bit_width
) - 1)
118 yield (1 << (i
+ 1)) - 1
121 class TestShiftedInts(unittest
.TestCase
):
126 0x004, 0x005, 0x006, 0x007,
127 0x008, 0x009, 0x00A, 0x00B, 0x00C, 0x00D, 0x00E, 0x00F,
128 0x010, 0x012, 0x014, 0x016, 0x018, 0x01A, 0x01C, 0x01E,
129 0x020, 0x024, 0x028, 0x02C, 0x030, 0x034, 0x038, 0x03C,
130 0x040, 0x048, 0x050, 0x058, 0x060, 0x068, 0x070, 0x078,
131 0x080, 0x090, 0x0A0, 0x0B0, 0x0C0, 0x0D0, 0x0E0, 0x0F0,
132 0x100, 0x120, 0x140, 0x160, 0x180, 0x1A0, 0x1C0, 0x1E0,
133 0x200, 0x240, 0x280, 0x2C0, 0x300, 0x340, 0x380, 0x3C0,
134 0x400, 0x480, 0x500, 0x580, 0x600, 0x680, 0x700, 0x780,
135 0x800, 0x900, 0xA00, 0xB00, 0xC00, 0xD00, 0xE00, 0xF00]
136 self
.assertEqual(list(shifted_ints(12, 4)), expected
)
139 def get_test_cases(core_config
,
143 if dividends
is None:
144 dividend_width
= core_config
.bit_width
+ core_config
.fract_width
145 dividends
= [*shifted_ints(dividend_width
,
146 max(3, core_config
.log2_radix
)),
147 *partitioned_ints(dividend_width
)]
149 assert isinstance(dividends
, list)
151 divisors
= [*shifted_ints(core_config
.bit_width
,
152 max(3, core_config
.log2_radix
)),
153 *partitioned_ints(core_config
.bit_width
)]
155 assert isinstance(divisors
, list)
156 if radicands
is None:
157 radicands
= [*shifted_ints(core_config
.bit_width
, 5),
158 *partitioned_ints(core_config
.bit_width
)]
160 assert isinstance(radicands
, list)
162 for alg_op
in reversed(Operation
): # put UDivRem at end
163 if alg_op
is Operation
.UDivRem
:
164 for dividend
in dividends
:
165 for divisor
in divisors
:
166 yield from generate_test_case(core_config
,
171 for radicand
in radicands
:
172 yield from generate_test_case(core_config
,
178 class DivPipeCoreTestPipeline(Elaboratable
):
179 def __init__(self
, core_config
, sync
):
180 self
.setup_stage
= DivPipeCoreSetupStage(core_config
)
181 self
.calculate_stages
= [
182 DivPipeCoreCalculateStage(core_config
, stage_index
)
183 for stage_index
in range(core_config
.num_calculate_stages
)]
184 self
.final_stage
= DivPipeCoreFinalStage(core_config
)
185 self
.interstage_signals
= [
186 DivPipeCoreInterstageData(core_config
, reset_less
=True)
187 for i
in range(core_config
.num_calculate_stages
+ 1)]
188 self
.i
= DivPipeCoreInputData(core_config
, reset_less
=True)
189 self
.o
= DivPipeCoreOutputData(core_config
, reset_less
=True)
192 def elaborate(self
, platform
):
194 stages
= [self
.setup_stage
, *self
.calculate_stages
, self
.final_stage
]
195 stage_inputs
= [self
.i
, *self
.interstage_signals
]
196 stage_outputs
= [*self
.interstage_signals
, self
.o
]
197 for stage
, input, output
in zip(stages
, stage_inputs
, stage_outputs
):
198 stage
.setup(m
, input)
199 assignments
= output
.eq(stage
.process(input))
201 m
.d
.sync
+= assignments
203 m
.d
.comb
+= assignments
208 # for interstage_signal in self.interstage_signals:
209 # yield from interstage_signal
213 class TestDivPipeCore(unittest
.TestCase
):
214 def handle_config(self
,
218 if test_cases
is None:
219 test_cases
= get_test_cases(core_config
)
220 test_cases
= list(test_cases
)
221 base_name
= f
"test_div_pipe_core_bit_width_{core_config.bit_width}"
222 base_name
+= f
"_fract_width_{core_config.fract_width}"
223 base_name
+= f
"_radix_{1 << core_config.log2_radix}"
226 with self
.subTest(part
="synthesize"):
227 dut
= DivPipeCoreTestPipeline(core_config
, sync
)
228 vl
= rtlil
.convert(dut
, ports
=[*dut
.i
, *dut
.o
])
229 with
open(f
"{base_name}.il", "w") as f
:
231 dut
= DivPipeCoreTestPipeline(core_config
, sync
)
233 vcd_file
=open(f
"{base_name}.vcd", "w"),
234 gtkw_file
=open(f
"{base_name}.gtkw", "w"),
235 traces
=[*dut
.traces()]) as sim
:
236 def generate_process():
237 for test_case
in test_cases
:
239 yield dut
.i
.dividend
.eq(test_case
.dividend
)
240 yield dut
.i
.divisor_radicand
.eq(test_case
.divisor_radicand
)
241 yield dut
.i
.operation
.eq(int(test_case
.core_op
))
245 # sync with generator
248 for _
in range(core_config
.num_calculate_stages
):
252 # now synched with generator
253 for test_case
in test_cases
:
256 quotient_root
= (yield dut
.o
.quotient_root
)
257 remainder
= (yield dut
.o
.remainder
)
258 with self
.subTest(test_case
=str(test_case
)):
259 self
.assertEqual(quotient_root
,
260 test_case
.quotient_root
)
261 self
.assertEqual(remainder
, test_case
.remainder
)
263 sim
.add_sync_process(generate_process
)
264 sim
.add_sync_process(check_process
)
267 def test_bit_width_2_fract_width_1_radix_2_comb(self
):
268 self
.handle_config(DivPipeCoreConfig(bit_width
=2,
273 def test_bit_width_2_fract_width_1_radix_2(self
):
274 self
.handle_config(DivPipeCoreConfig(bit_width
=2,
278 def test_bit_width_8_fract_width_4_radix_2_comb(self
):
279 self
.handle_config(DivPipeCoreConfig(bit_width
=8,
284 def test_bit_width_8_fract_width_4_radix_2(self
):
285 self
.handle_config(DivPipeCoreConfig(bit_width
=8,
289 def test_bit_width_32_fract_width_24_radix_8_comb(self
):
290 self
.handle_config(DivPipeCoreConfig(bit_width
=32,
295 def test_bit_width_32_fract_width_24_radix_8(self
):
296 self
.handle_config(DivPipeCoreConfig(bit_width
=32,
300 def test_bit_width_32_fract_width_28_radix_8_comb(self
):
301 self
.handle_config(DivPipeCoreConfig(bit_width
=32,
306 def test_bit_width_32_fract_width_28_radix_8(self
):
307 self
.handle_config(DivPipeCoreConfig(bit_width
=32,
311 # FIXME: add more test_* functions
314 if __name__
== '__main__':