new ObjectProxy class for use in pipelines
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Fri, 29 Mar 2019 01:24:17 +0000 (01:24 +0000)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Fri, 29 Mar 2019 01:24:17 +0000 (01:24 +0000)
src/add/pipeline.py
src/add/pipeline_example.py

index 617fa8969969ee55b22ac8b18d701bd59fb2ea18..fc5faa9129a38483231645ee621100b0c9bee979 100644 (file)
@@ -1,8 +1,13 @@
 """ Example 5: Making use of PyRTL and Introspection. """
 
 from nmigen import Signal
+from nmigen.hdl.rec import Record
+from nmigen import tracer
 from nmigen.compat.fhdl.bitcontainer import value_bits_sign
 
+from singlepipe import eq
+
+
 # The following example shows how pyrtl can be used to make some interesting
 # hardware structures using python introspection.  In particular, this example
 # makes a N-stage pipeline structure.  Any specific pipeline is then a derived
@@ -10,7 +15,59 @@ from nmigen.compat.fhdl.bitcontainer import value_bits_sign
 # stages, and new members with names not starting with "_" are to be registered
 # for the next stage.
 
-from singlepipe import eq
+
+class ObjectProxy:
+    def __init__(self, pipe, name=None):
+        self._pipe = pipe
+        if name is None:
+            name = tracer.get_var_name(default=None)
+        self.name = name
+
+    @classmethod
+    def like(cls, pipe, value, name=None, src_loc_at=0, **kwargs):
+        name = name or tracer.get_var_name(depth=2 + src_loc_at,
+                                            default="$like")
+
+        src_loc_at_1 = 1 + src_loc_at
+        r = ObjectProxy(pipe, value.name)
+        for a in value.ports():
+            aname = a.name
+            setattr(r, aname, a)
+        return r
+
+    def eq(self, i):
+        res = []
+        for a in self.ports():
+            aname = a.name
+            ai = getattr(i, aname)
+            res.append(a.eq(ai))
+        return res
+
+    def ports(self):
+        res = []
+        for aname in dir(self):
+            a = getattr(self, aname)
+            if isinstance(a, Signal) or isinstance(a, ObjectProxy) or \
+               isinstance(a, Record):
+                res.append(a)
+        return res
+
+    def __setattr__(self, name, value):
+        if name.startswith('_') or name == 'name':
+            # do not do anything tricky with variables starting with '_'
+            object.__setattr__(self, name, value)
+            return
+        #rname = "%s_%s" % (self.name, name)
+        rname = name
+        if isinstance(value, ObjectProxy):
+            new_pipereg = ObjectProxy.like(self._pipe, value,
+                                           name=rname, reset_less=True)
+        else:
+            new_pipereg = Signal.like(value, name=rname, reset_less=True)
+
+        object.__setattr__(self, name, new_pipereg)
+        self._pipe.sync += eq(new_pipereg, value)
+
 
 class SimplePipeline(object):
     """ Pipeline builder with auto generation of pipeline registers.
@@ -49,7 +106,11 @@ class SimplePipeline(object):
         rname = 'pipereg_' + pipereg_id + '_' + name
         #new_pipereg = Signal(value_bits_sign(value), name=rname,
         #                     reset_less=True)
-        new_pipereg = Signal.like(value, name=rname, reset_less = True)
+        if isinstance(value, ObjectProxy):
+            new_pipereg = ObjectProxy.like(self._pipe, value,
+                                           name=rname, reset_less = True)
+        else:
+            new_pipereg = Signal.like(value, name=rname, reset_less = True)
         if next_stage not in self._pipeline_register_map:
             self._pipeline_register_map[next_stage] = {}
         self._pipeline_register_map[next_stage][name] = new_pipereg
index 590e1db65465db985aeedd7f6337811b8e32c547..44d453da35a5ea648b9a1c7b026867416f0b4b80 100644 (file)
@@ -4,7 +4,7 @@ from nmigen import Module, Signal
 from nmigen.cli import main, verilog
 
 
-from pipeline import SimplePipeline
+from pipeline import SimplePipeline, ObjectProxy
 
 
 class SimplePipelineExample(SimplePipeline):
@@ -13,24 +13,36 @@ class SimplePipelineExample(SimplePipeline):
     def __init__(self, pipe):
         SimplePipeline.__init__(self, pipe)
         self._loopback = Signal(4)
+        self._obj = ObjectProxy(pipe)
+        self._obj.a = Signal(4)
+        self._obj.b = Signal(4)
         self._setup()
 
     def stage0(self):
         self.n = ~self._loopback
+        self.o = self._obj
 
     def stage1(self):
-        self.n = self.n + 1
+        self.n = self.n + self.o.a
+        self.o = self.o
+        self.o.a = self.n
+        self.o.b = self.o.b
 
     def stage2(self):
         localv = Signal(4)
         self._pipe.comb += localv.eq(2)
         self.n = self.n << localv
+        self.o = self.o
+        self.o.b = self.n + self.o.a + self.o.b
 
     def stage3(self):
         self.n = ~self.n
+        self.o = self.o
+        self.o.b = self.o.b + self.n
 
     def stage4(self):
-        self._pipe.sync += self._loopback.eq(self.n + 3)
+        self.o.b = self.o.b
+        self._pipe.sync += self._loopback.eq(self.n + 3 + self.o.b)
 
 class PipeModule: