From f3f9808d1f87bbaced94650797bfd8bce9cf6188 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Wed, 29 Jan 2020 18:27:29 +0100 Subject: [PATCH] interconnect/stream: add PipeValid and PipeWait to cut timing paths. --- litex/soc/interconnect/stream.py | 55 +++++++++++++++++++++++++++++++- test/test_stream.py | 49 ++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 test/test_stream.py diff --git a/litex/soc/interconnect/stream.py b/litex/soc/interconnect/stream.py index c1568e844..27be7975f 100644 --- a/litex/soc/interconnect/stream.py +++ b/litex/soc/interconnect/stream.py @@ -1,5 +1,5 @@ # This file is Copyright (c) 2015 Sebastien Bourdeauducq -# This file is Copyright (c) 2015-2019 Florent Kermarrec +# This file is Copyright (c) 2015-2020 Florent Kermarrec # This file is Copyright (c) 2018 Tim 'mithro' Ansell # 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 index 000000000..8b6b978b5 --- /dev/null +++ b/test/test_stream.py @@ -0,0 +1,49 @@ +# This file is Copyright (c) 2020 Florent Kermarrec +# 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)