4418b3fa8f07471b5b9067a6175f0306ad10c5d8
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
.z
.data_o
= Signal(width
)
36 self
.mid
= Signal(id_wid
, reset_less
=True)
43 return [self
.z
.eq(i
.z
), self
.mid
.eq(i
.mid
)]
51 def __init__(self
, width
, id_wid
=None, single_cycle
=False, compact
=True):
54 * width: bit-width of IEEE754. supported: 16, 32, 64
55 * id_wid: an identifier that is sync-connected to the input
56 * single_cycle: True indicates each stage to complete in 1 clock
57 * compact: True indicates a reduced number of stages
61 self
.single_cycle
= single_cycle
62 self
.compact
= compact
71 return FPADDBaseData(self
.width
, self
.id_wid
)
74 return FPOpData(self
.width
, self
.id_wid
)
76 def add_state(self
, state
):
77 self
.states
.append(state
)
80 def elaborate(self
, platform
=None):
81 """ creates the HDL code-fragment for FPAdd
84 m
.submodules
.out_z
= self
.o
.z
85 m
.submodules
.in_t
= self
.in_t
87 self
.get_compact_fragment(m
, platform
)
89 self
.get_longer_fragment(m
, platform
)
93 for state
in self
.states
:
94 with m
.State(state
.state_from
):
99 def get_longer_fragment(self
, m
, platform
=None):
101 get
= self
.add_state(FPGet2Op("get_ops", "special_cases",
106 get
.trigger_setup(m
, self
.in_t
.stb
, self
.in_t
.ack
)
108 sc
= self
.add_state(FPAddSpecialCases(self
.width
, self
.id_wid
))
109 sc
.setup(m
, a
, b
, self
.in_mid
)
111 dn
= self
.add_state(FPAddDeNorm(self
.width
, self
.id_wid
))
112 dn
.setup(m
, a
, b
, sc
.in_mid
)
114 if self
.single_cycle
:
115 alm
= self
.add_state(FPAddAlignSingle(self
.width
, self
.id_wid
))
116 alm
.setup(m
, dn
.out_a
, dn
.out_b
, dn
.in_mid
)
118 alm
= self
.add_state(FPAddAlignMulti(self
.width
, self
.id_wid
))
119 alm
.setup(m
, dn
.out_a
, dn
.out_b
, dn
.in_mid
)
121 add0
= self
.add_state(FPAddStage0(self
.width
, self
.id_wid
))
122 add0
.setup(m
, alm
.out_a
, alm
.out_b
, alm
.in_mid
)
124 add1
= self
.add_state(FPAddStage1(self
.width
, self
.id_wid
))
125 add1
.setup(m
, add0
.out_tot
, add0
.out_z
, add0
.in_mid
)
127 if self
.single_cycle
:
128 n1
= self
.add_state(FPNorm1Single(self
.width
, self
.id_wid
))
129 n1
.setup(m
, add1
.out_z
, add1
.out_of
, add0
.in_mid
)
131 n1
= self
.add_state(FPNorm1Multi(self
.width
, self
.id_wid
))
132 n1
.setup(m
, add1
.out_z
, add1
.out_of
, add1
.norm_stb
, add0
.in_mid
)
134 rn
= self
.add_state(FPRound(self
.width
, self
.id_wid
))
135 rn
.setup(m
, n1
.out_z
, n1
.out_roundz
, n1
.in_mid
)
137 cor
= self
.add_state(FPCorrections(self
.width
, self
.id_wid
))
138 cor
.setup(m
, rn
.out_z
, rn
.in_mid
)
140 pa
= self
.add_state(FPPack(self
.width
, self
.id_wid
))
141 pa
.setup(m
, cor
.out_z
, rn
.in_mid
)
143 ppz
= self
.add_state(FPPutZ("pack_put_z", pa
.out_z
, self
.out_z
,
144 pa
.in_mid
, self
.out_mid
))
146 pz
= self
.add_state(FPPutZ("put_z", sc
.out_z
, self
.out_z
,
147 pa
.in_mid
, self
.out_mid
))
149 def get_compact_fragment(self
, m
, platform
=None):
151 get
= FPGet2Op("get_ops", "special_cases", self
.width
, self
.id_wid
)
152 sc
= FPAddSpecialCasesDeNorm(self
.width
, self
.id_wid
)
153 alm
= FPAddAlignSingleAdd(self
.width
, self
.id_wid
)
154 n1
= FPNormToPack(self
.width
, self
.id_wid
)
156 get
.trigger_setup(m
, self
.in_t
.stb
, self
.in_t
.ack
)
158 chainlist
= [get
, sc
, alm
, n1
]
159 chain
= StageChain(chainlist
, specallocate
=True)
160 chain
.setup(m
, self
.i
)
162 for mod
in chainlist
:
163 sc
= self
.add_state(mod
)
165 ppz
= self
.add_state(FPPutZ("pack_put_z", n1
.out_z
.z
, self
.o
,
166 n1
.out_z
.mid
, self
.o
.mid
))
168 #pz = self.add_state(FPPutZ("put_z", sc.out_z.z, self.o,
169 # sc.o.mid, self.o.mid))
172 class FPADDBase(FPState
):
174 def __init__(self
, width
, id_wid
=None, single_cycle
=False):
177 * width: bit-width of IEEE754. supported: 16, 32, 64
178 * id_wid: an identifier that is sync-connected to the input
179 * single_cycle: True indicates each stage to complete in 1 clock
181 FPState
.__init
__(self
, "fpadd")
183 self
.single_cycle
= single_cycle
184 self
.mod
= FPADDBaseMod(width
, id_wid
, single_cycle
)
185 self
.o
= self
.ospec()
187 self
.in_t
= Trigger()
188 self
.i
= self
.ispec()
190 self
.z_done
= Signal(reset_less
=True) # connects to out_z Strobe
191 self
.in_accept
= Signal(reset_less
=True)
192 self
.add_stb
= Signal(reset_less
=True)
193 self
.add_ack
= Signal(reset
=0, reset_less
=True)
196 return self
.mod
.ispec()
199 return self
.mod
.ospec()
201 def setup(self
, m
, i
, add_stb
, in_mid
):
202 m
.d
.comb
+= [self
.i
.eq(i
),
203 self
.mod
.i
.eq(self
.i
),
204 self
.z_done
.eq(self
.mod
.o
.z
.trigger
),
205 #self.add_stb.eq(add_stb),
206 self
.mod
.in_t
.stb
.eq(self
.in_t
.stb
),
207 self
.in_t
.ack
.eq(self
.mod
.in_t
.ack
),
208 self
.o
.mid
.eq(self
.mod
.o
.mid
),
209 self
.o
.z
.v
.eq(self
.mod
.o
.z
.v
),
210 self
.o
.z
.valid_o
.eq(self
.mod
.o
.z
.valid_o
),
211 self
.mod
.o
.z
.ready_i
.eq(self
.o
.z
.ready_i_test
),
214 m
.d
.sync
+= self
.add_stb
.eq(add_stb
)
215 m
.d
.sync
+= self
.add_ack
.eq(0) # sets to zero when not in active state
216 m
.d
.sync
+= self
.o
.z
.ready_i
.eq(0) # likewise
217 #m.d.sync += self.in_t.stb.eq(0)
219 m
.submodules
.fpadd
= self
.mod
223 # in_accept is set on incoming strobe HIGH and ack LOW.
224 m
.d
.comb
+= self
.in_accept
.eq((~self
.add_ack
) & (self
.add_stb
))
226 #with m.If(self.in_t.ack):
227 # m.d.sync += self.in_t.stb.eq(0)
228 with m
.If(~self
.z_done
):
229 # not done: test for accepting an incoming operand pair
230 with m
.If(self
.in_accept
):
232 self
.add_ack
.eq(1), # acknowledge receipt...
233 self
.in_t
.stb
.eq(1), # initiate add
236 m
.d
.sync
+= [self
.add_ack
.eq(0),
238 self
.o
.z
.ready_i
.eq(1),
241 # done: acknowledge, and write out id and value
242 m
.d
.sync
+= [self
.add_ack
.eq(1),
249 if self
.in_mid
is not None:
250 m
.d
.sync
+= self
.out_mid
.eq(self
.mod
.out_mid
)
253 self
.out_z
.v
.eq(self
.mod
.out_z
.v
)
255 # move to output state on detecting z ack
256 with m
.If(self
.out_z
.trigger
):
257 m
.d
.sync
+= self
.out_z
.stb
.eq(0)
260 m
.d
.sync
+= self
.out_z
.stb
.eq(1)
264 """ FPADD: stages as follows:
270 FPAddBase---> FPAddBaseMod
272 PutZ GetOps->Specials->Align->Add1/2->Norm->Round/Pack->PutZ
274 FPAddBase is tricky: it is both a stage and *has* stages.
275 Connection to FPAddBaseMod therefore requires an in stb/ack
276 and an out stb/ack. Just as with Add1-Norm1 interaction, FPGetOp
277 needs to be the thing that raises the incoming stb.
280 def __init__(self
, width
, id_wid
=None, single_cycle
=False, rs_sz
=2):
283 * width: bit-width of IEEE754. supported: 16, 32, 64
284 * id_wid: an identifier that is sync-connected to the input
285 * single_cycle: True indicates each stage to complete in 1 clock
289 self
.single_cycle
= single_cycle
291 #self.out_z = FPOp(width)
292 self
.ids
= FPID(id_wid
)
295 for i
in range(rs_sz
):
298 in_a
.data_i
= Signal(width
)
299 in_b
.data_i
= Signal(width
)
300 in_a
.name
= "in_a_%d" % i
301 in_b
.name
= "in_b_%d" % i
302 rs
.append((in_a
, in_b
))
306 for i
in range(rs_sz
):
307 out_z
= FPOpOut(width
)
308 out_z
.data_o
= Signal(width
)
309 out_z
.name
= "out_z_%d" % i
311 self
.res
= Array(res
)
315 def add_state(self
, state
):
316 self
.states
.append(state
)
319 def elaborate(self
, platform
=None):
320 """ creates the HDL code-fragment for FPAdd
323 #m.submodules += self.rs
328 geta
= self
.add_state(FPGetOp("get_a", "get_b",
333 getb
= self
.add_state(FPGetOp("get_b", "fpadd",
338 ab
= FPADDBase(self
.width
, self
.id_wid
, self
.single_cycle
)
339 ab
= self
.add_state(ab
)
340 abd
= ab
.ispec() # create an input spec object for FPADDBase
341 m
.d
.sync
+= [abd
.a
.eq(a
), abd
.b
.eq(b
), abd
.mid
.eq(self
.ids
.in_mid
)]
342 ab
.setup(m
, abd
, getb
.out_decode
, self
.ids
.in_mid
)
345 pz
= self
.add_state(FPPutZIdx("put_z", o
.z
, self
.res
,
350 for state
in self
.states
:
351 with m
.State(state
.state_from
):
357 if __name__
== "__main__":
359 alu
= FPADD(width
=32, id_wid
=5, single_cycle
=True)
360 main(alu
, ports
=alu
.rs
[0][0].ports() + \
361 alu
.rs
[0][1].ports() + \
362 alu
.res
[0].ports() + \
363 [alu
.ids
.in_mid
, alu
.ids
.out_mid
])
365 alu
= FPADDBase(width
=32, id_wid
=5, single_cycle
=True)
366 main(alu
, ports
=[alu
.in_a
, alu
.in_b
] + \
368 alu
.out_z
.ports() + \
369 [alu
.in_mid
, alu
.out_mid
])
372 # works... but don't use, just do "python fname.py convert -t v"
373 #print (verilog.convert(alu, ports=[
374 # ports=alu.in_a.ports() + \
375 # alu.in_b.ports() + \