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)
38 return [self
.z
.eq(i
.z
), self
.mid
.eq(i
.mid
)]
41 return [self
.z
, self
.mid
]
46 def __init__(self
, width
, id_wid
=None, single_cycle
=False, compact
=True):
49 * width: bit-width of IEEE754. supported: 16, 32, 64
50 * id_wid: an identifier that is sync-connected to the input
51 * single_cycle: True indicates each stage to complete in 1 clock
52 * compact: True indicates a reduced number of stages
56 self
.single_cycle
= single_cycle
57 self
.compact
= compact
66 return FPADDBaseData(self
.width
, self
.id_wid
)
69 return FPOpData(self
.width
, self
.id_wid
)
71 def add_state(self
, state
):
72 self
.states
.append(state
)
75 def elaborate(self
, platform
=None):
76 """ creates the HDL code-fragment for FPAdd
79 m
.submodules
.out_z
= self
.o
.z
80 m
.submodules
.in_t
= self
.in_t
82 self
.get_compact_fragment(m
, platform
)
84 self
.get_longer_fragment(m
, platform
)
88 for state
in self
.states
:
89 with m
.State(state
.state_from
):
94 def get_longer_fragment(self
, m
, platform
=None):
96 get
= self
.add_state(FPGet2Op("get_ops", "special_cases",
101 get
.trigger_setup(m
, self
.in_t
.stb
, self
.in_t
.ack
)
103 sc
= self
.add_state(FPAddSpecialCases(self
.width
, self
.id_wid
))
104 sc
.setup(m
, a
, b
, self
.in_mid
)
106 dn
= self
.add_state(FPAddDeNorm(self
.width
, self
.id_wid
))
107 dn
.setup(m
, a
, b
, sc
.in_mid
)
109 if self
.single_cycle
:
110 alm
= self
.add_state(FPAddAlignSingle(self
.width
, self
.id_wid
))
111 alm
.setup(m
, dn
.out_a
, dn
.out_b
, dn
.in_mid
)
113 alm
= self
.add_state(FPAddAlignMulti(self
.width
, self
.id_wid
))
114 alm
.setup(m
, dn
.out_a
, dn
.out_b
, dn
.in_mid
)
116 add0
= self
.add_state(FPAddStage0(self
.width
, self
.id_wid
))
117 add0
.setup(m
, alm
.out_a
, alm
.out_b
, alm
.in_mid
)
119 add1
= self
.add_state(FPAddStage1(self
.width
, self
.id_wid
))
120 add1
.setup(m
, add0
.out_tot
, add0
.out_z
, add0
.in_mid
)
122 if self
.single_cycle
:
123 n1
= self
.add_state(FPNorm1Single(self
.width
, self
.id_wid
))
124 n1
.setup(m
, add1
.out_z
, add1
.out_of
, add0
.in_mid
)
126 n1
= self
.add_state(FPNorm1Multi(self
.width
, self
.id_wid
))
127 n1
.setup(m
, add1
.out_z
, add1
.out_of
, add1
.norm_stb
, add0
.in_mid
)
129 rn
= self
.add_state(FPRound(self
.width
, self
.id_wid
))
130 rn
.setup(m
, n1
.out_z
, n1
.out_roundz
, n1
.in_mid
)
132 cor
= self
.add_state(FPCorrections(self
.width
, self
.id_wid
))
133 cor
.setup(m
, rn
.out_z
, rn
.in_mid
)
135 pa
= self
.add_state(FPPack(self
.width
, self
.id_wid
))
136 pa
.setup(m
, cor
.out_z
, rn
.in_mid
)
138 ppz
= self
.add_state(FPPutZ("pack_put_z", pa
.out_z
, self
.out_z
,
139 pa
.in_mid
, self
.out_mid
))
141 pz
= self
.add_state(FPPutZ("put_z", sc
.out_z
, self
.out_z
,
142 pa
.in_mid
, self
.out_mid
))
144 def get_compact_fragment(self
, m
, platform
=None):
146 get
= FPGet2Op("get_ops", "special_cases", self
.width
, self
.id_wid
)
147 sc
= FPAddSpecialCasesDeNorm(self
.width
, self
.id_wid
)
148 alm
= FPAddAlignSingleAdd(self
.width
, self
.id_wid
)
149 n1
= FPNormToPack(self
.width
, self
.id_wid
)
151 get
.trigger_setup(m
, self
.in_t
.stb
, self
.in_t
.ack
)
153 chainlist
= [get
, sc
, alm
, n1
]
154 chain
= StageChain(chainlist
, specallocate
=True)
155 chain
.setup(m
, self
.i
)
157 for mod
in chainlist
:
158 sc
= self
.add_state(mod
)
160 ppz
= self
.add_state(FPPutZ("pack_put_z", n1
.out_z
.z
, self
.o
,
161 n1
.out_z
.mid
, self
.o
.mid
))
163 #pz = self.add_state(FPPutZ("put_z", sc.out_z.z, self.o,
164 # sc.o.mid, self.o.mid))
167 class FPADDBase(FPState
):
169 def __init__(self
, width
, id_wid
=None, single_cycle
=False):
172 * width: bit-width of IEEE754. supported: 16, 32, 64
173 * id_wid: an identifier that is sync-connected to the input
174 * single_cycle: True indicates each stage to complete in 1 clock
176 FPState
.__init
__(self
, "fpadd")
178 self
.single_cycle
= single_cycle
179 self
.mod
= FPADDBaseMod(width
, id_wid
, single_cycle
)
180 self
.o
= self
.ospec()
182 self
.in_t
= Trigger()
183 self
.i
= self
.ispec()
185 self
.z_done
= Signal(reset_less
=True) # connects to out_z Strobe
186 self
.in_accept
= Signal(reset_less
=True)
187 self
.add_stb
= Signal(reset_less
=True)
188 self
.add_ack
= Signal(reset
=0, reset_less
=True)
191 return self
.mod
.ispec()
194 return self
.mod
.ospec()
196 def setup(self
, m
, i
, add_stb
, in_mid
):
197 m
.d
.comb
+= [self
.i
.eq(i
),
198 self
.mod
.i
.eq(self
.i
),
199 self
.z_done
.eq(self
.mod
.o
.z
.trigger
),
200 #self.add_stb.eq(add_stb),
201 self
.mod
.in_t
.stb
.eq(self
.in_t
.stb
),
202 self
.in_t
.ack
.eq(self
.mod
.in_t
.ack
),
203 self
.o
.mid
.eq(self
.mod
.o
.mid
),
204 self
.o
.z
.v
.eq(self
.mod
.o
.z
.v
),
205 self
.o
.z
.o_valid
.eq(self
.mod
.o
.z
.o_valid
),
206 self
.mod
.o
.z
.i_ready
.eq(self
.o
.z
.i_ready_test
),
209 m
.d
.sync
+= self
.add_stb
.eq(add_stb
)
210 m
.d
.sync
+= self
.add_ack
.eq(0) # sets to zero when not in active state
211 m
.d
.sync
+= self
.o
.z
.i_ready
.eq(0) # likewise
212 #m.d.sync += self.in_t.stb.eq(0)
214 m
.submodules
.fpadd
= self
.mod
218 # in_accept is set on incoming strobe HIGH and ack LOW.
219 m
.d
.comb
+= self
.in_accept
.eq((~self
.add_ack
) & (self
.add_stb
))
221 #with m.If(self.in_t.ack):
222 # m.d.sync += self.in_t.stb.eq(0)
223 with m
.If(~self
.z_done
):
224 # not done: test for accepting an incoming operand pair
225 with m
.If(self
.in_accept
):
227 self
.add_ack
.eq(1), # acknowledge receipt...
228 self
.in_t
.stb
.eq(1), # initiate add
231 m
.d
.sync
+= [self
.add_ack
.eq(0),
233 self
.o
.z
.i_ready
.eq(1),
236 # done: acknowledge, and write out id and value
237 m
.d
.sync
+= [self
.add_ack
.eq(1),
244 if self
.in_mid
is not None:
245 m
.d
.sync
+= self
.out_mid
.eq(self
.mod
.out_mid
)
248 self
.out_z
.v
.eq(self
.mod
.out_z
.v
)
250 # move to output state on detecting z ack
251 with m
.If(self
.out_z
.trigger
):
252 m
.d
.sync
+= self
.out_z
.stb
.eq(0)
255 m
.d
.sync
+= self
.out_z
.stb
.eq(1)
259 """ FPADD: stages as follows:
265 FPAddBase---> FPAddBaseMod
267 PutZ GetOps->Specials->Align->Add1/2->Norm->Round/Pack->PutZ
269 FPAddBase is tricky: it is both a stage and *has* stages.
270 Connection to FPAddBaseMod therefore requires an in stb/ack
271 and an out stb/ack. Just as with Add1-Norm1 interaction, FPGetOp
272 needs to be the thing that raises the incoming stb.
275 def __init__(self
, width
, id_wid
=None, single_cycle
=False, rs_sz
=2):
278 * width: bit-width of IEEE754. supported: 16, 32, 64
279 * id_wid: an identifier that is sync-connected to the input
280 * single_cycle: True indicates each stage to complete in 1 clock
284 self
.single_cycle
= single_cycle
286 #self.out_z = FPOp(width)
287 self
.ids
= FPID(id_wid
)
290 for i
in range(rs_sz
):
293 in_a
.name
= "in_a_%d" % i
294 in_b
.name
= "in_b_%d" % i
295 rs
.append((in_a
, in_b
))
299 for i
in range(rs_sz
):
300 out_z
= FPOpOut(width
)
301 out_z
.name
= "out_z_%d" % i
303 self
.res
= Array(res
)
307 def add_state(self
, state
):
308 self
.states
.append(state
)
311 def get_fragment(self
, platform
=None):
312 """ creates the HDL code-fragment for FPAdd
315 m
.submodules
+= self
.rs
320 geta
= self
.add_state(FPGetOp("get_a", "get_b",
325 getb
= self
.add_state(FPGetOp("get_b", "fpadd",
330 ab
= FPADDBase(self
.width
, self
.id_wid
, self
.single_cycle
)
331 ab
= self
.add_state(ab
)
332 abd
= ab
.ispec() # create an input spec object for FPADDBase
333 m
.d
.sync
+= [abd
.a
.eq(a
), abd
.b
.eq(b
), abd
.mid
.eq(self
.ids
.in_mid
)]
334 ab
.setup(m
, abd
, getb
.out_decode
, self
.ids
.in_mid
)
337 pz
= self
.add_state(FPPutZIdx("put_z", o
.z
, self
.res
,
342 for state
in self
.states
:
343 with m
.State(state
.state_from
):
349 if __name__
== "__main__":
351 alu
= FPADD(width
=32, id_wid
=5, single_cycle
=True)
352 main(alu
, ports
=alu
.rs
[0][0].ports() + \
353 alu
.rs
[0][1].ports() + \
354 alu
.res
[0].ports() + \
355 [alu
.ids
.in_mid
, alu
.ids
.out_mid
])
357 alu
= FPADDBase(width
=32, id_wid
=5, single_cycle
=True)
358 main(alu
, ports
=[alu
.in_a
, alu
.in_b
] + \
360 alu
.out_z
.ports() + \
361 [alu
.in_mid
, alu
.out_mid
])
364 # works... but don't use, just do "python fname.py convert -t v"
365 #print (verilog.convert(alu, ports=[
366 # ports=alu.in_a.ports() + \
367 # alu.in_b.ports() + \