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