add bug cross-reference to #113 for FCVT unit tests
[ieee754fpu.git] / src / ieee754 / 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, Elaboratable
6 from nmigen.cli import main, verilog
7 from math import log
8
9 from ieee754.fpcommon.fpbase import (Overflow, OverflowMod,
10 FPNumBase, FPNumBaseRecord)
11 from ieee754.fpcommon.fpbase import FPState
12 from ieee754.fpcommon.getop import FPPipeContext
13 from ieee754.fpcommon.msbhigh import FPMSBHigh
14 from ieee754.fpcommon.exphigh import FPEXPHigh
15 from .postcalc import FPAddStage1Data
16
17
18 class FPNorm1Data:
19
20 def __init__(self, pspec):
21 width = pspec.width
22 self.roundz = Signal(reset_less=True, name="norm1_roundz")
23 self.z = FPNumBaseRecord(width, False)
24 self.out_do_z = Signal(reset_less=True)
25 self.oz = Signal(width, reset_less=True)
26 self.ctx = FPPipeContext(pspec)
27 self.muxid = self.ctx.muxid
28
29 def eq(self, i):
30 ret = [self.z.eq(i.z), self.out_do_z.eq(i.out_do_z), self.oz.eq(i.oz),
31 self.roundz.eq(i.roundz), self.ctx.eq(i.ctx)]
32 return ret
33
34
35 class FPNorm1ModSingle(Elaboratable):
36
37 def __init__(self, pspec, e_extra=False):
38 self.pspec = pspec
39 self.e_extra = e_extra
40 self.i = self.ispec()
41 self.o = self.ospec()
42
43 def ispec(self):
44 return FPAddStage1Data(self.pspec, e_extra=self.e_extra)
45
46 def ospec(self):
47 return FPNorm1Data(self.pspec)
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 of = OverflowMod("norm1of_")
62
63 #m.submodules.norm1_out_z = self.o.z
64 m.submodules.norm1_out_overflow = of
65 #m.submodules.norm1_in_z = self.i.z
66 #m.submodules.norm1_in_overflow = self.i.of
67
68 i = self.ispec()
69 i.of.guard.name = "norm1_i_of_guard"
70 i.of.round_bit.name = "norm1_i_of_roundbit"
71 i.of.sticky.name = "norm1_i_of_sticky"
72 i.of.m0.name = "norm1_i_of_m0"
73 m.submodules.norm1_insel_z = insel_z = FPNumBase(i.z)
74 #m.submodules.norm1_insel_overflow = iof = OverflowMod("iof")
75
76 espec = (len(insel_z.e), True)
77 mwid = self.o.z.m_width+2
78
79 msr = FPEXPHigh(mwid+2, espec[0])
80 m.submodules.norm_exp = msr
81
82 msb = FPMSBHigh(mwid+1, espec[0], True)
83 m.submodules.norm_msb = msb
84
85 m.d.comb += i.eq(self.i)
86 # initialise out from in (overridden below)
87 m.d.comb += self.o.z.eq(insel_z)
88 m.d.comb += Overflow.eq(of, i.of)
89 # normalisation increase/decrease conditions
90 decrease = Signal(reset_less=True)
91 increase = Signal(reset_less=True)
92 m.d.comb += decrease.eq(insel_z.m_msbzero & insel_z.exp_gt_n126)
93 m.d.comb += increase.eq(insel_z.exp_lt_n126)
94 # decrease exponent
95 with m.If(~self.i.out_do_z):
96 # concatenate s/r/g with mantissa
97 temp_m = Signal(mwid+2, reset_less=True)
98 m.d.comb += temp_m.eq(Cat(i.of.sticky, i.of.round_bit, i.of.guard,
99 insel_z.m)),
100
101 with m.If(decrease):
102 # make sure that the amount to decrease by does NOT
103 # go below the minimum non-INF/NaN exponent
104 m.d.comb += msb.limclz.eq(insel_z.exp_sub_n126)
105 m.d.comb += [
106 # inputs: mantissa and exponent
107 msb.m_in.eq(temp_m),
108 msb.e_in.eq(insel_z.e),
109
110 # outputs: mantissa first (s/r/g/m[3:])
111 self.o.z.m.eq(msb.m_out[3:]), # exclude bits 0&1
112 of.m0.eq(msb.m_out[3]), # copy of mantissa[0]
113 # overflow in bits 0..1: got shifted too (leave sticky)
114 of.guard.eq(msb.m_out[2]), # guard
115 of.round_bit.eq(msb.m_out[1]), # round
116 # now exponent out
117 self.o.z.e.eq(msb.e_out),
118 ]
119 # increase exponent
120 with m.Elif(increase):
121 ediff_n126 = Signal(espec, reset_less=True)
122 m.d.comb += [
123 # concatenate
124 ediff_n126.eq(insel_z.fp.N126 - insel_z.e),
125 # connect multi-shifter to inp/out m/e (and ediff)
126 msr.m_in.eq(temp_m),
127 msr.e_in.eq(insel_z.e),
128 msr.ediff.eq(ediff_n126),
129
130 # outputs: mantissa first (s/r/g/m[3:])
131 self.o.z.m.eq(msr.m_out[3:]),
132 of.m0.eq(msr.m_out[3]), # copy of mantissa[0]
133 # overflow in bits 0..2: got shifted too (leave sticky)
134 of.guard.eq(msr.m_out[2]), # guard
135 of.round_bit.eq(msr.m_out[1]), # round
136 of.sticky.eq(msr.m_out[0]), # sticky
137 # now exponent
138 self.o.z.e.eq(msr.e_out),
139 ]
140
141 m.d.comb += self.o.roundz.eq(of.roundz_out)
142 m.d.comb += self.o.ctx.eq(self.i.ctx)
143 m.d.comb += self.o.out_do_z.eq(self.i.out_do_z)
144 m.d.comb += self.o.oz.eq(self.i.oz)
145
146 return m
147
148
149 class FPNorm1ModMulti:
150
151 def __init__(self, pspec, single_cycle=True):
152 self.width = width
153 self.in_select = Signal(reset_less=True)
154 self.in_z = FPNumBase(width, False)
155 self.in_of = Overflow()
156 self.temp_z = FPNumBase(width, False)
157 self.temp_of = Overflow()
158 self.out_z = FPNumBase(width, False)
159 self.out_of = Overflow()
160
161 def elaborate(self, platform):
162 m = Module()
163
164 m.submodules.norm1_out_z = self.out_z
165 m.submodules.norm1_out_overflow = self.out_of
166 m.submodules.norm1_temp_z = self.temp_z
167 m.submodules.norm1_temp_of = self.temp_of
168 m.submodules.norm1_in_z = self.in_z
169 m.submodules.norm1_in_overflow = self.in_of
170
171 in_z = FPNumBase(self.width, False)
172 in_of = Overflow()
173 m.submodules.norm1_insel_z = in_z
174 m.submodules.norm1_insel_overflow = in_of
175
176 # select which of temp or in z/of to use
177 with m.If(self.in_select):
178 m.d.comb += in_z.eq(self.in_z)
179 m.d.comb += in_of.eq(self.in_of)
180 with m.Else():
181 m.d.comb += in_z.eq(self.temp_z)
182 m.d.comb += in_of.eq(self.temp_of)
183 # initialise out from in (overridden below)
184 m.d.comb += self.out_z.eq(in_z)
185 m.d.comb += self.out_of.eq(in_of)
186 # normalisation increase/decrease conditions
187 decrease = Signal(reset_less=True)
188 increase = Signal(reset_less=True)
189 m.d.comb += decrease.eq(in_z.m_msbzero & in_z.exp_gt_n126)
190 m.d.comb += increase.eq(in_z.exp_lt_n126)
191 m.d.comb += self.out_norm.eq(decrease | increase) # loop-end
192 # decrease exponent
193 with m.If(decrease):
194 m.d.comb += [
195 self.out_z.e.eq(in_z.e - 1), # DECREASE exponent
196 self.out_z.m.eq(in_z.m << 1), # shift mantissa UP
197 self.out_z.m[0].eq(in_of.guard), # steal guard (was tot[2])
198 self.out_of.guard.eq(in_of.round_bit), # round (was tot[1])
199 self.out_of.round_bit.eq(0), # reset round bit
200 self.out_of.m0.eq(in_of.guard),
201 ]
202 # increase exponent
203 with m.Elif(increase):
204 m.d.comb += [
205 self.out_z.e.eq(in_z.e + 1), # INCREASE exponent
206 self.out_z.m.eq(in_z.m >> 1), # shift mantissa DOWN
207 self.out_of.guard.eq(in_z.m[0]),
208 self.out_of.m0.eq(in_z.m[1]),
209 self.out_of.round_bit.eq(in_of.guard),
210 self.out_of.sticky.eq(in_of.sticky | in_of.round_bit)
211 ]
212
213 return m
214
215
216 class FPNorm1Single(FPState):
217
218 def __init__(self, width, id_wid, single_cycle=True):
219 FPState.__init__(self, "normalise_1")
220 self.mod = FPNorm1ModSingle(width)
221 self.o = self.ospec()
222 self.out_z = FPNumBase(width, False)
223 self.out_roundz = Signal(reset_less=True)
224
225 def ispec(self):
226 return self.mod.ispec()
227
228 def ospec(self):
229 return self.mod.ospec()
230
231 def setup(self, m, i):
232 """ links module to inputs and outputs
233 """
234 self.mod.setup(m, i)
235
236 def action(self, m):
237 m.next = "round"
238
239
240 class FPNorm1Multi(FPState):
241
242 def __init__(self, width, id_wid):
243 FPState.__init__(self, "normalise_1")
244 self.mod = FPNorm1ModMulti(width)
245 self.stb = Signal(reset_less=True)
246 self.ack = Signal(reset=0, reset_less=True)
247 self.out_norm = Signal(reset_less=True)
248 self.in_accept = Signal(reset_less=True)
249 self.temp_z = FPNumBase(width)
250 self.temp_of = Overflow()
251 self.out_z = FPNumBase(width)
252 self.out_roundz = Signal(reset_less=True)
253
254 def setup(self, m, in_z, in_of, norm_stb):
255 """ links module to inputs and outputs
256 """
257 self.mod.setup(m, in_z, in_of, norm_stb,
258 self.in_accept, self.temp_z, self.temp_of,
259 self.out_z, self.out_norm)
260
261 m.d.comb += self.stb.eq(norm_stb)
262 m.d.sync += self.ack.eq(0) # sets to zero when not in normalise_1 state
263
264 def action(self, m):
265 m.d.comb += self.in_accept.eq((~self.ack) & (self.stb))
266 m.d.sync += self.temp_of.eq(self.mod.out_of)
267 m.d.sync += self.temp_z.eq(self.out_z)
268 with m.If(self.out_norm):
269 with m.If(self.in_accept):
270 m.d.sync += [
271 self.ack.eq(1),
272 ]
273 with m.Else():
274 m.d.sync += self.ack.eq(0)
275 with m.Else():
276 # normalisation not required (or done).
277 m.next = "round"
278 m.d.sync += self.ack.eq(1)
279 m.d.sync += self.out_roundz.eq(self.mod.out_of.roundz)
280
281