1 # SPDX-License-Identifier: LGPL-2.1-or-later
2 # See Notices.txt for copyright information
3 """ Core of the div/rem/sqrt/rsqrt pipeline.
5 Special case handling, input/output conversion, and muxid handling are handled
6 outside of these classes.
8 Algorithms based on ``algorithm.FixedUDivRemSqrtRSqrt``.
12 ``dividend == quotient_root * divisor_radicand``
14 ``divisor_radicand == quotient_root * quotient_root``
16 ``1 == quotient_root * quotient_root * divisor_radicand``
18 The remainder is the left-hand-side of the comparison minus the
19 right-hand-side of the comparison in the above formulas.
21 from nmigen
import (Elaboratable
, Module
, Signal
)
25 #from ieee754.fpcommon.fpbase import FPNumBaseRecord
26 #from ieee754.fpcommon.getop import FPPipeContext
29 class DivPipeCoreConfig
:
30 """ Configuration for core of the div/rem/sqrt/rsqrt pipeline.
32 :attribute bit_width: base bit-width.
33 :attribute fract_width: base fract-width. Specifies location of base-2
35 :attribute log2_radix: number of bits of ``quotient_root`` that should be
36 computed per pipeline stage.
39 def __init__(self
, bit_width
, fract_width
, log2_radix
):
40 """ Create a ``DivPipeCoreConfig`` instance. """
41 self
.bit_width
= bit_width
42 self
.fract_width
= fract_width
43 self
.log2_radix
= log2_radix
47 return f
"DivPipeCoreConfig({self.bit_width}, " \
48 + f
"{self.fract_width}, {self.log2_radix})"
51 class DivPipeCoreOperation(enum
.IntEnum
):
52 """ Operation for ``DivPipeCore``.
54 :attribute UDivRem: unsigned divide/remainder.
55 :attribute SqrtRem: square-root/remainder.
56 :attribute RSqrtRem: reciprocal-square-root/remainder.
64 def create_signal(cls
, *, src_loc_at
=0, **kwargs
):
65 """ Create a signal that can contain a ``DivPipeCoreOperation``. """
66 return Signal(min=int(min(cls
)),
68 src_loc_at
=(src_loc_at
+ 1),
73 class DivPipeCoreInputData
:
74 """ input data type for ``DivPipeCore``.
76 :attribute core_config: ``DivPipeCoreConfig`` instance describing the
77 configuration to be used.
78 :attribute dividend: dividend for div/rem. Signal with a bit-width of
79 ``core_config.bit_width + core_config.fract_width`` and a fract-width
80 of ``core_config.fract_width * 2`` bits.
81 :attribute divisor_radicand: divisor for div/rem and radicand for
82 sqrt/rsqrt. Signal with a bit-width of ``core_config.bit_width`` and a
83 fract-width of ``core_config.fract_width`` bits.
84 :attribute operation: the ``DivPipeCoreOperation`` to be computed.
87 def __init__(self
, core_config
):
88 """ Create a ``DivPipeCoreInputData`` instance. """
89 self
.core_config
= core_config
90 self
.dividend
= Signal(core_config
.bit_width
+ core_config
.fract_width
,
92 self
.divisor_radicand
= Signal(core_config
.bit_width
, reset_less
=True)
94 # FIXME: this goes into (is replaced by) self.ctx.op
95 self
.operation
= DivPipeCoreOperation
.create_signal(reset_less
=True)
97 return # TODO: needs a width argument and a pspec
98 self
.z
= FPNumBaseRecord(width
, False)
99 self
.out_do_z
= Signal(reset_less
=True)
100 self
.oz
= Signal(width
, reset_less
=True)
102 self
.ctx
= FPPipeContext(width
, pspec
) # context: muxid, operator etc.
103 self
.muxid
= self
.ctx
.muxid
# annoying. complicated.
107 """ Get member signals. """
109 yield self
.divisor_radicand
110 yield self
.operation
# FIXME: delete. already covered by self.ctx
118 """ Assign member signals. """
119 return [self
.dividend
.eq(rhs
.dividend
),
120 self
.divisor_radicand
.eq(rhs
.divisor_radicand
),
121 self
.operation
.eq(rhs
.operation
)] # FIXME: delete.
123 return [self
.out_do_z
.eq(i
.out_do_z
), self
.oz
.eq(i
.oz
),
128 class DivPipeCoreInterstageData
:
129 """ interstage data type for ``DivPipeCore``.
131 :attribute core_config: ``DivPipeCoreConfig`` instance describing the
132 configuration to be used.
133 :attribute divisor_radicand: divisor for div/rem and radicand for
134 sqrt/rsqrt. Signal with a bit-width of ``core_config.bit_width`` and a
135 fract-width of ``core_config.fract_width`` bits.
136 :attribute operation: the ``DivPipeCoreOperation`` to be computed.
137 :attribute quotient_root: the quotient or root part of the result of the
138 operation. Signal with a bit-width of ``core_config.bit_width`` and a
139 fract-width of ``core_config.fract_width`` bits.
140 :attribute root_times_radicand: ``quotient_root * divisor_radicand``.
141 Signal with a bit-width of ``core_config.bit_width * 2`` and a
142 fract-width of ``core_config.fract_width * 2`` bits.
143 :attribute compare_lhs: The left-hand-side of the comparison in the
144 equation to be solved. Signal with a bit-width of
145 ``core_config.bit_width * 3`` and a fract-width of
146 ``core_config.fract_width * 3`` bits.
147 :attribute compare_rhs: The right-hand-side of the comparison in the
148 equation to be solved. Signal with a bit-width of
149 ``core_config.bit_width * 3`` and a fract-width of
150 ``core_config.fract_width * 3`` bits.
153 def __init__(self
, core_config
):
154 """ Create a ``DivPipeCoreInterstageData`` instance. """
155 self
.core_config
= core_config
156 self
.divisor_radicand
= Signal(core_config
.bit_width
, reset_less
=True)
157 # XXX FIXME: delete. already covered by self.ctx.op
158 self
.operation
= DivPipeCoreOperation
.create_signal(reset_less
=True)
159 self
.quotient_root
= Signal(core_config
.bit_width
, reset_less
=True)
160 self
.root_times_radicand
= Signal(core_config
.bit_width
* 2,
162 self
.compare_lhs
= Signal(core_config
.bit_width
* 3, reset_less
=True)
163 self
.compare_rhs
= Signal(core_config
.bit_width
* 3, reset_less
=True)
164 return # TODO: needs a width argument and a pspec
165 self
.z
= FPNumBaseRecord(width
, False)
166 self
.out_do_z
= Signal(reset_less
=True)
167 self
.oz
= Signal(width
, reset_less
=True)
169 self
.ctx
= FPPipeContext(width
, pspec
) # context: muxid, operator etc.
170 self
.muxid
= self
.ctx
.muxid
# annoying. complicated.
173 """ Get member signals. """
174 yield self
.divisor_radicand
175 yield self
.operation
# XXX FIXME: delete. already in self.ctx.op
176 yield self
.quotient_root
177 yield self
.root_times_radicand
178 yield self
.compare_lhs
179 yield self
.compare_rhs
187 """ Assign member signals. """
188 return [self
.divisor_radicand
.eq(rhs
.divisor_radicand
),
189 self
.operation
.eq(rhs
.operation
), # FIXME: delete.
190 self
.quotient_root
.eq(rhs
.quotient_root
),
191 self
.root_times_radicand
.eq(rhs
.root_times_radicand
),
192 self
.compare_lhs
.eq(rhs
.compare_lhs
),
193 self
.compare_rhs
.eq(rhs
.compare_rhs
)]
195 return [self
.out_do_z
.eq(i
.out_do_z
), self
.oz
.eq(i
.oz
),
199 class DivPipeCoreSetupStage(Elaboratable
):
200 """ Setup Stage of the core of the div/rem/sqrt/rsqrt pipeline.
203 def __init__(self
, core_config
):
204 """ Create a ``DivPipeCoreSetupStage`` instance."""
205 self
.core_config
= core_config
206 self
.i
= self
.ispec()
207 self
.o
= self
.ospec()
210 """ Get the input spec for this pipeline stage."""
211 return DivPipeCoreInputData(self
.core_config
)
214 """ Get the output spec for this pipeline stage."""
215 return DivPipeCoreInterstageData(self
.core_config
)
217 def setup(self
, m
, i
):
218 """ Pipeline stage setup. """
219 m
.submodules
.div_pipe_core_setup
= self
220 m
.d
.comb
+= self
.i
.eq(i
)
222 def process(self
, i
):
223 """ Pipeline stage process. """
224 return self
.o
# return processed data (ignore i)
226 def elaborate(self
, platform
):
227 """ Elaborate into ``Module``. """
230 m
.d
.comb
+= self
.o
.divisor_radicand
.eq(self
.i
.divisor_radicand
)
231 m
.d
.comb
+= self
.o
.quotient_root
.eq(0)
232 m
.d
.comb
+= self
.o
.root_times_radicand
.eq(0)
234 with m
.If(self
.i
.operation
== DivPipeCoreOperation
.UDivRem
):
235 m
.d
.comb
+= self
.o
.compare_lhs
.eq(self
.i
.dividend
236 << self
.core_config
.fract_width
)
237 with m
.Elif(self
.i
.operation
== DivPipeCoreOperation
.SqrtRem
):
238 m
.d
.comb
+= self
.o
.compare_lhs
.eq(
239 self
.i
.divisor_radicand
<< (self
.core_config
.fract_width
* 2))
240 with m
.Else(): # DivPipeCoreOperation.RSqrtRem
241 m
.d
.comb
+= self
.o
.compare_lhs
.eq(
242 1 << (self
.core_config
.fract_width
* 3))
244 m
.d
.comb
+= self
.o
.compare_rhs
.eq(0)
245 m
.d
.comb
+= self
.o
.operation
.eq(self
.i
.operation
)
249 # TODO: these as well
250 m
.d
.comb
+= self
.o
.oz
.eq(self
.i
.oz
)
251 m
.d
.comb
+= self
.o
.out_do_z
.eq(self
.i
.out_do_z
)
252 m
.d
.comb
+= self
.o
.ctx
.eq(self
.i
.ctx
)