simplify code
[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 # default bypass
82 m.d.comb += self.o.out_do_z.eq(1)
83
84 # if a is NaN or b is NaN return NaN
85 with m.If(abnan):
86 m.d.comb += self.o.z.nan(0)
87
88 # XXX WEIRDNESS for FP16 non-canonical NaN handling
89 # under review
90
91 ## if a is zero and b is NaN return -b
92 #with m.If(a.is_zero & (a.s==0) & b.is_nan):
93 # m.d.comb += self.o.out_do_z.eq(1)
94 # m.d.comb += z.create(b.s, b.e, Cat(b.m[3:-2], ~b.m[0]))
95
96 ## if b is zero and a is NaN return -a
97 #with m.Elif(b.is_zero & (b.s==0) & a.is_nan):
98 # m.d.comb += self.o.out_do_z.eq(1)
99 # m.d.comb += z.create(a.s, a.e, Cat(a.m[3:-2], ~a.m[0]))
100
101 ## if a is -zero and b is NaN return -b
102 #with m.Elif(a.is_zero & (a.s==1) & b.is_nan):
103 # m.d.comb += self.o.out_do_z.eq(1)
104 # m.d.comb += z.create(a.s & b.s, b.e, Cat(b.m[3:-2], 1))
105
106 ## if b is -zero and a is NaN return -a
107 #with m.Elif(b.is_zero & (b.s==1) & a.is_nan):
108 # m.d.comb += self.o.out_do_z.eq(1)
109 # m.d.comb += z.create(a.s & b.s, a.e, Cat(a.m[3:-2], 1))
110
111 # if a is inf return inf (or NaN)
112 with m.Elif(a1.is_inf):
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.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.z.create(a1.s & b1.s, b1.e, b1.m[3:-1])
125
126 # if a is zero return b
127 with m.Elif(a1.is_zero):
128 m.d.comb += self.o.z.create(b1.s, b1.e, b1.m[3:-1])
129
130 # if b is zero return a
131 with m.Elif(b1.is_zero):
132 m.d.comb += self.o.z.create(a1.s, a1.e, a1.m[3:-1])
133
134 # if a equal to -b return zero (+ve zero)
135 with m.Elif(aeqmb):
136 m.d.comb += self.o.z.zero(0)
137
138 # Denormalised Number checks next, so pass a/b data through
139 with m.Else():
140 m.d.comb += self.o.out_do_z.eq(0)
141
142 m.d.comb += self.o.oz.eq(self.o.z.v)
143 m.d.comb += self.o.ctx.eq(self.i.ctx)
144
145 return m
146
147
148 class FPAddSpecialCases(FPState):
149 """ special cases: NaNs, infs, zeros, denormalised
150 NOTE: some of these are unique to add. see "Special Operations"
151 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
152 """
153
154 def __init__(self, width, id_wid):
155 FPState.__init__(self, "special_cases")
156 self.mod = FPAddSpecialCasesMod(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 mid)
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 FPAddSpecialCasesDeNorm(FPState, SimpleHandshake):
176 """ special cases: NaNs, infs, zeros, denormalised
177 NOTE: some of these are unique to add. see "Special Operations"
178 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
179 """
180
181 def __init__(self, pspec):
182 FPState.__init__(self, "special_cases")
183 self.pspec = pspec
184 SimpleHandshake.__init__(self, self) # pipe is its own stage
185 self.out = self.ospec()
186
187 def ispec(self):
188 return FPADDBaseData(self.pspec) # SC ispec
189
190 def ospec(self):
191 return FPSCData(self.pspec, True) # DeNorm
192
193 def setup(self, m, i):
194 """ links module to inputs and outputs
195 """
196 smod = FPAddSpecialCasesMod(self.pspec)
197 dmod = FPAddDeNormMod(self.pspec, True)
198
199 chain = StageChain([smod, dmod])
200 chain.setup(m, i)
201
202 # only needed for break-out (early-out)
203 # self.out_do_z = smod.o.out_do_z
204
205 self.o = dmod.o
206
207 def process(self, i):
208 return self.o
209
210 def action(self, m):
211 # for break-out (early-out)
212 #with m.If(self.out_do_z):
213 # m.next = "put_z"
214 #with m.Else():
215 m.d.sync += self.out.eq(self.process(None))
216 m.next = "align"
217
218