cleanup and add a new common class, FPModBase
[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
6 from nmigen.cli import main, verilog
7 from math import log
8
9 from ieee754.fpcommon.modbase import FPModBase
10 from ieee754.fpcommon.fpbase import FPNumDecode
11 from nmutil.singlepipe import StageChain
12 from ieee754.pipeline import DynamicPipe
13
14 from ieee754.fpcommon.fpbase import FPNumBaseRecord
15 from ieee754.fpcommon.getop import FPADDBaseData
16 from ieee754.fpcommon.denorm import (FPSCData, FPAddDeNormMod)
17
18
19 class FPAddSpecialCasesMod(FPModBase):
20 """ special cases: NaNs, infs, zeros, denormalised
21 NOTE: some of these are unique to add. see "Special Operations"
22 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
23 """
24
25 def __init__(self, pspec):
26 super().__init__(pspec, "specialcases")
27
28 def ispec(self):
29 return FPADDBaseData(self.pspec)
30
31 def ospec(self):
32 return FPSCData(self.pspec, True)
33
34 def elaborate(self, platform):
35 m = Module()
36 comb = m.d.comb
37
38 # decode: XXX really should move to separate stage
39 width = self.pspec.width
40 a1 = FPNumBaseRecord(width)
41 b1 = FPNumBaseRecord(width)
42 m.submodules.sc_decode_a = a1 = FPNumDecode(None, a1)
43 m.submodules.sc_decode_b = b1 = FPNumDecode(None, b1)
44 comb += [a1.v.eq(self.i.a),
45 b1.v.eq(self.i.b),
46 self.o.a.eq(a1),
47 self.o.b.eq(b1)
48 ]
49
50 # temporaries used below
51 s_nomatch = Signal(reset_less=True)
52 m_match = Signal(reset_less=True)
53 e_match = Signal(reset_less=True)
54 aeqmb = Signal(reset_less=True)
55 abz = Signal(reset_less=True)
56 abnan = Signal(reset_less=True)
57 bexp128s = Signal(reset_less=True)
58
59 comb += s_nomatch.eq(a1.s != b1.s)
60 comb += m_match.eq(a1.m == b1.m)
61 comb += e_match.eq(a1.e == b1.e)
62 comb += aeqmb.eq(s_nomatch & m_match & e_match)
63 comb += abz.eq(a1.is_zero & b1.is_zero)
64 comb += abnan.eq(a1.is_nan | b1.is_nan)
65 comb += bexp128s.eq(b1.exp_128 & s_nomatch)
66
67 # default bypass
68 comb += self.o.out_do_z.eq(1)
69
70 # if a is NaN or b is NaN return NaN
71 with m.If(abnan):
72 comb += self.o.z.nan(0)
73
74 # XXX WEIRDNESS for FP16 non-canonical NaN handling
75 # under review
76
77 ## if a is zero and b is NaN return -b
78 #with m.If(a.is_zero & (a.s==0) & b.is_nan):
79 # comb += self.o.out_do_z.eq(1)
80 # comb += z.create(b.s, b.e, Cat(b.m[3:-2], ~b.m[0]))
81
82 ## if b is zero and a is NaN return -a
83 #with m.Elif(b.is_zero & (b.s==0) & a.is_nan):
84 # comb += self.o.out_do_z.eq(1)
85 # comb += z.create(a.s, a.e, Cat(a.m[3:-2], ~a.m[0]))
86
87 ## if a is -zero and b is NaN return -b
88 #with m.Elif(a.is_zero & (a.s==1) & b.is_nan):
89 # comb += self.o.out_do_z.eq(1)
90 # comb += z.create(a.s & b.s, b.e, Cat(b.m[3:-2], 1))
91
92 ## if b is -zero and a is NaN return -a
93 #with m.Elif(b.is_zero & (b.s==1) & a.is_nan):
94 # comb += self.o.out_do_z.eq(1)
95 # comb += z.create(a.s & b.s, a.e, Cat(a.m[3:-2], 1))
96
97 # if a is inf return inf (or NaN)
98 with m.Elif(a1.is_inf):
99 comb += self.o.z.inf(a1.s)
100 # if a is inf and signs don't match return NaN
101 with m.If(bexp128s):
102 comb += self.o.z.nan(0)
103
104 # if b is inf return inf
105 with m.Elif(b1.is_inf):
106 comb += self.o.z.inf(b1.s)
107
108 # if a is zero and b zero return signed-a/b
109 with m.Elif(abz):
110 comb += self.o.z.create(a1.s & b1.s, b1.e, b1.m[3:-1])
111
112 # if a is zero return b
113 with m.Elif(a1.is_zero):
114 comb += self.o.z.create(b1.s, b1.e, b1.m[3:-1])
115
116 # if b is zero return a
117 with m.Elif(b1.is_zero):
118 comb += self.o.z.create(a1.s, a1.e, a1.m[3:-1])
119
120 # if a equal to -b return zero (+ve zero)
121 with m.Elif(aeqmb):
122 comb += self.o.z.zero(0)
123
124 # Denormalised Number checks next, so pass a/b data through
125 with m.Else():
126 comb += self.o.out_do_z.eq(0)
127
128 comb += self.o.oz.eq(self.o.z.v)
129 comb += self.o.ctx.eq(self.i.ctx)
130
131 return m
132
133
134 class FPAddSpecialCasesDeNorm(DynamicPipe):
135 """ special cases: NaNs, infs, zeros, denormalised
136 NOTE: some of these are unique to add. see "Special Operations"
137 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
138 """
139
140 def __init__(self, pspec):
141 self.pspec = pspec
142 super().__init__(pspec)
143
144 def ispec(self):
145 return FPADDBaseData(self.pspec) # SC ispec
146
147 def ospec(self):
148 return FPSCData(self.pspec, True) # DeNorm
149
150 def setup(self, m, i):
151 """ links module to inputs and outputs
152 """
153 smod = FPAddSpecialCasesMod(self.pspec)
154 dmod = FPAddDeNormMod(self.pspec, True)
155
156 chain = StageChain([smod, dmod])
157 chain.setup(m, i)
158
159 # only needed for break-out (early-out)
160 # self.out_do_z = smod.o.out_do_z
161
162 self.o = dmod.o
163
164 def process(self, i):
165 return self.o
166