pipeline ObjectProxy objects now stored in module, not linked to output yet
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Tue, 2 Apr 2019 01:44:24 +0000 (02:44 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Tue, 2 Apr 2019 01:44:24 +0000 (02:44 +0100)
src/add/pipeline.py
src/add/pipeline_example.py
src/add/singlepipe.py
src/add/test_buf_pipe.py

index 1b0a745894c4d3309a64147c020e34aea80b5538..450703224fd3ffd3e3cc81f70bedb8b810baceb3 100644 (file)
@@ -28,6 +28,16 @@ def like(value, rname, pipe, pipemode=False):
                              reset_less=True)
         return Signal.like(value, name=rname, reset_less=True)
 
+def get_assigns(_assigns):
+    assigns = []
+    for e in _assigns:
+        if isinstance(e, ObjectProxy):
+            assigns += get_assigns(e._assigns)
+        else:
+            assigns.append(e)
+    return assigns
+
+
 def get_eqs(_eqs):
     eqs = []
     for e in _eqs:
@@ -46,6 +56,7 @@ class ObjectProxy:
         self.name = name
         self._pipemode = pipemode
         self._eqs = []
+        self._assigns = []
         self._preg_map = {}
 
     @classmethod
@@ -116,6 +127,9 @@ class ObjectProxy:
         elif self._m:
             print ("OP !pipemode assign", new_pipereg, value, type(value))
             self._m.d.comb += eq(new_pipereg, value)
+        else:
+            print ("OP !pipemode !m", new_pipereg, value, type(value))
+            self._assigns += eq(new_pipereg, value)
 
 
 class PipelineStage:
@@ -143,6 +157,7 @@ class PipelineStage:
                 print ("make current", self._stagename, m)
         self._pipemode = pipemode
         self._eqs = []
+        self._assigns = []
 
     def __getattr__(self, name):
         try:
@@ -168,18 +183,28 @@ class PipelineStage:
         self._preg_map[next_stage][name] = new_pipereg
         if self._pipemode:
             self._eqs.append(value)
-            print ("pipemode: append", new_pipereg, value)
+            assign = eq(new_pipereg, value)
+            print ("pipemode: append", new_pipereg, value, assign)
+            if isinstance(value, ObjectProxy):
+                print ("OP, assigns:", value._assigns)
+                self._assigns += value._assigns
             #self._m.d.comb += assign
-        else:
+            self._assigns += assign
+        elif self._m:
             print ("!pipemode: assign", new_pipereg, value)
             assign = eq(new_pipereg, value)
             self._m.d.sync += assign
+        else:
+            print ("!pipemode !m: defer assign", new_pipereg, value)
+            assign = eq(new_pipereg, value)
+            self._assigns += assign
 
 
 class AutoStage(StageCls):
-    def __init__(self, inspecs, outspecs, eqs):
-        self.inspecs, self.outspecs, self.eqs = inspecs, outspecs, eqs
-        self.o = self.ospec()
+    def __init__(self, inspecs, outspecs, eqs, assigns):
+        self.inspecs, self.outspecs = inspecs, outspecs
+        self.eqs, self.assigns = eqs, assigns
+        #self.o = self.ospec()
     def ispec(self): return self.like(self.inspecs)
     def ospec(self): return self.like(self.outspecs)
     def like(self, specs):
@@ -190,12 +215,23 @@ class AutoStage(StageCls):
 
     def process(self, i):
         print ("stage process", i)
-        return self.o
+        return self.eqs
 
     def setup(self, m, i):
         print ("stage setup", i)
-        m.d.sync += eq(i, self.eqs)
-        m.d.comb += eq(self.o, i)
+        #m.d.comb += eq(self.o, i)
+
+
+class AutoPipe(UnbufferedPipeline):
+    def __init__(self, stage, assigns):
+        UnbufferedPipeline.__init__(self, stage)
+        self.assigns = assigns
+
+    def elaborate(self, platform):
+        m = UnbufferedPipeline.elaborate(self, platform)
+        m.d.comb += self.assigns
+        print ("assigns", self.assigns)
+        return m
 
 
 class PipeManager:
@@ -207,9 +243,9 @@ class PipeManager:
     @contextmanager
     def Stage(self, name, prev=None, ispec=None):
         print ("start stage", name)
-        stage = PipelineStage(name, self.m, prev, self.pipemode, ispec=ispec)
+        stage = PipelineStage(name, None, prev, self.pipemode, ispec=ispec)
         try:
-            yield stage, stage._m
+            yield stage, self.m #stage._m
         finally:
             pass
         if self.pipemode:
@@ -220,10 +256,12 @@ class PipeManager:
                 inspecs = self.get_specs(stage, name)
             outspecs = self.get_specs(stage, '__nextstage__', liked=True)
             eqs = get_eqs(stage._eqs)
+            assigns = get_assigns(stage._assigns)
             print ("stage eqs", name, eqs)
