interconnect/stream: add PipeValid and PipeWait to cut timing paths.
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Wed, 29 Jan 2020 17:27:29 +0000 (18:27 +0100)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Wed, 29 Jan 2020 17:27:29 +0000 (18:27 +0100)
litex/soc/interconnect/stream.py
test/test_stream.py [new file with mode: 0644]

index c1568e8442cc50417109f4811bfe1b6096af0d7e..27be7975fb98568682539418c47151d55652bd18 100644 (file)
@@ -1,5 +1,5 @@
 # This file is Copyright (c) 2015 Sebastien Bourdeauducq <sb@m-labs.hk>
-# This file is Copyright (c) 2015-2019 Florent Kermarrec <florent@enjoy-digital.fr>
+# This file is Copyright (c) 2015-2020 Florent Kermarrec <florent@enjoy-digital.fr>
 # This file is Copyright (c) 2018 Tim 'mithro' Ansell <me@mith.ro>
 # License: BSD
 
@@ -607,6 +607,59 @@ class Buffer(PipelinedActor):
                 self.source.param.eq(self.sink.param)
             )
 
+# Pipe ---------------------------------------------------------------------------------------------
+
+class PipeValid(Module):
+    """Pipe valid/payload to cut timing path"""
+    def __init__(self, layout):
+        self.sink   = sink   = Endpoint(layout)
+        self.source = source = Endpoint(layout)
+
+        # # #
+
+        # Pipe when source is not valid or is ready.
+        self.sync += [
+            If(~source.valid | source.ready,
+                source.valid.eq(sink.valid),
+                source.first.eq(sink.first),
+                source.last.eq(sink.last),
+                source.payload.eq(sink.payload),
+                source.param.eq(sink.param),
+            )
+        ]
+        self.comb += sink.ready.eq(~source.valid | source.ready)
+
+
+class PipeReady(Module):
+    """Pipe ready to cut timing path"""
+    def __init__(self, layout):
+        self.sink   = sink   = Endpoint(layout)
+        self.source = source = Endpoint(layout)
+
+        # # #
+
+        valid  = Signal()
+        sink_d = Endpoint(layout)
+
+        self.sync += [
+            If(sink.valid & ~source.ready,
+                valid.eq(1)
+            ).Elif(source.ready,
+                valid.eq(0)
+            ),
+            If(~source.ready & ~valid,
+                sink_d.eq(sink)
+            )
+        ]
+        self.comb += [
+            sink.ready.eq(~valid),
+            If(valid,
+                sink_d.connect(source, omit={"ready"})
+            ).Else(
+                sink.connect(source, omit={"ready"})
+            )
+        ]
+
 # Cast ---------------------------------------------------------------------------------------------
 
 class Cast(CombinatorialActor):
diff --git a/test/test_stream.py b/test/test_stream.py
new file mode 100644 (file)
index 0000000..8b6b978
--- /dev/null
@@ -0,0 +1,49 @@
+# This file is Copyright (c) 2020 Florent Kermarrec <florent@enjoy-digital.fr>
+# License: BSD
+
+import unittest
+import random
+
+from migen import *
+
+from litex.soc.interconnect.stream import *
+
+
+class TestStream(unittest.TestCase):
+    def pipe_test(self, dut):
+        prng = random.Random(42)
+        def generator(dut, valid_rand=90):
+            for data in range(128):
+                yield dut.sink.valid.eq(1)
+                yield dut.sink.data.eq(data)
+                yield
+                while (yield dut.sink.ready) == 0:
+                    yield
+                yield dut.sink.valid.eq(0)
+                while prng.randrange(100) < valid_rand:
+                    yield
+
+        def checker(dut, ready_rand=90):
+            dut.errors = 0
+            for data in range(128):
+                yield dut.source.ready.eq(0)
+                yield
+                while (yield dut.source.valid) == 0:
+                    yield
+                while prng.randrange(100) < ready_rand:
+                    yield
+                yield dut.source.ready.eq(1)
+                yield
+                if ((yield dut.source.data) != data):
+                    dut.errors += 1
+            yield
+        run_simulation(dut, [generator(dut), checker(dut)])
+        self.assertEqual(dut.errors, 0)
+
+    def test_pipe_valid(self):
+        dut = PipeValid([("data", 8)])
+        self.pipe_test(dut)
+
+    def test_pipe_ready(self):
+        dut = PipeReady([("data", 8)])
+        self.pipe_test(dut)