convert FPOpData to __iter__
[ieee754fpu.git] / src / add / fpadd / statemachine.py
1 # IEEE Floating Point Adder (Single Precision)
2 # Copyright (C) Jonathan P Dawson 2013
3 # 2013-12-12
4
5 from nmigen import Module, Signal, Cat, Mux, Array, Const
6 from nmigen.cli import main, verilog
7 from math import log
8
9 from fpbase import FPOpIn, FPOpOut
10 from fpbase import Trigger
11 from singlepipe import (StageChain, SimpleHandshake)
12
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)
24
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
30
31
32 class FPOpData:
33 def __init__(self, width, id_wid):
34 self.z = FPOpOut(width)
35 self.mid = Signal(id_wid, reset_less=True)
36
37 def __iter__(self):
38 yield self.z
39 yield self.mid
40
41 def eq(self, i):
42 return [self.z.eq(i.z), self.mid.eq(i.mid)]
43
44 def ports(self):
45 return list(self)
46
47
48 class FPADDBaseMod:
49
50 def __init__(self, width, id_wid=None, single_cycle=False, compact=True):
51 """ IEEE754 FP Add
52
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
57 """
58 self.width = width
59 self.id_wid = id_wid
60 self.single_cycle = single_cycle
61 self.compact = compact
62
63 self.in_t = Trigger()
64 self.i = self.ispec()
65 self.o = self.ospec()
66
67 self.states = []
68
69 def ispec(self):
70 return FPADDBaseData(self.width, self.id_wid)
71
72 def ospec(self):
73 return FPOpData(self.width, self.id_wid)
74
75 def add_state(self, state):
76 self.states.append(state)
77 return state
78
79 def elaborate(self, platform=None):
80 """ creates the HDL code-fragment for FPAdd
81 """
82 m = Module()
83 m.submodules.out_z = self.o.z
84 m.submodules.in_t = self.in_t
85 if self.compact:
86 self.get_compact_fragment(m, platform)
87 else:
88 self.get_longer_fragment(m, platform)
89
90 with m.FSM() as fsm:
91
92 for state in self.states:
93 with m.State(state.state_from):
94 state.action(m)
95
96 return m
97
98 def get_longer_fragment(self, m, platform=None):
99
100 get = self.add_state(FPGet2Op("get_ops", "special_cases",
101 self.width))
102 get.setup(m, self.i)
103 a = get.out_op1
104 b = get.out_op2
105 get.trigger_setup(m, self.in_t.stb, self.in_t.ack)
106
107 sc = self.add_state(FPAddSpecialCases(self.width, self.id_wid))
108 sc.setup(m, a, b, self.in_mid)
109
110 dn = self.add_state(FPAddDeNorm(self.width, self.id_wid))
111 dn.setup(m, a, b, sc.in_mid)
112
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)
116 else:
117 alm = self.add_state(FPAddAlignMulti(self.width, self.id_wid))
118 alm.setup(m, dn.out_a, dn.out_b, dn.in_mid)
119
120 add0 = self.add_state(FPAddStage0(self.width, self.id_wid))
121 add0.setup(m, alm.out_a, alm.out_b, alm.in_mid)
122
123 add1 = self.add_state(FPAddStage1(self.width, self.id_wid))
124 add1.setup(m, add0.out_tot, add0.out_z, add0.in_mid)
125
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)
129 else:
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)
132
133 rn = self.add_state(FPRound(self.width, self.id_wid))
134 rn.setup(m, n1.out_z, n1.out_roundz, n1.in_mid)
135
136 cor = self.add_state(FPCorrections(self.width, self.id_wid))
137 cor.setup(m, rn.out_z, rn.in_mid)
138
139 pa = self.add_state(FPPack(self.width, self.id_wid))
140 pa.setup(m, cor.out_z, rn.in_mid)
141
142 ppz = self.add_state(FPPutZ("pack_put_z", pa.out_z, self.out_z,
143 pa.in_mid, self.out_mid))
144
145 pz = self.add_state(FPPutZ("put_z", sc.out_z, self.out_z,
146 pa.in_mid, self.out_mid))
147
148 def get_compact_fragment(self, m, platform=None):
149
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)
154
155 get.trigger_setup(m, self.in_t.stb, self.in_t.ack)
156
157 chainlist = [get, sc, alm, n1]
158 chain = StageChain(chainlist, specallocate=True)
159 chain.setup(m, self.i)
160
161 for mod in chainlist:
162 sc = self.add_state(mod)
163
164 ppz = self.add_state(FPPutZ("pack_put_z", n1.out_z.z, self.o,
165 n1.out_z.mid, self.o.mid))
166
167 #pz = self.add_state(FPPutZ("put_z", sc.out_z.z, self.o,
168 # sc.o.mid, self.o.mid))
169
170
171 class FPADDBase(FPState):
172
173 def __init__(self, width, id_wid=None, single_cycle=False):
174 """ IEEE754 FP Add
175
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
179 """
180 FPState.__init__(self, "fpadd")
181 self.width = width
182 self.single_cycle = single_cycle
183 self.mod = FPADDBaseMod(width, id_wid, single_cycle)
184 self.o = self.ospec()
185
186 self.in_t = Trigger()
187 self.i = self.ispec()
188
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)
193
194 def ispec(self):
195 return self.mod.ispec()
196
197 def ospec(self):
198 return self.mod.ospec()
199
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.o_valid.eq(self.mod.o.z.o_valid),
210 self.mod.o.z.i_ready.eq(self.o.z.i_ready_test),
211 ]
212
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.i_ready.eq(0) # likewise
216 #m.d.sync += self.in_t.stb.eq(0)
217
218 m.submodules.fpadd = self.mod
219
220 def action(self, m):
221
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))
224
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):
230 m.d.sync += [
231 self.add_ack.eq(1), # acknowledge receipt...
232 self.in_t.stb.eq(1), # initiate add
233 ]
234 with m.Else():
235 m.d.sync += [self.add_ack.eq(0),
236 self.in_t.stb.eq(0),
237 self.o.z.i_ready.eq(1),
238 ]
239 with m.Else():
240 # done: acknowledge, and write out id and value
241 m.d.sync += [self.add_ack.eq(1),
242 self.in_t.stb.eq(0)
243 ]
244 m.next = "put_z"
245
246 return
247
248 if self.in_mid is not None:
249 m.d.sync += self.out_mid.eq(self.mod.out_mid)
250
251 m.d.sync += [
252 self.out_z.v.eq(self.mod.out_z.v)
253 ]
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)
257 m.next = "put_z"
258 with m.Else():
259 m.d.sync += self.out_z.stb.eq(1)
260
261
262 class FPADD(FPID):
263 """ FPADD: stages as follows:
264
265 FPGetOp (a)
266 |
267 FPGetOp (b)
268 |
269 FPAddBase---> FPAddBaseMod
270 | |
271 PutZ GetOps->Specials->Align->Add1/2->Norm->Round/Pack->PutZ
272
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.
277 """
278
279 def __init__(self, width, id_wid=None, single_cycle=False, rs_sz=2):
280 """ IEEE754 FP Add
281
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
285 """
286 self.width = width
287 self.id_wid = id_wid
288 self.single_cycle = single_cycle
289
290 #self.out_z = FPOp(width)
291 self.ids = FPID(id_wid)
292
293 rs = []
294 for i in range(rs_sz):
295 in_a = FPOpIn(width)
296 in_b = FPOpIn(width)
297 in_a.name = "in_a_%d" % i
298 in_b.name = "in_b_%d" % i
299 rs.append((in_a, in_b))
300 self.rs = Array(rs)
301
302 res = []
303 for i in range(rs_sz):
304 out_z = FPOpOut(width)
305 out_z.name = "out_z_%d" % i
306 res.append(out_z)
307 self.res = Array(res)
308
309 self.states = []
310
311 def add_state(self, state):
312 self.states.append(state)
313 return state
314
315 def elaborate(self, platform=None):
316 """ creates the HDL code-fragment for FPAdd
317 """
318 m = Module()
319 #m.submodules += self.rs
320
321 in_a = self.rs[0][0]
322 in_b = self.rs[0][1]
323
324 geta = self.add_state(FPGetOp("get_a", "get_b",
325 in_a, self.width))
326 geta.setup(m, in_a)
327 a = geta.out_op
328
329 getb = self.add_state(FPGetOp("get_b", "fpadd",
330 in_b, self.width))
331 getb.setup(m, in_b)
332 b = getb.out_op
333
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)
339 o = ab.o
340
341 pz = self.add_state(FPPutZIdx("put_z", o.z, self.res,
342 o.mid, "get_a"))
343
344 with m.FSM() as fsm:
345
346 for state in self.states:
347 with m.State(state.state_from):
348 state.action(m)
349
350 return m
351
352
353 if __name__ == "__main__":
354 if True:
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])
360 else:
361 alu = FPADDBase(width=32, id_wid=5, single_cycle=True)
362 main(alu, ports=[alu.in_a, alu.in_b] + \
363 alu.in_t.ports() + \
364 alu.out_z.ports() + \
365 [alu.in_mid, alu.out_mid])
366
367
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() + \
372 # alu.out_z.ports())