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