reorganise imports
[ieee754fpu.git] / src / ieee754 / 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 ieee754.fpcommon.getop import (FPGetOp, FPADDBaseData, FPGet2Op)
15 from ieee754.fpcommon.denorm import (FPSCData, FPAddDeNorm)
16 from ieee754.fpcommon.postcalc import FPAddStage1Data
17 from ieee754.fpcommon.postnormalise import (FPNorm1Data,
18 FPNorm1Single, FPNorm1Multi)
19 from ieee754.fpcommon.roundz import (FPRoundData, FPRound)
20 from ieee754.fpcommon.corrections import FPCorrections
21 from ieee754.fpcommon.pack import (FPPackData, FPPackMod, FPPack)
22 from ieee754.fpcommon.normtopack import FPNormToPack
23 from ieee754.fpcommon.putz import (FPPutZ, FPPutZIdx)
24
25 from .specialcases import (FPAddSpecialCases, FPAddSpecialCasesDeNorm)
26 from .align import (FPAddAlignMulti, FPAddAlignSingle)
27 from .add0 import (FPAddStage0Data, FPAddStage0)
28 from .add1 import (FPAddStage1Mod, FPAddStage1)
29 from .addstages import FPAddAlignSingleAdd
30
31
32 class FPOpData:
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)
37
38 def __iter__(self):
39 yield self.z
40 yield self.mid
41
42 def eq(self, i):
43 return [self.z.eq(i.z), self.mid.eq(i.mid)]
44
45 def ports(self):
46 return list(self)
47
48
49 class FPADDBaseMod:
50
51 def __init__(self, width, id_wid=None, single_cycle=False, compact=True):
52 """ IEEE754 FP Add
53
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
58 """
59 self.width = width
60 self.id_wid = id_wid
61 self.single_cycle = single_cycle
62 self.compact = compact
63
64 self.in_t = Trigger()
65 self.i = self.ispec()
66 self.o = self.ospec()
67
68 self.states = []
69
70 def ispec(self):
71 return FPADDBaseData(self.width, self.id_wid)
72
73 def ospec(self):
74 return FPOpData(self.width, self.id_wid)
75
76 def add_state(self, state):
77 self.states.append(state)
78 return state
79
80 def elaborate(self, platform=None):
81 """ creates the HDL code-fragment for FPAdd
82 """
83 m = Module()
84 m.submodules.out_z = self.o.z
85 m.submodules.in_t = self.in_t
86 if self.compact:
87 self.get_compact_fragment(m, platform)
88 else:
89 self.get_longer_fragment(m, platform)
90
91 with m.FSM() as fsm:
92
93 for state in self.states:
94 with m.State(state.state_from):
95 state.action(m)
96
97 return m
98
99 def get_longer_fragment(self, m, platform=None):
100
101 get = self.add_state(FPGet2Op("get_ops", "special_cases",
102 self.width))
103 get.setup(m, self.i)
104 a = get.out_op1
105 b = get.out_op2
106 get.trigger_setup(m, self.in_t.stb, self.in_t.ack)
107
108 sc = self.add_state(FPAddSpecialCases(self.width, self.id_wid))
109 sc.setup(m, a, b, self.in_mid)
110
111 dn = self.add_state(FPAddDeNorm(self.width, self.id_wid))
112 dn.setup(m, a, b, sc.in_mid)
113
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)
117 else:
118 alm = self.add_state(FPAddAlignMulti(self.width, self.id_wid))
119 alm.setup(m, dn.out_a, dn.out_b, dn.in_mid)
120
121 add0 = self.add_state(FPAddStage0(self.width, self.id_wid))
122 add0.setup(m, alm.out_a, alm.out_b, alm.in_mid)
123
124 add1 = self.add_state(FPAddStage1(self.width, self.id_wid))
125 add1.setup(m, add0.out_tot, add0.out_z, add0.in_mid)
126
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)
130 else:
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)
133
134 rn = self.add_state(FPRound(self.width, self.id_wid))
135 rn.setup(m, n1.out_z, n1.out_roundz, n1.in_mid)
136
137 cor = self.add_state(FPCorrections(self.width, self.id_wid))
138 cor.setup(m, rn.out_z, rn.in_mid)
139
140 pa = self.add_state(FPPack(self.width, self.id_wid))
141 pa.setup(m, cor.out_z, rn.in_mid)
142
143 ppz = self.add_state(FPPutZ("pack_put_z", pa.out_z, self.out_z,
144 pa.in_mid, self.out_mid))
145
146 pz = self.add_state(FPPutZ("put_z", sc.out_z, self.out_z,
147 pa.in_mid, self.out_mid))
148
149 def get_compact_fragment(self, m, platform=None):
150
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)
155
156 get.trigger_setup(m, self.in_t.stb, self.in_t.ack)
157
158 chainlist = [get, sc, alm, n1]
159 chain = StageChain(chainlist, specallocate=True)
160 chain.setup(m, self.i)
161
162 for mod in chainlist:
163 sc = self.add_state(mod)
164
165 ppz = self.add_state(FPPutZ("pack_put_z", n1.out_z.z, self.o,
166 n1.out_z.mid, self.o.mid))
167
168 #pz = self.add_state(FPPutZ("put_z", sc.out_z.z, self.o,
169 # sc.o.mid, self.o.mid))
170
171
172 class FPADDBase(FPState):
173
174 def __init__(self, width, id_wid=None, single_cycle=False):
175 """ IEEE754 FP Add
176
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
180 """
181 FPState.__init__(self, "fpadd")
182 self.width = width
183 self.single_cycle = single_cycle
184 self.mod = FPADDBaseMod(width, id_wid, single_cycle)
185 self.o = self.ospec()
186
187 self.in_t = Trigger()
188 self.i = self.ispec()
189
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)
194
195 def ispec(self):
196 return self.mod.ispec()
197
198 def ospec(self):
199 return self.mod.ospec()
200
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),
212 ]
213
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)
218
219 m.submodules.fpadd = self.mod
220
221 def action(self, m):
222
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))
225
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):
231 m.d.sync += [
232 self.add_ack.eq(1), # acknowledge receipt...
233 self.in_t.stb.eq(1), # initiate add
234 ]
235 with m.Else():
236 m.d.sync += [self.add_ack.eq(0),
237 self.in_t.stb.eq(0),
238 self.o.z.ready_i.eq(1),
239 ]
240 with m.Else():
241 # done: acknowledge, and write out id and value
242 m.d.sync += [self.add_ack.eq(1),
243 self.in_t.stb.eq(0)
244 ]
245 m.next = "put_z"
246
247 return
248
249 if self.in_mid is not None:
250 m.d.sync += self.out_mid.eq(self.mod.out_mid)
251
252 m.d.sync += [
253 self.out_z.v.eq(self.mod.out_z.v)
254 ]
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)
258 m.next = "put_z"
259 with m.Else():
260 m.d.sync += self.out_z.stb.eq(1)
261
262
263 class FPADD(FPID):
264 """ FPADD: stages as follows:
265
266 FPGetOp (a)
267 |
268 FPGetOp (b)
269 |
270 FPAddBase---> FPAddBaseMod
271 | |
272 PutZ GetOps->Specials->Align->Add1/2->Norm->Round/Pack->PutZ
273
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.
278 """
279
280 def __init__(self, width, id_wid=None, single_cycle=False, rs_sz=2):
281 """ IEEE754 FP Add
282
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
286 """
287 self.width = width
288 self.id_wid = id_wid
289 self.single_cycle = single_cycle
290
291 #self.out_z = FPOp(width)
292 self.ids = FPID(id_wid)
293
294 rs = []
295 for i in range(rs_sz):
296 in_a = FPOpIn(width)
297 in_b = FPOpIn(width)
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))
303 self.rs = Array(rs)
304
305 res = []
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
310 res.append(out_z)
311 self.res = Array(res)
312
313 self.states = []
314
315 def add_state(self, state):
316 self.states.append(state)
317 return state
318
319 def elaborate(self, platform=None):
320 """ creates the HDL code-fragment for FPAdd
321 """
322 m = Module()
323 #m.submodules += self.rs
324
325 in_a = self.rs[0][0]
326 in_b = self.rs[0][1]
327
328 geta = self.add_state(FPGetOp("get_a", "get_b",
329 in_a, self.width))
330 geta.setup(m, in_a)
331 a = geta.out_op
332
333 getb = self.add_state(FPGetOp("get_b", "fpadd",
334 in_b, self.width))
335 getb.setup(m, in_b)
336 b = getb.out_op
337
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)
343 o = ab.o
344
345 pz = self.add_state(FPPutZIdx("put_z", o.z, self.res,
346 o.mid, "get_a"))
347
348 with m.FSM() as fsm:
349
350 for state in self.states:
351 with m.State(state.state_from):
352 state.action(m)
353
354 return m
355
356
357 if __name__ == "__main__":
358 if True:
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])
364 else:
365 alu = FPADDBase(width=32, id_wid=5, single_cycle=True)
366 main(alu, ports=[alu.in_a, alu.in_b] + \
367 alu.in_t.ports() + \
368 alu.out_z.ports() + \
369 [alu.in_mid, alu.out_mid])
370
371
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() + \
376 # alu.out_z.ports())