use and return module instead of m.d in pipeline introspection
[ieee754fpu.git] / src / add / pipeline.py
1 """ Example 5: Making use of PyRTL and Introspection. """
2
3 from nmigen import Signal
4 from nmigen.hdl.rec import Record
5 from nmigen import tracer
6 from nmigen.compat.fhdl.bitcontainer import value_bits_sign
7 from contextlib import contextmanager
8
9 from singlepipe import eq
10
11
12 # The following example shows how pyrtl can be used to make some interesting
13 # hardware structures using python introspection. In particular, this example
14 # makes a N-stage pipeline structure. Any specific pipeline is then a derived
15 # class of SimplePipeline where methods with names starting with "stage" are
16 # stages, and new members with names not starting with "_" are to be registered
17 # for the next stage.
18
19
20 class ObjectProxy:
21 def __init__(self, pipe, name=None):
22 self._pipe = pipe
23 if name is None:
24 name = tracer.get_var_name(default=None)
25 self.name = name
26
27 @classmethod
28 def like(cls, pipe, value, name=None, src_loc_at=0, **kwargs):
29 name = name or tracer.get_var_name(depth=2 + src_loc_at,
30 default="$like")
31
32 src_loc_at_1 = 1 + src_loc_at
33 r = ObjectProxy(pipe, value.name)
34 for a in value.ports():
35 aname = a.name
36 setattr(r, aname, a)
37 return r
38
39 def eq(self, i):
40 res = []
41 for a in self.ports():
42 aname = a.name
43 ai = getattr(i, aname)
44 res.append(a.eq(ai))
45 return res
46
47 def ports(self):
48 res = []
49 for aname in dir(self):
50 a = getattr(self, aname)
51 if isinstance(a, Signal) or isinstance(a, ObjectProxy) or \
52 isinstance(a, Record):
53 res.append(a)
54 return res
55
56 def __setattr__(self, name, value):
57 if name.startswith('_') or name == 'name':
58 # do not do anything tricky with variables starting with '_'
59 object.__setattr__(self, name, value)
60 return
61 #rname = "%s_%s" % (self.name, name)
62 rname = name
63 if isinstance(value, ObjectProxy):
64 new_pipereg = ObjectProxy.like(self._pipe, value,
65 name=rname, reset_less=True)
66 else:
67 new_pipereg = Signal.like(value, name=rname, reset_less=True)
68
69 object.__setattr__(self, name, new_pipereg)
70 self._pipe.sync += eq(new_pipereg, value)
71
72
73 class PipelineStage:
74 """ Pipeline builder stage with auto generation of pipeline registers.
75 """
76
77 def __init__(self, m, prev=None):
78 self._m = m
79 self._preg_map = {}
80 self._prev_stage = prev
81 if prev:
82 print ("prev", prev._preg_map)
83 if prev._current_stage_num in prev._preg_map:
84 m = prev._preg_map[prev._current_stage_num]
85 self._preg_map[prev._current_stage_num] = m
86 self._current_stage_num = prev._current_stage_num + 1
87 if self._current_stage_num in prev._preg_map:
88 m = prev._preg_map[self._current_stage_num]
89 self._preg_map[self._current_stage_num] = m
90 print ("make current", m)
91 else:
92 self._current_stage_num = 0
93
94 def __getattr__(self, name):
95 try:
96 return self._preg_map[self._current_stage_num][name]
97 except KeyError:
98 raise AttributeError(
99 'error, no pipeline register "%s" defined for stage %d'
100 % (name, self._current_stage_num))
101
102 def __setattr__(self, name, value):
103 if name.startswith('_'):
104 # do not do anything tricky with variables starting with '_'
105 object.__setattr__(self, name, value)
106 return
107 next_stage = self._current_stage_num + 1
108 pipereg_id = str(self._current_stage_num) + 'to' + str(next_stage)
109 rname = 'pipereg_' + pipereg_id + '_' + name
110 #new_pipereg = Signal(value_bits_sign(value), name=rname,
111 # reset_less=True)
112 if isinstance(value, ObjectProxy):
113 new_pipereg = ObjectProxy.like(self._pipe, value,
114 name=rname, reset_less = True)
115 else:
116 new_pipereg = Signal.like(value, name=rname, reset_less = True)
117 if next_stage not in self._preg_map:
118 self._preg_map[next_stage] = {}
119 self._preg_map[next_stage][name] = new_pipereg
120 self._m.d.sync += eq(new_pipereg, value)
121
122
123 class PipeManager:
124 def __init__(self, m):
125 self.m = m
126
127 @contextmanager
128 def Stage(self, prev=None):
129 stage = PipelineStage(self.m, prev)
130 try:
131 yield stage, stage._m
132 finally:
133 pass
134
135 class SimplePipeline:
136 """ Pipeline builder with auto generation of pipeline registers.
137 """
138
139 def __init__(self, pipe):
140 self._pipe = pipe
141 self._pipeline_register_map = {}
142 self._current_stage_num = 0
143
144 def _setup(self):
145 stage_list = []
146 for method in dir(self):
147 if method.startswith('stage'):
148 stage_list.append(method)
149 for stage in sorted(stage_list):
150 stage_method = getattr(self, stage)
151 stage_method()
152 self._current_stage_num += 1
153
154 def __getattr__(self, name):
155 try:
156 return self._pipeline_register_map[self._current_stage_num][name]
157 except KeyError:
158 raise AttributeError(
159 'error, no pipeline register "%s" defined for stage %d'
160 % (name, self._current_stage_num))
161
162 def __setattr__(self, name, value):
163 if name.startswith('_'):
164 # do not do anything tricky with variables starting with '_'
165 object.__setattr__(self, name, value)
166 return
167 next_stage = self._current_stage_num + 1
168 pipereg_id = str(self._current_stage_num) + 'to' + str(next_stage)
169 rname = 'pipereg_' + pipereg_id + '_' + name
170 #new_pipereg = Signal(value_bits_sign(value), name=rname,
171 # reset_less=True)
172 if isinstance(value, ObjectProxy):
173 new_pipereg = ObjectProxy.like(self._pipe, value,
174 name=rname, reset_less = True)
175 else:
176 new_pipereg = Signal.like(value, name=rname, reset_less = True)
177 if next_stage not in self._pipeline_register_map:
178 self._pipeline_register_map[next_stage] = {}
179 self._pipeline_register_map[next_stage][name] = new_pipereg
180 self._pipe.sync += eq(new_pipereg, value)
181