1 """ Example 5: Making use of PyRTL and Introspection. """
3 from collections
.abc
import Sequence
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
11 from nmutil
.nmoperator
import eq
12 from nmutil
.singlepipe
import StageCls
, ControlBase
, BufferedHandshake
13 from nmutil
.singlepipe
import UnbufferedPipeline
16 # The following example shows how pyrtl can be used to make some interesting
17 # hardware structures using python introspection. In particular, this example
18 # makes a N-stage pipeline structure. Any specific pipeline is then a derived
19 # class of SimplePipeline where methods with names starting with "stage" are
20 # stages, and new members with names not starting with "_" are to be registered
23 def like(value
, rname
, pipe
, pipemode
=False):
24 if isinstance(value
, ObjectProxy
):
25 return ObjectProxy
.like(pipe
, value
, pipemode
=pipemode
,
26 name
=rname
, reset_less
=True)
28 return Signal(value_bits_sign(value
), name
=rname
,
30 return Signal
.like(value
, name
=rname
, reset_less
=True)
32 def get_assigns(_assigns
):
35 if isinstance(e
, ObjectProxy
):
36 assigns
+= get_assigns(e
._assigns
)
45 if isinstance(e
, ObjectProxy
):
46 eqs
+= get_eqs(e
._eqs
)
53 def __init__(self
, m
, name
=None, pipemode
=False, syncmode
=True):
56 name
= tracer
.get_var_name(default
=None)
58 self
._pipemode
= pipemode
59 self
._syncmode
= syncmode
65 def like(cls
, m
, value
, pipemode
=False, name
=None, src_loc_at
=0, **kwargs
):
66 name
= name
or tracer
.get_var_name(depth
=2 + src_loc_at
,
69 src_loc_at_1
= 1 + src_loc_at
70 r
= ObjectProxy(m
, value
.name
, pipemode
)
71 #for a, aname in value._preg_map.items():
72 # r._preg_map[aname] = like(a, aname, m, pipemode)
73 for a
in value
.ports():
75 r
._preg
_map
[aname
] = like(a
, aname
, m
, pipemode
)
80 for a
in self
.ports():
82 ai
= self
._preg
_map
[aname
]
83 subobjs
.append(repr(ai
))
84 return "<OP %s>" % subobjs
86 def get_specs(self
, liked
=False):
88 for k
, v
in self
._preg
_map
.items():
89 #v = like(v, k, stage._m)
91 if isinstance(v
, ObjectProxy
):
96 print ("ObjectProxy eq", self
, i
)
98 for a
in self
.ports():
100 ai
= i
._preg
_map
[aname
]
106 for aname
, a
in self
._preg
_map
.items():
107 if isinstance(a
, Signal
) or isinstance(a
, ObjectProxy
) or \
108 isinstance(a
, Record
):
110 #print ("ObjectPorts", res)
113 def __getattr__(self
, name
):
115 v
= self
._preg
_map
[name
]
117 #return like(v, name, self._m)
119 raise AttributeError(
120 'error, no pipeline register "%s" defined for OP %s'
123 def __setattr__(self
, name
, value
):
124 if name
.startswith('_') or name
in ['name', 'ports', 'eq', 'like']:
125 # do not do anything tricky with variables starting with '_'
126 object.__setattr
__(self
, name
, value
)
128 #rname = "%s_%s" % (self.name, name)
130 new_pipereg
= like(value
, rname
, self
._m
, self
._pipemode
)
131 self
._preg
_map
[name
] = new_pipereg
132 #object.__setattr__(self, name, new_pipereg)
134 #print ("OP pipemode", self._syncmode, new_pipereg, value)
135 assign
= eq(new_pipereg
, value
)
137 self
._m
.d
.sync
+= assign
139 self
._m
.d
.comb
+= assign
141 #print ("OP !pipemode assign", new_pipereg, value, type(value))
142 self
._m
.d
.comb
+= eq(new_pipereg
, value
)
144 #print ("OP !pipemode !m", new_pipereg, value, type(value))
145 self
._assigns
+= eq(new_pipereg
, value
)
146 if isinstance(value
, ObjectProxy
):
147 #print ("OP, defer assigns:", value._assigns)
148 self
._assigns
+= value
._assigns
149 self
._eqs
.append(value
._eqs
)
153 """ Pipeline builder stage with auto generation of pipeline registers.
156 def __init__(self
, name
, m
, prev
=None, pipemode
=False, ispec
=None):
158 self
._stagename
= name
159 self
._preg
_map
= {'__nextstage__': {}}
160 self
._prev
_stage
= prev
163 self
._preg
_map
[self
._stagename
] = ispec
165 print ("prev", prev
._stagename
, prev
._preg
_map
)
166 #if prev._stagename in prev._preg_map:
167 # m = prev._preg_map[prev._stagename]
168 # self._preg_map[prev._stagename] = m
169 if '__nextstage__' in prev
._preg
_map
:
170 m
= prev
._preg
_map
['__nextstage__']
172 self
._preg
_map
[self
._stagename
] = m
173 #for k, v in m.items():
174 #m[k] = like(v, k, self._m)
175 print ("make current", self
._stagename
, m
)
176 self
._pipemode
= pipemode
180 def __getattribute__(self
, name
):
181 if name
.startswith('_'):
182 return object.__getattribute
__(self
, name
)
183 #if name in self._preg_map['__nextstage__']:
184 # return self._preg_map['__nextstage__'][name]
186 print ("getattr", name
, object.__getattribute
__(self
, '_preg_map'))
187 v
= self
._preg
_map
[self
._stagename
][name
]
189 #return like(v, name, self._m)
191 raise AttributeError(
192 'error, no pipeline register "%s" defined for stage %s'
193 % (name
, self
._stagename
))
195 def __setattr__(self
, name
, value
):
196 if name
.startswith('_'):
197 # do not do anything tricky with variables starting with '_'
198 object.__setattr
__(self
, name
, value
)
200 pipereg_id
= self
._stagename
201 rname
= 'pipereg_' + pipereg_id
+ '_' + name
202 new_pipereg
= like(value
, rname
, self
._m
, self
._pipemode
)
203 next_stage
= '__nextstage__'
204 if next_stage
not in self
._preg
_map
:
205 self
._preg
_map
[next_stage
] = {}
206 self
._preg
_map
[next_stage
][name
] = new_pipereg
207 print ("setattr", name
, value
, self
._preg
_map
)
209 self
._eqs
[name
] = new_pipereg
210 assign
= eq(new_pipereg
, value
)
211 print ("pipemode: append", new_pipereg
, value
, assign
)
212 if isinstance(value
, ObjectProxy
):
213 print ("OP, assigns:", value
._assigns
)
214 self
._assigns
+= value
._assigns
215 self
._eqs
[name
]._eqs
= value
._eqs
216 #self._m.d.comb += assign
217 self
._assigns
+= assign
219 print ("!pipemode: assign", new_pipereg
, value
)
220 assign
= eq(new_pipereg
, value
)
221 self
._m
.d
.sync
+= assign
223 print ("!pipemode !m: defer assign", new_pipereg
, value
)
224 assign
= eq(new_pipereg
, value
)
225 self
._eqs
[name
] = new_pipereg
226 self
._assigns
+= assign
227 if isinstance(value
, ObjectProxy
):
228 print ("OP, defer assigns:", value
._assigns
)
229 self
._assigns
+= value
._assigns
230 self
._eqs
[name
]._eqs
= value
._eqs
235 res
.append(like(v
, v
.name
, None, pipemode
=True))
239 if not isinstance(specs
, dict):
240 return like(specs
, specs
.name
, None, pipemode
=True)
242 for k
, v
in specs
.items():
247 class AutoStage(StageCls
):
248 def __init__(self
, inspecs
, outspecs
, eqs
, assigns
):
249 self
.inspecs
, self
.outspecs
= inspecs
, outspecs
250 self
.eqs
, self
.assigns
= eqs
, assigns
251 #self.o = self.ospec()
252 def ispec(self
): return likedict(self
.inspecs
)
253 def ospec(self
): return likedict(self
.outspecs
)
255 def process(self
, i
):
256 print ("stage process", i
)
259 def setup(self
, m
, i
):
260 print ("stage setup i", i
, m
)
261 print ("stage setup inspecs", self
.inspecs
)
262 print ("stage setup outspecs", self
.outspecs
)
263 print ("stage setup eqs", self
.eqs
)
264 #self.o = self.ospec()
265 m
.d
.comb
+= eq(self
.inspecs
, i
)
266 #m.d.comb += eq(self.outspecs, self.eqs)
267 #m.d.comb += eq(self.o, i)
270 class AutoPipe(UnbufferedPipeline
):
271 def __init__(self
, stage
, assigns
):
272 UnbufferedPipeline
.__init
__(self
, stage
)
273 self
.assigns
= assigns
275 def elaborate(self
, platform
):
276 m
= UnbufferedPipeline
.elaborate(self
, platform
)
277 m
.d
.comb
+= self
.assigns
278 print ("assigns", self
.assigns
, m
)
283 def __init__(self
, m
, pipemode
=False, pipetype
=None):
285 self
.pipemode
= pipemode
286 self
.pipetype
= pipetype
289 def Stage(self
, name
, prev
=None, ispec
=None):
291 ispec
= likedict(ispec
)
292 print ("start stage", name
, ispec
)
293 stage
= PipelineStage(name
, None, prev
, self
.pipemode
, ispec
=ispec
)
295 yield stage
, self
.m
#stage._m
300 print ("use ispec", stage
._ispec
)
301 inspecs
= stage
._ispec
303 inspecs
= self
.get_specs(stage
, name
)
304 #inspecs = likedict(inspecs)
305 outspecs
= self
.get_specs(stage
, '__nextstage__', liked
=True)
306 print ("stage inspecs", name
, inspecs
)
307 print ("stage outspecs", name
, outspecs
)
308 eqs
= stage
._eqs
# get_eqs(stage._eqs)
309 assigns
= get_assigns(stage
._assigns
)
310 print ("stage eqs", name
, eqs
)
311 print ("stage assigns", name
, assigns
)
312 s
= AutoStage(inspecs
, outspecs
, eqs
, assigns
)
313 self
.stages
.append(s
)
314 print ("end stage", name
, self
.pipemode
, "\n")
316 def get_specs(self
, stage
, name
, liked
=False):
317 return stage
._preg
_map
[name
]
318 if name
in stage
._preg
_map
:
320 for k
, v
in stage
._preg
_map
[name
].items():
321 #v = like(v, k, stage._m)
323 #if isinstance(v, ObjectProxy):
324 # res += v.get_specs()
332 def __exit__(self
, *args
):
333 print ("exit stage", args
)
336 for s
in self
.stages
:
337 print ("stage specs", s
, s
.inspecs
, s
.outspecs
)
338 if self
.pipetype
== 'buffered':
339 p
= BufferedHandshake(s
)
341 p
= AutoPipe(s
, s
.assigns
)
343 self
.m
.submodules
+= p
345 self
.m
.d
.comb
+= cb
.connect(pipes
)
348 class SimplePipeline
:
349 """ Pipeline builder with auto generation of pipeline registers.
352 def __init__(self
, m
):
354 self
._pipeline
_register
_map
= {}
355 self
._current
_stage
_num
= 0
359 for method
in dir(self
):
360 if method
.startswith('stage'):
361 stage_list
.append(method
)
362 for stage
in sorted(stage_list
):
363 stage_method
= getattr(self
, stage
)
365 self
._current
_stage
_num
+= 1
367 def __getattr__(self
, name
):
369 return self
._pipeline
_register
_map
[self
._current
_stage
_num
][name
]
371 raise AttributeError(
372 'error, no pipeline register "%s" defined for stage %d'
373 % (name
, self
._current
_stage
_num
))
375 def __setattr__(self
, name
, value
):
376 if name
.startswith('_'):
377 # do not do anything tricky with variables starting with '_'
378 object.__setattr
__(self
, name
, value
)
380 next_stage
= self
._current
_stage
_num
+ 1
381 pipereg_id
= str(self
._current
_stage
_num
) + 'to' + str(next_stage
)
382 rname
= 'pipereg_' + pipereg_id
+ '_' + name
383 #new_pipereg = Signal(value_bits_sign(value), name=rname,
385 if isinstance(value
, ObjectProxy
):
386 new_pipereg
= ObjectProxy
.like(self
._m
, value
,
387 name
=rname
, reset_less
= True)
389 new_pipereg
= Signal
.like(value
, name
=rname
, reset_less
= True)
390 if next_stage
not in self
._pipeline
_register
_map
:
391 self
._pipeline
_register
_map
[next_stage
] = {}
392 self
._pipeline
_register
_map
[next_stage
][name
] = new_pipereg
393 self
._m
.d
.sync
+= eq(new_pipereg
, value
)