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