add sync handshake logic
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sun, 7 Apr 2019 09:30:20 +0000 (10:30 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sun, 7 Apr 2019 09:30:20 +0000 (10:30 +0100)
src/add/singlepipe.py
src/add/test_buf_pipe.py

index 590773a891872d7de63f917dc998002f5990a846..b27e90ef75e9690b20061590f1ba5538d0bbee21 100644 (file)
@@ -636,6 +636,93 @@ class BufferedPipeline(ControlBase):
         return self.m
 
 
+class BufferedPipeline2(ControlBase):
+    """ buffered pipeline stage.  data and strobe signals travel in sync.
+        if ever the input is ready and the output is not, processed data
+        is shunted in a temporary register.
+
+        Argument: stage.  see Stage API above
+
+        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, stage_ctl=False):
+        ControlBase.__init__(self, stage_ctl=stage_ctl)
+        self.stage = stage
+
+        # set up the input and output data
+        self.p.i_data = stage.ispec() # input type
+        self.n.o_data = stage.ospec()
+
+    def elaborate(self, platform):
+
+        self.m = ControlBase._elaborate(self, platform)
+
+        result = self.stage.ospec()
+        r_busy = Signal(reset=0)
+        if hasattr(self.stage, "setup"):
+            self.stage.setup(self.m, self.p.i_data)
+
+        # establish some combinatorial temporaries
+        o_n_validn = Signal(reset_less=True)
+        n_i_ready = Signal(reset_less=True, name="n_i_rdy_data")
+        o_n_valid_i_n_ready = Signal(reset_less=True)
+        p_i_valid = Signal(reset_less=True)
+        self.m.d.comb += [p_i_valid.eq(self.p.i_valid_test),
+                     o_n_validn.eq(~self.n.o_valid),
+                     n_i_ready.eq(self.n.i_ready_test),
+                     o_n_valid_i_n_ready.eq(self.n.o_valid & n_i_ready),
+        ]
+
+        # store result of processing in combinatorial temporary
+        self.m.d.comb += eq(result, self.stage.process(self.p.i_data))
+
+        with self.m.If(self.p.o_ready): # output is ready
+            with self.m.If(p_i_valid):   # and input is valid
+                self.m.d.sync += [r_busy.eq(1),
+                                  eq(self.n.o_data, result), # update output
+                                 ]
+            # else stay in idle condition (output ready, but input wasn't valid)
+
+        # output valid but not ready, and input is ready
+        with self.m.Elif(o_n_valid_i_n_ready):
+            # output transaction just took place
+            self.m.d.sync += [r_busy.eq(0),
+                              self.n.o_valid.eq(0), # set output invalid
+                             ]
+        # 
+        with self.m.Elif(o_n_validn):
+            # can check here for data valid
+            self.m.d.sync += [self.n.o_valid.eq(1),
+                              #eq(self.n.o_data, result), # update output
+                       ]
+
+        #self.m.d.comb += self.p._o_ready.eq(~r_busy)
+        self.m.d.comb += self.p._o_ready.eq(~(((~n_i_ready)&(self.n.o_valid))| \
+                                            (r_busy)))
+        return self.m
+
+
 class UnbufferedPipeline(ControlBase):
     """ A simple pipeline stage with single-clock synchronisation
         and two-way valid/ready synchronised signalling.
index 8c15773268add1cc9894d764f96b42aeb2a711da..d9cee5616ce9c3fc98c0dcda30daba64a6f79ffd 100644 (file)
@@ -25,6 +25,7 @@ from example_buf_pipe import ExampleStageCls
 from example_buf_pipe import PrevControl, NextControl, BufferedPipeline
 from example_buf_pipe import StageChain, ControlBase, StageCls
 from singlepipe import UnbufferedPipeline2
+from singlepipe import BufferedPipeline2
 
 from random import randint, seed
 
@@ -209,7 +210,7 @@ class Test5:
                     send = True
                 else:
                     send = randint(0, send_range) != 0
-                #send = True
+                send = True
                 o_p_ready = yield self.dut.p.o_ready
                 if not o_p_ready:
                     yield
@@ -228,7 +229,7 @@ class Test5:
             stall_range = randint(0, 3)
             for j in range(randint(1,10)):
                 ready = randint(0, stall_range) != 0
-                #ready = True
+                ready = True
                 yield self.dut.n.i_ready.eq(ready)
                 yield
                 o_n_valid = yield self.dut.n.o_valid
@@ -591,7 +592,7 @@ class ExampleStageDelayCls(StageCls):
         fashion
     """
 
-    def __init__(self, valid_trigger=2):
+    def __init__(self, valid_trigger=3):
         self.count = Signal(2)
         self.valid_trigger = valid_trigger
 
@@ -692,11 +693,11 @@ class ExampleBufPipe3(ControlBase):
 # Test 15
 ######################################################################
 
-class ExampleBufModeAdd1Pipe(BufferedPipeline):
+class ExampleBufModeAdd1Pipe(BufferedPipeline2):
 
     def __init__(self):
         stage = ExampleStageCls()
-        BufferedPipeline.__init__(self, stage, buffermode=False)
+        BufferedPipeline2.__init__(self, stage)
 
 
 ######################################################################
@@ -885,28 +886,28 @@ if __name__ == '__main__':
     with open("test_unbufpipe13.il", "w") as f:
         f.write(vl)
 
-    print ("test 14")
-    dut = ExampleBufPipe3()
+    print ("test 15)")
+    dut = ExampleBufModeAdd1Pipe()
     data = data_chain1()
-    test = Test5(dut, test9_resultfn, data=data)
-    run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufpipe14.vcd")
+    test = Test5(dut, test12_resultfn, data=data)
+    run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufunbuf15.vcd")
     ports = [dut.p.i_valid, dut.n.i_ready,
              dut.n.o_valid, dut.p.o_ready] + \
              [dut.p.i_data] + [dut.n.o_data]
     vl = rtlil.convert(dut, ports=ports)
-    with open("test_bufpipe14.il", "w") as f:
+    with open("test_bufunbuf15.il", "w") as f:
         f.write(vl)
 
-    print ("test 15)")
-    dut = ExampleBufModeAdd1Pipe()
+    print ("test 14")
+    dut = ExampleBufPipe3()
     data = data_chain1()
-    test = Test5(dut, test12_resultfn, data=data)
-    run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufunbuf15.vcd")
+    test = Test5(dut, test9_resultfn, data=data)
+    run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufpipe14.vcd")
     ports = [dut.p.i_valid, dut.n.i_ready,
              dut.n.o_valid, dut.p.o_ready] + \
              [dut.p.i_data] + [dut.n.o_data]
     vl = rtlil.convert(dut, ports=ports)
-    with open("test_bufunbuf15.il", "w") as f:
+    with open("test_bufpipe14.il", "w") as f:
         f.write(vl)
 
     print ("test 16)")