378855a034f05d428cf14bc8412b2e2afbb7e3e4
[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 me = a1.rmw
77 ms = a1.rmw - self.o.z.rmw
78 print ("ms-me", ms, me)
79
80 # intermediaries
81 exp_sub_n126 = Signal((a1.e_width, True), reset_less=True)
82 exp_gt127 = Signal(reset_less=True)
83 # constants from z1, at the bit-width of a1.
84 N126 = Const(z1.fp.N126.value, (a1.e_width, True))
85 P127 = Const(z1.fp.P127.value, (a1.e_width, True))
86 m.d.comb += exp_sub_n126.eq(a1.e - N126)
87 m.d.comb += exp_gt127.eq(a1.e > P127)
88
89 # if a zero, return zero (signed)
90 with m.If(a1.exp_n127):
91 m.d.comb += self.o.z.zero(a1.s)
92 m.d.comb += self.o.out_do_z.eq(1)
93
94 # if a range outside z's min range (-126)
95 with m.Elif(exp_sub_n126 < 0):
96 m.d.comb += self.o.of.guard.eq(a1.m[ms-1])
97 m.d.comb += self.o.of.round_bit.eq(a1.m[ms-2])
98 m.d.comb += self.o.of.sticky.eq(a1.m[:ms-2].bool())
99 m.d.comb += self.o.of.m0.eq(a1.m[ms]) # bit of a1
100
101 m.d.comb += self.o.z.s.eq(a1.s)
102 m.d.comb += self.o.z.e.eq(a1.e)
103 m.d.comb += self.o.z.m.eq(a1.m[-self.o.z.rmw-1:])
104 m.d.comb += self.o.z.m[-1].eq(1)
105
106 # if a is inf return inf
107 with m.Elif(a1.is_inf):
108 m.d.comb += self.o.z.inf(a1.s)
109 m.d.comb += self.o.out_do_z.eq(1)
110
111 # if a is NaN return NaN
112 with m.Elif(a1.is_nan):
113 m.d.comb += self.o.z.nan(0)
114 m.d.comb += self.o.out_do_z.eq(1)
115
116 # if a mantissa greater than 127, return inf
117 with m.Elif(exp_gt127):
118 print ("inf", self.o.z.inf(a1.s))
119 m.d.comb += self.o.z.inf(a1.s)
120 m.d.comb += self.o.out_do_z.eq(1)
121
122 # ok after all that, anything else should fit fine (whew)
123 with m.Else():
124 m.d.comb += self.o.of.guard.eq(a1.m[ms-1])
125 m.d.comb += self.o.of.round_bit.eq(a1.m[ms-2])
126 m.d.comb += self.o.of.sticky.eq(a1.m[:ms-2].bool())
127 m.d.comb += self.o.of.m0.eq(a1.m[ms]) # bit of a1
128
129 # XXX TODO: this is basically duplicating FPRoundMod. hmmm...
130 print ("alen", a1.e_start, z1.fp.N126, N126)
131 print ("m1", self.o.z.rmw, a1.m[-self.o.z.rmw-1:])
132 mo = Signal(self.o.z.m_width-1)
133 m.d.comb += mo.eq(a1.m[ms:me])
134 with m.If(self.o.of.roundz):
135 with m.If((~mo == 0)): # all 1s
136 m.d.comb += self.o.z.create(a1.s, a1.e+1, mo+1)
137 with m.Else():
138 m.d.comb += self.o.z.create(a1.s, a1.e, mo+1)
139 with m.Else():
140 m.d.comb += self.o.z.create(a1.s, a1.e, a1.m[-self.o.z.rmw-1:])
141 m.d.comb += self.o.out_do_z.eq(1)
142
143 # copy the context (muxid, operator)
144 m.d.comb += self.o.oz.eq(self.o.z.v)
145 m.d.comb += self.o.ctx.eq(self.i.ctx)
146
147 return m
148
149
150 class FPCVTSpecialCases(FPState):
151 """ special cases: NaNs, infs, zeros, denormalised
152 """
153
154 def __init__(self, in_width, out_width, id_wid):
155 FPState.__init__(self, "special_cases")
156 self.mod = FPCVTSpecialCasesMod(in_width, out_width)
157 self.out_z = self.mod.ospec()
158 self.out_do_z = Signal(reset_less=True)
159
160 def setup(self, m, i):
161 """ links module to inputs and outputs
162 """
163 self.mod.setup(m, i, self.out_do_z)
164 m.d.sync += self.out_z.v.eq(self.mod.out_z.v) # only take the output
165 m.d.sync += self.out_z.ctx.eq(self.mod.o.ctx) # (and context)
166
167 def action(self, m):
168 self.idsync(m)
169 with m.If(self.out_do_z):
170 m.next = "put_z"
171 with m.Else():
172 m.next = "denormalise"
173
174
175 class FPCVTSpecialCasesDeNorm(FPState, SimpleHandshake):
176 """ special cases: NaNs, infs, zeros, denormalised
177 """
178
179 def __init__(self, in_pspec, out_pspec):
180 FPState.__init__(self, "special_cases")
181 sc = FPCVTSpecialCasesMod(in_pspec, out_pspec)
182 SimpleHandshake.__init__(self, sc)
183 self.out = self.ospec(None)
184
185
186 class FPCVTBasePipe(ControlBase):
187 def __init__(self, in_pspec, out_pspec):
188 ControlBase.__init__(self)
189 self.pipe1 = FPCVTSpecialCasesDeNorm(in_pspec, out_pspec)
190 self.pipe2 = FPNormToPack(out_pspec, e_extra=True)
191
192 self._eqs = self.connect([self.pipe1, self.pipe2])
193
194 def elaborate(self, platform):
195 m = ControlBase.elaborate(self, platform)
196 m.submodules.scnorm = self.pipe1
197 m.submodules.normpack = self.pipe2
198 m.d.comb += self._eqs
199 return m
200
201
202 class FPCVTMuxInOut(ReservationStations):
203 """ Reservation-Station version of FPCVT pipeline.
204
205 * fan-in on inputs (an array of FPADDBaseData: a,b,mid)
206 * 2-stage multiplier pipeline
207 * fan-out on outputs (an array of FPPackData: z,mid)
208
209 Fan-in and Fan-out are combinatorial.
210 """
211 def __init__(self, in_width, out_width, num_rows, op_wid=0):
212 self.op_wid = op_wid
213 self.id_wid = num_bits(in_width)
214 self.out_id_wid = num_bits(out_width)
215
216 self.in_pspec = {}
217 self.in_pspec['id_wid'] = self.id_wid
218 self.in_pspec['op_wid'] = self.op_wid
219 self.in_pspec['width'] = in_width
220
221 self.out_pspec = {}
222 self.out_pspec['id_wid'] = self.out_id_wid
223 self.out_pspec['op_wid'] = op_wid
224 self.out_pspec['width'] = out_width
225
226 self.alu = FPCVTBasePipe(self.in_pspec, self.out_pspec)
227 ReservationStations.__init__(self, num_rows)
228
229 def i_specfn(self):
230 return FPADDBaseData(self.in_pspec)
231
232 def o_specfn(self):
233 return FPPackData(self.out_pspec)