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 FPOp
10 from fpbase
import Trigger
11 from singlepipe
import (StageChain
, UnbufferedPipeline
)
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
):
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 get_fragment(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
.stb
.eq(self
.mod
.o
.z
.stb
),
206 self
.mod
.o
.z
.ack
.eq(self
.o
.z
.ack
),
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
.ack
.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),
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
):
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() + \