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