# 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
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):
--- /dev/null
+# 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)