move FPModBase and FPModBaseChain to nmutil
[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
13 from nmigen.cli import main, verilog
14 from math import log
15
16 from nmutil.pipemodbase import FPModBase, FPModBaseChain
17 from ieee754.fpcommon.fpbase import FPNumDecode, FPNumBaseRecord
18 from ieee754.fpcommon.getop import FPADDBaseData
19 from ieee754.fpcommon.denorm import (FPSCData, FPAddDeNormMod)
20 from ieee754.fpmul.align import FPAlignModSingle
21 from ieee754.div_rem_sqrt_rsqrt.core import DivPipeCoreOperation as DP
22
23
24 class FPDIVSpecialCasesMod(FPModBase):
25 """ special cases: NaNs, infs, zeros, denormalised
26 see "Special Operations"
27 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
28 """
29
30 def __init__(self, pspec):
31 super().__init__(pspec, "specialcases")
32
33 def ispec(self):
34 return FPADDBaseData(self.pspec)
35
36 def ospec(self):
37 return FPSCData(self.pspec, False)
38
39 def elaborate(self, platform):
40 m = Module()
41 comb = m.d.comb
42
43 # decode: XXX really should move to separate stage
44 a1 = FPNumBaseRecord(self.pspec.width, False, name="a1")
45 b1 = FPNumBaseRecord(self.pspec.width, False, name="b1")
46 m.submodules.sc_decode_a = a1 = FPNumDecode(None, a1)
47 m.submodules.sc_decode_b = b1 = FPNumDecode(None, b1)
48 comb += [a1.v.eq(self.i.a),
49 b1.v.eq(self.i.b),
50 self.o.a.eq(a1),
51 self.o.b.eq(b1)
52 ]
53
54 # temporaries (used below)
55 sabx = Signal(reset_less=True) # sign a xor b (sabx, get it?)
56 abnan = Signal(reset_less=True)
57 abinf = Signal(reset_less=True)
58
59 comb += sabx.eq(a1.s ^ b1.s)
60 comb += abnan.eq(a1.is_nan | b1.is_nan)
61 comb += abinf.eq(a1.is_inf & b1.is_inf)
62
63 # default (overridden if needed)
64 comb += self.o.out_do_z.eq(1)
65
66 # select one of 3 different sets of specialcases (DIV, SQRT, RSQRT)
67 with m.Switch(self.i.ctx.op):
68
69 with m.Case(int(DP.UDivRem)): # DIV
70
71 # if a is NaN or b is NaN return NaN
72 with m.If(abnan):
73 comb += self.o.z.nan(0)
74
75 # if a is inf and b is Inf return NaN
76 with m.Elif(abinf):
77 comb += self.o.z.nan(0)
78
79 # if a is inf return inf
80 with m.Elif(a1.is_inf):
81 comb += self.o.z.inf(sabx)
82
83 # if b is inf return zero
84 with m.Elif(b1.is_inf):
85 comb += self.o.z.zero(sabx)
86
87 # if a is zero return zero (or NaN if b is zero)
88 with m.Elif(a1.is_zero):
89 comb += self.o.z.zero(sabx)
90 # b is zero return NaN
91 with m.If(b1.is_zero):
92 comb += self.o.z.nan(0)
93
94 # if b is zero return Inf
95 with m.Elif(b1.is_zero):
96 comb += self.o.z.inf(sabx)
97
98 # Denormalised Number checks next, so pass a/b data through
99 with m.Else():
100 comb += self.o.out_do_z.eq(0)
101
102 with m.Case(int(DP.SqrtRem)): # SQRT
103
104 # if a is zero return zero
105 with m.If(a1.is_zero):
106 comb += self.o.z.zero(a1.s)
107
108 # -ve number is NaN
109 with m.Elif(a1.s):
110 comb += self.o.z.nan(0)
111
112 # if a is inf return inf
113 with m.Elif(a1.is_inf):
114 comb += self.o.z.inf(sabx)
115
116 # if a is NaN return NaN
117 with m.Elif(a1.is_nan):
118 comb += self.o.z.nan(0)
119
120 # Denormalised Number checks next, so pass a/b data through
121 with m.Else():
122 comb += self.o.out_do_z.eq(0)
123
124 with m.Case(int(DP.RSqrtRem)): # RSQRT
125
126 # if a is NaN return canonical NaN
127 with m.If(a1.is_nan):
128 comb += self.o.z.nan(0)
129
130 # if a is +/- zero return +/- INF
131 with m.Elif(a1.is_zero):
132 # this includes the "weird" case 1/sqrt(-0) == -Inf
133 comb += self.o.z.inf(a1.s)
134
135 # -ve number is canonical NaN
136 with m.Elif(a1.s):
137 comb += self.o.z.nan(0)
138
139 # if a is inf return zero (-ve already excluded, above)
140 with m.Elif(a1.is_inf):
141 comb += self.o.z.zero(0)
142
143 # Denormalised Number checks next, so pass a/b data through
144 with m.Else():
145 comb += self.o.out_do_z.eq(0)
146
147 comb += self.o.oz.eq(self.o.z.v)
148 comb += self.o.ctx.eq(self.i.ctx)
149
150 return m
151
152
153 class FPDIVSpecialCasesDeNorm(FPModBaseChain):
154 """ special cases: NaNs, infs, zeros, denormalised
155 """
156
157 def get_chain(self):
158 """ links module to inputs and outputs
159 """
160 smod = FPDIVSpecialCasesMod(self.pspec)
161 dmod = FPAddDeNormMod(self.pspec, False)
162 amod = FPAlignModSingle(self.pspec, False)
163
164 return [smod, dmod, amod]