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
41 self
.dividend
= dividend
42 self
.divisor_radicand
= divisor_radicand
44 self
.quotient_root
= quotient_root
45 self
.remainder
= remainder
46 self
.core_config
= core_config
50 return get_core_op(self
.alg_op
)
53 bit_width
= self
.core_config
.bit_width
54 fract_width
= self
.core_config
.fract_width
55 dividend_str
= show_fixed(self
.dividend
,
57 bit_width
+ fract_width
)
58 divisor_radicand_str
= show_fixed(self
.divisor_radicand
,
61 quotient_root_str
= show_fixed(self
.quotient_root
,
64 remainder_str
= show_fixed(self
.remainder
,
67 return f
"{{dividend={dividend_str}, " \
68 + f
"divisor_radicand={divisor_radicand_str}, " \
69 + f
"op={self.alg_op.name}, " \
70 + f
"quotient_root={quotient_root_str}, " \
71 + f
"remainder={remainder_str}, " \
72 + f
"config={self.core_config}}}"
75 def generate_test_case(core_config
, dividend
, divisor_radicand
, alg_op
):
76 bit_width
= core_config
.bit_width
77 fract_width
= core_config
.fract_width
78 if alg_op
is Operation
.UDivRem
:
79 if divisor_radicand
== 0:
81 quotient_root
, remainder
= div_rem(dividend
,
85 remainder
<<= fract_width
86 elif alg_op
is Operation
.SqrtRem
:
87 root_remainder
= fixed_sqrt(Fixed
.from_bits(divisor_radicand
,
91 quotient_root
= root_remainder
.root
.bits
92 remainder
= root_remainder
.remainder
.bits
<< fract_width
94 assert alg_op
is Operation
.RSqrtRem
95 if divisor_radicand
== 0:
97 root_remainder
= fixed_rsqrt(Fixed
.from_bits(divisor_radicand
,
101 quotient_root
= root_remainder
.root
.bits
102 remainder
= root_remainder
.remainder
.bits
103 if quotient_root
>= (1 << bit_width
):
105 yield TestCaseData(dividend
,
113 def get_test_cases(core_config
,
117 if dividends
is None:
118 dividends
= range(1 << (core_config
.bit_width
119 + core_config
.fract_width
))
121 assert isinstance(dividends
, list)
123 divisors
= range(1 << core_config
.bit_width
)
125 assert isinstance(divisors
, list)
126 if radicands
is None:
127 radicands
= range(1 << core_config
.bit_width
)
129 assert isinstance(radicands
, list)
131 for alg_op
in Operation
:
132 if alg_op
is Operation
.UDivRem
:
133 for dividend
in dividends
:
134 for divisor
in divisors
:
135 yield from generate_test_case(core_config
,
140 for radicand
in radicands
:
141 yield from generate_test_case(core_config
,
147 class DivPipeCoreTestPipeline(Elaboratable
):
148 def __init__(self
, core_config
, sync
=True):
149 self
.setup_stage
= DivPipeCoreSetupStage(core_config
)
150 self
.calculate_stages
= [
151 DivPipeCoreCalculateStage(core_config
, stage_index
)
152 for stage_index
in range(core_config
.num_calculate_stages
)]
153 self
.final_stage
= DivPipeCoreFinalStage(core_config
)
154 self
.interstage_signals
= [
155 DivPipeCoreInterstageData(core_config
, reset_less
=True)
156 for i
in range(core_config
.num_calculate_stages
+ 1)]
157 self
.i
= DivPipeCoreInputData(core_config
, reset_less
=True)
158 self
.o
= DivPipeCoreOutputData(core_config
, reset_less
=True)
161 def elaborate(self
, platform
):
163 stages
= [self
.setup_stage
, *self
.calculate_stages
, self
.final_stage
]
164 stage_inputs
= [self
.i
, *self
.interstage_signals
]
165 stage_outputs
= [*self
.interstage_signals
, self
.o
]
166 for stage
, input, output
in zip(stages
, stage_inputs
, stage_outputs
):
167 stage
.setup(m
, input)
168 assignments
= output
.eq(stage
.process(input))
170 m
.d
.sync
+= assignments
172 m
.d
.comb
+= assignments
177 # for interstage_signal in self.interstage_signals:
178 # yield from interstage_signal
182 class TestDivPipeCore(unittest
.TestCase
):
183 def handle_case(self
,
189 if dividends
is not None:
190 dividends
= list(dividends
)
191 if divisors
is not None:
192 divisors
= list(divisors
)
193 if radicands
is not None:
194 radicands
= list(radicands
)
196 def gen_test_cases():
197 yield from get_test_cases(core_config
,
201 base_name
= f
"div_pipe_core_bit_width_{core_config.bit_width}"
202 base_name
+= f
"_fract_width_{core_config.fract_width}"
203 base_name
+= f
"_radix_{1 << core_config.log2_radix}"
204 with self
.subTest(part
="synthesize"):
205 dut
= DivPipeCoreTestPipeline(core_config
)
206 vl
= rtlil
.convert(dut
, ports
=[*dut
.i
, *dut
.o
])
207 with
open(f
"{base_name}.il", "w") as f
:
209 dut
= DivPipeCoreTestPipeline(core_config
)
211 vcd_file
=open(f
"{base_name}.vcd", "w"),
212 gtkw_file
=open(f
"{base_name}.gtkw", "w"),
213 traces
=[*dut
.traces()]) as sim
:
214 def generate_process():
215 for test_case
in gen_test_cases():
216 yield dut
.i
.dividend
.eq(test_case
.dividend
)
217 yield dut
.i
.divisor_radicand
.eq(test_case
.divisor_radicand
)
218 yield dut
.i
.operation
.eq(int(test_case
.core_op
))
223 # sync with generator
226 for _
in range(core_config
.num_calculate_stages
):
230 # now synched with generator
231 for test_case
in gen_test_cases():
233 quotient_root
= (yield dut
.o
.quotient_root
)
234 remainder
= (yield dut
.o
.remainder
)
235 with self
.subTest(test_case
=str(test_case
)):
236 self
.assertEqual(quotient_root
,
237 test_case
.quotient_root
)
238 self
.assertEqual(remainder
, test_case
.remainder
)
241 sim
.add_sync_process(generate_process
)
242 sim
.add_sync_process(check_process
)
245 def test_bit_width_8_fract_width_4_radix_2(self
):
246 self
.handle_case(DivPipeCoreConfig(bit_width
=8,
249 dividends
=[*range(1 << 8),
250 *range(1 << 8, 1 << 12, 1 << 4)],
253 # FIXME: add more test_* functions
256 if __name__
== '__main__':