interconnect/stream: add PipeValid and PipeWait to cut timing paths.
This commit is contained in:
parent
b22ad1acfb
commit
f3f9808d1f
|
@ -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):
|
||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue