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 singlepipe
import eq
, StageCls
, ControlBase
, BufferedPipeline
12 from singlepipe
import UnbufferedPipeline
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
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)
27 return Signal(value_bits_sign(value
), name
=rname
,
29 return Signal
.like(value
, name
=rname
, reset_less
=True)
34 if isinstance(e
, ObjectProxy
):
35 eqs
+= get_eqs(e
._eqs
)
42 def __init__(self
, m
, name
=None, pipemode
=False):
45 name
= tracer
.get_var_name(default
=None)
47 self
._pipemode
= pipemode
52 def like(cls
, m
, value
, pipemode
=False, name
=None, src_loc_at
=0, **kwargs
):
53 name
= name
or tracer
.get_var_name(depth
=2 + src_loc_at
,
56 src_loc_at_1
= 1 + src_loc_at
57 r
= ObjectProxy(m
, value
.name
, pipemode
)
58 #for a, aname in value._preg_map.items():
59 # r._preg_map[aname] = like(a, aname, m, pipemode)
60 for a
in value
.ports():
62 r
._preg
_map
[aname
] = like(a
, aname
, m
, pipemode
)
67 for a
in self
.ports():
69 ai
= self
._preg
_map
[aname
]
70 subobjs
.append(repr(ai
))
71 return "<OP %s>" % subobjs
74 print ("ObjectProxy eq", self
, i
)
76 for a
in self
.ports():
78 ai
= i
._preg
_map
[aname
]
84 for aname
, a
in self
._preg
_map
.items():
85 if isinstance(a
, Signal
) or isinstance(a
, ObjectProxy
) or \
86 isinstance(a
, Record
):
88 print ("ObjectPorts", res
)
91 def __getattr__(self
, name
):
93 v
= self
._preg
_map
[name
]
95 #return like(v, name, self._m)
98 'error, no pipeline register "%s" defined for OP %s'
101 def __setattr__(self
, name
, value
):
102 if name
.startswith('_') or name
in ['name', 'ports', 'eq', 'like']:
103 # do not do anything tricky with variables starting with '_'
104 object.__setattr
__(self
, name
, value
)
106 #rname = "%s_%s" % (self.name, name)
108 new_pipereg
= like(value
, rname
, self
._m
, self
._pipemode
)
109 self
._preg
_map
[name
] = new_pipereg
110 #object.__setattr__(self, name, new_pipereg)
112 print ("OP pipemode", new_pipereg
, value
)
113 #self._eqs.append(value)
114 #self._m.d.comb += eq(new_pipereg, value)
117 print ("OP !pipemode assign", new_pipereg
, value
, type(value
))
118 self
._m
.d
.comb
+= eq(new_pipereg
, value
)
122 """ Pipeline builder stage with auto generation of pipeline registers.
125 def __init__(self
, name
, m
, prev
=None, pipemode
=False, ispec
=None):
127 self
._stagename
= name
129 self
._prev
_stage
= prev
132 print ("prev", prev
._stagename
, prev
._preg
_map
)
133 if prev
._stagename
in prev
._preg
_map
:
134 m
= prev
._preg
_map
[prev
._stagename
]
135 self
._preg
_map
[prev
._stagename
] = m
136 #for k, v in m.items():
137 #m[k] = like(v, k, self._m)
138 if '__nextstage__' in prev
._preg
_map
:
139 m
= prev
._preg
_map
['__nextstage__']
140 self
._preg
_map
[self
._stagename
] = m
141 #for k, v in m.items():
142 #m[k] = like(v, k, self._m)
143 print ("make current", self
._stagename
, m
)
144 self
._pipemode
= pipemode
147 def __getattr__(self
, name
):
149 v
= self
._preg
_map
[self
._stagename
][name
]
151 #return like(v, name, self._m)
153 raise AttributeError(
154 'error, no pipeline register "%s" defined for stage %s'
155 % (name
, self
._stagename
))
157 def __setattr__(self
, name
, value
):
158 if name
.startswith('_'):
159 # do not do anything tricky with variables starting with '_'
160 object.__setattr
__(self
, name
, value
)
162 pipereg_id
= self
._stagename
163 rname
= 'pipereg_' + pipereg_id
+ '_' + name
164 new_pipereg
= like(value
, rname
, self
._m
, self
._pipemode
)
165 next_stage
= '__nextstage__'
166 if next_stage
not in self
._preg
_map
:
167 self
._preg
_map
[next_stage
] = {}
168 self
._preg
_map
[next_stage
][name
] = new_pipereg
170 self
._eqs
.append(value
)
171 print ("pipemode: append", new_pipereg
, value
)
172 #self._m.d.comb += assign
174 print ("!pipemode: assign", new_pipereg
, value
)
175 assign
= eq(new_pipereg
, value
)
176 self
._m
.d
.sync
+= assign
179 class AutoStage(StageCls
):
180 def __init__(self
, inspecs
, outspecs
, eqs
):
181 self
.inspecs
, self
.outspecs
, self
.eqs
= inspecs
, outspecs
, eqs
182 self
.o
= self
.ospec()
183 def ispec(self
): return self
.like(self
.inspecs
)
184 def ospec(self
): return self
.like(self
.outspecs
)
185 def like(self
, specs
):
188 res
.append(like(v
, v
.name
, None, pipemode
=True))
191 def process(self
, i
):
192 print ("stage process", i
)
195 def setup(self
, m
, i
):
196 print ("stage setup", i
)
197 m
.d
.sync
+= eq(i
, self
.eqs
)
198 m
.d
.comb
+= eq(self
.o
, i
)
202 def __init__(self
, m
, pipemode
=False, pipetype
=None):
204 self
.pipemode
= pipemode
205 self
.pipetype
= pipetype
208 def Stage(self
, name
, prev
=None, ispec
=None):
209 print ("start stage", name
)
210 stage
= PipelineStage(name
, self
.m
, prev
, self
.pipemode
, ispec
=ispec
)
212 yield stage
, stage
._m
217 print ("use ispec", stage
._ispec
)
218 inspecs
= stage
._ispec
220 inspecs
= self
.get_specs(stage
, name
)
221 outspecs
= self
.get_specs(stage
, '__nextstage__', liked
=True)
222 eqs
= get_eqs(stage
._eqs
)
223 print ("stage eqs", name
, eqs
)
224 s
= AutoStage(inspecs
, outspecs
, eqs
)
225 self
.stages
.append(s
)
226 print ("end stage", name
, "\n")
228 def get_specs(self
, stage
, name
, liked
=False):
229 if name
in stage
._preg
_map
:
231 for k
, v
in stage
._preg
_map
[name
].items():
232 #v = like(v, k, stage._m)
241 def __exit__(self
, *args
):
242 print ("exit stage", args
)
245 for s
in self
.stages
:
246 print ("stage specs", s
, s
.inspecs
, s
.outspecs
)
247 if self
.pipetype
== 'buffered':
248 p
= BufferedPipeline(s
)
250 p
= UnbufferedPipeline(s
)
252 self
.m
.submodules
+= p
254 self
.m
.d
.comb
+= cb
.connect(pipes
)
257 class SimplePipeline
:
258 """ Pipeline builder with auto generation of pipeline registers.
261 def __init__(self
, m
):
263 self
._pipeline
_register
_map
= {}
264 self
._current
_stage
_num
= 0
268 for method
in dir(self
):
269 if method
.startswith('stage'):
270 stage_list
.append(method
)
271 for stage
in sorted(stage_list
):
272 stage_method
= getattr(self
, stage
)
274 self
._current
_stage
_num
+= 1
276 def __getattr__(self
, name
):
278 return self
._pipeline
_register
_map
[self
._current
_stage
_num
][name
]
280 raise AttributeError(
281 'error, no pipeline register "%s" defined for stage %d'
282 % (name
, self
._current
_stage
_num
))
284 def __setattr__(self
, name
, value
):
285 if name
.startswith('_'):
286 # do not do anything tricky with variables starting with '_'
287 object.__setattr
__(self
, name
, value
)
289 next_stage
= self
._current
_stage
_num
+ 1
290 pipereg_id
= str(self
._current
_stage
_num
) + 'to' + str(next_stage
)
291 rname
= 'pipereg_' + pipereg_id
+ '_' + name
292 #new_pipereg = Signal(value_bits_sign(value), name=rname,
294 if isinstance(value
, ObjectProxy
):
295 new_pipereg
= ObjectProxy
.like(self
._m
, value
,
296 name
=rname
, reset_less
= True)
298 new_pipereg
= Signal
.like(value
, name
=rname
, reset_less
= True)
299 if next_stage
not in self
._pipeline
_register
_map
:
300 self
._pipeline
_register
_map
[next_stage
] = {}
301 self
._pipeline
_register
_map
[next_stage
][name
] = new_pipereg
302 self
._m
.d
.sync
+= eq(new_pipereg
, value
)