ded2e03dbc55dba55bff72d46bcb858eb8289744
2 This work is funded through NLnet under Grant 2019-02-012
9 from collections
.abc
import Sequence
10 from nmigen
import Signal
11 from nmigen
.hdl
.rec
import Record
12 from nmigen
import tracer
13 from nmigen
.compat
.fhdl
.bitcontainer
import value_bits_sign
14 from contextlib
import contextmanager
15 from nmutil
.nmoperator
import eq
16 from nmutil
.singlepipe
import StageCls
, ControlBase
, BufferedHandshake
17 from nmutil
.singlepipe
import UnbufferedPipeline
19 """ Example 5: Making use of PyRTL and Introspection.
21 The following example shows how pyrtl can be used to make some interesting
22 hardware structures using python introspection. In particular, this example
23 makes a N-stage pipeline structure. Any specific pipeline is then a derived
24 class of SimplePipeline where methods with names starting with "stage" are
25 stages, and new members with names not starting with "_" are to be registered
30 def like(value
, rname
, pipe
, pipemode
=False):
31 if isinstance(value
, ObjectProxy
):
32 return ObjectProxy
.like(pipe
, value
, pipemode
=pipemode
,
33 name
=rname
, reset_less
=True)
35 return Signal(value_bits_sign(value
), name
=rname
,
37 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
244 res
.append(like(v
, v
.name
, None, pipemode
=True))
249 if not isinstance(specs
, dict):
250 return like(specs
, specs
.name
, None, pipemode
=True)
252 for k
, v
in specs
.items():
257 class AutoStage(StageCls
):
258 def __init__(self
, inspecs
, outspecs
, eqs
, assigns
):
259 self
.inspecs
, self
.outspecs
= inspecs
, outspecs
260 self
.eqs
, self
.assigns
= eqs
, assigns
261 #self.o = self.ospec()
263 def ispec(self
): return likedict(self
.inspecs
)
264 def ospec(self
): return likedict(self
.outspecs
)
266 def process(self
, i
):
267 print("stage process", i
)
270 def setup(self
, m
, i
):
271 print("stage setup i", i
, m
)
272 print("stage setup inspecs", self
.inspecs
)
273 print("stage setup outspecs", self
.outspecs
)
274 print("stage setup eqs", self
.eqs
)
275 #self.o = self.ospec()
276 m
.d
.comb
+= eq(self
.inspecs
, i
)
277 #m.d.comb += eq(self.outspecs, self.eqs)
278 #m.d.comb += eq(self.o, i)
281 class AutoPipe(UnbufferedPipeline
):
282 def __init__(self
, stage
, assigns
):
283 UnbufferedPipeline
.__init
__(self
, stage
)
284 self
.assigns
= assigns
286 def elaborate(self
, platform
):
287 m
= UnbufferedPipeline
.elaborate(self
, platform
)
288 m
.d
.comb
+= self
.assigns
289 print("assigns", self
.assigns
, m
)
294 def __init__(self
, m
, pipemode
=False, pipetype
=None):
296 self
.pipemode
= pipemode
297 self
.pipetype
= pipetype
300 def Stage(self
, name
, prev
=None, ispec
=None):
302 ispec
= likedict(ispec
)
303 print("start stage", name
, ispec
)
304 stage
= PipelineStage(name
, None, prev
, self
.pipemode
, ispec
=ispec
)
306 yield stage
, self
.m
# stage._m
311 print("use ispec", stage
._ispec
)
312 inspecs
= stage
._ispec
314 inspecs
= self
.get_specs(stage
, name
)
315 #inspecs = likedict(inspecs)
316 outspecs
= self
.get_specs(stage
, '__nextstage__', liked
=True)
317 print("stage inspecs", name
, inspecs
)
318 print("stage outspecs", name
, outspecs
)
319 eqs
= stage
._eqs
# get_eqs(stage._eqs)
320 assigns
= get_assigns(stage
._assigns
)
321 print("stage eqs", name
, eqs
)
322 print("stage assigns", name
, assigns
)
323 s
= AutoStage(inspecs
, outspecs
, eqs
, assigns
)
324 self
.stages
.append(s
)
325 print("end stage", name
, self
.pipemode
, "\n")
327 def get_specs(self
, stage
, name
, liked
=False):
328 return stage
._preg
_map
[name
]
329 if name
in stage
._preg
_map
:
331 for k
, v
in stage
._preg
_map
[name
].items():
332 #v = like(v, k, stage._m)
334 # if isinstance(v, ObjectProxy):
335 # res += v.get_specs()
343 def __exit__(self
, *args
):
344 print("exit stage", args
)
347 for s
in self
.stages
:
348 print("stage specs", s
, s
.inspecs
, s
.outspecs
)
349 if self
.pipetype
== 'buffered':
350 p
= BufferedHandshake(s
)
352 p
= AutoPipe(s
, s
.assigns
)
354 self
.m
.submodules
+= p
356 self
.m
.d
.comb
+= cb
.connect(pipes
)
359 class SimplePipeline
:
360 """ Pipeline builder with auto generation of pipeline registers.
363 def __init__(self
, m
):
365 self
._pipeline
_register
_map
= {}
366 self
._current
_stage
_num
= 0
370 for method
in dir(self
):
371 if method
.startswith('stage'):
372 stage_list
.append(method
)
373 for stage
in sorted(stage_list
):
374 stage_method
= getattr(self
, stage
)
376 self
._current
_stage
_num
+= 1
378 def __getattr__(self
, name
):
380 return self
._pipeline
_register
_map
[self
._current
_stage
_num
][name
]
382 raise AttributeError(
383 'error, no pipeline register "%s" defined for stage %d'
384 % (name
, self
._current
_stage
_num
))
386 def __setattr__(self
, name
, value
):
387 if name
.startswith('_'):
388 # do not do anything tricky with variables starting with '_'
389 object.__setattr
__(self
, name
, value
)
391 next_stage
= self
._current
_stage
_num
+ 1
392 pipereg_id
= str(self
._current
_stage
_num
) + 'to' + str(next_stage
)
393 rname
= 'pipereg_' + pipereg_id
+ '_' + name
394 # new_pipereg = Signal(value_bits_sign(value), name=rname,
396 if isinstance(value
, ObjectProxy
):
397 new_pipereg
= ObjectProxy
.like(self
._m
, value
,
398 name
=rname
, reset_less
=True)
400 new_pipereg
= Signal
.like(value
, name
=rname
, reset_less
=True)
401 if next_stage
not in self
._pipeline
_register
_map
:
402 self
._pipeline
_register
_map
[next_stage
] = {}
403 self
._pipeline
_register
_map
[next_stage
][name
] = new_pipereg
404 self
._m
.d
.sync
+= eq(new_pipereg
, value
)