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