split out normalisation to separate module
[ieee754fpu.git] / src / add / fpcommon / postnormalise.py
1 # IEEE Floating Point Adder (Single Precision)
2 # Copyright (C) Jonathan P Dawson 2013
3 # 2013-12-12
4
5 from nmigen import Module, Signal, Cat, Mux, Array, Const
6 from nmigen.lib.coding import PriorityEncoder
7 from nmigen.cli import main, verilog
8 from math import log
9
10 from fpbase import FPNumIn, FPNumOut, FPOp, Overflow, FPBase, FPNumBase
11 from fpbase import MultiShiftRMerge, Trigger
12 from singlepipe import (ControlBase, StageChain, UnbufferedPipeline,
13 PassThroughStage)
14 from multipipe import CombMuxOutPipe
15 from multipipe import PriorityCombMuxInPipe
16
17 from fpbase import FPState
18 from .postcalc import FPAddStage1Data
19
20
21 class FPNorm1Data:
22
23 def __init__(self, width, id_wid):
24 self.roundz = Signal(reset_less=True)
25 self.z = FPNumBase(width, False)
26 self.out_do_z = Signal(reset_less=True)
27 self.oz = Signal(width, reset_less=True)
28 self.mid = Signal(id_wid, reset_less=True)
29
30 def eq(self, i):
31 return [self.z.eq(i.z), self.out_do_z.eq(i.out_do_z), self.oz.eq(i.oz),
32 self.roundz.eq(i.roundz), self.mid.eq(i.mid)]
33
34
35 class FPNorm1ModSingle:
36
37 def __init__(self, width, id_wid):
38 self.width = width
39 self.id_wid = id_wid
40 self.i = self.ispec()
41 self.o = self.ospec()
42
43 def ispec(self):
44 return FPAddStage1Data(self.width, self.id_wid)
45
46 def ospec(self):
47 return FPNorm1Data(self.width, self.id_wid)
48
49 def setup(self, m, i):
50 """ links module to inputs and outputs
51 """
52 m.submodules.normalise_1 = self
53 m.d.comb += self.i.eq(i)
54
55 def process(self, i):
56 return self.o
57
58 def elaborate(self, platform):
59 m = Module()
60
61 mwid = self.o.z.m_width+2
62 pe = PriorityEncoder(mwid)
63 m.submodules.norm_pe = pe
64
65 of = Overflow()
66 m.d.comb += self.o.roundz.eq(of.roundz)
67
68 m.submodules.norm1_out_z = self.o.z
69 m.submodules.norm1_out_overflow = of
70 m.submodules.norm1_in_z = self.i.z
71 m.submodules.norm1_in_overflow = self.i.of
72
73 i = self.ispec()
74 m.submodules.norm1_insel_z = i.z
75 m.submodules.norm1_insel_overflow = i.of
76
77 espec = (len(i.z.e), True)
78 ediff_n126 = Signal(espec, reset_less=True)
79 msr = MultiShiftRMerge(mwid, espec)
80 m.submodules.multishift_r = msr
81
82 m.d.comb += i.eq(self.i)
83 # initialise out from in (overridden below)
84 m.d.comb += self.o.z.eq(i.z)
85 m.d.comb += of.eq(i.of)
86 # normalisation increase/decrease conditions
87 decrease = Signal(reset_less=True)
88 increase = Signal(reset_less=True)
89 m.d.comb += decrease.eq(i.z.m_msbzero & i.z.exp_gt_n126)
90 m.d.comb += increase.eq(i.z.exp_lt_n126)
91 # decrease exponent
92 with m.If(~self.i.out_do_z):
93 with m.If(decrease):
94 # *sigh* not entirely obvious: count leading zeros (clz)
95 # with a PriorityEncoder: to find from the MSB
96 # we reverse the order of the bits.
97 temp_m = Signal(mwid, reset_less=True)
98 temp_s = Signal(mwid+1, reset_less=True)
99 clz = Signal((len(i.z.e), True), reset_less=True)
100 # make sure that the amount to decrease by does NOT
101 # go below the minimum non-INF/NaN exponent
102 limclz = Mux(i.z.exp_sub_n126 > pe.o, pe.o,
103 i.z.exp_sub_n126)
104 m.d.comb += [
105 # cat round and guard bits back into the mantissa
106 temp_m.eq(Cat(i.of.round_bit, i.of.guard, i.z.m)),
107 pe.i.eq(temp_m[::-1]), # inverted
108 clz.eq(limclz), # count zeros from MSB down
109 temp_s.eq(temp_m << clz), # shift mantissa UP
110 self.o.z.e.eq(i.z.e - clz), # DECREASE exponent
111 self.o.z.m.eq(temp_s[2:]), # exclude bits 0&1
112 of.m0.eq(temp_s[2]), # copy of mantissa[0]
113 # overflow in bits 0..1: got shifted too (leave sticky)
114 of.guard.eq(temp_s[1]), # guard
115 of.round_bit.eq(temp_s[0]), # round
116 ]
117 # increase exponent
118 with m.Elif(increase):
119 temp_m = Signal(mwid+1, reset_less=True)
120 m.d.comb += [
121 temp_m.eq(Cat(i.of.sticky, i.of.round_bit, i.of.guard,
122 i.z.m)),
123 ediff_n126.eq(i.z.N126 - i.z.e),
124 # connect multi-shifter to inp/out mantissa (and ediff)
125 msr.inp.eq(temp_m),
126 msr.diff.eq(ediff_n126),
127 self.o.z.m.eq(msr.m[3:]),
128 of.m0.eq(temp_s[3]), # copy of mantissa[0]
129 # overflow in bits 0..1: got shifted too (leave sticky)
130 of.guard.eq(temp_s[2]), # guard
131 of.round_bit.eq(temp_s[1]), # round
132 of.sticky.eq(temp_s[0]), # sticky
133 self.o.z.e.eq(i.z.e + ediff_n126),
134 ]
135
136 m.d.comb += self.o.mid.eq(self.i.mid)
137 m.d.comb += self.o.out_do_z.eq(self.i.out_do_z)
138 m.d.comb += self.o.oz.eq(self.i.oz)
139
140 return m
141
142
143 class FPNorm1ModMulti:
144
145 def __init__(self, width, single_cycle=True):
146 self.width = width
147 self.in_select = Signal(reset_less=True)
148 self.in_z = FPNumBase(width, False)
149 self.in_of = Overflow()
150 self.temp_z = FPNumBase(width, False)
151 self.temp_of = Overflow()
152 self.out_z = FPNumBase(width, False)
153 self.out_of = Overflow()
154
155 def elaborate(self, platform):
156 m = Module()
157
158 m.submodules.norm1_out_z = self.out_z
159 m.submodules.norm1_out_overflow = self.out_of
160 m.submodules.norm1_temp_z = self.temp_z
161 m.submodules.norm1_temp_of = self.temp_of
162 m.submodules.norm1_in_z = self.in_z
163 m.submodules.norm1_in_overflow = self.in_of
164
165 in_z = FPNumBase(self.width, False)
166 in_of = Overflow()
167 m.submodules.norm1_insel_z = in_z
168 m.submodules.norm1_insel_overflow = in_of
169
170 # select which of temp or in z/of to use
171 with m.If(self.in_select):
172 m.d.comb += in_z.eq(self.in_z)
173 m.d.comb += in_of.eq(self.in_of)
174 with m.Else():
175 m.d.comb += in_z.eq(self.temp_z)
176 m.d.comb += in_of.eq(self.temp_of)
177 # initialise out from in (overridden below)
178 m.d.comb += self.out_z.eq(in_z)
179 m.d.comb += self.out_of.eq(in_of)
180 # normalisation increase/decrease conditions
181 decrease = Signal(reset_less=True)
182 increase = Signal(reset_less=True)
183 m.d.comb += decrease.eq(in_z.m_msbzero & in_z.exp_gt_n126)
184 m.d.comb += increase.eq(in_z.exp_lt_n126)
185 m.d.comb += self.out_norm.eq(decrease | increase) # loop-end
186 # decrease exponent
187 with m.If(decrease):
188 m.d.comb += [
189 self.out_z.e.eq(in_z.e - 1), # DECREASE exponent
190 self.out_z.m.eq(in_z.m << 1), # shift mantissa UP
191 self.out_z.m[0].eq(in_of.guard), # steal guard (was tot[2])
192 self.out_of.guard.eq(in_of.round_bit), # round (was tot[1])
193 self.out_of.round_bit.eq(0), # reset round bit
194 self.out_of.m0.eq(in_of.guard),
195 ]
196 # increase exponent
197 with m.Elif(increase):
198 m.d.comb += [
199 self.out_z.e.eq(in_z.e + 1), # INCREASE exponent
200 self.out_z.m.eq(in_z.m >> 1), # shift mantissa DOWN
201 self.out_of.guard.eq(in_z.m[0]),
202 self.out_of.m0.eq(in_z.m[1]),
203 self.out_of.round_bit.eq(in_of.guard),
204 self.out_of.sticky.eq(in_of.sticky | in_of.round_bit)
205 ]
206
207 return m
208
209
210 class FPNorm1Single(FPState):
211
212 def __init__(self, width, id_wid, single_cycle=True):
213 FPState.__init__(self, "normalise_1")
214 self.mod = FPNorm1ModSingle(width)
215 self.o = self.ospec()
216 self.out_z = FPNumBase(width, False)
217 self.out_roundz = Signal(reset_less=True)
218
219 def ispec(self):
220 return self.mod.ispec()
221
222 def ospec(self):
223 return self.mod.ospec()
224
225 def setup(self, m, i):
226 """ links module to inputs and outputs
227 """
228 self.mod.setup(m, i)
229
230 def action(self, m):
231 m.next = "round"
232
233
234 class FPNorm1Multi(FPState):
235
236 def __init__(self, width, id_wid):
237 FPState.__init__(self, "normalise_1")
238 self.mod = FPNorm1ModMulti(width)
239 self.stb = Signal(reset_less=True)
240 self.ack = Signal(reset=0, reset_less=True)
241 self.out_norm = Signal(reset_less=True)
242 self.in_accept = Signal(reset_less=True)
243 self.temp_z = FPNumBase(width)
244 self.temp_of = Overflow()
245 self.out_z = FPNumBase(width)
246 self.out_roundz = Signal(reset_less=True)
247
248 def setup(self, m, in_z, in_of, norm_stb):
249 """ links module to inputs and outputs
250 """
251 self.mod.setup(m, in_z, in_of, norm_stb,
252 self.in_accept, self.temp_z, self.temp_of,
253 self.out_z, self.out_norm)
254
255 m.d.comb += self.stb.eq(norm_stb)
256 m.d.sync += self.ack.eq(0) # sets to zero when not in normalise_1 state
257
258 def action(self, m):
259 m.d.comb += self.in_accept.eq((~self.ack) & (self.stb))
260 m.d.sync += self.temp_of.eq(self.mod.out_of)
261 m.d.sync += self.temp_z.eq(self.out_z)
262 with m.If(self.out_norm):
263 with m.If(self.in_accept):
264 m.d.sync += [
265 self.ack.eq(1),
266 ]
267 with m.Else():
268 m.d.sync += self.ack.eq(0)
269 with m.Else():
270 # normalisation not required (or done).
271 m.next = "round"
272 m.d.sync += self.ack.eq(1)
273 m.d.sync += self.out_roundz.eq(self.mod.out_of.roundz)
274
275