feed87f7bf4e40186638bf0dbbc5a5975dc1d8b7
[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 StageChain
18
19 from ieee754.pipeline import DynamicPipe
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 FPDIVSpecialCasesDeNorm(DynamicPipe):
178 """ special cases: NaNs, infs, zeros, denormalised
179 """
180
181 def __init__(self, pspec):
182 self.pspec = pspec
183 super().__init__(pspec)
184 self.out = self.ospec()
185
186 def ispec(self):
187 return FPADDBaseData(self.pspec) # SpecialCases ispec
188
189 def ospec(self):
190 return FPSCData(self.pspec, False) # Align ospec
191
192 def setup(self, m, i):
193 """ links module to inputs and outputs
194 """
195 smod = FPDIVSpecialCasesMod(self.pspec)
196 dmod = FPAddDeNormMod(self.pspec, False)
197 amod = FPAlignModSingle(self.pspec, False)
198
199 chain = StageChain([smod, dmod, amod])
200 chain.setup(m, i)
201
202 # only needed for break-out (early-out)
203 # self.out_do_z = smod.o.out_do_z
204
205 self.o = amod.o
206
207 def process(self, i):
208 return self.o