use SimpleHandshake instead of UnbufferedPipeline
[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 FPOp
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 = FPOp(width)
35 self.mid = Signal(id_wid, reset_less=True)
36
37 def eq(self, i):
38 return [self.z.eq(i.z), self.mid.eq(i.mid)]
39
40 def ports(self):
41 return [self.z, self.mid]
42
43
44 class FPADDBaseMod:
45
46 def __init__(self, width, id_wid=None, single_cycle=False, compact=True):
47 """ IEEE754 FP Add
48
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
53 """
54 self.width = width
55 self.id_wid = id_wid
56 self.single_cycle = single_cycle
57 self.compact = compact
58
59 self.in_t = Trigger()
60 self.i = self.ispec()
61 self.o = self.ospec()
62
63 self.states = []
64
65 def ispec(self):
66 return FPADDBaseData(self.width, self.id_wid)
67
68 def ospec(self):
69 return FPOpData(self.width, self.id_wid)
70
71 def add_state(self, state):
72 self.states.append(state)
73 return state
74
75 def get_fragment(self, platform=None):
76 """ creates the HDL code-fragment for FPAdd
77 """
78 m = Module()
79 m.submodules.out_z = self.o.z
80 m.submodules.in_t = self.in_t
81 if self.compact:
82 self.get_compact_fragment(m, platform)
83 else:
84 self.get_longer_fragment(m, platform)
85
86 with m.FSM() as fsm:
87
88 for state in self.states:
89 with m.State(state.state_from):
90 state.action(m)
91
92 return m
93
94 def get_longer_fragment(self, m, platform=None):
95
96 get = self.add_state(FPGet2Op("get_ops", "special_cases",
97 self.width))
98 get.setup(m, self.i)
99 a = get.out_op1
100 b = get.out_op2
101 get.trigger_setup(m, self.in_t.stb, self.in_t.ack)
102
103 sc = self.add_state(FPAddSpecialCases(self.width, self.id_wid))
104 sc.setup(m, a, b, self.in_mid)
105
106 dn = self.add_state(FPAddDeNorm(self.width, self.id_wid))
107 dn.setup(m, a, b, sc.in_mid)
108
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)
112 else:
113 alm = self.add_state(FPAddAlignMulti(self.width, self.id_wid))
114 alm.setup(m, dn.out_a, dn.out_b, dn.in_mid)
115
116 add0 = self.add_state(FPAddStage0(self.width, self.id_wid))
117 add0.setup(m, alm.out_a, alm.out_b, alm.in_mid)
118
119 add1 = self.add_state(FPAddStage1(self.width, self.id_wid))
120 add1.setup(m, add0.out_tot, add0.out_z, add0.in_mid)
121
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)
125 else:
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)
128
129 rn = self.add_state(FPRound(self.width, self.id_wid))
130 rn.setup(m, n1.out_z, n1.out_roundz, n1.in_mid)
131
132 cor = self.add_state(FPCorrections(self.width, self.id_wid))
133 cor.setup(m, rn.out_z, rn.in_mid)
134
135 pa = self.add_state(FPPack(self.width, self.id_wid))
136 pa.setup(m, cor.out_z, rn.in_mid)
137
138 ppz = self.add_state(FPPutZ("pack_put_z", pa.out_z, self.out_z,
139 pa.in_mid, self.out_mid))
140
141 pz = self.add_state(FPPutZ("put_z", sc.out_z, self.out_z,
142 pa.in_mid, self.out_mid))
143
144 def get_compact_fragment(self, m, platform=None):
145
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)
150
151 get.trigger_setup(m, self.in_t.stb, self.in_t.ack)
152
153 chainlist = [get, sc, alm, n1]
154 chain = StageChain(chainlist, specallocate=True)
155 chain.setup(m, self.i)
156
157 for mod in chainlist:
158 sc = self.add_state(mod)
159
160 ppz = self.add_state(FPPutZ("pack_put_z", n1.out_z.z, self.o,
161 n1.out_z.mid, self.o.mid))
162
163 #pz = self.add_state(FPPutZ("put_z", sc.out_z.z, self.o,
164 # sc.o.mid, self.o.mid))
165
166
167 class FPADDBase(FPState):
168
169 def __init__(self, width, id_wid=None, single_cycle=False):
170 """ IEEE754 FP Add
171
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
175 """
176 FPState.__init__(self, "fpadd")
177 self.width = width
178 self.single_cycle = single_cycle
179 self.mod = FPADDBaseMod(width, id_wid, single_cycle)
180 self.o = self.ospec()
181
182 self.in_t = Trigger()
183 self.i = self.ispec()
184
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)
189
190 def ispec(self):
191 return self.mod.ispec()
192
193 def ospec(self):
194 return self.mod.ospec()
195
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),
207 ]
208
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)
213
214 m.submodules.fpadd = self.mod
215
216 def action(self, m):
217
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))
220
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):
226 m.d.sync += [
227 self.add_ack.eq(1), # acknowledge receipt...
228 self.in_t.stb.eq(1), # initiate add
229 ]
230 with m.Else():
231 m.d.sync += [self.add_ack.eq(0),
232 self.in_t.stb.eq(0),
233 self.o.z.ack.eq(1),
234 ]
235 with m.Else():
236 # done: acknowledge, and write out id and value
237 m.d.sync += [self.add_ack.eq(1),
238 self.in_t.stb.eq(0)
239 ]
240 m.next = "put_z"
241
242 return
243
244 if self.in_mid is not None:
245 m.d.sync += self.out_mid.eq(self.mod.out_mid)
246
247 m.d.sync += [
248 self.out_z.v.eq(self.mod.out_z.v)
249 ]
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)
253 m.next = "put_z"
254 with m.Else():
255 m.d.sync += self.out_z.stb.eq(1)
256
257
258 class FPADD(FPID):
259 """ FPADD: stages as follows:
260
261 FPGetOp (a)
262 |
263 FPGetOp (b)
264 |
265 FPAddBase---> FPAddBaseMod
266 | |
267 PutZ GetOps->Specials->Align->Add1/2->Norm->Round/Pack->PutZ
268
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.
273 """
274
275 def __init__(self, width, id_wid=None, single_cycle=False, rs_sz=2):
276 """ IEEE754 FP Add
277
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
281 """
282 self.width = width
283 self.id_wid = id_wid
284 self.single_cycle = single_cycle
285
286 #self.out_z = FPOp(width)
287 self.ids = FPID(id_wid)
288
289 rs = []
290 for i in range(rs_sz):
291 in_a = FPOp(width)
292 in_b = FPOp(width)
293 in_a.name = "in_a_%d" % i
294 in_b.name = "in_b_%d" % i
295 rs.append((in_a, in_b))
296 self.rs = Array(rs)
297
298 res = []
299 for i in range(rs_sz):
300 out_z = FPOp(width)
301 out_z.name = "out_z_%d" % i
302 res.append(out_z)
303 self.res = Array(res)
304
305 self.states = []
306
307 def add_state(self, state):
308 self.states.append(state)
309 return state
310
311 def get_fragment(self, platform=None):
312 """ creates the HDL code-fragment for FPAdd
313 """
314 m = Module()
315 m.submodules += self.rs
316
317 in_a = self.rs[0][0]
318 in_b = self.rs[0][1]
319
320 geta = self.add_state(FPGetOp("get_a", "get_b",
321 in_a, self.width))
322 geta.setup(m, in_a)
323 a = geta.out_op
324
325 getb = self.add_state(FPGetOp("get_b", "fpadd",
326 in_b, self.width))
327 getb.setup(m, in_b)
328 b = getb.out_op
329
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)
335 o = ab.o
336
337 pz = self.add_state(FPPutZIdx("put_z", o.z, self.res,
338 o.mid, "get_a"))
339
340 with m.FSM() as fsm:
341
342 for state in self.states:
343 with m.State(state.state_from):
344 state.action(m)
345
346 return m
347
348
349 if __name__ == "__main__":
350 if True:
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])
356 else:
357 alu = FPADDBase(width=32, id_wid=5, single_cycle=True)
358 main(alu, ports=[alu.in_a, alu.in_b] + \
359 alu.in_t.ports() + \
360 alu.out_z.ports() + \
361 [alu.in_mid, alu.out_mid])
362
363
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() + \
368 # alu.out_z.ports())