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
.cli
import main
, verilog
9 from fpbase
import FPOpIn
, FPOpOut
10 from fpbase
import Trigger
11 from singlepipe
import (StageChain
, SimpleHandshake
)
13 from fpbase
import FPState
, FPID
14 from fpcommon
.getop
import (FPGetOp
, FPADDBaseData
, FPGet2Op
)
15 from fpcommon
.denorm
import (FPSCData
, FPAddDeNorm
)
16 from fpcommon
.postcalc
import FPAddStage1Data
17 from fpcommon
.postnormalise
import (FPNorm1Data
,
18 FPNorm1Single
, FPNorm1Multi
)
19 from fpcommon
.roundz
import (FPRoundData
, FPRound
)
20 from fpcommon
.corrections
import FPCorrections
21 from fpcommon
.pack
import (FPPackData
, FPPackMod
, FPPack
)
22 from fpcommon
.normtopack
import FPNormToPack
23 from fpcommon
.putz
import (FPPutZ
, FPPutZIdx
)
25 from fpadd
.specialcases
import (FPAddSpecialCases
, FPAddSpecialCasesDeNorm
)
26 from fpadd
.align
import (FPAddAlignMulti
, FPAddAlignSingle
)
27 from fpadd
.add0
import (FPAddStage0Data
, FPAddStage0
)
28 from fpadd
.add1
import (FPAddStage1Mod
, FPAddStage1
)
29 from fpadd
.addstages
import FPAddAlignSingleAdd
33 def __init__(self
, width
, id_wid
):
34 self
.z
= FPOpOut(width
)
35 self
.mid
= Signal(id_wid
, reset_less
=True)
42 return [self
.z
.eq(i
.z
), self
.mid
.eq(i
.mid
)]
50 def __init__(self
, width
, id_wid
=None, single_cycle
=False, compact
=True):
53 * width: bit-width of IEEE754. supported: 16, 32, 64
54 * id_wid: an identifier that is sync-connected to the input
55 * single_cycle: True indicates each stage to complete in 1 clock
56 * compact: True indicates a reduced number of stages
60 self
.single_cycle
= single_cycle
61 self
.compact
= compact
70 return FPADDBaseData(self
.width
, self
.id_wid
)
73 return FPOpData(self
.width
, self
.id_wid
)
75 def add_state(self
, state
):
76 self
.states
.append(state
)
79 def elaborate(self
, platform
=None):
80 """ creates the HDL code-fragment for FPAdd
83 m
.submodules
.out_z
= self
.o
.z
84 m
.submodules
.in_t
= self
.in_t
86 self
.get_compact_fragment(m
, platform
)
88 self
.get_longer_fragment(m
, platform
)
92 for state
in self
.states
:
93 with m
.State(state
.state_from
):
98 def get_longer_fragment(self
, m
, platform
=None):
100 get
= self
.add_state(FPGet2Op("get_ops", "special_cases",
105 get
.trigger_setup(m
, self
.in_t
.stb
, self
.in_t
.ack
)
107 sc
= self
.add_state(FPAddSpecialCases(self
.width
, self
.id_wid
))
108 sc
.setup(m
, a
, b
, self
.in_mid
)
110 dn
= self
.add_state(FPAddDeNorm(self
.width
, self
.id_wid
))
111 dn
.setup(m
, a
, b
, sc
.in_mid
)
113 if self
.single_cycle
:
114 alm
= self
.add_state(FPAddAlignSingle(self
.width
, self
.id_wid
))
115 alm
.setup(m
, dn
.out_a
, dn
.out_b
, dn
.in_mid
)
117 alm
= self
.add_state(FPAddAlignMulti(self
.width
, self
.id_wid
))
118 alm
.setup(m
, dn
.out_a
, dn
.out_b
, dn
.in_mid
)
120 add0
= self
.add_state(FPAddStage0(self
.width
, self
.id_wid
))
121 add0
.setup(m
, alm
.out_a
, alm
.out_b
, alm
.in_mid
)
123 add1
= self
.add_state(FPAddStage1(self
.width
, self
.id_wid
))
124 add1
.setup(m
, add0
.out_tot
, add0
.out_z
, add0
.in_mid
)
126 if self
.single_cycle
:
127 n1
= self
.add_state(FPNorm1Single(self
.width
, self
.id_wid
))
128 n1
.setup(m
, add1
.out_z
, add1
.out_of
, add0
.in_mid
)
130 n1
= self
.add_state(FPNorm1Multi(self
.width
, self
.id_wid
))
131 n1
.setup(m
, add1
.out_z
, add1
.out_of
, add1
.norm_stb
, add0
.in_mid
)
133 rn
= self
.add_state(FPRound(self
.width
, self
.id_wid
))
134 rn
.setup(m
, n1
.out_z
, n1
.out_roundz
, n1
.in_mid
)
136 cor
= self
.add_state(FPCorrections(self
.width
, self
.id_wid
))
137 cor
.setup(m
, rn
.out_z
, rn
.in_mid
)
139 pa
= self
.add_state(FPPack(self
.width
, self
.id_wid
))
140 pa
.setup(m
, cor
.out_z
, rn
.in_mid
)
142 ppz
= self
.add_state(FPPutZ("pack_put_z", pa
.out_z
, self
.out_z
,
143 pa
.in_mid
, self
.out_mid
))
145 pz
= self
.add_state(FPPutZ("put_z", sc
.out_z
, self
.out_z
,
146 pa
.in_mid
, self
.out_mid
))
148 def get_compact_fragment(self
, m
, platform
=None):
150 get
= FPGet2Op("get_ops", "special_cases", self
.width
, self
.id_wid
)
151 sc
= FPAddSpecialCasesDeNorm(self
.width
, self
.id_wid
)
152 alm
= FPAddAlignSingleAdd(self
.width
, self
.id_wid
)
153 n1
= FPNormToPack(self
.width
, self
.id_wid
)
155 get
.trigger_setup(m
, self
.in_t
.stb
, self
.in_t
.ack
)
157 chainlist
= [get
, sc
, alm
, n1
]
158 chain
= StageChain(chainlist
, specallocate
=True)
159 chain
.setup(m
, self
.i
)
161 for mod
in chainlist
:
162 sc
= self
.add_state(mod
)
164 ppz
= self
.add_state(FPPutZ("pack_put_z", n1
.out_z
.z
, self
.o
,
165 n1
.out_z
.mid
, self
.o
.mid
))
167 #pz = self.add_state(FPPutZ("put_z", sc.out_z.z, self.o,
168 # sc.o.mid, self.o.mid))
171 class FPADDBase(FPState
):
173 def __init__(self
, width
, id_wid
=None, single_cycle
=False):
176 * width: bit-width of IEEE754. supported: 16, 32, 64
177 * id_wid: an identifier that is sync-connected to the input
178 * single_cycle: True indicates each stage to complete in 1 clock
180 FPState
.__init
__(self
, "fpadd")
182 self
.single_cycle
= single_cycle
183 self
.mod
= FPADDBaseMod(width
, id_wid
, single_cycle
)
184 self
.o
= self
.ospec()
186 self
.in_t
= Trigger()
187 self
.i
= self
.ispec()
189 self
.z_done
= Signal(reset_less
=True) # connects to out_z Strobe
190 self
.in_accept
= Signal(reset_less
=True)
191 self
.add_stb
= Signal(reset_less
=True)
192 self
.add_ack
= Signal(reset
=0, reset_less
=True)
195 return self
.mod
.ispec()
198 return self
.mod
.ospec()
200 def setup(self
, m
, i
, add_stb
, in_mid
):
201 m
.d
.comb
+= [self
.i
.eq(i
),
202 self
.mod
.i
.eq(self
.i
),
203 self
.z_done
.eq(self
.mod
.o
.z
.trigger
),
204 #self.add_stb.eq(add_stb),
205 self
.mod
.in_t
.stb
.eq(self
.in_t
.stb
),
206 self
.in_t
.ack
.eq(self
.mod
.in_t
.ack
),
207 self
.o
.mid
.eq(self
.mod
.o
.mid
),
208 self
.o
.z
.v
.eq(self
.mod
.o
.z
.v
),
209 self
.o
.z
.valid_o
.eq(self
.mod
.o
.z
.valid_o
),
210 self
.mod
.o
.z
.ready_i
.eq(self
.o
.z
.ready_i_test
),
213 m
.d
.sync
+= self
.add_stb
.eq(add_stb
)
214 m
.d
.sync
+= self
.add_ack
.eq(0) # sets to zero when not in active state
215 m
.d
.sync
+= self
.o
.z
.ready_i
.eq(0) # likewise
216 #m.d.sync += self.in_t.stb.eq(0)
218 m
.submodules
.fpadd
= self
.mod
222 # in_accept is set on incoming strobe HIGH and ack LOW.
223 m
.d
.comb
+= self
.in_accept
.eq((~self
.add_ack
) & (self
.add_stb
))
225 #with m.If(self.in_t.ack):
226 # m.d.sync += self.in_t.stb.eq(0)
227 with m
.If(~self
.z_done
):
228 # not done: test for accepting an incoming operand pair
229 with m
.If(self
.in_accept
):
231 self
.add_ack
.eq(1), # acknowledge receipt...
232 self
.in_t
.stb
.eq(1), # initiate add
235 m
.d
.sync
+= [self
.add_ack
.eq(0),
237 self
.o
.z
.ready_i
.eq(1),
240 # done: acknowledge, and write out id and value
241 m
.d
.sync
+= [self
.add_ack
.eq(1),
248 if self
.in_mid
is not None:
249 m
.d
.sync
+= self
.out_mid
.eq(self
.mod
.out_mid
)
252 self
.out_z
.v
.eq(self
.mod
.out_z
.v
)
254 # move to output state on detecting z ack
255 with m
.If(self
.out_z
.trigger
):
256 m
.d
.sync
+= self
.out_z
.stb
.eq(0)
259 m
.d
.sync
+= self
.out_z
.stb
.eq(1)
263 """ FPADD: stages as follows:
269 FPAddBase---> FPAddBaseMod
271 PutZ GetOps->Specials->Align->Add1/2->Norm->Round/Pack->PutZ
273 FPAddBase is tricky: it is both a stage and *has* stages.
274 Connection to FPAddBaseMod therefore requires an in stb/ack
275 and an out stb/ack. Just as with Add1-Norm1 interaction, FPGetOp
276 needs to be the thing that raises the incoming stb.
279 def __init__(self
, width
, id_wid
=None, single_cycle
=False, rs_sz
=2):
282 * width: bit-width of IEEE754. supported: 16, 32, 64
283 * id_wid: an identifier that is sync-connected to the input
284 * single_cycle: True indicates each stage to complete in 1 clock
288 self
.single_cycle
= single_cycle
290 #self.out_z = FPOp(width)
291 self
.ids
= FPID(id_wid
)
294 for i
in range(rs_sz
):
297 in_a
.name
= "in_a_%d" % i
298 in_b
.name
= "in_b_%d" % i
299 rs
.append((in_a
, in_b
))
303 for i
in range(rs_sz
):
304 out_z
= FPOpOut(width
)
305 out_z
.name
= "out_z_%d" % i
307 self
.res
= Array(res
)
311 def add_state(self
, state
):
312 self
.states
.append(state
)
315 def elaborate(self
, platform
=None):
316 """ creates the HDL code-fragment for FPAdd
319 #m.submodules += self.rs
324 geta
= self
.add_state(FPGetOp("get_a", "get_b",
329 getb
= self
.add_state(FPGetOp("get_b", "fpadd",
334 ab
= FPADDBase(self
.width
, self
.id_wid
, self
.single_cycle
)
335 ab
= self
.add_state(ab
)
336 abd
= ab
.ispec() # create an input spec object for FPADDBase
337 m
.d
.sync
+= [abd
.a
.eq(a
), abd
.b
.eq(b
), abd
.mid
.eq(self
.ids
.in_mid
)]
338 ab
.setup(m
, abd
, getb
.out_decode
, self
.ids
.in_mid
)
341 pz
= self
.add_state(FPPutZIdx("put_z", o
.z
, self
.res
,
346 for state
in self
.states
:
347 with m
.State(state
.state_from
):
353 if __name__
== "__main__":
355 alu
= FPADD(width
=32, id_wid
=5, single_cycle
=True)
356 main(alu
, ports
=alu
.rs
[0][0].ports() + \
357 alu
.rs
[0][1].ports() + \
358 alu
.res
[0].ports() + \
359 [alu
.ids
.in_mid
, alu
.ids
.out_mid
])
361 alu
= FPADDBase(width
=32, id_wid
=5, single_cycle
=True)
362 main(alu
, ports
=[alu
.in_a
, alu
.in_b
] + \
364 alu
.out_z
.ports() + \
365 [alu
.in_mid
, alu
.out_mid
])
368 # works... but don't use, just do "python fname.py convert -t v"
369 #print (verilog.convert(alu, ports=[
370 # ports=alu.in_a.ports() + \
371 # alu.in_b.ports() + \