switch to exact version of cython
[ieee754fpu.git] / src / ieee754 / fpadd / specialcases.py
1 # IEEE Floating Point Adder (Single Precision)
2 # Copyright (C) Jonathan P Dawson 2013
3 # 2013-12-12
4
5 from nmigen import Module, Signal, Cat, Mux
6
7 from nmutil.pipemodbase import PipeModBase, PipeModBaseChain
8 from ieee754.fpcommon.fpbase import FPFormat, FPNumDecode, FPRoundingMode
9
10 from ieee754.fpcommon.fpbase import FPNumBaseRecord
11 from ieee754.fpcommon.basedata import FPBaseData
12 from ieee754.fpcommon.denorm import (FPSCData, FPAddDeNormMod)
13
14
15 class FPAddInputData(FPBaseData):
16 def __init__(self, pspec):
17 super().__init__(pspec)
18 self.is_sub = Signal(reset=False)
19
20 def eq(self, i):
21 ret = super().eq(i)
22 ret.append(self.is_sub.eq(i.is_sub))
23 return ret
24
25 def __iter__(self):
26 yield from super().__iter__()
27 yield self.is_sub
28
29 def ports(self):
30 return list(self)
31
32
33 class FPAddSpecialCasesMod(PipeModBase):
34 """ special cases: NaNs, infs, zeros, denormalised
35 NOTE: some of these are unique to add. see "Special Operations"
36 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
37 """
38
39 def __init__(self, pspec):
40 super().__init__(pspec, "specialcases")
41
42 def ispec(self):
43 return FPAddInputData(self.pspec)
44
45 def ospec(self):
46 return FPSCData(self.pspec, True)
47
48 def elaborate(self, platform):
49 m = Module()
50 comb = m.d.comb
51
52 # decode: XXX really should move to separate stage
53 width = self.pspec.width
54 a1 = FPNumBaseRecord(width)
55 b1 = FPNumBaseRecord(width)
56 m.submodules.sc_decode_a = a1 = FPNumDecode(None, a1)
57 m.submodules.sc_decode_b = b1 = FPNumDecode(None, b1)
58 flip_b_sign = Signal()
59 b_is_nan = Signal()
60 comb += [
61 b_is_nan.eq(FPFormat.standard(width).is_nan(self.i.b)),
62 flip_b_sign.eq(self.i.is_sub & ~b_is_nan),
63 a1.v.eq(self.i.a),
64 b1.v.eq(self.i.b ^ (flip_b_sign << (width - 1))),
65 self.o.a.eq(a1),
66 self.o.b.eq(b1)
67 ]
68
69 zero_sign_array = FPRoundingMode.make_array(FPRoundingMode.zero_sign)
70
71 # temporaries used below
72 s_nomatch = Signal(reset_less=True)
73 s_match = Signal(reset_less=True)
74 m_match = Signal(reset_less=True)
75 e_match = Signal(reset_less=True)
76 absa = Signal(reset_less=True) # a1.s & b1.s
77 t_aeqmb = Signal(reset_less=True)
78 t_a1inf = Signal(reset_less=True)
79 t_b1inf = Signal(reset_less=True)
80 t_a1zero = Signal(reset_less=True)
81 t_b1zero = Signal(reset_less=True)
82 t_abz = Signal(reset_less=True)
83 t_abnan = Signal(reset_less=True)
84 bexp128s = Signal(reset_less=True)
85 t_special = Signal(reset_less=True)
86
87 comb += s_nomatch.eq(a1.s != b1.s)
88 comb += s_match.eq(a1.s == b1.s)
89 comb += m_match.eq(a1.m == b1.m)
90 comb += e_match.eq(a1.e == b1.e)
91
92 # logic-chain (matches comments, below) gives an if-elif-elif-elif...
93 comb += t_abnan.eq(a1.is_nan | b1.is_nan)
94 comb += t_a1inf.eq(a1.is_inf)
95 comb += t_b1inf.eq(b1.is_inf)
96 comb += t_abz.eq(a1.is_zero & b1.is_zero)
97 comb += t_a1zero.eq(a1.is_zero)
98 comb += t_b1zero.eq(b1.is_zero)
99 comb += t_aeqmb.eq(s_nomatch & m_match & e_match)
100 comb += t_special.eq(Cat(t_aeqmb, t_b1zero, t_a1zero, t_abz,
101 t_b1inf, t_a1inf, t_abnan).bool())
102
103 comb += absa.eq(a1.s & b1.s)
104 comb += bexp128s.eq(b1.exp_128 & s_nomatch)
105
106 # prepare inf/zero/nans
107 z_zero = FPNumBaseRecord(width, False, name="z_zero")
108 z_default_zero = FPNumBaseRecord(width, False, name="z_default_zero")
109 z_default_nan = FPNumBaseRecord(width, False, name="z_default_nan")
110 z_quieted_a = FPNumBaseRecord(width, False, name="z_quieted_a")
111 z_quieted_b = FPNumBaseRecord(width, False, name="z_quieted_b")
112 z_infa = FPNumBaseRecord(width, False, name="z_infa")
113 z_infb = FPNumBaseRecord(width, False, name="z_infb")
114 comb += z_zero.zero(0)
115 comb += z_default_zero.zero(zero_sign_array[self.i.rm])
116 comb += z_default_nan.nan(0)
117 comb += z_quieted_a.quieted_nan(a1)
118 comb += z_quieted_b.quieted_nan(b1)
119 comb += z_infa.inf(a1.s)
120 comb += z_infb.inf(b1.s)
121
122 # any special-cases it's a "special".
123 comb += self.o.out_do_z.eq(t_special)
124
125 # this is the logic-decision-making for special-cases:
126 # if a is NaN or b is NaN return NaN
127 # if a is NaN return quieted_nan(a)
128 # else return quieted_nan(b)
129 # elif a is inf return inf (or NaN)
130 # if a is inf and signs don't match return NaN
131 # else return inf(a)
132 # elif b is inf return inf(b)
133 # elif a is zero and b zero with same sign return a
134 # elif a equal to -b return zero (sign determined by rounding-mode)
135 # elif a is zero return b
136 # elif b is zero return a
137
138 # XXX *sigh* there are better ways to do this...
139 # one of them: use a priority-picker!
140 # in reverse-order, accumulate Muxing
141
142 oz = 0
143 oz = Mux(t_b1zero, a1.v, oz)
144 oz = Mux(t_a1zero, b1.v, oz)
145 oz = Mux(t_aeqmb, z_default_zero.v, oz)
146 oz = Mux(t_abz & s_match, a1.v, oz)
147 oz = Mux(t_b1inf, z_infb.v, oz)
148 oz = Mux(t_a1inf, Mux(bexp128s, z_default_nan.v, z_infa.v), oz)
149 oz = Mux(t_abnan, Mux(a1.is_nan, z_quieted_a.v, z_quieted_b.v), oz)
150
151 comb += self.o.oz.eq(oz)
152
153 comb += self.o.ctx.eq(self.i.ctx)
154
155 comb += self.o.rm.eq(self.i.rm)
156
157 return m
158
159
160 class FPAddSpecialCasesDeNorm(PipeModBaseChain):
161 """ special cases chain
162 """
163
164 def get_chain(self):
165 """ links module to inputs and outputs
166 """
167 smod = FPAddSpecialCasesMod(self.pspec)
168 dmod = FPAddDeNormMod(self.pspec, True)
169
170 return [smod, dmod]