split out common code for both pipeline designs
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Tue, 19 Mar 2019 04:36:29 +0000 (04:36 +0000)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Tue, 19 Mar 2019 04:36:29 +0000 (04:36 +0000)
src/add/example_buf_pipe.py

index 8b9c74f78f8abc4ef1be278e374eb6e024cfa55f..15d368c291d5a737c1b73c5a53bfa82c00ce09bb 100644 (file)
@@ -107,32 +107,8 @@ def eq(o, i):
     return res
 
 
-class BufferedPipeline:
-    """ buffered pipeline stage.  data and strobe signals travel in sync.
-        if ever the input is ready and the output is not, processed data
-        is stored in a temporary register.
-
-        stage-1   p.i_valid >>in   stage   n.o_valid out>>   stage+1
-        stage-1   p.o_ready <<out  stage   n.i_ready <<in    stage+1
-        stage-1   p.i_data  >>in   stage   n.o_data  out>>   stage+1
-                              |             |
-                            process --->----^
-                              |             |
-                              +-- r_data ->-+
-
-        input data p.i_data is read (only), is processed and goes into an
-        intermediate result store [process()].  this is updated combinatorially.
-
-        in a non-stall condition, the intermediate result will go into the
-        output (update_output).  however if ever there is a stall, it goes
-        into r_data instead [update_buffer()].
-
-        when the non-stall condition is released, r_data is the first
-        to be transferred to the output [flush_buffer()], and the stall
-        condition cleared.
-
-        on the next cycle (as long as stall is not raised again) the
-        input may begin to be processed and transferred directly to output.
+class PipelineBase:
+    """ Common functions for Pipeline API
     """
     def __init__(self, stage):
         """ pass in a "stage" which may be either a static class or a class
@@ -141,10 +117,9 @@ class BufferedPipeline:
             * ispec: returns output signals to the output specification
             * process: takes an input instance and returns processed data
 
-            p.i_data -> process() -> result --> n.o_data
-                                       |           ^
-                                       |           |
-                                       +-> r_data -+
+            User must also:
+            * add i_data member to PrevControl and
+            * add o_data member to NextControl
         """
         self.stage = stage
 
@@ -152,12 +127,6 @@ class BufferedPipeline:
         self.p = PrevControl()
         self.n = NextControl()
 
-        # set up the input and output data
-        self.p.i_data = stage.ispec() # input type
-        self.r_data   = stage.ospec() # all these are output type
-        self.result   = stage.ospec()
-        self.n.o_data = stage.ospec()
-
     def connect_to_next(self, nxt):
         """ helper function to connect to the next stage data/valid/ready.
         """
@@ -180,6 +149,49 @@ class BufferedPipeline:
         """
         return eq(self.p.i_data, i)
 
+    def ports(self):
+        return [self.p.i_valid, self.n.i_ready,
+                self.n.o_valid, self.p.o_ready,
+                self.p.i_data, self.n.o_data
+               ]
+
+
+class BufferedPipeline(PipelineBase):
+    """ buffered pipeline stage.  data and strobe signals travel in sync.
+        if ever the input is ready and the output is not, processed data
+        is stored in a temporary register.
+
+        stage-1   p.i_valid >>in   stage   n.o_valid out>>   stage+1
+        stage-1   p.o_ready <<out  stage   n.i_ready <<in    stage+1
+        stage-1   p.i_data  >>in   stage   n.o_data  out>>   stage+1
+                              |             |
+                            process --->----^
+                              |             |
+                              +-- r_data ->-+
+
+        input data p.i_data is read (only), is processed and goes into an
+        intermediate result store [process()].  this is updated combinatorially.
+
+        in a non-stall condition, the intermediate result will go into the
+        output (update_output).  however if ever there is a stall, it goes
+        into r_data instead [update_buffer()].
+
+        when the non-stall condition is released, r_data is the first
+        to be transferred to the output [flush_buffer()], and the stall
+        condition cleared.
+
+        on the next cycle (as long as stall is not raised again) the
+        input may begin to be processed and transferred directly to output.
+    """
+    def __init__(self, stage):
+        PipelineBase.__init__(self, stage)
+
+        # set up the input and output data
+        self.p.i_data = stage.ispec() # input type
+        self.r_data   = stage.ospec() # all these are output type
+        self.result   = stage.ospec()
+        self.n.o_data = stage.ospec()
+
     def update_buffer(self):
         """ copies the result into the intermediate register r_data,
             which will need to be outputted on a subsequent cycle
@@ -197,9 +209,6 @@ class BufferedPipeline:
         """
         return eq(self.n.o_data, self.r_data)
 
-    def ports(self):
-        return [self.p.i_data, self.n.o_data]
-
     def elaborate(self, platform):
         m = Module()
         if hasattr(self.stage, "setup"):
@@ -251,11 +260,6 @@ class BufferedPipeline:
 
         return m
 
-    def ports(self):
-        return [self.p.i_valid, self.n.i_ready,
-                self.n.o_valid, self.p.o_ready,
-               ]
-
 
 class ExampleAddStage:
     """ an example of how to use the buffered pipeline, as a class instance
@@ -312,7 +316,7 @@ class ExampleBufPipe(BufferedPipeline):
         BufferedPipeline.__init__(self, ExampleStage)
 
 
-class CombPipe:
+class CombPipe(PipelineBase):
     """A simple pipeline stage containing combinational logic that can execute
     completely in one clock cycle.
 
@@ -338,11 +342,8 @@ class CombPipe:
     """
 
     def __init__(self, stage):
-        self.stage = stage
+        PipelineBase.__init__(self, stage)
         self._data_valid = Signal()
-        # set up input and output IO ACK (prev/next ready/valid)
-        self.p = PrevControl()
-        self.n = NextControl()
 
         # set up the input and output data
         self.p.i_data = stage.ispec() # input type
@@ -351,11 +352,6 @@ class CombPipe:
         self.n.o_data = stage.ospec() # output type
         self.n.o_data.name = "outdata"
 
-    def set_input(self, i):
-        """ helper function to set the input data
-        """
-        return eq(self.p.i_data, i)
-
     def elaborate(self, platform):
         m = Module()
         if hasattr(self.stage, "setup"):
@@ -370,9 +366,6 @@ class CombPipe:
         m.d.comb += eq(self.n.o_data, self.result)
         return m
 
-    def ports(self):
-        return [self.p.i_data, self.n.o_data]
-
 
 class ExampleCombPipe(CombPipe):
     """ an example of how to use the combinatorial pipeline.