tidyup
[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.If(self.i.ctx.op == 0): # DIV
77
78 # if a is NaN or b is NaN return NaN
79 with m.If(abnan):
80 comb += self.o.out_do_z.eq(1)
81 comb += self.o.z.nan(0)
82
83 # if a is inf and b is Inf return NaN
84 with m.Elif(abinf):
85 comb += self.o.out_do_z.eq(1)
86 comb += self.o.z.nan(0)
87
88 # if a is inf return inf
89 with m.Elif(a1.is_inf):
90 comb += self.o.out_do_z.eq(1)
91 comb += self.o.z.inf(sabx)
92
93 # if b is inf return zero
94 with m.Elif(b1.is_inf):
95 comb += self.o.out_do_z.eq(1)
96 comb += self.o.z.zero(sabx)
97
98 # if a is zero return zero (or NaN if b is zero)
99 with m.Elif(a1.is_zero):
100 comb += self.o.out_do_z.eq(1)
101 comb += self.o.z.zero(sabx)
102 # b is zero return NaN
103 with m.If(b1.is_zero):
104 comb += self.o.z.nan(0)
105
106 # if b is zero return Inf
107 with m.Elif(b1.is_zero):
108 comb += self.o.out_do_z.eq(1)
109 comb += self.o.z.inf(sabx)
110
111 # Denormalised Number checks next, so pass a/b data through
112 with m.Else():
113 comb += self.o.out_do_z.eq(0)
114
115 with m.If(self.i.ctx.op == 1): # SQRT
116
117 # if a is zero return zero
118 with m.If(a1.is_zero):
119 comb += self.o.out_do_z.eq(1)
120 comb += self.o.z.zero(a1.s)
121
122 # -ve number is NaN
123 with m.Elif(a1.s):
124 comb += self.o.out_do_z.eq(1)
125 comb += self.o.z.nan(0)
126
127 # if a is inf return inf
128 with m.Elif(a1.is_inf):
129 comb += self.o.out_do_z.eq(1)
130 comb += self.o.z.inf(sabx)
131
132 # if a is NaN return NaN
133 with m.Elif(a1.is_nan):
134 comb += self.o.out_do_z.eq(1)
135 comb += self.o.z.nan(0)
136
137 # Denormalised Number checks next, so pass a/b data through
138 with m.Else():
139 comb += self.o.out_do_z.eq(0)
140
141 with m.If(self.i.ctx.op == 2): # RSQRT
142
143 # if a is NaN return canonical NaN
144 with m.If(a1.is_nan):
145 comb += self.o.out_do_z.eq(1)
146 comb += self.o.z.nan(0)
147
148 # if a is +/- zero return +/- INF
149 with m.Elif(a1.is_zero):
150 comb += self.o.out_do_z.eq(1)
151 # this includes the "weird" case 1/sqrt(-0) == -Inf
152 comb += self.o.z.inf(a1.s)
153
154 # -ve number is canonical NaN
155 with m.Elif(a1.s):
156 comb += self.o.out_do_z.eq(1)
157 comb += self.o.z.nan(0)
158
159 # if a is inf return zero (-ve already excluded, above)
160 with m.Elif(a1.is_inf):
161 comb += self.o.out_do_z.eq(1)
162 comb += self.o.z.zero(0)
163
164 # Denormalised Number checks next, so pass a/b data through
165 with m.Else():
166 comb += self.o.out_do_z.eq(0)
167
168 comb += self.o.oz.eq(self.o.z.v)
169 comb += self.o.ctx.eq(self.i.ctx)
170
171 return m
172
173
174 class FPDIVSpecialCases(FPState):
175 """ special cases: NaNs, infs, zeros, denormalised
176 NOTE: some of these are unique to div. see "Special Operations"
177 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
178 """
179
180 def __init__(self, pspec):
181 FPState.__init__(self, "special_cases")
182 self.mod = FPDIVSpecialCasesMod(pspec)
183 self.out_z = self.mod.ospec()
184 self.out_do_z = Signal(reset_less=True)
185
186 def setup(self, m, i):
187 """ links module to inputs and outputs
188 """
189 self.mod.setup(m, i, self.out_do_z)
190 m.d.sync += self.out_z.v.eq(self.mod.out_z.v) # only take the output
191 m.d.sync += self.out_z.mid.eq(self.mod.o.mid) # (and mid)
192
193 def action(self, m):
194 self.idsync(m)
195 with m.If(self.out_do_z):
196 m.next = "put_z"
197 with m.Else():
198 m.next = "denormalise"
199
200
201 class FPDIVSpecialCasesDeNorm(FPState, SimpleHandshake):
202 """ special cases: NaNs, infs, zeros, denormalised
203 """
204
205 def __init__(self, pspec):
206 FPState.__init__(self, "special_cases")
207 self.pspec = pspec
208 SimpleHandshake.__init__(self, self) # pipe is its own stage
209 self.out = self.ospec()
210
211 def ispec(self):
212 return FPADDBaseData(self.pspec) # SpecialCases ispec
213
214 def ospec(self):
215 return FPSCData(self.pspec, False) # Align ospec
216
217 def setup(self, m, i):
218 """ links module to inputs and outputs
219 """
220 smod = FPDIVSpecialCasesMod(self.pspec)
221 dmod = FPAddDeNormMod(self.pspec, False)
222 amod = FPAlignModSingle(self.pspec, False)
223
224 chain = StageChain([smod, dmod, amod])
225 chain.setup(m, i)
226
227 # only needed for break-out (early-out)
228 # self.out_do_z = smod.o.out_do_z
229
230 self.o = amod.o
231
232 def process(self, i):
233 return self.o
234
235 def action(self, m):
236 # for break-out (early-out)
237 #with m.If(self.out_do_z):
238 # m.next = "put_z"
239 #with m.Else():
240 m.d.sync += self.out.eq(self.process(None))
241 m.next = "align"