1 # SPDX-License-Identifier: LGPL-2.1-or-later
2 # See Notices.txt for copyright information
4 from .core
import (DivPipeCoreConfig
, DivPipeCoreSetupStage
,
5 DivPipeCoreCalculateStage
, DivPipeCoreFinalStage
,
6 DivPipeCoreOperation
, DivPipeCoreInputData
,
7 DivPipeCoreInterstageData
, DivPipeCoreOutputData
)
8 from .algorithm
import (FixedUDivRemSqrtRSqrt
, Fixed
, Operation
, div_rem
,
9 fixed_sqrt
, fixed_rsqrt
)
11 from nmigen
import Module
, Elaboratable
12 from nmigen
.hdl
.ir
import Fragment
13 from nmigen
.back
import rtlil
14 from nmigen
.back
.pysim
import Simulator
, Delay
, Tick
17 def show_fixed(bits
, fract_width
, bit_width
):
18 fixed
= Fixed
.from_bits(bits
, fract_width
, bit_width
, False)
19 return f
"{str(fixed)}:{repr(fixed)}"
22 def get_core_op(alg_op
):
23 if alg_op
is Operation
.UDivRem
:
24 return DivPipeCoreOperation
.UDivRem
25 if alg_op
is Operation
.SqrtRem
:
26 return DivPipeCoreOperation
.SqrtRem
27 assert alg_op
is Operation
.RSqrtRem
28 return DivPipeCoreOperation
.RSqrtRem
39 self
.dividend
= dividend
40 self
.divisor_radicand
= divisor_radicand
42 self
.quotient_root
= quotient_root
43 self
.remainder
= remainder
44 self
.core_config
= core_config
48 return get_core_op(self
.alg_op
)
51 bit_width
= self
.core_config
.bit_width
52 fract_width
= self
.core_config
.fract_width
53 dividend_str
= show_fixed(dividend
,
55 bit_width
+ fract_width
)
56 divisor_radicand_str
= show_fixed(divisor_radicand
,
59 quotient_root_str
= self
.show_fixed(quotient_root
,
62 remainder_str
= self
.show_fixed(remainder
,
65 return f
"{{dividend={dividend_str}, " \
66 + f
"divisor_radicand={divisor_radicand_str}, " \
67 + f
"op={self.alg_op.name}, " \
68 + f
"quotient_root={quotient_root_str}, " \
69 + f
"remainder={remainder_str}, " \
70 + f
"config={self.core_config}}}"
73 def generate_test_case(core_config
, dividend
, divisor_radicand
, alg_op
):
74 bit_width
= core_config
.bit_width
75 fract_width
= core_config
.fract_width
76 if alg_op
is Operation
.UDivRem
:
77 if divisor_radicand
== 0:
79 quotient_root
, remainder
= div_rem(dividend
,
83 remainder
<<= fract_width
84 elif alg_op
is Operation
.SqrtRem
:
85 root_remainder
= fixed_sqrt(Fixed
.from_bits(divisor_radicand
,
89 quotient_root
= root_remainder
.root
.bits
90 remainder
= root_remainder
.remainder
.bits
<< fract_width
92 assert alg_op
is Operation
.RSqrtRem
93 if divisor_radicand
== 0:
95 root_remainder
= fixed_rsqrt(Fixed
.from_bits(divisor_radicand
,
99 quotient_root
= root_remainder
.root
.bits
100 remainder
= root_remainder
.remainder
.bits
101 if quotient_root
>= (1 << bit_width
):
103 yield TestCaseData(dividend
,
111 def get_test_cases(core_config
,
114 radicand_range
=None):
115 if dividend_range
is None:
116 dividend_range
= range(1 << (core_config
.bit_width
117 + core_config
.fract_width
))
118 if divisor_range
is None:
119 divisor_range
= range(1 << core_config
.bit_width
)
120 if radicand_range
is None:
121 radicand_range
= range(1 << core_config
.bit_width
)
123 for alg_op
in Operation
:
124 if alg_op
is Operation
.UDivRem
:
125 for dividend
in dividend_range
:
126 for divisor
in divisor_range
:
127 yield from generate_test_case(core_config
,
132 for radicand
in radicand_range
:
133 yield from generate_test_case(core_config
,
139 class DivPipeCoreTestPipeline(Elaboratable
):
140 def __init__(self
, core_config
):
141 self
.setup_stage
= DivPipeCoreSetupStage(core_config
)
142 self
.calculate_stages
= [
143 DivPipeCoreCalculateStage(core_config
, stage_index
)
144 for stage_index
in range(core_config
.num_calculate_stages
)]
145 self
.final_stage
= DivPipeCoreFinalStage(core_config
)
146 self
.interstage_signals
= [
147 DivPipeCoreInterstageData(core_config
, reset_less
=False)
148 for i
in range(core_config
.num_calculate_stages
+ 1)]
149 self
.i
= DivPipeCoreInputData(core_config
, reset_less
=False)
150 self
.o
= DivPipeCoreOutputData(core_config
, reset_less
=False)
152 def elaborate(self
, platform
):
154 stages
= [self
.setup_stage
, *self
.calculate_stages
, self
.final_stage
]
155 stage_inputs
= [self
.i
, *self
.interstage_signals
]
156 stage_outputs
= [*self
.interstage_signals
, self
.o
]
157 for stage
, input, output
in zip(stages
, stage_inputs
, stage_outputs
):
158 stage
.setup(m
, input)
159 m
.d
.sync
+= output
.eq(stage
.process(input))
165 for interstage_signal
in self
.interstage_signals
:
166 yield from interstage_signal
170 class TestDivPipeCore(unittest
.TestCase
):
171 def handle_case(self
,
175 radicand_range
=None):
176 def gen_test_cases():
177 yield from get_test_cases(core_config
,
181 base_name
= f
"div_pipe_core_bit_width_{core_config.bit_width}"
182 base_name
+= f
"_fract_width_{core_config.fract_width}"
183 base_name
+= f
"_radix_{1 << core_config.log2_radix}"
184 with self
.subTest(part
="synthesize"):
185 dut
= DivPipeCoreTestPipeline(core_config
)
186 vl
= rtlil
.convert(dut
, ports
=[*dut
.i
, *dut
.o
])
187 with
open(f
"{base_name}.il", "w") as f
:
189 self
.fail("generated invalid rtlil") # FIXME: remove when fixed
190 dut
= DivPipeCoreTestPipeline(core_config
)
192 vcd_file
=f
"{base_name}.vcd",
193 gtkw_file
=f
"{base_name}.gtkw",
194 traces
=[*dut
.traces()]) as sim
:
195 def generate_process():
196 for test_case
in gen_test_cases():
197 yield dut
.i
.dividend
.eq(test_case
.dividend
)
198 yield dut
.i
.divisor_radicand
.eq(test_case
.divisor_radicand
)
199 yield dut
.i
.operation
.eq(test_case
.core_op
)
204 # sync with generator
206 for _
in core_config
.num_calculate_stages
:
210 # now synched with generator
211 for test_case
in gen_test_cases():
213 quotient_root
= (yield dut
.o
.quotient_root
)
214 remainder
= (yield dut
.o
.remainder
)
215 with self
.subTest(test_case
=str(test_case
)):
216 self
.assertEqual(quotient_root
,
217 test_case
.quotient_root
)
218 self
.assertEqual(remainder
, test_case
.remainder
)
221 sim
.add_sync_process(generate_process
)
222 sim
.add_sync_process(check_process
)
225 def test_bit_width_8_fract_width_4_radix_2(self
):
226 self
.handle_case(DivPipeCoreConfig(bit_width
=8,