duplicate FPRound inside fcvt
[ieee754fpu.git] / src / ieee754 / fcvt / pipeline.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
6 from nmigen.cli import main, verilog
7
8 from nmutil.singlepipe import ControlBase
9 from nmutil.concurrentunit import ReservationStations, num_bits
10
11 from ieee754.fpcommon.getop import FPADDBaseData
12 from ieee754.fpcommon.denorm import FPSCData
13 from ieee754.fpcommon.pack import FPPackData
14 from ieee754.fpcommon.normtopack import FPNormToPack
15 from ieee754.fpcommon.postcalc import FPAddStage1Data
16
17
18 from nmigen import Module, Signal, Elaboratable
19 from math import log
20
21 from ieee754.fpcommon.fpbase import FPNumIn, FPNumOut, FPNumBaseRecord
22 from ieee754.fpcommon.fpbase import FPState, FPNumBase
23 from ieee754.fpcommon.getop import FPPipeContext
24
25 from nmigen import Module, Signal, Cat, Const, Elaboratable
26
27 from ieee754.fpcommon.fpbase import FPNumDecode, FPNumBaseRecord
28 from nmutil.singlepipe import SimpleHandshake, StageChain
29
30 from ieee754.fpcommon.fpbase import FPState, FPID
31 from ieee754.fpcommon.getop import FPADDBaseData
32
33
34 class FPCVTSpecialCasesMod(Elaboratable):
35 """ special cases: NaNs, infs, zeros, denormalised
36 see "Special Operations"
37 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
38 """
39
40 def __init__(self, in_pspec, out_pspec):
41 self.in_pspec = in_pspec
42 self.out_pspec = out_pspec
43 self.i = self.ispec()
44 self.o = self.ospec()
45
46 def ispec(self):
47 return FPADDBaseData(self.in_pspec)
48
49 def ospec(self):
50 return FPAddStage1Data(self.out_pspec, e_extra=True)
51
52 def setup(self, m, i):
53 """ links module to inputs and outputs
54 """
55 m.submodules.specialcases = self
56 m.d.comb += self.i.eq(i)
57
58 def process(self, i):
59 return self.o
60
61 def elaborate(self, platform):
62 m = Module()
63
64 #m.submodules.sc_out_z = self.o.z
65
66 # decode: XXX really should move to separate stage
67 print ("in_width out", self.in_pspec['width'],
68 self.out_pspec['width'])
69 a1 = FPNumBaseRecord(self.in_pspec['width'], False)
70 print ("a1", a1.width, a1.rmw, a1.e_width, a1.e_start, a1.e_end)
71 m.submodules.sc_decode_a = a1 = FPNumDecode(None, a1)
72 m.d.comb += a1.v.eq(self.i.a)
73 z1 = self.o.z
74 print ("z1", z1.width, z1.rmw, z1.e_width, z1.e_start, z1.e_end)
75
76 # intermediaries
77 exp_sub_n126 = Signal((a1.e_width, True), reset_less=True)
78 exp_gt127 = Signal(reset_less=True)
79 # constants from z1, at the bit-width of a1.
80 N126 = Const(z1.fp.N126.value, (a1.e_width, True))
81 P127 = Const(z1.fp.P127.value, (a1.e_width, True))
82 m.d.comb += exp_sub_n126.eq(a1.e - N126)
83 m.d.comb += exp_gt127.eq(a1.e > P127)
84
85 # overflow
86 m.d.comb += self.o.of.guard.eq(a1.m[-self.o.z.rmw-2])
87 m.d.comb += self.o.of.round_bit.eq(a1.m[-self.o.z.rmw-3])
88 m.d.comb += self.o.of.sticky.eq(a1.m[:-self.o.z.rmw-1] != 0)
89 m.d.comb += self.o.of.m0.eq(self.o.z.m[0])
90
91 # if a zero, return zero (signed)
92 with m.If(a1.exp_n127):
93 m.d.comb += self.o.z.zero(a1.s)
94 m.d.comb += self.o.out_do_z.eq(1)
95
96 # if a range outside z's min range (-126)
97 with m.Elif(exp_sub_n126 < 0):
98 m.d.comb += self.o.z.s.eq(a1.s)
99 m.d.comb += self.o.z.e.eq(a1.e)
100 m.d.comb += self.o.z.m.eq(a1.m[-self.o.z.rmw-1:])
101 m.d.comb += self.o.z.m[-1].eq(1)
102
103 # if a is inf return inf
104 with m.Elif(a1.is_inf):
105 m.d.comb += self.o.z.inf(a1.s)
106 m.d.comb += self.o.out_do_z.eq(1)
107
108 # if a is NaN return NaN
109 with m.Elif(a1.is_nan):
110 m.d.comb += self.o.z.nan(0)
111 m.d.comb += self.o.out_do_z.eq(1)
112
113 # if a mantissa greater than 127, return inf
114 with m.Elif(exp_gt127):
115 print ("inf", self.o.z.inf(a1.s))
116 m.d.comb += self.o.z.inf(a1.s)
117 m.d.comb += self.o.out_do_z.eq(1)
118
119 # ok after all that, anything else should fit fine (whew)
120 with m.Else():
121 # XXX TODO: this is basically duplicating FPRoundMod. hmmm...
122 print ("alen", a1.e_start, z1.fp.N126, N126)
123 print ("m1", self.o.z.rmw, a1.m[-self.o.z.rmw-1:])
124 mo = Signal(self.o.z.m_width-1)
125 m.d.comb += mo.eq(a1.m[-self.o.z.rmw-1:])
126 with m.If(self.o.of.roundz):
127 with m.If((~mo == 0)): # all 1s
128 m.d.comb += self.o.z.create(a1.s, a1.e+1, mo+1)
129 with m.Else():
130 m.d.comb += self.o.z.create(a1.s, a1.e, mo+1)
131 with m.Else():
132 m.d.comb += self.o.z.create(a1.s, a1.e, a1.m[-self.o.z.rmw-1:])
133 m.d.comb += self.o.out_do_z.eq(1)
134
135 # copy the context (muxid, operator)
136 m.d.comb += self.o.oz.eq(self.o.z.v)
137 m.d.comb += self.o.ctx.eq(self.i.ctx)
138
139 return m
140
141
142 class FPCVTSpecialCases(FPState):
143 """ special cases: NaNs, infs, zeros, denormalised
144 """
145
146 def __init__(self, in_width, out_width, id_wid):
147 FPState.__init__(self, "special_cases")
148 self.mod = FPCVTSpecialCasesMod(in_width, out_width)
149 self.out_z = self.mod.ospec()
150 self.out_do_z = Signal(reset_less=True)
151
152 def setup(self, m, i):
153 """ links module to inputs and outputs
154 """
155 self.mod.setup(m, i, self.out_do_z)
156 m.d.sync += self.out_z.v.eq(self.mod.out_z.v) # only take the output
157 m.d.sync += self.out_z.ctx.eq(self.mod.o.ctx) # (and context)
158
159 def action(self, m):
160 self.idsync(m)
161 with m.If(self.out_do_z):
162 m.next = "put_z"
163 with m.Else():
164 m.next = "denormalise"
165
166
167 class FPCVTSpecialCasesDeNorm(FPState, SimpleHandshake):
168 """ special cases: NaNs, infs, zeros, denormalised
169 """
170
171 def __init__(self, in_pspec, out_pspec):
172 FPState.__init__(self, "special_cases")
173 sc = FPCVTSpecialCasesMod(in_pspec, out_pspec)
174 SimpleHandshake.__init__(self, sc)
175 self.out = self.ospec(None)
176
177
178 class FPCVTBasePipe(ControlBase):
179 def __init__(self, in_pspec, out_pspec):
180 ControlBase.__init__(self)
181 self.pipe1 = FPCVTSpecialCasesDeNorm(in_pspec, out_pspec)
182 self.pipe2 = FPNormToPack(out_pspec, e_extra=True)
183
184 self._eqs = self.connect([self.pipe1, self.pipe2])
185
186 def elaborate(self, platform):
187 m = ControlBase.elaborate(self, platform)
188 m.submodules.scnorm = self.pipe1
189 m.submodules.normpack = self.pipe2
190 m.d.comb += self._eqs
191 return m
192
193
194 class FPCVTMuxInOut(ReservationStations):
195 """ Reservation-Station version of FPCVT pipeline.
196
197 * fan-in on inputs (an array of FPADDBaseData: a,b,mid)
198 * 2-stage multiplier pipeline
199 * fan-out on outputs (an array of FPPackData: z,mid)
200
201 Fan-in and Fan-out are combinatorial.
202 """
203 def __init__(self, in_width, out_width, num_rows, op_wid=0):
204 self.op_wid = op_wid
205 self.id_wid = num_bits(in_width)
206 self.out_id_wid = num_bits(out_width)
207
208 self.in_pspec = {}
209 self.in_pspec['id_wid'] = self.id_wid
210 self.in_pspec['op_wid'] = self.op_wid
211 self.in_pspec['width'] = in_width
212
213 self.out_pspec = {}
214 self.out_pspec['id_wid'] = self.out_id_wid
215 self.out_pspec['op_wid'] = op_wid
216 self.out_pspec['width'] = out_width
217
218 self.alu = FPCVTBasePipe(self.in_pspec, self.out_pspec)
219 ReservationStations.__init__(self, num_rows)
220
221 def i_specfn(self):
222 return FPADDBaseData(self.in_pspec)
223
224 def o_specfn(self):
225 return FPPackData(self.out_pspec)