add operand down pipeline chain
[ieee754fpu.git] / src / ieee754 / fpadd / specialcases.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 from math import log
8
9 from ieee754.fpcommon.fpbase import FPNumDecode
10 from nmutil.singlepipe import SimpleHandshake, StageChain
11
12 from ieee754.fpcommon.fpbase import FPState, FPID, FPNumBaseRecord
13 from ieee754.fpcommon.getop import FPADDBaseData
14 from ieee754.fpcommon.denorm import (FPSCData, FPAddDeNormMod)
15
16
17 class FPAddSpecialCasesMod(Elaboratable):
18 """ special cases: NaNs, infs, zeros, denormalised
19 NOTE: some of these are unique to add. see "Special Operations"
20 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
21 """
22
23 def __init__(self, width, id_wid, op_wid=None):
24 self.width = width
25 self.id_wid = id_wid
26 self.op_wid = op_wid
27 self.i = self.ispec()
28 self.o = self.ospec()
29
30 def ispec(self):
31 return FPADDBaseData(self.width, self.id_wid, self.op_wid)
32
33 def ospec(self):
34 return FPSCData(self.width, self.id_wid, True, self.op_wid)
35
36 def setup(self, m, i):
37 """ links module to inputs and outputs
38 """
39 m.submodules.specialcases = self
40 m.d.comb += self.i.eq(i)
41
42 def process(self, i):
43 return self.o
44
45 def elaborate(self, platform):
46 m = Module()
47
48 #m.submodules.sc_out_z = self.o.z
49
50 # decode: XXX really should move to separate stage
51 a1 = FPNumBaseRecord(self.width)
52 b1 = FPNumBaseRecord(self.width)
53 m.submodules.sc_decode_a = a1 = FPNumDecode(None, a1)
54 m.submodules.sc_decode_b = b1 = FPNumDecode(None, b1)
55 m.d.comb += [a1.v.eq(self.i.a),
56 b1.v.eq(self.i.b),
57 self.o.a.eq(a1),
58 self.o.b.eq(b1)
59 ]
60
61 s_nomatch = Signal(reset_less=True)
62 m.d.comb += s_nomatch.eq(a1.s != b1.s)
63
64 m_match = Signal(reset_less=True)
65 m.d.comb += m_match.eq(a1.m == b1.m)
66
67 e_match = Signal(reset_less=True)
68 m.d.comb += e_match.eq(a1.e == b1.e)
69
70 aeqmb = Signal(reset_less=True)
71 m.d.comb += aeqmb.eq(s_nomatch & m_match & e_match)
72
73 abz = Signal(reset_less=True)
74 m.d.comb += abz.eq(a1.is_zero & b1.is_zero)
75
76 abnan = Signal(reset_less=True)
77 m.d.comb += abnan.eq(a1.is_nan | b1.is_nan)
78
79 bexp128s = Signal(reset_less=True)
80 m.d.comb += bexp128s.eq(b1.exp_128 & s_nomatch)
81
82 # if a is NaN or b is NaN return NaN
83 with m.If(abnan):
84 m.d.comb += self.o.out_do_z.eq(1)
85 m.d.comb += self.o.z.nan(0)
86
87 # XXX WEIRDNESS for FP16 non-canonical NaN handling
88 # under review
89
90 ## if a is zero and b is NaN return -b
91 #with m.If(a.is_zero & (a.s==0) & b.is_nan):
92 # m.d.comb += self.o.out_do_z.eq(1)
93 # m.d.comb += z.create(b.s, b.e, Cat(b.m[3:-2], ~b.m[0]))
94
95 ## if b is zero and a is NaN return -a
96 #with m.Elif(b.is_zero & (b.s==0) & a.is_nan):
97 # m.d.comb += self.o.out_do_z.eq(1)
98 # m.d.comb += z.create(a.s, a.e, Cat(a.m[3:-2], ~a.m[0]))
99
100 ## if a is -zero and b is NaN return -b
101 #with m.Elif(a.is_zero & (a.s==1) & b.is_nan):
102 # m.d.comb += self.o.out_do_z.eq(1)
103 # m.d.comb += z.create(a.s & b.s, b.e, Cat(b.m[3:-2], 1))
104
105 ## if b is -zero and a is NaN return -a
106 #with m.Elif(b.is_zero & (b.s==1) & a.is_nan):
107 # m.d.comb += self.o.out_do_z.eq(1)
108 # m.d.comb += z.create(a.s & b.s, a.e, Cat(a.m[3:-2], 1))
109
110 # if a is inf return inf (or NaN)
111 with m.Elif(a1.is_inf):
112 m.d.comb += self.o.out_do_z.eq(1)
113 m.d.comb += self.o.z.inf(a1.s)
114 # if a is inf and signs don't match return NaN
115 with m.If(bexp128s):
116 m.d.comb += self.o.z.nan(0)
117
118 # if b is inf return inf
119 with m.Elif(b1.is_inf):
120 m.d.comb += self.o.out_do_z.eq(1)
121 m.d.comb += self.o.z.inf(b1.s)
122
123 # if a is zero and b zero return signed-a/b
124 with m.Elif(abz):
125 m.d.comb += self.o.out_do_z.eq(1)
126 m.d.comb += self.o.z.create(a1.s & b1.s, b1.e, b1.m[3:-1])
127
128 # if a is zero return b
129 with m.Elif(a1.is_zero):
130 m.d.comb += self.o.out_do_z.eq(1)
131 m.d.comb += self.o.z.create(b1.s, b1.e, b1.m[3:-1])
132
133 # if b is zero return a
134 with m.Elif(b1.is_zero):
135 m.d.comb += self.o.out_do_z.eq(1)
136 m.d.comb += self.o.z.create(a1.s, a1.e, a1.m[3:-1])
137
138 # if a equal to -b return zero (+ve zero)
139 with m.Elif(aeqmb):
140 m.d.comb += self.o.out_do_z.eq(1)
141 m.d.comb += self.o.z.zero(0)
142
143 # Denormalised Number checks next, so pass a/b data through
144 with m.Else():
145 m.d.comb += self.o.out_do_z.eq(0)
146
147 m.d.comb += self.o.oz.eq(self.o.z.v)
148 m.d.comb += self.o.mid.eq(self.i.mid)
149 if self.o.op_wid:
150 m.d.comb += self.o.op.eq(self.i.op)
151
152 return m
153
154
155 class FPAddSpecialCases(FPState):
156 """ special cases: NaNs, infs, zeros, denormalised
157 NOTE: some of these are unique to add. see "Special Operations"
158 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
159 """
160
161 def __init__(self, width, id_wid):
162 FPState.__init__(self, "special_cases")
163 self.mod = FPAddSpecialCasesMod(width)
164 self.out_z = self.mod.ospec()
165 self.out_do_z = Signal(reset_less=True)
166
167 def setup(self, m, i):
168 """ links module to inputs and outputs
169 """
170 self.mod.setup(m, i, self.out_do_z)
171 m.d.sync += self.out_z.v.eq(self.mod.out_z.v) # only take the output
172 m.d.sync += self.out_z.mid.eq(self.mod.o.mid) # (and mid)
173
174 def action(self, m):
175 self.idsync(m)
176 with m.If(self.out_do_z):
177 m.next = "put_z"
178 with m.Else():
179 m.next = "denormalise"
180
181
182 class FPAddSpecialCasesDeNorm(FPState, SimpleHandshake):
183 """ special cases: NaNs, infs, zeros, denormalised
184 NOTE: some of these are unique to add. see "Special Operations"
185 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
186 """
187
188 def __init__(self, width, id_wid, op_wid=None):
189 FPState.__init__(self, "special_cases")
190 self.width = width
191 self.id_wid = id_wid
192 self.op_wid = op_wid
193 SimpleHandshake.__init__(self, self) # pipe is its own stage
194 self.out = self.ospec()
195
196 def ispec(self):
197 return FPADDBaseData(self.width, self.id_wid, self.op_wid) # SC ispec
198
199 def ospec(self):
200 return FPSCData(self.width, self.id_wid, True, self.op_wid) # DeNorm
201
202 def setup(self, m, i):
203 """ links module to inputs and outputs
204 """
205 smod = FPAddSpecialCasesMod(self.width, self.id_wid)
206 dmod = FPAddDeNormMod(self.width, self.id_wid)
207
208 chain = StageChain([smod, dmod])
209 chain.setup(m, i)
210
211 # only needed for break-out (early-out)
212 # self.out_do_z = smod.o.out_do_z
213
214 self.o = dmod.o
215
216 def process(self, i):
217 return self.o
218
219 def action(self, m):
220 # for break-out (early-out)
221 #with m.If(self.out_do_z):
222 # m.next = "put_z"
223 #with m.Else():
224 m.d.sync += self.out.eq(self.process(None))
225 m.next = "align"
226
227