run tests in parallel
[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, Mux
13 from nmigen.cli import main, verilog
14 from math import log
15
16 from nmutil.pipemodbase import PipeModBase, PipeModBaseChain
17 from ieee754.fpcommon.fpbase import FPNumDecode, FPNumBaseRecord
18 from ieee754.fpcommon.basedata import FPBaseData
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(PipeModBase):
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 FPBaseData(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 width = self.pspec.width
45 a1 = FPNumBaseRecord(width, False, name="a1")
46 b1 = FPNumBaseRecord(width, False, name="b1")
47 m.submodules.sc_decode_a = a1 = FPNumDecode(None, a1)
48 m.submodules.sc_decode_b = b1 = FPNumDecode(None, b1)
49 comb += [a1.v.eq(self.i.a),
50 b1.v.eq(self.i.b),
51 self.o.a.eq(a1),
52 self.o.b.eq(b1)
53 ]
54
55 # temporaries (used below)
56 sabx = Signal(reset_less=True) # sign a xor b (sabx, get it?)
57 t_abnan = Signal(reset_less=True)
58 t_abinf = Signal(reset_less=True)
59 t_a1inf = Signal(reset_less=True)
60 t_b1inf = Signal(reset_less=True)
61 t_a1nan = Signal(reset_less=True)
62 t_a1zero = Signal(reset_less=True)
63 t_b1zero = Signal(reset_less=True)
64 t_abz = Signal(reset_less=True)
65 t_special_div = Signal(reset_less=True)
66 t_special_sqrt = Signal(reset_less=True) # sqrt/rsqrt
67
68 comb += sabx.eq(a1.s ^ b1.s)
69 comb += t_abnan.eq(a1.is_nan | b1.is_nan)
70 comb += t_abinf.eq(a1.is_inf & b1.is_inf)
71 comb += t_a1inf.eq(a1.is_inf)
72 comb += t_b1inf.eq(b1.is_inf)
73 comb += t_a1nan.eq(a1.is_nan)
74 comb += t_abz.eq(a1.is_zero & b1.is_zero)
75 comb += t_a1zero.eq(a1.is_zero)
76 comb += t_b1zero.eq(b1.is_zero)
77
78 # prepare inf/zero/nans
79 z_zero = FPNumBaseRecord(width, False, name="z_zero")
80 z_zeroa = FPNumBaseRecord(width, False, name="z_zeroa")
81 z_zeroab = FPNumBaseRecord(width, False, name="z_zeroab")
82 z_nan = FPNumBaseRecord(width, False, name="z_nan")
83 z_infa = FPNumBaseRecord(width, False, name="z_infa")
84 z_infb = FPNumBaseRecord(width, False, name="z_infb")
85 z_infab = FPNumBaseRecord(width, False, name="z_infab")
86 comb += z_zero.zero(0)
87 comb += z_zeroa.zero(a1.s)
88 comb += z_zeroab.zero(sabx)
89 comb += z_nan.nan(0)
90 comb += z_infa.inf(a1.s)
91 comb += z_infb.inf(b1.s)
92 comb += z_infab.inf(sabx)
93
94 comb += t_special_div.eq(Cat(t_b1zero, t_a1zero, t_b1inf, t_a1inf,
95 t_abinf, t_abnan).bool())
96 comb += t_special_sqrt.eq(Cat(t_a1zero, a1.s, t_a1inf,
97 t_a1nan).bool())
98
99 # select one of 3 different sets of specialcases (DIV, SQRT, RSQRT)
100 with m.Switch(self.i.ctx.op):
101
102 ########## DIV ############
103 with m.Case(int(DP.UDivRem)):
104
105 # any special cases?
106 comb += self.o.out_do_z.eq(t_special_div)
107
108 # if a is NaN or b is NaN return NaN
109 # if a is inf and b is Inf return NaN
110 # if a is inf return inf
111 # if b is inf return zero
112 # if a is zero return zero (or NaN if b is zero)
113 # b is zero return NaN
114 # if b is zero return Inf
115
116 # sigh inverse order on the above, Mux-cascade
117 oz = 0
118 oz = Mux(t_b1zero, z_infab.v, oz)
119 oz = Mux(t_a1zero, Mux(t_b1zero, z_nan.v, z_zeroab.v), oz)
120 oz = Mux(t_b1inf, z_zeroab.v, oz)
121 oz = Mux(t_a1inf, z_infab.v, oz)
122 oz = Mux(t_abinf, z_nan.v, oz)
123 oz = Mux(t_abnan, z_nan.v, oz)
124
125 comb += self.o.oz.eq(oz)
126
127 ########## SQRT ############
128 with m.Case(int(DP.SqrtRem)):
129
130 # any special cases?
131 comb += self.o.out_do_z.eq(t_special_sqrt)
132
133 # if a is zero return zero
134 # -ve number is NaN
135 # if a is inf return inf
136 # if a is NaN return NaN
137
138 # inverse-order (mux-tree)
139 oz = 0
140 oz = Mux(t_a1nan, z_nan.v, oz)
141 oz = Mux(t_a1inf, z_infab.v, oz)
142 oz = Mux(a1.s, z_nan.v, oz)
143 oz = Mux(t_a1zero, z_zeroa.v, oz)
144
145 comb += self.o.oz.eq(oz)
146
147 ########## RSQRT ############
148 with m.Case(int(DP.RSqrtRem)):
149
150 # any special cases?
151 comb += self.o.out_do_z.eq(t_special_sqrt)
152
153 # if a is NaN return canonical NaN
154 # if a is +/- zero return +/- INF
155 # this includes the "weird" case 1/sqrt(-0) == -Inf
156 # -ve number is canonical NaN
157 # if a is inf return zero (-ve already excluded, above)
158
159 # inverse-order (mux-tree)
160 oz = 0
161 oz = Mux(t_a1inf, z_zero.v, oz)
162 oz = Mux(a1.s, z_nan.v, oz)
163 oz = Mux(t_a1zero, z_infa.v, oz)
164 oz = Mux(t_a1nan, z_nan.v, oz)
165
166 comb += self.o.oz.eq(oz)
167
168 # pass through context
169 comb += self.o.ctx.eq(self.i.ctx)
170
171 return m
172
173
174 class FPDIVSpecialCasesDeNorm(PipeModBaseChain):
175 """ special cases: NaNs, infs, zeros, denormalised
176 """
177
178 def get_chain(self):
179 """ links module to inputs and outputs
180 """
181 smod = FPDIVSpecialCasesMod(self.pspec)
182 dmod = FPAddDeNormMod(self.pspec, False)
183 amod = FPAlignModSingle(self.pspec, False)
184
185 return [smod, dmod, amod]