31a490685c8ed902852186e5569110354f9b9427
[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 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 FPAddSpecialCasesMod(PipeModBase):
16 """ special cases: NaNs, infs, zeros, denormalised
17 NOTE: some of these are unique to add. see "Special Operations"
18 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
19 """
20
21 def __init__(self, pspec):
22 super().__init__(pspec, "specialcases")
23
24 def ispec(self):
25 return FPBaseData(self.pspec)
26
27 def ospec(self):
28 return FPSCData(self.pspec, True)
29
30 def elaborate(self, platform):
31 m = Module()
32 comb = m.d.comb
33
34 # decode: XXX really should move to separate stage
35 width = self.pspec.width
36 a1 = FPNumBaseRecord(width)
37 b1 = FPNumBaseRecord(width)
38 m.submodules.sc_decode_a = a1 = FPNumDecode(None, a1)
39 m.submodules.sc_decode_b = b1 = FPNumDecode(None, b1)
40 comb += [a1.v.eq(self.i.a),
41 b1.v.eq(self.i.b),
42 self.o.a.eq(a1),
43 self.o.b.eq(b1)
44 ]
45
46 zero_sign_array = FPRoundingMode.make_array(FPRoundingMode.zero_sign)
47
48 # temporaries used below
49 s_nomatch = Signal(reset_less=True)
50 s_match = Signal(reset_less=True)
51 m_match = Signal(reset_less=True)
52 e_match = Signal(reset_less=True)
53 absa = Signal(reset_less=True) # a1.s & b1.s
54 t_aeqmb = Signal(reset_less=True)
55 t_a1inf = Signal(reset_less=True)
56 t_b1inf = Signal(reset_less=True)
57 t_a1zero = Signal(reset_less=True)
58 t_b1zero = Signal(reset_less=True)
59 t_abz = Signal(reset_less=True)
60 t_abnan = Signal(reset_less=True)
61 bexp128s = Signal(reset_less=True)
62 t_special = Signal(reset_less=True)
63
64 comb += s_nomatch.eq(a1.s != b1.s)
65 comb += s_match.eq(a1.s == b1.s)
66 comb += m_match.eq(a1.m == b1.m)
67 comb += e_match.eq(a1.e == b1.e)
68
69 # logic-chain (matches comments, below) gives an if-elif-elif-elif...
70 comb += t_abnan.eq(a1.is_nan | b1.is_nan)
71 comb += t_a1inf.eq(a1.is_inf)
72 comb += t_b1inf.eq(b1.is_inf)
73 comb += t_abz.eq(a1.is_zero & b1.is_zero)
74 comb += t_a1zero.eq(a1.is_zero)
75 comb += t_b1zero.eq(b1.is_zero)
76 comb += t_aeqmb.eq(s_nomatch & m_match & e_match)
77 comb += t_special.eq(Cat(t_aeqmb, t_b1zero, t_a1zero, t_abz,
78 t_b1inf, t_a1inf, t_abnan).bool())
79
80 comb += absa.eq(a1.s & b1.s)
81 comb += bexp128s.eq(b1.exp_128 & s_nomatch)
82
83 # prepare inf/zero/nans
84 z_zero = FPNumBaseRecord(width, False, name="z_zero")
85 z_default_zero = FPNumBaseRecord(width, False, name="z_default_zero")
86 z_default_nan = FPNumBaseRecord(width, False, name="z_default_nan")
87 z_quieted_a = FPNumBaseRecord(width, False, name="z_quieted_a")
88 z_quieted_b = FPNumBaseRecord(width, False, name="z_quieted_b")
89 z_infa = FPNumBaseRecord(width, False, name="z_infa")
90 z_infb = FPNumBaseRecord(width, False, name="z_infb")
91 comb += z_zero.zero(0)
92 comb += z_default_zero.zero(zero_sign_array[self.i.rm])
93 comb += z_default_nan.nan(0)
94 comb += z_quieted_a.quieted_nan(a1)
95 comb += z_quieted_b.quieted_nan(b1)
96 comb += z_infa.inf(a1.s)
97 comb += z_infb.inf(b1.s)
98
99 # any special-cases it's a "special".
100 comb += self.o.out_do_z.eq(t_special)
101
102 # this is the logic-decision-making for special-cases:
103 # if a is NaN or b is NaN return NaN
104 # if a is NaN return quieted_nan(a)
105 # else return quieted_nan(b)
106 # elif a is inf return inf (or NaN)
107 # if a is inf and signs don't match return NaN
108 # else return inf(a)
109 # elif b is inf return inf(b)
110 # elif a is zero and b zero with same sign return a
111 # elif a equal to -b return zero (sign determined by rounding-mode)
112 # elif a is zero return b
113 # elif b is zero return a
114
115 # XXX *sigh* there are better ways to do this...
116 # one of them: use a priority-picker!
117 # in reverse-order, accumulate Muxing
118
119 oz = 0
120 oz = Mux(t_b1zero, a1.v, oz)
121 oz = Mux(t_a1zero, b1.v, oz)
122 oz = Mux(t_aeqmb, z_default_zero.v, oz)
123 oz = Mux(t_abz & s_match, a1.v, oz)
124 oz = Mux(t_b1inf, z_infb.v, oz)
125 oz = Mux(t_a1inf, Mux(bexp128s, z_default_nan.v, z_infa.v), oz)
126 oz = Mux(t_abnan, Mux(a1.is_nan, z_quieted_a.v, z_quieted_b.v), oz)
127
128 comb += self.o.oz.eq(oz)
129
130 comb += self.o.ctx.eq(self.i.ctx)
131
132 comb += self.o.rm.eq(self.i.rm)
133
134 return m
135
136
137 class FPAddSpecialCasesDeNorm(PipeModBaseChain):
138 """ special cases chain
139 """
140
141 def get_chain(self):
142 """ links module to inputs and outputs
143 """
144 smod = FPAddSpecialCasesMod(self.pspec)
145 dmod = FPAddDeNormMod(self.pspec, True)
146
147 return [smod, dmod]