1 # IEEE Floating Point Adder (Single Precision)
2 # Copyright (C) Jonathan P Dawson 2013
5 from nmigen
import Module
, Signal
, Cat
, Mux
7 from nmutil
.pipemodbase
import PipeModBase
, PipeModBaseChain
8 from ieee754
.fpcommon
.fpbase
import FPFormat
, FPNumDecode
, FPRoundingMode
10 from ieee754
.fpcommon
.fpbase
import FPNumBaseRecord
11 from ieee754
.fpcommon
.basedata
import FPBaseData
12 from ieee754
.fpcommon
.denorm
import (FPSCData
, FPAddDeNormMod
)
15 class FPAddInputData(FPBaseData
):
16 def __init__(self
, pspec
):
17 super().__init
__(pspec
)
18 self
.is_sub
= Signal(reset
=False)
22 ret
.append(self
.is_sub
.eq(i
.is_sub
))
26 yield from super().__iter
__()
33 class FPAddSpecialCasesMod(PipeModBase
):
34 """ special cases: NaNs, infs, zeros, denormalised
35 NOTE: some of these are unique to add. see "Special Operations"
36 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
39 def __init__(self
, pspec
):
40 super().__init
__(pspec
, "specialcases")
43 return FPAddInputData(self
.pspec
)
46 return FPSCData(self
.pspec
, True)
48 def elaborate(self
, platform
):
52 # decode: XXX really should move to separate stage
53 width
= self
.pspec
.width
54 a1
= FPNumBaseRecord(width
)
55 b1
= FPNumBaseRecord(width
)
56 m
.submodules
.sc_decode_a
= a1
= FPNumDecode(None, a1
)
57 m
.submodules
.sc_decode_b
= b1
= FPNumDecode(None, b1
)
58 flip_b_sign
= Signal()
61 b_is_nan
.eq(FPFormat
.standard(width
).is_nan(self
.i
.b
)),
62 flip_b_sign
.eq(self
.i
.is_sub
& ~b_is_nan
),
64 b1
.v
.eq(self
.i
.b ^
(flip_b_sign
<< (width
- 1))),
69 zero_sign_array
= FPRoundingMode
.make_array(FPRoundingMode
.zero_sign
)
71 # temporaries used below
72 s_nomatch
= Signal(reset_less
=True)
73 s_match
= Signal(reset_less
=True)
74 m_match
= Signal(reset_less
=True)
75 e_match
= Signal(reset_less
=True)
76 absa
= Signal(reset_less
=True) # a1.s & b1.s
77 t_aeqmb
= Signal(reset_less
=True)
78 t_a1inf
= Signal(reset_less
=True)
79 t_b1inf
= Signal(reset_less
=True)
80 t_a1zero
= Signal(reset_less
=True)
81 t_b1zero
= Signal(reset_less
=True)
82 t_abz
= Signal(reset_less
=True)
83 t_abnan
= Signal(reset_less
=True)
84 bexp128s
= Signal(reset_less
=True)
85 t_special
= Signal(reset_less
=True)
87 comb
+= s_nomatch
.eq(a1
.s
!= b1
.s
)
88 comb
+= s_match
.eq(a1
.s
== b1
.s
)
89 comb
+= m_match
.eq(a1
.m
== b1
.m
)
90 comb
+= e_match
.eq(a1
.e
== b1
.e
)
92 # logic-chain (matches comments, below) gives an if-elif-elif-elif...
93 comb
+= t_abnan
.eq(a1
.is_nan | b1
.is_nan
)
94 comb
+= t_a1inf
.eq(a1
.is_inf
)
95 comb
+= t_b1inf
.eq(b1
.is_inf
)
96 comb
+= t_abz
.eq(a1
.is_zero
& b1
.is_zero
)
97 comb
+= t_a1zero
.eq(a1
.is_zero
)
98 comb
+= t_b1zero
.eq(b1
.is_zero
)
99 comb
+= t_aeqmb
.eq(s_nomatch
& m_match
& e_match
)
100 comb
+= t_special
.eq(Cat(t_aeqmb
, t_b1zero
, t_a1zero
, t_abz
,
101 t_b1inf
, t_a1inf
, t_abnan
).bool())
103 comb
+= absa
.eq(a1
.s
& b1
.s
)
104 comb
+= bexp128s
.eq(b1
.exp_128
& s_nomatch
)
106 # prepare inf/zero/nans
107 z_zero
= FPNumBaseRecord(width
, False, name
="z_zero")
108 z_default_zero
= FPNumBaseRecord(width
, False, name
="z_default_zero")
109 z_default_nan
= FPNumBaseRecord(width
, False, name
="z_default_nan")
110 z_quieted_a
= FPNumBaseRecord(width
, False, name
="z_quieted_a")
111 z_quieted_b
= FPNumBaseRecord(width
, False, name
="z_quieted_b")
112 z_infa
= FPNumBaseRecord(width
, False, name
="z_infa")
113 z_infb
= FPNumBaseRecord(width
, False, name
="z_infb")
114 comb
+= z_zero
.zero(0)
115 comb
+= z_default_zero
.zero(zero_sign_array
[self
.i
.rm
])
116 comb
+= z_default_nan
.nan(0)
117 comb
+= z_quieted_a
.quieted_nan(a1
)
118 comb
+= z_quieted_b
.quieted_nan(b1
)
119 comb
+= z_infa
.inf(a1
.s
)
120 comb
+= z_infb
.inf(b1
.s
)
122 # any special-cases it's a "special".
123 comb
+= self
.o
.out_do_z
.eq(t_special
)
125 # this is the logic-decision-making for special-cases:
126 # if a is NaN or b is NaN return NaN
127 # if a is NaN return quieted_nan(a)
128 # else return quieted_nan(b)
129 # elif a is inf return inf (or NaN)
130 # if a is inf and signs don't match return NaN
132 # elif b is inf return inf(b)
133 # elif a is zero and b zero with same sign return a
134 # elif a equal to -b return zero (sign determined by rounding-mode)
135 # elif a is zero return b
136 # elif b is zero return a
138 # XXX *sigh* there are better ways to do this...
139 # one of them: use a priority-picker!
140 # in reverse-order, accumulate Muxing
143 oz
= Mux(t_b1zero
, a1
.v
, oz
)
144 oz
= Mux(t_a1zero
, b1
.v
, oz
)
145 oz
= Mux(t_aeqmb
, z_default_zero
.v
, oz
)
146 oz
= Mux(t_abz
& s_match
, a1
.v
, oz
)
147 oz
= Mux(t_b1inf
, z_infb
.v
, oz
)
148 oz
= Mux(t_a1inf
, Mux(bexp128s
, z_default_nan
.v
, z_infa
.v
), oz
)
149 oz
= Mux(t_abnan
, Mux(a1
.is_nan
, z_quieted_a
.v
, z_quieted_b
.v
), oz
)
151 comb
+= self
.o
.oz
.eq(oz
)
153 comb
+= self
.o
.ctx
.eq(self
.i
.ctx
)
155 comb
+= self
.o
.rm
.eq(self
.i
.rm
)
160 class FPAddSpecialCasesDeNorm(PipeModBaseChain
):
161 """ special cases chain
165 """ links module to inputs and outputs
167 smod
= FPAddSpecialCasesMod(self
.pspec
)
168 dmod
= FPAddDeNormMod(self
.pspec
, True)