use specialcase fpdiv "switch" and use DP op
[ieee754fpu.git] / src / ieee754 / fpdiv / specialcases.py
1 """ IEEE Floating Point Divider
2
3 Copyright (C) 2019 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
4 Copyright (C) 2019 Jacob Lifshay
5
6 Relevant bugreports:
7 * http://bugs.libre-riscv.org/show_bug.cgi?id=99
8 * http://bugs.libre-riscv.org/show_bug.cgi?id=43
9 * http://bugs.libre-riscv.org/show_bug.cgi?id=44
10 """
11
12 from nmigen import Module, Signal, Cat, Const, Elaboratable
13 from nmigen.cli import main, verilog
14 from math import log
15
16 from ieee754.fpcommon.fpbase import FPNumDecode, FPNumBaseRecord
17 from nmutil.singlepipe import SimpleHandshake, StageChain
18
19 from ieee754.fpcommon.fpbase import FPState, FPID
20 from ieee754.fpcommon.getop import FPADDBaseData
21 from ieee754.fpcommon.denorm import (FPSCData, FPAddDeNormMod)
22 from ieee754.fpmul.align import FPAlignModSingle
23 from ieee754.div_rem_sqrt_rsqrt.core import DivPipeCoreOperation as DP
24
25
26 class FPDIVSpecialCasesMod(Elaboratable):
27 """ special cases: NaNs, infs, zeros, denormalised
28 see "Special Operations"
29 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
30 """
31
32 def __init__(self, pspec):
33 self.pspec = pspec
34 self.i = self.ispec()
35 self.o = self.ospec()
36
37 def ispec(self):
38 return FPADDBaseData(self.pspec)
39
40 def ospec(self):
41 return FPSCData(self.pspec, False)
42
43 def setup(self, m, i):
44 """ links module to inputs and outputs
45 """
46 m.submodules.specialcases = self
47 m.d.comb += self.i.eq(i)
48
49 def process(self, i):
50 return self.o
51
52 def elaborate(self, platform):
53 m = Module()
54 comb = m.d.comb
55
56 # decode: XXX really should move to separate stage
57 a1 = FPNumBaseRecord(self.pspec.width, False, name="a1")
58 b1 = FPNumBaseRecord(self.pspec.width, False, name="b1")
59 m.submodules.sc_decode_a = a1 = FPNumDecode(None, a1)
60 m.submodules.sc_decode_b = b1 = FPNumDecode(None, b1)
61 comb += [a1.v.eq(self.i.a),
62 b1.v.eq(self.i.b),
63 self.o.a.eq(a1),
64 self.o.b.eq(b1)
65 ]
66
67 sabx = Signal(reset_less=True) # sign a xor b (sabx, get it?)
68 comb += sabx.eq(a1.s ^ b1.s)
69
70 abnan = Signal(reset_less=True)
71 comb += abnan.eq(a1.is_nan | b1.is_nan)
72
73 abinf = Signal(reset_less=True)
74 comb += abinf.eq(a1.is_inf & b1.is_inf)
75
76 with m.Switch(self.i.ctx.op):
77
78 with m.Case(int(DP.UDivRem)): # DIV
79
80 # if a is NaN or b is NaN return NaN
81 with m.If(abnan):
82 comb += self.o.out_do_z.eq(1)
83 comb += self.o.z.nan(0)
84
85 # if a is inf and b is Inf return NaN
86 with m.Elif(abinf):
87 comb += self.o.out_do_z.eq(1)
88 comb += self.o.z.nan(0)
89
90 # if a is inf return inf
91 with m.Elif(a1.is_inf):
92 comb += self.o.out_do_z.eq(1)
93 comb += self.o.z.inf(sabx)
94
95 # if b is inf return zero
96 with m.Elif(b1.is_inf):
97 comb += self.o.out_do_z.eq(1)
98 comb += self.o.z.zero(sabx)
99
100 # if a is zero return zero (or NaN if b is zero)
101 with m.Elif(a1.is_zero):
102 comb += self.o.out_do_z.eq(1)
103 comb += self.o.z.zero(sabx)
104 # b is zero return NaN
105 with m.If(b1.is_zero):
106 comb += self.o.z.nan(0)
107
108 # if b is zero return Inf
109 with m.Elif(b1.is_zero):
110 comb += self.o.out_do_z.eq(1)
111 comb += self.o.z.inf(sabx)
112
113 # Denormalised Number checks next, so pass a/b data through
114 with m.Else():
115 comb += self.o.out_do_z.eq(0)
116
117 with m.Case(int(DP.SqrtRem)): # SQRT
118
119 # if a is zero return zero
120 with m.If(a1.is_zero):
121 comb += self.o.out_do_z.eq(1)
122 comb += self.o.z.zero(a1.s)
123
124 # -ve number is NaN
125 with m.Elif(a1.s):
126 comb += self.o.out_do_z.eq(1)
127 comb += self.o.z.nan(0)
128
129 # if a is inf return inf
130 with m.Elif(a1.is_inf):
131 comb += self.o.out_do_z.eq(1)
132 comb += self.o.z.inf(sabx)
133
134 # if a is NaN return NaN
135 with m.Elif(a1.is_nan):
136 comb += self.o.out_do_z.eq(1)
137 comb += self.o.z.nan(0)
138
139 # Denormalised Number checks next, so pass a/b data through
140 with m.Else():
141 comb += self.o.out_do_z.eq(0)
142
143 with m.Case(int(DP.RSqrtRem)): # RSQRT
144
145 # if a is NaN return canonical NaN
146 with m.If(a1.is_nan):
147 comb += self.o.out_do_z.eq(1)
148 comb += self.o.z.nan(0)
149
150 # if a is +/- zero return +/- INF
151 with m.Elif(a1.is_zero):
152 comb += self.o.out_do_z.eq(1)
153 # this includes the "weird" case 1/sqrt(-0) == -Inf
154 comb += self.o.z.inf(a1.s)
155
156 # -ve number is canonical NaN
157 with m.Elif(a1.s):
158 comb += self.o.out_do_z.eq(1)
159 comb += self.o.z.nan(0)
160
161 # if a is inf return zero (-ve already excluded, above)
162 with m.Elif(a1.is_inf):
163 comb += self.o.out_do_z.eq(1)
164 comb += self.o.z.zero(0)
165
166 # Denormalised Number checks next, so pass a/b data through
167 with m.Else():
168 comb += self.o.out_do_z.eq(0)
169
170 comb += self.o.oz.eq(self.o.z.v)
171 comb += self.o.ctx.eq(self.i.ctx)
172
173 return m
174
175
176 class FPDIVSpecialCases(FPState):
177 """ special cases: NaNs, infs, zeros, denormalised
178 NOTE: some of these are unique to div. see "Special Operations"
179 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
180 """
181
182 def __init__(self, pspec):
183 FPState.__init__(self, "special_cases")
184 self.mod = FPDIVSpecialCasesMod(pspec)
185 self.out_z = self.mod.ospec()
186 self.out_do_z = Signal(reset_less=True)
187
188 def setup(self, m, i):
189 """ links module to inputs and outputs
190 """
191 self.mod.setup(m, i, self.out_do_z)
192 m.d.sync += self.out_z.v.eq(self.mod.out_z.v) # only take the output
193 m.d.sync += self.out_z.mid.eq(self.mod.o.mid) # (and mid)
194
195 def action(self, m):
196 self.idsync(m)
197 with m.If(self.out_do_z):
198 m.next = "put_z"
199 with m.Else():
200 m.next = "denormalise"
201
202
203 class FPDIVSpecialCasesDeNorm(FPState, SimpleHandshake):
204 """ special cases: NaNs, infs, zeros, denormalised
205 """
206
207 def __init__(self, pspec):
208 FPState.__init__(self, "special_cases")
209 self.pspec = pspec
210 SimpleHandshake.__init__(self, self) # pipe is its own stage
211 self.out = self.ospec()
212
213 def ispec(self):
214 return FPADDBaseData(self.pspec) # SpecialCases ispec
215
216 def ospec(self):
217 return FPSCData(self.pspec, False) # Align ospec
218
219 def setup(self, m, i):
220 """ links module to inputs and outputs
221 """
222 smod = FPDIVSpecialCasesMod(self.pspec)
223 dmod = FPAddDeNormMod(self.pspec, False)
224 amod = FPAlignModSingle(self.pspec, False)
225
226 chain = StageChain([smod, dmod, amod])
227 chain.setup(m, i)
228
229 # only needed for break-out (early-out)
230 # self.out_do_z = smod.o.out_do_z
231
232 self.o = amod.o
233
234 def process(self, i):
235 return self.o
236
237 def action(self, m):
238 # for break-out (early-out)
239 #with m.If(self.out_do_z):
240 # m.next = "put_z"
241 #with m.Else():
242 m.d.sync += self.out.eq(self.process(None))
243 m.next = "align"