e87212d1c7ef86dd2525a03b44c0934dd695adac
[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 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 aeqmb = Signal(reset_less=True)
53 abz = Signal(reset_less=True)
54 abnan = Signal(reset_less=True)
55 bexp128s = Signal(reset_less=True)
56
57 comb += s_nomatch.eq(a1.s != b1.s)
58 comb += m_match.eq(a1.m == b1.m)
59 comb += e_match.eq(a1.e == b1.e)
60 comb += aeqmb.eq(s_nomatch & m_match & e_match)
61 comb += abz.eq(a1.is_zero & b1.is_zero)
62 comb += abnan.eq(a1.is_nan | b1.is_nan)
63 comb += bexp128s.eq(b1.exp_128 & s_nomatch)
64
65 # default bypass
66 comb += self.o.out_do_z.eq(1)
67
68 # if a is NaN or b is NaN return NaN
69 with m.If(abnan):
70 comb += self.o.z.nan(0)
71
72 # XXX WEIRDNESS for FP16 non-canonical NaN handling
73 # under review
74
75 ## if a is zero and b is NaN return -b
76 #with m.If(a.is_zero & (a.s==0) & b.is_nan):
77 # comb += self.o.out_do_z.eq(1)
78 # comb += z.create(b.s, b.e, Cat(b.m[3:-2], ~b.m[0]))
79
80 ## if b is zero and a is NaN return -a
81 #with m.Elif(b.is_zero & (b.s==0) & a.is_nan):
82 # comb += self.o.out_do_z.eq(1)
83 # comb += z.create(a.s, a.e, Cat(a.m[3:-2], ~a.m[0]))
84
85 ## if a is -zero and b is NaN return -b
86 #with m.Elif(a.is_zero & (a.s==1) & b.is_nan):
87 # comb += self.o.out_do_z.eq(1)
88 # comb += z.create(a.s & b.s, b.e, Cat(b.m[3:-2], 1))
89
90 ## if b is -zero and a is NaN return -a
91 #with m.Elif(b.is_zero & (b.s==1) & a.is_nan):
92 # comb += self.o.out_do_z.eq(1)
93 # comb += z.create(a.s & b.s, a.e, Cat(a.m[3:-2], 1))
94
95 # if a is inf return inf (or NaN)
96 with m.Elif(a1.is_inf):
97 comb += self.o.z.inf(a1.s)
98 # if a is inf and signs don't match return NaN
99 with m.If(bexp128s):
100 comb += self.o.z.nan(0)
101
102 # if b is inf return inf
103 with m.Elif(b1.is_inf):
104 comb += self.o.z.inf(b1.s)
105
106 # if a is zero and b zero return signed-a/b
107 with m.Elif(abz):
108 comb += self.o.z.create(a1.s & b1.s, b1.e, b1.m[3:-1])
109
110 # if a is zero return b
111 with m.Elif(a1.is_zero):
112 comb += self.o.z.create(b1.s, b1.e, b1.m[3:-1])
113
114 # if b is zero return a
115 with m.Elif(b1.is_zero):
116 comb += self.o.z.create(a1.s, a1.e, a1.m[3:-1])
117
118 # if a equal to -b return zero (+ve zero)
119 with m.Elif(aeqmb):
120 comb += self.o.z.zero(0)
121
122 # Denormalised Number checks next, so pass a/b data through
123 with m.Else():
124 comb += self.o.out_do_z.eq(0)
125
126 comb += self.o.oz.eq(self.o.z.v)
127 comb += self.o.ctx.eq(self.i.ctx)
128
129 return m
130
131
132 class FPAddSpecialCasesDeNorm(PipeModBaseChain):
133 """ special cases chain
134 """
135
136 def get_chain(self):
137 """ links module to inputs and outputs
138 """
139 smod = FPAddSpecialCasesMod(self.pspec)
140 dmod = FPAddDeNormMod(self.pspec, True)
141
142 return [smod, dmod]