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 obj
= FixedUDivRemSqrtRSqrt(dividend
,
83 core_config
.log2_radix
)
85 yield TestCaseData(dividend
,
93 def get_test_cases(core_config
,
98 dividends
= range(1 << (core_config
.bit_width
99 + core_config
.fract_width
))
101 assert isinstance(dividends
, list)
103 divisors
= range(1 << core_config
.bit_width
)
105 assert isinstance(divisors
, list)
106 if radicands
is None:
107 radicands
= range(1 << core_config
.bit_width
)
109 assert isinstance(radicands
, list)
111 for alg_op
in Operation
:
112 if alg_op
is Operation
.UDivRem
:
113 for dividend
in dividends
:
114 for divisor
in divisors
:
115 yield from generate_test_case(core_config
,
120 for radicand
in radicands
:
121 yield from generate_test_case(core_config
,
127 class DivPipeCoreTestPipeline(Elaboratable
):
128 def __init__(self
, core_config
, sync
):
129 self
.setup_stage
= DivPipeCoreSetupStage(core_config
)
130 self
.calculate_stages
= [
131 DivPipeCoreCalculateStage(core_config
, stage_index
)
132 for stage_index
in range(core_config
.num_calculate_stages
)]
133 self
.final_stage
= DivPipeCoreFinalStage(core_config
)
134 self
.interstage_signals
= [
135 DivPipeCoreInterstageData(core_config
, reset_less
=True)
136 for i
in range(core_config
.num_calculate_stages
+ 1)]
137 self
.i
= DivPipeCoreInputData(core_config
, reset_less
=True)
138 self
.o
= DivPipeCoreOutputData(core_config
, reset_less
=True)
141 def elaborate(self
, platform
):
143 stages
= [self
.setup_stage
, *self
.calculate_stages
, self
.final_stage
]
144 stage_inputs
= [self
.i
, *self
.interstage_signals
]
145 stage_outputs
= [*self
.interstage_signals
, self
.o
]
146 for stage
, input, output
in zip(stages
, stage_inputs
, stage_outputs
):
147 stage
.setup(m
, input)
148 assignments
= output
.eq(stage
.process(input))
150 m
.d
.sync
+= assignments
152 m
.d
.comb
+= assignments
157 # for interstage_signal in self.interstage_signals:
158 # yield from interstage_signal
162 class TestDivPipeCore(unittest
.TestCase
):
163 def handle_case(self
,
169 if dividends
is not None:
170 dividends
= list(dividends
)
171 if divisors
is not None:
172 divisors
= list(divisors
)
173 if radicands
is not None:
174 radicands
= list(radicands
)
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
, sync
)
186 vl
= rtlil
.convert(dut
, ports
=[*dut
.i
, *dut
.o
])
187 with
open(f
"{base_name}.il", "w") as f
:
189 dut
= DivPipeCoreTestPipeline(core_config
, sync
)
191 vcd_file
=open(f
"{base_name}.vcd", "w"),
192 gtkw_file
=open(f
"{base_name}.gtkw", "w"),
193 traces
=[*dut
.traces()]) as sim
:
194 def generate_process():
195 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(int(test_case
.core_op
))
203 # sync with generator
206 for _
in range(core_config
.num_calculate_stages
):
210 # now synched with generator
211 for test_case
in gen_test_cases():
214 quotient_root
= (yield dut
.o
.quotient_root
)
215 remainder
= (yield dut
.o
.remainder
)
216 with self
.subTest(test_case
=str(test_case
)):
217 self
.assertEqual(quotient_root
,
218 test_case
.quotient_root
)
219 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,
229 dividends
=[*range(1 << 8),
230 *range(1 << 8, 1 << 12, 1 << 4)],
233 def test_bit_width_2_fract_width_1_radix_2(self
):
234 self
.handle_case(DivPipeCoreConfig(bit_width
=2,
239 # FIXME: add more test_* functions
242 if __name__
== '__main__':