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
):
23 if isinstance(value
, ObjectProxy
):
24 return ObjectProxy
.like(pipe
, value
, name
=rname
, reset_less
=True)
26 return Signal(value_bits_sign(value
), name
=rname
,
28 return Signal
.like(value
, name
=rname
, reset_less
=True)
32 def __init__(self
, pipe
, name
=None):
35 name
= tracer
.get_var_name(default
=None)
39 def like(cls
, pipe
, value
, name
=None, src_loc_at
=0, **kwargs
):
40 name
= name
or tracer
.get_var_name(depth
=2 + src_loc_at
,
43 src_loc_at_1
= 1 + src_loc_at
44 r
= ObjectProxy(pipe
, value
.name
)
45 for a
in value
.ports():
52 for a
in self
.ports():
54 ai
= getattr(i
, aname
)
60 for aname
in dir(self
):
61 a
= getattr(self
, aname
)
62 if isinstance(a
, Signal
) or isinstance(a
, ObjectProxy
) or \
63 isinstance(a
, Record
):
67 def __setattr__(self
, name
, value
):
68 if name
.startswith('_') or name
== 'name':
69 # do not do anything tricky with variables starting with '_'
70 object.__setattr
__(self
, name
, value
)
72 #rname = "%s_%s" % (self.name, name)
74 new_pipereg
= like(value
, rname
, self
._pipe
)
75 object.__setattr
__(self
, name
, new_pipereg
)
76 self
._pipe
.sync
+= eq(new_pipereg
, value
)
80 """ Pipeline builder stage with auto generation of pipeline registers.
83 def __init__(self
, name
, m
, prev
=None, pipemode
=False):
85 self
._stagename
= name
87 self
._prev
_stage
= prev
89 print ("prev", prev
._stagename
, prev
._preg
_map
)
90 if prev
._stagename
in prev
._preg
_map
:
91 m
= prev
._preg
_map
[prev
._stagename
]
92 self
._preg
_map
[prev
._stagename
] = m
93 for k
, v
in m
.items():
94 m
[k
] = like(v
, k
, self
._m
)
95 if '__nextstage__' in prev
._preg
_map
:
96 m
= prev
._preg
_map
['__nextstage__']
97 self
._preg
_map
[self
._stagename
] = m
98 for k
, v
in m
.items():
99 m
[k
] = like(v
, k
, self
._m
)
100 print ("make current", self
._stagename
, m
)
101 self
._pipemode
= pipemode
104 def __getattr__(self
, name
):
106 v
= self
._preg
_map
[self
._stagename
][name
]
108 #return like(v, name, self._m)
110 raise AttributeError(
111 'error, no pipeline register "%s" defined for stage %s'
112 % (name
, self
._stagename
))
114 def __setattr__(self
, name
, value
):
115 if name
.startswith('_'):
116 # do not do anything tricky with variables starting with '_'
117 object.__setattr
__(self
, name
, value
)
119 pipereg_id
= self
._stagename
120 rname
= 'pipereg_' + pipereg_id
+ '_' + name
121 new_pipereg
= like(value
, rname
, self
._m
)
122 next_stage
= '__nextstage__'
123 if next_stage
not in self
._preg
_map
:
124 self
._preg
_map
[next_stage
] = {}
125 self
._preg
_map
[next_stage
][name
] = new_pipereg
127 self
._eqs
.append(value
)
128 print ("!pipemode: append", new_pipereg
, value
)
129 #self._m.d.comb += assign
131 print ("!pipemode: assign", new_pipereg
, value
)
132 assign
= eq(new_pipereg
, value
)
133 self
._m
.d
.sync
+= assign
136 class AutoStage(StageCls
):
137 def __init__(self
, inspecs
, outspecs
, eqs
):
138 self
.inspecs
, self
.outspecs
, self
.eqs
= inspecs
, outspecs
, eqs
139 def ispec(self
): return self
.inspecs
140 def ospec(self
): return self
.outspecs
141 def process(self
, i
):
143 #def setup(self, m, i): #m.d.comb += self.eqs
147 def __init__(self
, m
, pipemode
=False):
149 self
.pipemode
= pipemode
152 def Stage(self
, name
, prev
=None):
153 stage
= PipelineStage(name
, self
.m
, prev
, self
.pipemode
)
155 yield stage
, stage
._m
159 inspecs
= self
.get_specs(stage
, name
)
160 outspecs
= self
.get_specs(stage
, '__nextstage__', liked
=True)
161 s
= AutoStage(inspecs
, outspecs
, stage
._eqs
)
162 self
.stages
.append(s
)
164 def get_specs(self
, stage
, name
, liked
=False):
165 if name
in stage
._preg
_map
:
167 for k
, v
in stage
._preg
_map
[name
].items():
168 #v = like(v, k, stage._m)
177 def __exit__(self
, *args
):
181 for s
in self
.stages
:
182 print (s
, s
.inspecs
, s
.outspecs
)
183 p
= UnbufferedPipeline(s
)
185 self
.m
.submodules
+= p
187 #self.m.d.comb += cb.connect(pipes)
190 class SimplePipeline
:
191 """ Pipeline builder with auto generation of pipeline registers.
194 def __init__(self
, pipe
):
196 self
._pipeline
_register
_map
= {}
197 self
._current
_stage
_num
= 0
201 for method
in dir(self
):
202 if method
.startswith('stage'):
203 stage_list
.append(method
)
204 for stage
in sorted(stage_list
):
205 stage_method
= getattr(self
, stage
)
207 self
._current
_stage
_num
+= 1
209 def __getattr__(self
, name
):
211 return self
._pipeline
_register
_map
[self
._current
_stage
_num
][name
]
213 raise AttributeError(
214 'error, no pipeline register "%s" defined for stage %d'
215 % (name
, self
._current
_stage
_num
))
217 def __setattr__(self
, name
, value
):
218 if name
.startswith('_'):
219 # do not do anything tricky with variables starting with '_'
220 object.__setattr
__(self
, name
, value
)
222 next_stage
= self
._current
_stage
_num
+ 1
223 pipereg_id
= str(self
._current
_stage
_num
) + 'to' + str(next_stage
)
224 rname
= 'pipereg_' + pipereg_id
+ '_' + name
225 #new_pipereg = Signal(value_bits_sign(value), name=rname,
227 if isinstance(value
, ObjectProxy
):
228 new_pipereg
= ObjectProxy
.like(self
._pipe
, value
,
229 name
=rname
, reset_less
= True)
231 new_pipereg
= Signal
.like(value
, name
=rname
, reset_less
= True)
232 if next_stage
not in self
._pipeline
_register
_map
:
233 self
._pipeline
_register
_map
[next_stage
] = {}
234 self
._pipeline
_register
_map
[next_stage
][name
] = new_pipereg
235 self
._pipe
.sync
+= eq(new_pipereg
, value
)