new ObjectProxy class for use in pipelines
[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
8 from singlepipe import eq
9
10
11 # The following example shows how pyrtl can be used to make some interesting
12 # hardware structures using python introspection. In particular, this example
13 # makes a N-stage pipeline structure. Any specific pipeline is then a derived
14 # class of SimplePipeline where methods with names starting with "stage" are
15 # stages, and new members with names not starting with "_" are to be registered
16 # for the next stage.
17
18
19 class ObjectProxy:
20 def __init__(self, pipe, name=None):
21 self._pipe = pipe
22 if name is None:
23 name = tracer.get_var_name(default=None)
24 self.name = name
25
26 @classmethod
27 def like(cls, pipe, value, name=None, src_loc_at=0, **kwargs):
28 name = name or tracer.get_var_name(depth=2 + src_loc_at,
29 default="$like")
30
31 src_loc_at_1 = 1 + src_loc_at
32 r = ObjectProxy(pipe, value.name)
33 for a in value.ports():
34 aname = a.name
35 setattr(r, aname, a)
36 return r
37
38 def eq(self, i):
39 res = []
40 for a in self.ports():
41 aname = a.name
42 ai = getattr(i, aname)
43 res.append(a.eq(ai))
44 return res
45
46 def ports(self):
47 res = []
48 for aname in dir(self):
49 a = getattr(self, aname)
50 if isinstance(a, Signal) or isinstance(a, ObjectProxy) or \
51 isinstance(a, Record):
52 res.append(a)
53 return res
54
55 def __setattr__(self, name, value):
56 if name.startswith('_') or name == 'name':
57 # do not do anything tricky with variables starting with '_'
58 object.__setattr__(self, name, value)
59 return
60 #rname = "%s_%s" % (self.name, name)
61 rname = name
62 if isinstance(value, ObjectProxy):
63 new_pipereg = ObjectProxy.like(self._pipe, value,
64 name=rname, reset_less=True)
65 else:
66 new_pipereg = Signal.like(value, name=rname, reset_less=True)
67
68 object.__setattr__(self, name, new_pipereg)
69 self._pipe.sync += eq(new_pipereg, value)
70
71
72 class SimplePipeline(object):
73 """ Pipeline builder with auto generation of pipeline registers.
74 """
75
76 def __init__(self, pipe):
77 self._pipe = pipe
78 self._pipeline_register_map = {}
79 self._current_stage_num = 0
80
81 def _setup(self):
82 stage_list = []
83 for method in dir(self):
84 if method.startswith('stage'):
85 stage_list.append(method)
86 for stage in sorted(stage_list):
87 stage_method = getattr(self, stage)
88 stage_method()
89 self._current_stage_num += 1
90
91 def __getattr__(self, name):
92 try:
93 return self._pipeline_register_map[self._current_stage_num][name]
94 except KeyError:
95 raise AttributeError(
96 'error, no pipeline register "%s" defined for stage %d'
97 % (name, self._current_stage_num))
98
99 def __setattr__(self, name, value):
100 if name.startswith('_'):
101 # do not do anything tricky with variables starting with '_'
102 object.__setattr__(self, name, value)
103 return
104 next_stage = self._current_stage_num + 1
105 pipereg_id = str(self._current_stage_num) + 'to' + str(next_stage)
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 if next_stage not in self._pipeline_register_map:
115 self._pipeline_register_map[next_stage] = {}
116 self._pipeline_register_map[next_stage][name] = new_pipereg
117 self._pipe.sync += eq(new_pipereg, value)
118