1 # IEEE Floating Point Adder (Single Precision)
2 # Copyright (C) Jonathan P Dawson 2013
5 from nmigen
import Module
, Signal
, Cat
, Mux
, Array
, Const
6 from nmigen
.lib
.coding
import PriorityEncoder
7 from nmigen
.cli
import main
, verilog
10 from fpbase
import FPNumIn
, FPNumOut
, FPOp
, Overflow
, FPBase
, FPNumBase
11 from fpbase
import MultiShiftRMerge
, Trigger
12 from singlepipe
import (ControlBase
, StageChain
, UnbufferedPipeline
,
14 from multipipe
import CombMuxOutPipe
15 from multipipe
import PriorityCombMuxInPipe
17 from fpbase
import FPState
, FPID
18 from fpcommon
.getop
import FPADDBaseData
19 from fpcommon
.denorm
import (FPSCData
, FPAddDeNormMod
, FPAddDeNorm
)
22 class FPAddSpecialCasesMod
:
23 """ special cases: NaNs, infs, zeros, denormalised
24 NOTE: some of these are unique to add. see "Special Operations"
25 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
28 def __init__(self
, width
, id_wid
):
35 return FPADDBaseData(self
.width
, self
.id_wid
)
38 return FPSCData(self
.width
, self
.id_wid
)
40 def setup(self
, m
, i
):
41 """ links module to inputs and outputs
43 m
.submodules
.specialcases
= self
44 m
.d
.comb
+= self
.i
.eq(i
)
49 def elaborate(self
, platform
):
52 m
.submodules
.sc_out_z
= self
.o
.z
54 # decode: XXX really should move to separate stage
55 a1
= FPNumIn(None, self
.width
)
56 b1
= FPNumIn(None, self
.width
)
57 m
.submodules
.sc_decode_a
= a1
58 m
.submodules
.sc_decode_b
= b1
59 m
.d
.comb
+= [a1
.decode(self
.i
.a
),
64 m
.d
.comb
+= s_nomatch
.eq(a1
.s
!= b1
.s
)
67 m
.d
.comb
+= m_match
.eq(a1
.m
== b1
.m
)
69 # if a is NaN or b is NaN return NaN
70 with m
.If(a1
.is_nan | b1
.is_nan
):
71 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
72 m
.d
.comb
+= self
.o
.z
.nan(0)
74 # XXX WEIRDNESS for FP16 non-canonical NaN handling
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 # m.d.comb += self.o.out_do_z.eq(1)
80 # m.d.comb += z.create(b.s, b.e, Cat(b.m[3:-2], ~b.m[0]))
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 # m.d.comb += self.o.out_do_z.eq(1)
85 # m.d.comb += z.create(a.s, a.e, Cat(a.m[3:-2], ~a.m[0]))
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 # m.d.comb += self.o.out_do_z.eq(1)
90 # m.d.comb += z.create(a.s & b.s, b.e, Cat(b.m[3:-2], 1))
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 # m.d.comb += self.o.out_do_z.eq(1)
95 # m.d.comb += z.create(a.s & b.s, a.e, Cat(a.m[3:-2], 1))
97 # if a is inf return inf (or NaN)
98 with m
.Elif(a1
.is_inf
):
99 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
100 m
.d
.comb
+= self
.o
.z
.inf(a1
.s
)
101 # if a is inf and signs don't match return NaN
102 with m
.If(b1
.exp_128
& s_nomatch
):
103 m
.d
.comb
+= self
.o
.z
.nan(0)
105 # if b is inf return inf
106 with m
.Elif(b1
.is_inf
):
107 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
108 m
.d
.comb
+= self
.o
.z
.inf(b1
.s
)
110 # if a is zero and b zero return signed-a/b
111 with m
.Elif(a1
.is_zero
& b1
.is_zero
):
112 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
113 m
.d
.comb
+= self
.o
.z
.create(a1
.s
& b1
.s
, b1
.e
, b1
.m
[3:-1])
115 # if a is zero return b
116 with m
.Elif(a1
.is_zero
):
117 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
118 m
.d
.comb
+= self
.o
.z
.create(b1
.s
, b1
.e
, b1
.m
[3:-1])
120 # if b is zero return a
121 with m
.Elif(b1
.is_zero
):
122 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
123 m
.d
.comb
+= self
.o
.z
.create(a1
.s
, a1
.e
, a1
.m
[3:-1])
125 # if a equal to -b return zero (+ve zero)
126 with m
.Elif(s_nomatch
& m_match
& (a1
.e
== b1
.e
)):
127 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
128 m
.d
.comb
+= self
.o
.z
.zero(0)
130 # Denormalised Number checks next, so pass a/b data through
132 m
.d
.comb
+= self
.o
.out_do_z
.eq(0)
133 m
.d
.comb
+= self
.o
.a
.eq(a1
)
134 m
.d
.comb
+= self
.o
.b
.eq(b1
)
136 m
.d
.comb
+= self
.o
.oz
.eq(self
.o
.z
.v
)
137 m
.d
.comb
+= self
.o
.mid
.eq(self
.i
.mid
)
142 class FPAddSpecialCases(FPState
):
143 """ special cases: NaNs, infs, zeros, denormalised
144 NOTE: some of these are unique to add. see "Special Operations"
145 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
148 def __init__(self
, width
, id_wid
):
149 FPState
.__init
__(self
, "special_cases")
150 self
.mod
= FPAddSpecialCasesMod(width
)
151 self
.out_z
= self
.mod
.ospec()
152 self
.out_do_z
= Signal(reset_less
=True)
154 def setup(self
, m
, i
):
155 """ links module to inputs and outputs
157 self
.mod
.setup(m
, i
, self
.out_do_z
)
158 m
.d
.sync
+= self
.out_z
.v
.eq(self
.mod
.out_z
.v
) # only take the output
159 m
.d
.sync
+= self
.out_z
.mid
.eq(self
.mod
.o
.mid
) # (and mid)
163 with m
.If(self
.out_do_z
):
166 m
.next
= "denormalise"
169 class FPAddSpecialCasesDeNorm(FPState
, UnbufferedPipeline
):
170 """ special cases: NaNs, infs, zeros, denormalised
171 NOTE: some of these are unique to add. see "Special Operations"
172 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
175 def __init__(self
, width
, id_wid
):
176 FPState
.__init
__(self
, "special_cases")
179 UnbufferedPipeline
.__init
__(self
, self
) # pipe is its own stage
180 self
.out
= self
.ospec()
183 return FPADDBaseData(self
.width
, self
.id_wid
) # SpecialCases ispec
186 return FPSCData(self
.width
, self
.id_wid
) # DeNorm ospec
188 def setup(self
, m
, i
):
189 """ links module to inputs and outputs
191 smod
= FPAddSpecialCasesMod(self
.width
, self
.id_wid
)
192 dmod
= FPAddDeNormMod(self
.width
, self
.id_wid
)
194 chain
= StageChain([smod
, dmod
])
197 # only needed for break-out (early-out)
198 # self.out_do_z = smod.o.out_do_z
202 def process(self
, i
):
206 # for break-out (early-out)
207 #with m.If(self.out_do_z):
210 m
.d
.sync
+= self
.out
.eq(self
.process(None))