combine specialcases if/elif tests into single temporaries
[ieee754fpu.git] / src / add / 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
6 from nmigen.cli import main, verilog
7 from math import log
8
9 from fpbase import FPNumIn
10 from singlepipe import UnbufferedPipeline, StageChain
11
12 from fpbase import FPState, FPID
13 from fpcommon.getop import FPADDBaseData
14 from fpcommon.denorm import (FPSCData, FPAddDeNormMod)
15
16
17 class FPAddSpecialCasesMod:
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):
24 self.width = width
25 self.id_wid = id_wid
26 self.i = self.ispec()
27 self.o = self.ospec()
28
29 def ispec(self):
30 return FPADDBaseData(self.width, self.id_wid)
31
32 def ospec(self):
33 return FPSCData(self.width, self.id_wid)
34
35 def setup(self, m, i):
36 """ links module to inputs and outputs
37 """
38 m.submodules.specialcases = self
39 m.d.comb += self.i.eq(i)
40
41 def process(self, i):
42 return self.o
43
44 def elaborate(self, platform):
45 m = Module()
46
47 m.submodules.sc_out_z = self.o.z
48
49 # decode: XXX really should move to separate stage
50 a1 = FPNumIn(None, self.width)
51 b1 = FPNumIn(None, self.width)
52 m.submodules.sc_decode_a = a1
53 m.submodules.sc_decode_b = b1
54 m.d.comb += [a1.decode(self.i.a),
55 b1.decode(self.i.b),
56 ]
57
58 s_nomatch = Signal(reset_less=True)
59 m.d.comb += s_nomatch.eq(a1.s != b1.s)
60
61 m_match = Signal(reset_less=True)
62 m.d.comb += m_match.eq(a1.m == b1.m)
63
64 e_match = Signal(reset_less=True)
65 m.d.comb += m_match.eq(a1.e == b1.e)
66
67 aeqmb = Signal(reset_less=True)
68 m.d.comb += aeqmb.eq(s_nomatch & m_match & e_match)
69
70 abz = Signal(reset_less=True)
71 m.d.comb += abz.eq(a1.is_zero & b1.is_zero)
72
73 abnan = Signal(reset_less=True)
74 m.d.comb += abnan.eq(a1.is_nan | b1.is_nan)
75
76 bexp128s = Signal(reset_less=True)
77 m.d.comb += bexp128s.eq(b1.exp_128 & s_nomatch)
78
79 # if a is NaN or b is NaN return NaN
80 with m.If(abnan):
81 m.d.comb += self.o.out_do_z.eq(1)
82 m.d.comb += self.o.z.nan(0)
83
84 # XXX WEIRDNESS for FP16 non-canonical NaN handling
85 # under review
86
87 ## if a is zero and b is NaN return -b
88 #with m.If(a.is_zero & (a.s==0) & b.is_nan):
89 # m.d.comb += self.o.out_do_z.eq(1)
90 # m.d.comb += z.create(b.s, b.e, Cat(b.m[3:-2], ~b.m[0]))
91
92 ## if b is zero and a is NaN return -a
93 #with m.Elif(b.is_zero & (b.s==0) & a.is_nan):
94 # m.d.comb += self.o.out_do_z.eq(1)
95 # m.d.comb += z.create(a.s, a.e, Cat(a.m[3:-2], ~a.m[0]))
96
97 ## if a is -zero and b is NaN return -b
98 #with m.Elif(a.is_zero & (a.s==1) & b.is_nan):
99 # m.d.comb += self.o.out_do_z.eq(1)
100 # m.d.comb += z.create(a.s & b.s, b.e, Cat(b.m[3:-2], 1))
101
102 ## if b is -zero and a is NaN return -a
103 #with m.Elif(b.is_zero & (b.s==1) & a.is_nan):
104 # m.d.comb += self.o.out_do_z.eq(1)
105 # m.d.comb += z.create(a.s & b.s, a.e, Cat(a.m[3:-2], 1))
106
107 # if a is inf return inf (or NaN)
108 with m.Elif(a1.is_inf):
109 m.d.comb += self.o.out_do_z.eq(1)
110 m.d.comb += self.o.z.inf(a1.s)
111 # if a is inf and signs don't match return NaN
112 with m.If(bexp128s):
113 m.d.comb += self.o.z.nan(0)
114
115 # if b is inf return inf
116 with m.Elif(b1.is_inf):
117 m.d.comb += self.o.out_do_z.eq(1)
118 m.d.comb += self.o.z.inf(b1.s)
119
120 # if a is zero and b zero return signed-a/b
121 with m.Elif(abz):
122 m.d.comb += self.o.out_do_z.eq(1)
123 m.d.comb += self.o.z.create(a1.s & b1.s, b1.e, b1.m[3:-1])
124
125 # if a is zero return b
126 with m.Elif(a1.is_zero):
127 m.d.comb += self.o.out_do_z.eq(1)
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.out_do_z.eq(1)
133 m.d.comb += self.o.z.create(a1.s, a1.e, a1.m[3:-1])
134
135 # if a equal to -b return zero (+ve zero)
136 with m.Elif(aeqmb):
137 m.d.comb += self.o.out_do_z.eq(1)
138 m.d.comb += self.o.z.zero(0)
139
140 # Denormalised Number checks next, so pass a/b data through
141 with m.Else():
142 m.d.comb += self.o.out_do_z.eq(0)
143 m.d.comb += self.o.a.eq(a1)
144 m.d.comb += self.o.b.eq(b1)
145
146 m.d.comb += self.o.oz.eq(self.o.z.v)
147 m.d.comb += self.o.mid.eq(self.i.mid)
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.mid.eq(self.mod.o.mid) # (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, UnbufferedPipeline):
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, width, id_wid):
186 FPState.__init__(self, "special_cases")
187 self.width = width
188 self.id_wid = id_wid
189 UnbufferedPipeline.__init__(self, self) # pipe is its own stage
190 self.out = self.ospec()
191
192 def ispec(self):
193 return FPADDBaseData(self.width, self.id_wid) # SpecialCases ispec
194
195 def ospec(self):
196 return FPSCData(self.width, self.id_wid) # DeNorm ospec
197
198 def setup(self, m, i):
199 """ links module to inputs and outputs
200 """
201 smod = FPAddSpecialCasesMod(self.width, self.id_wid)
202 dmod = FPAddDeNormMod(self.width, self.id_wid)
203
204 chain = StageChain([smod, dmod])
205 chain.setup(m, i)
206
207 # only needed for break-out (early-out)
208 # self.out_do_z = smod.o.out_do_z
209
210 self.o = dmod.o
211
212 def process(self, i):
213 return self.o
214
215 def action(self, m):
216 # for break-out (early-out)
217 #with m.If(self.out_do_z):
218 # m.next = "put_z"
219 #with m.Else():
220 m.d.sync += self.out.eq(self.process(None))
221 m.next = "align"
222
223