move fpcommon to separate subdir
[ieee754fpu.git] / src / ieee754 / 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 FPNumDecode
10 from singlepipe import SimpleHandshake, 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 = FPNumDecode(None, self.width)
51 b1 = FPNumDecode(None, self.width)
52 m.submodules.sc_decode_a = a1
53 m.submodules.sc_decode_b = 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.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, 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, width, id_wid):
186 FPState.__init__(self, "special_cases")
187 self.width = width
188 self.id_wid = id_wid
189 SimpleHandshake.__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