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