-            s = AutoStage(inspecs, outspecs, eqs)
+            print ("stage assigns", name, assigns)
+            s = AutoStage(inspecs, outspecs, eqs, assigns)
             self.stages.append(s)
-        print ("end stage", name, "\n")
+        print ("end stage", name, self.pipemode, "\n")
 
     def get_specs(self, stage, name, liked=False):
         if name in stage._preg_map:
@@ -247,7 +285,7 @@ class PipeManager:
             if self.pipetype == 'buffered':
                 p = BufferedPipeline(s)
             else:
-                p = UnbufferedPipeline(s)
+                p = AutoPipe(s, s.assigns)
             pipes.append(p)
             self.m.submodules += p
 
index c3e0e32843a9c6fd196ce696171c288f7c28eab9..b84496e71039b04c34646e67021832b1260cc18f 100644 (file)
@@ -119,10 +119,10 @@ class PipelineStageObjectExample:
 
         m = Module()
 
-        o = ObjectProxy(m, pipemode=False)
+        o = ObjectProxy(None, pipemode=False)
         o.a = Signal(4)
         o.b = Signal(4)
-        self.obj = o
+        self._obj = o
 
         localv2 = Signal(4)
         m.d.sync += localv2.eq(localv2 + 3)
@@ -133,13 +133,13 @@ class PipelineStageObjectExample:
         with PipeManager(m, pipemode=True) as pipe:
 
             with pipe.Stage("first",
-                            ispec=[self._loopback, self.obj]) as (p, m):
+                            ispec=[self._loopback, self._obj]) as (p, m):
                 p.n = ~self._loopback
-                p.o = self.obj
+                p.o = self._obj
             with pipe.Stage("second", p) as (p, m):
                 #p.n = ~self._loopback + 2
                 p.n = p.n + Const(2)
-                o = ObjectProxy(m, pipemode=False)
+                o = ObjectProxy(None, pipemode=False)
                 o.a = p.n
                 o.b = p.o.b + p.n + Const(5)
                 p.o = o
@@ -148,7 +148,7 @@ class PipelineStageObjectExample:
                 localv = Signal(4)
                 m.d.comb += localv.eq(2)
                 p.n = p.n << localv
-                o = ObjectProxy(m, pipemode=False)
+                o = ObjectProxy(None, pipemode=False)
                 o.b = p.n + p.o.b + p.o.a
                 p.o = o
 
