move wrapping of stage into StageHandler
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Mon, 29 Apr 2019 00:14:25 +0000 (01:14 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Mon, 29 Apr 2019 00:14:25 +0000 (01:14 +0100)
src/add/iocontrol.py
src/add/singlepipe.py
src/add/test_buf_pipe.py

index dfa04797ec52fe44934d1054a8d32251086f5b95..423694b9ae43f7970633cfafd5750eae77fce723 100644 (file)
@@ -436,18 +436,19 @@ class StageChain(StageCls):
         return self.o # conform to Stage API: return last-loop output
 
 
-class StageHandler(Elaboratable):
-    """ Stage handling class
+class StageHandler: # (Elaboratable):
+    """ Stage handling (wrapper) class: makes e.g. static classes "real"
+        (instances) and provides a way to allocate data_i and data_o
     """
-    def __init__(self, ctrl, stage):
-        """
-        """
-        if stage is not None:
-            self.new_data(self, self, "data")
+    def __init__(self, stage): self.stage = stage
+    def ispec(self, name): return _spec(self.stage.ispec, name)
+    def ospec(self, name): return _spec(self.stage.ospec, name)
+    def process(self, i): return self.stage.process(i)
 
-    @property
-    def data_r(self):
-        return self.stage.process(self.p.data_i)
+    def setup(self, m, i):
+        if self.stage is None or not hasattr(self.stage, "setup"):
+            return
+        self.stage.setup(m, i)
 
     def _postprocess(self, i): # XXX DISABLED
         return i # RETURNS INPUT
@@ -458,33 +459,11 @@ class StageHandler(Elaboratable):
     def new_data(self, p, n, name):
         """ allocates new data_i and data_o
         """
-        self.p.data_i = _spec(p.stage.ispec, "%s_i" % name)
-        self.n.data_o = _spec(n.stage.ospec, "%s_o" % name)
-
-    def elaborate(self, platform):
-        """ handles case where stage has dynamic ready/valid functions
-        """
-        m = Module()
-        m.submodules.p = self.p
-        m.submodules.n = self.n
+        return (_spec(p.stage.ispec, "%s_i" % name),
+                _spec(n.stage.ospec, "%s_o" % name))
 
-        if self.stage is not None and hasattr(self.stage, "setup"):
-            self.stage.setup(m, self.p.data_i)
 
-        if not self.p.stage_ctl:
-            return m
-
-        # intercept the previous (outgoing) "ready", combine with stage ready
-        m.d.comb += self.p.s_ready_o.eq(self.p._ready_o & self.stage.d_ready)
-
-        # intercept the next (incoming) "ready" and combine it with data valid
-        sdv = self.stage.d_valid(self.n.ready_i)
-        m.d.comb += self.n.d_valid.eq(self.n.ready_i & sdv)
-
-        return m
-
-
-class ControlBase(StageHandler):
+class ControlBase(StageHandler, Elaboratable):
     """ Common functions for Pipeline API.  Note: a "pipeline stage" only
         exists (conceptually) when a ControlBase derivative is handed
         a Stage (combinatorial block)
@@ -499,13 +478,22 @@ class ControlBase(StageHandler):
             * add data_i member to PrevControl (p) and
             * add data_o member to NextControl (n)
         """
-        self.stage = stage
-
         # set up input and output IO ACK (prev/next ready/valid)
         self.p = PrevControl(in_multi, stage_ctl)
         self.n = NextControl(stage_ctl)
 
-        StageHandler.__init__(self, self, stage)
+        self.sh = StageHandler(stage)
+        if stage is not None:
+            self.new_data(self, self, "data")
+
+    def new_data(self, p, n, name):
+        """ allocates new data_i and data_o
+        """
+        self.p.data_i, self.n.data_o = self.sh.new_data(p.sh, n.sh, name)
+
+    @property
+    def data_r(self):
+        return self.sh.process(self.p.data_i)
 
     def connect_to_next(self, nxt):
         """ helper function to connect to the next stage data/valid/ready.
@@ -582,6 +570,29 @@ class ControlBase(StageHandler):
 
         return eqs
 
+    def elaborate(self, platform):
+        """ handles case where stage has dynamic ready/valid functions
+        """
+        m = Module()
+        m.submodules.p = self.p
+        m.submodules.n = self.n
+
+        self.sh.setup(m, self.p.data_i)
+
+        if not self.p.stage_ctl:
+            return m
+
+        stage = self.sh.stage
+
+        # intercept the previous (outgoing) "ready", combine with stage ready
+        m.d.comb += self.p.s_ready_o.eq(self.p._ready_o & stage.d_ready)
+
+        # intercept the next (incoming) "ready" and combine it with data valid
+        sdv = stage.d_valid(self.n.ready_i)
+        m.d.comb += self.n.d_valid.eq(self.n.ready_i & sdv)
+
+        return m
+
     def set_input(self, i):
         """ helper function to set the input data
         """
index 5f7dc66ba23679ddb5b783ac395d99782bfbc7b2..5be2d919d5a7fed75a29d4900141f978c46e4942 100644 (file)
@@ -184,8 +184,8 @@ class BufferedHandshake(ControlBase):
     def elaborate(self, platform):
         self.m = ControlBase.elaborate(self, platform)
 
-        result = _spec(self.stage.ospec, "r_tmp")
-        r_data = _spec(self.stage.ospec, "r_data")
+        result = _spec(self.sh.ospec, "r_tmp")
+        r_data = _spec(self.sh.ospec, "r_data")
 
         # establish some combinatorial temporaries
         o_n_validn = Signal(reset_less=True)
@@ -280,7 +280,7 @@ class SimpleHandshake(ControlBase):
         self.m = m = ControlBase.elaborate(self, platform)
 
         r_busy = Signal()
-        result = _spec(self.stage.ospec, "r_tmp")
+        result = _spec(self.sh.ospec, "r_tmp")
 
         # establish some combinatorial temporaries
         n_ready_i = Signal(reset_less=True, name="n_i_rdy_data")
@@ -388,7 +388,7 @@ class UnbufferedPipeline(ControlBase):
         self.m = m = ControlBase.elaborate(self, platform)
 
         data_valid = Signal() # is data valid or not
-        r_data = _spec(self.stage.ospec, "r_tmp") # output type
+        r_data = _spec(self.sh.ospec, "r_tmp") # output type
 
         # some temporaries
         p_valid_i = Signal(reset_less=True)
@@ -474,7 +474,7 @@ class UnbufferedPipeline2(ControlBase):
         self.m = m = ControlBase.elaborate(self, platform)
 
         buf_full = Signal() # is data valid or not
-        buf = _spec(self.stage.ospec, "r_tmp") # output type
+        buf = _spec(self.sh.ospec, "r_tmp") # output type
 
         # some temporaries
         p_valid_i = Signal(reset_less=True)
@@ -543,7 +543,7 @@ class PassThroughHandshake(ControlBase):
     def elaborate(self, platform):
         self.m = m = ControlBase.elaborate(self, platform)
 
-        r_data = _spec(self.stage.ospec, "r_tmp") # output type
+        r_data = _spec(self.sh.ospec, "r_tmp") # output type
 
         # temporaries
         p_valid_i = Signal(reset_less=True)
@@ -621,7 +621,7 @@ class FIFOControl(ControlBase):
         m.submodules.fifo = fifo
 
         # store result of processing in combinatorial temporary
-        result = _spec(self.stage.ospec, "r_temp")
+        result = _spec(self.sh.ospec, "r_temp")
         m.d.comb += nmoperator.eq(result, self.data_r)
 
         # connect previous rdy/valid/data - do cat on data_i
index 7155c90894bcb0f00a8d12e8cbc03cfb4af336c7..1408164a02647ccdfabc35db1cdca056367c6306 100644 (file)
@@ -635,8 +635,8 @@ class ExampleStageDelayCls(StageCls, Elaboratable):
 class ExampleBufDelayedPipe(BufferedHandshake):
 
     def __init__(self):
-        stage = ExampleStageDelayCls(valid_trigger=2)
-        BufferedHandshake.__init__(self, stage, stage_ctl=True)
+        self.stage = ExampleStageDelayCls(valid_trigger=2)
+        BufferedHandshake.__init__(self, self.stage, stage_ctl=True)
 
     def elaborate(self, platform):
         m = BufferedHandshake.elaborate(self, platform)
@@ -667,8 +667,8 @@ def resultfn_12(data_o, expected, i, o):
 class ExampleUnBufDelayedPipe(BufferedHandshake):
 
     def __init__(self):
-        stage = ExampleStageDelayCls(valid_trigger=3)
-        BufferedHandshake.__init__(self, stage, stage_ctl=True)
+        self.stage = ExampleStageDelayCls(valid_trigger=3)
+        BufferedHandshake.__init__(self, self.stage, stage_ctl=True)
 
     def elaborate(self, platform):
         m = BufferedHandshake.elaborate(self, platform)