interconnect/stream: add PipeValid and PipeWait to cut timing paths.

This commit is contained in:
Florent Kermarrec 2020-01-29 18:27:29 +01:00
parent b22ad1acfb
commit f3f9808d1f
2 changed files with 103 additions and 1 deletions

View File

@ -1,5 +1,5 @@
# This file is Copyright (c) 2015 Sebastien Bourdeauducq <sb@m-labs.hk> # 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> # This file is Copyright (c) 2018 Tim 'mithro' Ansell <me@mith.ro>
# License: BSD # License: BSD
@ -607,6 +607,59 @@ class Buffer(PipelinedActor):
self.source.param.eq(self.sink.param) 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 --------------------------------------------------------------------------------------------- # Cast ---------------------------------------------------------------------------------------------
class Cast(CombinatorialActor): class Cast(CombinatorialActor):

49
test/test_stream.py Normal file
View File

@ -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)