index 033536a62d7778ed942f65d411e89ac6ece34215..c49c34167c3710c8f16a5fc8220fb1c5ff6f41db 100644 (file)
@@ -396,7 +396,6 @@ class ControlBase:
             * add i_data member to PrevControl (p) and
             * add o_data member to NextControl (n)
         """
-
         # set up input and output IO ACK (prev/next ready/valid)
         self.p = PrevControl(in_multi)
         self.n = NextControl()
@@ -527,56 +526,57 @@ class BufferedPipeline(ControlBase):
         self.n.o_data = stage.ospec()
 
     def elaborate(self, platform):
-        m = Module()
+
+        self.m = Module()
 
         result = self.stage.ospec()
         r_data = self.stage.ospec()
         if hasattr(self.stage, "setup"):
-            self.stage.setup(m, self.p.i_data)
+            self.stage.setup(self.m, self.p.i_data)
 
         # establish some combinatorial temporaries
         o_n_validn = Signal(reset_less=True)
         i_p_valid_o_p_ready = Signal(reset_less=True)
         p_i_valid = Signal(reset_less=True)
-        m.d.comb += [p_i_valid.eq(self.p.i_valid_logic()),
+        self.m.d.comb += [p_i_valid.eq(self.p.i_valid_logic()),
                      o_n_validn.eq(~self.n.o_valid),
                      i_p_valid_o_p_ready.eq(p_i_valid & self.p.o_ready),
         ]
 
         # store result of processing in combinatorial temporary
-        m.d.comb += eq(result, self.stage.process(self.p.i_data))
+        self.m.d.comb += eq(result, self.stage.process(self.p.i_data))
 
         # if not in stall condition, update the temporary register
-        with m.If(self.p.o_ready): # not stalled
-            m.d.sync += eq(r_data, result) # update buffer
+        with self.m.If(self.p.o_ready): # not stalled
+            self.m.d.sync += eq(r_data, result) # update buffer
 
-        with m.If(self.n.i_ready): # next stage is ready
-            with m.If(self.p.o_ready): # not stalled
+        with self.m.If(self.n.i_ready): # next stage is ready
+            with self.m.If(self.p.o_ready): # not stalled
                 # nothing in buffer: send (processed) input direct to output
-                m.d.sync += [self.n.o_valid.eq(p_i_valid),
-                             eq(self.n.o_data, result), # update output
+                self.m.d.sync += [self.n.o_valid.eq(p_i_valid),
+                                  eq(self.n.o_data, result), # update output
                             ]
-            with m.Else(): # p.o_ready is false, and something is in buffer.
+            with self.m.Else(): # p.o_ready is false, and something in buffer
                 # Flush the [already processed] buffer to the output port.
-                m.d.sync += [self.n.o_valid.eq(1),      # declare reg empty
-                             eq(self.n.o_data, r_data), # flush buffer
-                             self.p.o_ready.eq(1),      # clear stall condition
+                self.m.d.sync += [self.n.o_valid.eq(1),      # declare reg empty
+                                  eq(self.n.o_data, r_data), # flush buffer
+                                  self.p.o_ready.eq(1),      # clear stall
                             ]
                 # ignore input, since p.o_ready is also false.
 
         # (n.i_ready) is false here: next stage is ready
-        with m.Elif(o_n_validn): # next stage being told "ready"
-            m.d.sync += [self.n.o_valid.eq(p_i_valid),
-                         self.p.o_ready.eq(1), # Keep the buffer empty
-                         eq(self.n.o_data, result), # set output data
+        with self.m.Elif(o_n_validn): # next stage being told "ready"
+            self.m.d.sync += [self.n.o_valid.eq(p_i_valid),
+                              self.p.o_ready.eq(1), # Keep the buffer empty
+                              eq(self.n.o_data, result), # set output data
                         ]
 
         # (n.i_ready) false and (n.o_valid) true:
-        with m.Elif(i_p_valid_o_p_ready):
+        with self.m.Elif(i_p_valid_o_p_ready):
             # If next stage *is* ready, and not stalled yet, accept input
-            m.d.sync += self.p.o_ready.eq(~(p_i_valid & self.n.o_valid))
+            self.m.d.sync += self.p.o_ready.eq(~(p_i_valid & self.n.o_valid))
 
-        return m
+        return self.m
 
 
 class UnbufferedPipeline(ControlBase):
@@ -625,27 +625,27 @@ class UnbufferedPipeline(ControlBase):
         self.n.o_data = stage.ospec() # output type
 
     def elaborate(self, platform):
-        m = Module()
+        self.m = Module()
 
         data_valid = Signal() # is data valid or not
         r_data = self.stage.ispec() # input type
         if hasattr(self.stage, "setup"):
-            self.stage.setup(m, r_data)
+            self.stage.setup(self.m, r_data)
 
         # some temporarie
         p_i_valid = Signal(reset_less=True)
         pv = Signal(reset_less=True)
-        m.d.comb += p_i_valid.eq(self.p.i_valid_logic())
-        m.d.comb += pv.eq(self.p.i_valid & self.p.o_ready)
+        self.m.d.comb += p_i_valid.eq(self.p.i_valid_logic())
+        self.m.d.comb += pv.eq(self.p.i_valid & self.p.o_ready)
 
-        m.d.comb += self.n.o_valid.eq(data_valid)
-        m.d.comb += self.p.o_ready.eq(~data_valid | self.n.i_ready)
-        m.d.sync += data_valid.eq(p_i_valid | \
+        self.m.d.comb += self.n.o_valid.eq(data_valid)
+        self.m.d.comb += self.p.o_ready.eq(~data_valid | self.n.i_ready)
+        self.m.d.sync += data_valid.eq(p_i_valid | \
                                         (~self.n.i_ready & data_valid))
-        with m.If(pv):
-            m.d.sync += eq(r_data, self.p.i_data)
-        m.d.comb += eq(self.n.o_data, self.stage.process(r_data))
-        return m
+        with self.m.If(pv):
+            self.m.d.sync += eq(r_data, self.p.i_data)
+        self.m.d.comb += eq(self.n.o_data, self.stage.process(r_data))
+        return self.m
 
 
 class PassThroughStage(StageCls):
index af725d23c89581e96614fe821e8d2bdeab53ffa8..fce4ff67b788b07b064f9a85d083f6e5f15d0f6f 100644 (file)
@@ -333,14 +333,14 @@ def test9_resultfn(o_data, expected, i, o):
 
 class SetLessThan:
     def __init__(self, width, signed):
-        self.src1 = Signal((width, signed))
-        self.src2 = Signal((width, signed))
-        self.output = Signal(width)
+        self.m = Module()
+        self.src1 = Signal((width, signed), name="src1")
+        self.src2 = Signal((width, signed), name="src2")
+        self.output = Signal(width, name="out")
 
     def elaborate(self, platform):
-        m = Module()
-        m.d.comb += self.output.eq(Mux(self.src1 < self.src2, 1, 0))
-        return m
+        self.m.d.comb += self.output.eq(Mux(self.src1 < self.src2, 1, 0))
+        return self.m
 
 
 class LTStage(StageCls):
@@ -350,10 +350,10 @@ class LTStage(StageCls):
         self.slt = SetLessThan(16, True)
 
     def ispec(self):
-        return (Signal(16), Signal(16))
+        return (Signal(16, name="sig1"), Signal(16, "sig2"))
 
     def ospec(self):
-        return Signal(16)
+        return Signal(16, "out")
 
     def setup(self, m, i):
         self.o = Signal(16)