1 # IEEE Floating Point Adder (Single Precision)
2 # Copyright (C) Jonathan P Dawson 2013
5 from nmigen
import Module
, Signal
6 from nmigen
.cli
import main
, verilog
8 from fpbase
import FPNum
, FPOp
, Overflow
, FPBase
13 def __init__(self
, width
):
17 self
.in_a
= FPOp(width
)
18 self
.in_b
= FPOp(width
)
19 self
.out_z
= FPOp(width
)
21 def get_fragment(self
, platform
=None):
22 """ creates the HDL code-fragment for FPAdd
29 z
= FPNum(self
.width
, False)
31 tot
= Signal(28) # sticky/round/guard bits, 23 result, 1 overflow
40 with m
.State("get_a"):
41 self
.get_op(m
, self
.in_a
, a
, "get_b")
46 with m
.State("get_b"):
47 self
.get_op(m
, self
.in_b
, b
, "special_cases")
50 # special cases: NaNs, infs, zeros, denormalised
51 # NOTE: some of these are unique to add. see "Special Operations"
52 # https://steve.hollasch.net/cgindex/coding/ieeefloat.html
54 with m
.State("special_cases"):
56 # if a is NaN or b is NaN return NaN
57 with m
.If(a
.is_nan() | b
.is_nan()):
61 # if a is inf return inf (or NaN)
62 with m
.Elif(a
.is_inf()):
64 m
.d
.sync
+= z
.inf(a
.s
)
65 # if a is inf and signs don't match return NaN
66 with m
.If((b
.e
== b
.P128
) & (a
.s
!= b
.s
)):
67 m
.d
.sync
+= z
.nan(b
.s
)
69 # if b is inf return inf
70 with m
.Elif(b
.is_inf()):
72 m
.d
.sync
+= z
.inf(b
.s
)
74 # if a is zero and b zero return signed-a/b
75 with m
.Elif(a
.is_zero() & b
.is_zero()):
77 m
.d
.sync
+= z
.create(a
.s
& b
.s
, b
.e
[0:8], b
.m
[3:-1])
79 # if a is zero return b
80 with m
.Elif(a
.is_zero()):
82 m
.d
.sync
+= z
.create(b
.s
, b
.e
[0:8], b
.m
[3:-1])
84 # if b is zero return a
85 with m
.Elif(b
.is_zero()):
87 m
.d
.sync
+= z
.create(a
.s
, a
.e
[0:8], a
.m
[3:-1])
89 # Denormalised Number checks
92 self
.denormalise(m
, a
)
93 self
.denormalise(m
, b
)
96 # align. NOTE: this does *not* do single-cycle multi-shifting,
97 # it *STAYS* in the align state until the exponents match
99 with m
.State("align"):
100 # exponent of a greater than b: increment b exp, shift b mant
101 with m
.If(a
.e
> b
.e
):
102 m
.d
.sync
+= b
.shift_down()
103 # exponent of b greater than a: increment a exp, shift a mant
104 with m
.Elif(a
.e
< b
.e
):
105 m
.d
.sync
+= a
.shift_down()
106 # exponents equal: move to next stage.
111 # First stage of add. covers same-sign (add) and subtract
112 # special-casing when mantissas are greater or equal, to
113 # give greatest accuracy.
115 with m
.State("add_0"):
117 m
.d
.sync
+= z
.e
.eq(a
.e
)
118 # same-sign (both negative or both positive) add mantissas
119 with m
.If(a
.s
== b
.s
):
124 # a mantissa greater than b, use a
125 with m
.Elif(a
.m
>= b
.m
):
130 # b mantissa greater than a, use b
138 # Second stage of add: preparation for normalisation.
139 # detects when tot sum is too big (tot[27] is kinda a carry bit)
141 with m
.State("add_1"):
142 m
.next
= "normalise_1"
143 # tot[27] gets set when the sum overflows. shift result down
148 of
.round_bit
.eq(tot
[2]),
149 of
.sticky
.eq(tot
[1] | tot
[0]),
157 of
.round_bit
.eq(tot
[1]),
162 # First stage of normalisation.
164 with m
.State("normalise_1"):
165 self
.normalise_1(m
, z
, of
, "normalise_2")
168 # Second stage of normalisation.
170 with m
.State("normalise_2"):
171 self
.normalise_2(m
, z
, of
, "round")
176 with m
.State("round"):
177 self
.roundz(m
, z
, of
, "corrections")
182 with m
.State("corrections"):
183 self
.corrections(m
, z
, "pack")
188 with m
.State("pack"):
189 self
.pack(m
, z
, "put_z")
194 with m
.State("put_z"):
195 self
.put_z(m
, z
, self
.out_z
, "get_a")
200 if __name__
== "__main__":
201 alu
= FPADD(width
=32)
202 main(alu
, ports
=alu
.in_a
.ports() + alu
.in_b
.ports() + alu
.out_z
.ports())
205 # works... but don't use, just do "python fname.py convert -t v"
206 #print (verilog.convert(alu, ports=[
207 # ports=alu.in_a.ports() + \
208 # alu.in_b.ports() + \