2 This work is funded through NLnet under Grant 2019-02-012
9 """ Example 5: Making use of PyRTL and Introspection. """
11 from collections
.abc
import Sequence
13 from nmigen
import Signal
14 from nmigen
.hdl
.rec
import Record
15 from nmigen
import tracer
16 from nmigen
.compat
.fhdl
.bitcontainer
import value_bits_sign
17 from contextlib
import contextmanager
19 from nmutil
.nmoperator
import eq
20 from nmutil
.singlepipe
import StageCls
, ControlBase
, BufferedHandshake
21 from nmutil
.singlepipe
import UnbufferedPipeline
24 # The following example shows how pyrtl can be used to make some interesting
25 # hardware structures using python introspection. In particular, this example
26 # makes a N-stage pipeline structure. Any specific pipeline is then a derived
27 # class of SimplePipeline where methods with names starting with "stage" are
28 # stages, and new members with names not starting with "_" are to be registered
31 def like(value
, rname
, pipe
, pipemode
=False):
32 if isinstance(value
, ObjectProxy
):
33 return ObjectProxy
.like(pipe
, value
, pipemode
=pipemode
,
34 name
=rname
, reset_less
=True)
36 return Signal(value_bits_sign(value
), name
=rname
,
38 return Signal
.like(value
, name
=rname
, reset_less
=True)
40 def get_assigns(_assigns
):
43 if isinstance(e
, ObjectProxy
):
44 assigns
+= get_assigns(e
._assigns
)
53 if isinstance(e
, ObjectProxy
):
54 eqs
+= get_eqs(e
._eqs
)
61 def __init__(self
, m
, name
=None, pipemode
=False, syncmode
=True):
64 name
= tracer
.get_var_name(default
=None)
66 self
._pipemode
= pipemode
67 self
._syncmode
= syncmode
73 def like(cls
, m
, value
, pipemode
=False, name
=None, src_loc_at
=0, **kwargs
):
74 name
= name
or tracer
.get_var_name(depth
=2 + src_loc_at
,
77 src_loc_at_1
= 1 + src_loc_at
78 r
= ObjectProxy(m
, value
.name
, pipemode
)
79 #for a, aname in value._preg_map.items():
80 # r._preg_map[aname] = like(a, aname, m, pipemode)
81 for a
in value
.ports():
83 r
._preg
_map
[aname
] = like(a
, aname
, m
, pipemode
)
88 for a
in self
.ports():
90 ai
= self
._preg
_map
[aname
]
91 subobjs
.append(repr(ai
))
92 return "<OP %s>" % subobjs
94 def get_specs(self
, liked
=False):
96 for k
, v
in self
._preg
_map
.items():
97 #v = like(v, k, stage._m)
99 if isinstance(v
, ObjectProxy
):
104 print ("ObjectProxy eq", self
, i
)
106 for a
in self
.ports():
108 ai
= i
._preg
_map
[aname
]
114 for aname
, a
in self
._preg
_map
.items():
115 if isinstance(a
, Signal
) or isinstance(a
, ObjectProxy
) or \
116 isinstance(a
, Record
):
118 #print ("ObjectPorts", res)
121 def __getattr__(self
, name
):
123 v
= self
._preg
_map
[name
]
125 #return like(v, name, self._m)
127 raise AttributeError(
128 'error, no pipeline register "%s" defined for OP %s'
131 def __setattr__(self
, name
, value
):
132 if name
.startswith('_') or name
in ['name', 'ports', 'eq', 'like']:
133 # do not do anything tricky with variables starting with '_'
134 object.__setattr
__(self
, name
, value
)
136 #rname = "%s_%s" % (self.name, name)
138 new_pipereg
= like(value
, rname
, self
._m
, self
._pipemode
)
139 self
._preg
_map
[name
] = new_pipereg
140 #object.__setattr__(self, name, new_pipereg)
142 #print ("OP pipemode", self._syncmode, new_pipereg, value)
143 assign
= eq(new_pipereg
, value
)
145 self
._m
.d
.sync
+= assign
147 self
._m
.d
.comb
+= assign
149 #print ("OP !pipemode assign", new_pipereg, value, type(value))
150 self
._m
.d
.comb
+= eq(new_pipereg
, value
)
152 #print ("OP !pipemode !m", new_pipereg, value, type(value))
153 self
._assigns
+= eq(new_pipereg
, value
)
154 if isinstance(value
, ObjectProxy
):
155 #print ("OP, defer assigns:", value._assigns)
156 self
._assigns
+= value
._assigns
157 self
._eqs
.append(value
._eqs
)
161 """ Pipeline builder stage with auto generation of pipeline registers.
164 def __init__(self
, name
, m
, prev
=None, pipemode
=False, ispec
=None):
166 self
._stagename
= name
167 self
._preg
_map
= {'__nextstage__': {}}
168 self
._prev
_stage
= prev
171 self
._preg
_map
[self
._stagename
] = ispec
173 print ("prev", prev
._stagename
, prev
._preg
_map
)
174 #if prev._stagename in prev._preg_map:
175 # m = prev._preg_map[prev._stagename]
176 # self._preg_map[prev._stagename] = m
177 if '__nextstage__' in prev
._preg
_map
:
178 m
= prev
._preg
_map
['__nextstage__']
180 self
._preg
_map
[self
._stagename
] = m
181 #for k, v in m.items():
182 #m[k] = like(v, k, self._m)
183 print ("make current", self
._stagename
, m
)
184 self
._pipemode
= pipemode
188 def __getattribute__(self
, name
):
189 if name
.startswith('_'):
190 return object.__getattribute
__(self
, name
)
191 #if name in self._preg_map['__nextstage__']:
192 # return self._preg_map['__nextstage__'][name]
194 print ("getattr", name
, object.__getattribute
__(self
, '_preg_map'))
195 v
= self
._preg
_map
[self
._stagename
][name
]
197 #return like(v, name, self._m)
199 raise AttributeError(
200 'error, no pipeline register "%s" defined for stage %s'
201 % (name
, self
._stagename
))
203 def __setattr__(self
, name
, value
):
204 if name
.startswith('_'):
205 # do not do anything tricky with variables starting with '_'
206 object.__setattr
__(self
, name
, value
)
208 pipereg_id
= self
._stagename
209 rname
= 'pipereg_' + pipereg_id
+ '_' + name
210 new_pipereg
= like(value
, rname
, self
._m
, self
._pipemode
)
211 next_stage
= '__nextstage__'
212 if next_stage
not in self
._preg
_map
:
213 self
._preg
_map
[next_stage
] = {}
214 self
._preg
_map
[next_stage
][name
] = new_pipereg
215 print ("setattr", name
, value
, self
._preg
_map
)
217 self
._eqs
[name
] = new_pipereg
218 assign
= eq(new_pipereg
, value
)
219 print ("pipemode: append", new_pipereg
, value
, assign
)
220 if isinstance(value
, ObjectProxy
):
221 print ("OP, assigns:", value
._assigns
)
222 self
._assigns
+= value
._assigns
223 self
._eqs
[name
]._eqs
= value
._eqs
224 #self._m.d.comb += assign
225 self
._assigns
+= assign
227 print ("!pipemode: assign", new_pipereg
, value
)
228 assign
= eq(new_pipereg
, value
)
229 self
._m
.d
.sync
+= assign
231 print ("!pipemode !m: defer assign", new_pipereg
, value
)
232 assign
= eq(new_pipereg
, value
)
233 self
._eqs
[name
] = new_pipereg
234 self
._assigns
+= assign
235 if isinstance(value
, ObjectProxy
):
236 print ("OP, defer assigns:", value
._assigns
)
237 self
._assigns
+= value
._assigns
238 self
._eqs
[name
]._eqs
= value
._eqs
243 res
.append(like(v
, v
.name
, None, pipemode
=True))
247 if not isinstance(specs
, dict):
248 return like(specs
, specs
.name
, None, pipemode
=True)
250 for k
, v
in specs
.items():
255 class AutoStage(StageCls
):
256 def __init__(self
, inspecs
, outspecs
, eqs
, assigns
):
257 self
.inspecs
, self
.outspecs
= inspecs
, outspecs
258 self
.eqs
, self
.assigns
= eqs
, assigns
259 #self.o = self.ospec()
260 def ispec(self
): return likedict(self
.inspecs
)
261 def ospec(self
): return likedict(self
.outspecs
)
263 def process(self
, i
):
264 print ("stage process", i
)
267 def setup(self
, m
, i
):
268 print ("stage setup i", i
, m
)
269 print ("stage setup inspecs", self
.inspecs
)
270 print ("stage setup outspecs", self
.outspecs
)
271 print ("stage setup eqs", self
.eqs
)
272 #self.o = self.ospec()
273 m
.d
.comb
+= eq(self
.inspecs
, i
)
274 #m.d.comb += eq(self.outspecs, self.eqs)
275 #m.d.comb += eq(self.o, i)
278 class AutoPipe(UnbufferedPipeline
):
279 def __init__(self
, stage
, assigns
):
280 UnbufferedPipeline
.__init
__(self
, stage
)
281 self
.assigns
= assigns
283 def elaborate(self
, platform
):
284 m
= UnbufferedPipeline
.elaborate(self
, platform
)
285 m
.d
.comb
+= self
.assigns
286 print ("assigns", self
.assigns
, m
)
291 def __init__(self
, m
, pipemode
=False, pipetype
=None):
293 self
.pipemode
= pipemode
294 self
.pipetype
= pipetype
297 def Stage(self
, name
, prev
=None, ispec
=None):
299 ispec
= likedict(ispec
)
300 print ("start stage", name
, ispec
)
301 stage
= PipelineStage(name
, None, prev
, self
.pipemode
, ispec
=ispec
)
303 yield stage
, self
.m
#stage._m
308 print ("use ispec", stage
._ispec
)
309 inspecs
= stage
._ispec
311 inspecs
= self
.get_specs(stage
, name
)
312 #inspecs = likedict(inspecs)
313 outspecs
= self
.get_specs(stage
, '__nextstage__', liked
=True)
314 print ("stage inspecs", name
, inspecs
)
315 print ("stage outspecs", name
, outspecs
)
316 eqs
= stage
._eqs
# get_eqs(stage._eqs)
317 assigns
= get_assigns(stage
._assigns
)
318 print ("stage eqs", name
, eqs
)
319 print ("stage assigns", name
, assigns
)
320 s
= AutoStage(inspecs
, outspecs
, eqs
, assigns
)
321 self
.stages
.append(s
)
322 print ("end stage", name
, self
.pipemode
, "\n")
324 def get_specs(self
, stage
, name
, liked
=False):
325 return stage
._preg
_map
[name
]
326 if name
in stage
._preg
_map
:
328 for k
, v
in stage
._preg
_map
[name
].items():
329 #v = like(v, k, stage._m)
331 #if isinstance(v, ObjectProxy):
332 # res += v.get_specs()
340 def __exit__(self
, *args
):
341 print ("exit stage", args
)
344 for s
in self
.stages
:
345 print ("stage specs", s
, s
.inspecs
, s
.outspecs
)
346 if self
.pipetype
== 'buffered':
347 p
= BufferedHandshake(s
)
349 p
= AutoPipe(s
, s
.assigns
)
351 self
.m
.submodules
+= p
353 self
.m
.d
.comb
+= cb
.connect(pipes
)
356 class SimplePipeline
:
357 """ Pipeline builder with auto generation of pipeline registers.
360 def __init__(self
, m
):
362 self
._pipeline
_register
_map
= {}
363 self
._current
_stage
_num
= 0
367 for method
in dir(self
):
368 if method
.startswith('stage'):
369 stage_list
.append(method
)
370 for stage
in sorted(stage_list
):
371 stage_method
= getattr(self
, stage
)
373 self
._current
_stage
_num
+= 1
375 def __getattr__(self
, name
):
377 return self
._pipeline
_register
_map
[self
._current
_stage
_num
][name
]
379 raise AttributeError(
380 'error, no pipeline register "%s" defined for stage %d'
381 % (name
, self
._current
_stage
_num
))
383 def __setattr__(self
, name
, value
):
384 if name
.startswith('_'):
385 # do not do anything tricky with variables starting with '_'
386 object.__setattr
__(self
, name
, value
)
388 next_stage
= self
._current
_stage
_num
+ 1
389 pipereg_id
= str(self
._current
_stage
_num
) + 'to' + str(next_stage
)
390 rname
= 'pipereg_' + pipereg_id
+ '_' + name
391 #new_pipereg = Signal(value_bits_sign(value), name=rname,
393 if isinstance(value
, ObjectProxy
):
394 new_pipereg
= ObjectProxy
.like(self
._m
, value
,
395 name
=rname
, reset_less
= True)
397 new_pipereg
= Signal
.like(value
, name
=rname
, reset_less
= True)
398 if next_stage
not in self
._pipeline
_register
_map
:
399 self
._pipeline
_register
_map
[next_stage
] = {}
400 self
._pipeline
_register
_map
[next_stage
][name
] = new_pipereg
401 self
._m
.d
.sync
+= eq(new_pipereg
, value
)