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