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