comment out print debug statements
[ieee754fpu.git] / src / add / pipeline.py
1 """ Example 5: Making use of PyRTL and Introspection. """
2
3 from collections.abc import Sequence
4
5 from nmigen import Signal
6 from nmigen.hdl.rec import Record
7 from nmigen import tracer
8 from nmigen.compat.fhdl.bitcontainer import value_bits_sign
9 from contextlib import contextmanager
10
11 from singlepipe import eq, StageCls, ControlBase, BufferedHandshake
12 from singlepipe import UnbufferedPipeline
13
14
15 # The following example shows how pyrtl can be used to make some interesting
16 # hardware structures using python introspection. In particular, this example
17 # makes a N-stage pipeline structure. Any specific pipeline is then a derived
18 # class of SimplePipeline where methods with names starting with "stage" are
19 # stages, and new members with names not starting with "_" are to be registered
20 # for the next stage.
21
22 def like(value, rname, pipe, pipemode=False):
23 if isinstance(value, ObjectProxy):
24 return ObjectProxy.like(pipe, value, pipemode=pipemode,
25 name=rname, reset_less=True)
26 else:
27 return Signal(value_bits_sign(value), name=rname,
28 reset_less=True)
29 return Signal.like(value, name=rname, reset_less=True)
30
31 def get_assigns(_assigns):
32 assigns = []
33 for e in _assigns:
34 if isinstance(e, ObjectProxy):
35 assigns += get_assigns(e._assigns)
36 else:
37 assigns.append(e)
38 return assigns
39
40
41 def get_eqs(_eqs):
42 eqs = []
43 for e in _eqs:
44 if isinstance(e, ObjectProxy):
45 eqs += get_eqs(e._eqs)
46 else:
47 eqs.append(e)
48 return eqs
49
50
51 class ObjectProxy:
52 def __init__(self, m, name=None, pipemode=False, syncmode=True):
53 self._m = m
54 if name is None:
55 name = tracer.get_var_name(default=None)
56 self.name = name
57 self._pipemode = pipemode
58 self._syncmode = syncmode
59 self._eqs = {}
60 self._assigns = []
61 self._preg_map = {}
62
63 @classmethod
64 def like(cls, m, value, pipemode=False, name=None, src_loc_at=0, **kwargs):
65 name = name or tracer.get_var_name(depth=2 + src_loc_at,
66 default="$like")
67
68 src_loc_at_1 = 1 + src_loc_at
69 r = ObjectProxy(m, value.name, pipemode)
70 #for a, aname in value._preg_map.items():
71 # r._preg_map[aname] = like(a, aname, m, pipemode)
72 for a in value.ports():
73 aname = a.name
74 r._preg_map[aname] = like(a, aname, m, pipemode)
75 return r
76
77 def __repr__(self):
78 subobjs = []
79 for a in self.ports():
80 aname = a.name
81 ai = self._preg_map[aname]
82 subobjs.append(repr(ai))
83 return "<OP %s>" % subobjs
84
85 def get_specs(self, liked=False):
86 res = []
87 for k, v in self._preg_map.items():
88 #v = like(v, k, stage._m)
89 res.append(v)
90 if isinstance(v, ObjectProxy):
91 res += v.get_specs()
92 return res
93
94 def eq(self, i):
95 print ("ObjectProxy eq", self, i)
96 res = []
97 for a in self.ports():
98 aname = a.name
99 ai = i._preg_map[aname]
100 res.append(a.eq(ai))
101 return res
102
103 def ports(self):
104 res = []
105 for aname, a in self._preg_map.items():
106 if isinstance(a, Signal) or isinstance(a, ObjectProxy) or \
107 isinstance(a, Record):
108 res.append(a)
109 #print ("ObjectPorts", res)
110 return res
111
112 def __getattr__(self, name):
113 try:
114 v = self._preg_map[name]
115 return v
116 #return like(v, name, self._m)
117 except KeyError:
118 raise AttributeError(
119 'error, no pipeline register "%s" defined for OP %s'
120 % (name, self.name))
121
122 def __setattr__(self, name, value):
123 if name.startswith('_') or name in ['name', 'ports', 'eq', 'like']:
124 # do not do anything tricky with variables starting with '_'
125 object.__setattr__(self, name, value)
126 return
127 #rname = "%s_%s" % (self.name, name)
128 rname = name
129 new_pipereg = like(value, rname, self._m, self._pipemode)
130 self._preg_map[name] = new_pipereg
131 #object.__setattr__(self, name, new_pipereg)
132 if self._pipemode:
133 #print ("OP pipemode", self._syncmode, new_pipereg, value)
134 assign = eq(new_pipereg, value)
135 if self._syncmode:
136 self._m.d.sync += assign
137 else:
138 self._m.d.comb += assign
139 elif self._m:
140 #print ("OP !pipemode assign", new_pipereg, value, type(value))
141 self._m.d.comb += eq(new_pipereg, value)
142 else:
143 #print ("OP !pipemode !m", new_pipereg, value, type(value))
144 self._assigns += eq(new_pipereg, value)
145 if isinstance(value, ObjectProxy):
146 #print ("OP, defer assigns:", value._assigns)
147 self._assigns += value._assigns
148 self._eqs.append(value._eqs)
149
150
151 class PipelineStage:
152 """ Pipeline builder stage with auto generation of pipeline registers.
153 """
154
155 def __init__(self, name, m, prev=None, pipemode=False, ispec=None):
156 self._m = m
157 self._stagename = name
158 self._preg_map = {'__nextstage__': {}}
159 self._prev_stage = prev
160 self._ispec = ispec
161 if ispec:
162 self._preg_map[self._stagename] = ispec
163 if prev:
164 print ("prev", prev._stagename, prev._preg_map)
165 #if prev._stagename in prev._preg_map:
166 # m = prev._preg_map[prev._stagename]
167 # self._preg_map[prev._stagename] = m
168 if '__nextstage__' in prev._preg_map:
169 m = prev._preg_map['__nextstage__']
170 m = likedict(m)
171 self._preg_map[self._stagename] = m
172 #for k, v in m.items():
173 #m[k] = like(v, k, self._m)
174 print ("make current", self._stagename, m)
175 self._pipemode = pipemode
176 self._eqs = {}
177 self._assigns = []
178
179 def __getattribute__(self, name):
180 if name.startswith('_'):
181 return object.__getattribute__(self, name)
182 #if name in self._preg_map['__nextstage__']:
183 # return self._preg_map['__nextstage__'][name]
184 try:
185 print ("getattr", name, object.__getattribute__(self, '_preg_map'))
186 v = self._preg_map[self._stagename][name]
187 return v
188 #return like(v, name, self._m)
189 except KeyError:
190 raise AttributeError(
191 'error, no pipeline register "%s" defined for stage %s'
192 % (name, self._stagename))
193
194 def __setattr__(self, name, value):
195 if name.startswith('_'):
196 # do not do anything tricky with variables starting with '_'
197 object.__setattr__(self, name, value)
198 return
199 pipereg_id = self._stagename
200 rname = 'pipereg_' + pipereg_id + '_' + name
201 new_pipereg = like(value, rname, self._m, self._pipemode)
202 next_stage = '__nextstage__'
203 if next_stage not in self._preg_map:
204 self._preg_map[next_stage] = {}
205 self._preg_map[next_stage][name] = new_pipereg
206 print ("setattr", name, value, self._preg_map)
207 if self._pipemode:
208 self._eqs[name] = new_pipereg
209 assign = eq(new_pipereg, value)
210 print ("pipemode: append", new_pipereg, value, assign)
211 if isinstance(value, ObjectProxy):
212 print ("OP, assigns:", value._assigns)
213 self._assigns += value._assigns
214 self._eqs[name]._eqs = value._eqs
215 #self._m.d.comb += assign
216 self._assigns += assign
217 elif self._m:
218 print ("!pipemode: assign", new_pipereg, value)
219 assign = eq(new_pipereg, value)
220 self._m.d.sync += assign
221 else:
222 print ("!pipemode !m: defer assign", new_pipereg, value)
223 assign = eq(new_pipereg, value)
224 self._eqs[name] = new_pipereg
225 self._assigns += assign
226 if isinstance(value, ObjectProxy):
227 print ("OP, defer assigns:", value._assigns)
228 self._assigns += value._assigns
229 self._eqs[name]._eqs = value._eqs
230
231 def likelist(specs):
232 res = []
233 for v in specs:
234 res.append(like(v, v.name, None, pipemode=True))
235 return res
236
237 def likedict(specs):
238 if not isinstance(specs, dict):
239 return like(specs, specs.name, None, pipemode=True)
240 res = {}
241 for k, v in specs.items():
242 res[k] = likedict(v)
243 return res
244
245
246 class AutoStage(StageCls):
247 def __init__(self, inspecs, outspecs, eqs, assigns):
248 self.inspecs, self.outspecs = inspecs, outspecs
249 self.eqs, self.assigns = eqs, assigns
250 #self.o = self.ospec()
251 def ispec(self): return likedict(self.inspecs)
252 def ospec(self): return likedict(self.outspecs)
253
254 def process(self, i):
255 print ("stage process", i)
256 return self.eqs
257
258 def setup(self, m, i):
259 print ("stage setup i", i, m)
260 print ("stage setup inspecs", self.inspecs)
261 print ("stage setup outspecs", self.outspecs)
262 print ("stage setup eqs", self.eqs)
263 #self.o = self.ospec()
264 m.d.comb += eq(self.inspecs, i)
265 #m.d.comb += eq(self.outspecs, self.eqs)
266 #m.d.comb += eq(self.o, i)
267
268
269 class AutoPipe(UnbufferedPipeline):
270 def __init__(self, stage, assigns):
271 UnbufferedPipeline.__init__(self, stage)
272 self.assigns = assigns
273
274 def elaborate(self, platform):
275 m = UnbufferedPipeline.elaborate(self, platform)
276 m.d.comb += self.assigns
277 print ("assigns", self.assigns, m)
278 return m
279
280
281 class PipeManager:
282 def __init__(self, m, pipemode=False, pipetype=None):
283 self.m = m
284 self.pipemode = pipemode
285 self.pipetype = pipetype
286
287 @contextmanager
288 def Stage(self, name, prev=None, ispec=None):
289 if ispec:
290 ispec = likedict(ispec)
291 print ("start stage", name, ispec)
292 stage = PipelineStage(name, None, prev, self.pipemode, ispec=ispec)
293 try:
294 yield stage, self.m #stage._m
295 finally:
296 pass
297 if self.pipemode:
298 if stage._ispec:
299 print ("use ispec", stage._ispec)
300 inspecs = stage._ispec
301 else:
302 inspecs = self.get_specs(stage, name)
303 #inspecs = likedict(inspecs)
304 outspecs = self.get_specs(stage, '__nextstage__', liked=True)
305 print ("stage inspecs", name, inspecs)
306 print ("stage outspecs", name, outspecs)
307 eqs = stage._eqs # get_eqs(stage._eqs)
308 assigns = get_assigns(stage._assigns)
309 print ("stage eqs", name, eqs)
310 print ("stage assigns", name, assigns)
311 s = AutoStage(inspecs, outspecs, eqs, assigns)
312 self.stages.append(s)
313 print ("end stage", name, self.pipemode, "\n")
314
315 def get_specs(self, stage, name, liked=False):
316 return stage._preg_map[name]
317 if name in stage._preg_map:
318 res = []
319 for k, v in stage._preg_map[name].items():
320 #v = like(v, k, stage._m)
321 res.append(v)
322 #if isinstance(v, ObjectProxy):
323 # res += v.get_specs()
324 return res
325 return {}
326
327 def __enter__(self):
328 self.stages = []
329 return self
330
331 def __exit__(self, *args):
332 print ("exit stage", args)
333 pipes = []
334 cb = ControlBase()
335 for s in self.stages:
336 print ("stage specs", s, s.inspecs, s.outspecs)
337 if self.pipetype == 'buffered':
338 p = BufferedHandshake(s)
339 else:
340 p = AutoPipe(s, s.assigns)
341 pipes.append(p)
342 self.m.submodules += p
343
344 self.m.d.comb += cb.connect(pipes)
345
346
347 class SimplePipeline:
348 """ Pipeline builder with auto generation of pipeline registers.
349 """
350
351 def __init__(self, m):
352 self._m = m
353 self._pipeline_register_map = {}
354 self._current_stage_num = 0
355
356 def _setup(self):
357 stage_list = []
358 for method in dir(self):
359 if method.startswith('stage'):
360 stage_list.append(method)
361 for stage in sorted(stage_list):
362 stage_method = getattr(self, stage)
363 stage_method()
364 self._current_stage_num += 1
365
366 def __getattr__(self, name):
367 try:
368 return self._pipeline_register_map[self._current_stage_num][name]
369 except KeyError:
370 raise AttributeError(
371 'error, no pipeline register "%s" defined for stage %d'
372 % (name, self._current_stage_num))
373
374 def __setattr__(self, name, value):
375 if name.startswith('_'):
376 # do not do anything tricky with variables starting with '_'
377 object.__setattr__(self, name, value)
378 return
379 next_stage = self._current_stage_num + 1
380 pipereg_id = str(self._current_stage_num) + 'to' + str(next_stage)
381 rname = 'pipereg_' + pipereg_id + '_' + name
382 #new_pipereg = Signal(value_bits_sign(value), name=rname,
383 # reset_less=True)
384 if isinstance(value, ObjectProxy):
385 new_pipereg = ObjectProxy.like(self._m, value,
386 name=rname, reset_less = True)
387 else:
388 new_pipereg = Signal.like(value, name=rname, reset_less = True)
389 if next_stage not in self._pipeline_register_map:
390 self._pipeline_register_map[next_stage] = {}
391 self._pipeline_register_map[next_stage][name] = new_pipereg
392 self._m.d.sync += eq(new_pipereg, value)
393