use stage names instead of numbers in the pipeline
[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, name, m, prev=None):
78 self._m = m
79 self._stagename = name
80 self._preg_map = {}
81 self._prev_stage = prev
82 if prev:
83 print ("prev", prev._preg_map)
84 if prev._stagename in prev._preg_map:
85 m = prev._preg_map[prev._stagename]
86 self._preg_map[prev._stagename] = m
87 if '__nextstage__' in prev._preg_map:
88 m = prev._preg_map['__nextstage__']
89 self._preg_map[self._stagename] = m
90 print ("make current", m)
91
92 def __getattr__(self, name):
93 try:
94 return self._preg_map[self._stagename][name]
95 except KeyError:
96 raise AttributeError(
97 'error, no pipeline register "%s" defined for stage %d'
98 % (name, self._stagename))
99
100 def __setattr__(self, name, value):
101 if name.startswith('_'):
102 # do not do anything tricky with variables starting with '_'
103 object.__setattr__(self, name, value)
104 return
105 pipereg_id = self._stagename
106 rname = 'pipereg_' + pipereg_id + '_' + name
107 #new_pipereg = Signal(value_bits_sign(value), name=rname,
108 # reset_less=True)
109 if isinstance(value, ObjectProxy):
110 new_pipereg = ObjectProxy.like(self._pipe, value,
111 name=rname, reset_less = True)
112 else:
113 new_pipereg = Signal.like(value, name=rname, reset_less = True)
114 next_stage = '__nextstage__'
115 if next_stage not in self._preg_map:
116 self._preg_map[next_stage] = {}
117 self._preg_map[next_stage][name] = new_pipereg
118 self._m.d.sync += eq(new_pipereg, value)
119
120
121 class PipeManager:
122 def __init__(self, m):
123 self.m = m
124
125 @contextmanager
126 def Stage(self, name, prev=None):
127 stage = PipelineStage(name, self.m, prev)
128 try:
129 yield stage, stage._m
130 finally:
131 pass
132
133 class SimplePipeline:
134 """ Pipeline builder with auto generation of pipeline registers.
135 """
136
137 def __init__(self, pipe):
138 self._pipe = pipe
139 self._pipeline_register_map = {}
140 self._current_stage_num = 0
141
142 def _setup(self):
143 stage_list = []
144 for method in dir(self):
145 if method.startswith('stage'):
146 stage_list.append(method)
147 for stage in sorted(stage_list):
148 stage_method = getattr(self, stage)
149 stage_method()
150 self._current_stage_num += 1
151
152 def __getattr__(self, name):
153 try:
154 return self._pipeline_register_map[self._current_stage_num][name]
155 except KeyError:
156 raise AttributeError(
157 'error, no pipeline register "%s" defined for stage %d'
158 % (name, self._current_stage_num))
159
160 def __setattr__(self, name, value):
161 if name.startswith('_'):
162 # do not do anything tricky with variables starting with '_'
163 object.__setattr__(self, name, value)
164 return
165 next_stage = self._current_stage_num + 1
166 pipereg_id = str(self._current_stage_num) + 'to' + str(next_stage)
167 rname = 'pipereg_' + pipereg_id + '_' + name
168 #new_pipereg = Signal(value_bits_sign(value), name=rname,
169 # reset_less=True)
170 if isinstance(value, ObjectProxy):
171 new_pipereg = ObjectProxy.like(self._pipe, value,
172 name=rname, reset_less = True)
173 else:
174 new_pipereg = Signal.like(value, name=rname, reset_less = True)
175 if next_stage not in self._pipeline_register_map:
176 self._pipeline_register_map[next_stage] = {}
177 self._pipeline_register_map[next_stage][name] = new_pipereg
178 self._pipe.sync += eq(new_pipereg, value)
179