soc/interconnect/axi: lock AXILiteArbiter until all requests have been responded to

This commit is contained in:
Jędrzej Boczar 2020-07-20 18:08:56 +02:00
parent baf23c9c9b
commit 214cfdcaeb
2 changed files with 111 additions and 17 deletions

View File

@ -936,15 +936,40 @@ class AXILiteInterconnectPointToPoint(Module):
def __init__(self, master, slave): def __init__(self, master, slave):
self.comb += master.connect(slave) self.comb += master.connect(slave)
class AXILiteRequestCounter(Module):
def __init__(self, request, response, max_requests=256):
self.counter = counter = Signal(max=max_requests)
self.full = full = Signal()
self.empty = empty = Signal()
self.stall = stall = Signal()
self.comb += [
full.eq(counter == max_requests - 1),
empty.eq(counter == 0),
stall.eq(request & full),
]
self.sync += [
If(request & response,
counter.eq(counter)
).Elif(request & ~full,
counter.eq(counter + 1)
).Elif(response & ~empty,
counter.eq(counter - 1)
),
]
class AXILiteArbiter(Module): class AXILiteArbiter(Module):
"""AXI Lite arbiter """AXI Lite arbiter
Arbitrate between master interfaces and connect one to the target. Arbitrate between master interfaces and connect one to the target.
Arbitration is done separately for write and read channels. New master will not be selected until all requests have been responded to.
Arbitration for write and read channels is done separately.
""" """
def __init__(self, masters, target): def __init__(self, masters, target):
self.submodules.rr_write = roundrobin.RoundRobin(len(masters)) self.submodules.rr_write = roundrobin.RoundRobin(len(masters), roundrobin.SP_CE)
self.submodules.rr_read = roundrobin.RoundRobin(len(masters)) self.submodules.rr_read = roundrobin.RoundRobin(len(masters), roundrobin.SP_CE)
def get_sig(interface, channel, name): def get_sig(interface, channel, name):
return getattr(getattr(interface, channel), name) return getattr(getattr(interface, channel), name)
@ -968,9 +993,23 @@ class AXILiteArbiter(Module):
else: else:
self.comb += dest.eq(source) self.comb += dest.eq(source)
# allow to change rr.grant only after all requests from a master have been responded to
self.submodules.wr_counter = wr_counter = AXILiteRequestCounter(
request=target.aw.valid & target.aw.ready, response=target.b.valid & target.b.ready)
self.submodules.rd_counter = rd_counter = AXILiteRequestCounter(
request=target.ar.valid & target.ar.ready, response=target.r.valid & target.r.ready)
# switch to next request only if there are no responses pending
self.comb += [
self.rr_write.ce.eq(~(target.aw.valid | target.w.valid | target.b.valid) & wr_counter.empty),
self.rr_read.ce.eq(~(target.ar.valid | target.r.valid) & rd_counter.empty),
]
# connect bus requests to round-robin selectors # connect bus requests to round-robin selectors
self.comb += self.rr_write.request.eq(Cat(*[m.aw.valid | m.w.valid | m.b.valid for m in masters])) self.comb += [
self.comb += self.rr_read.request.eq(Cat(*[m.ar.valid | m.r.valid for m in masters])) self.rr_write.request.eq(Cat(*[m.aw.valid | m.w.valid | m.b.valid for m in masters])),
self.rr_read.request.eq(Cat(*[m.ar.valid | m.r.valid for m in masters])),
]
class AXILiteDecoder(Module): class AXILiteDecoder(Module):
# slaves is a list of pairs: # slaves is a list of pairs:

View File

@ -330,32 +330,34 @@ class TestAXI(unittest.TestCase):
# TestAXILite -------------------------------------------------------------------------------------- # TestAXILite --------------------------------------------------------------------------------------
class AXILiteChecker: class AXILiteChecker:
def __init__(self, latency=None, rdata_generator=None): def __init__(self, ready_latency=None, response_latency=None, rdata_generator=None):
self.latency = latency or (lambda: 0) self.ready_latency = ready_latency or (lambda: 0)
self.response_latency = response_latency or (lambda: 0)
self.rdata_generator = rdata_generator or (lambda adr: 0xbaadc0de) self.rdata_generator = rdata_generator or (lambda adr: 0xbaadc0de)
self.writes = [] # (addr, data, strb) self.writes = [] # (addr, data, strb)
self.reads = [] # (addr, data) self.reads = [] # (addr, data)
def delay(self): def delay(self, latency):
for _ in range(self.latency()): for _ in range(latency()):
yield yield
def handle_write(self, axi_lite): def handle_write(self, axi_lite):
while not (yield axi_lite.aw.valid): while not (yield axi_lite.aw.valid):
yield yield
yield from self.delay() yield from self.delay(self.ready_latency)
addr = (yield axi_lite.aw.addr) addr = (yield axi_lite.aw.addr)
yield axi_lite.aw.ready.eq(1) yield axi_lite.aw.ready.eq(1)
yield yield
yield axi_lite.aw.ready.eq(0) yield axi_lite.aw.ready.eq(0)
while not (yield axi_lite.w.valid): while not (yield axi_lite.w.valid):
yield yield
yield from self.delay() yield from self.delay(self.ready_latency)
data = (yield axi_lite.w.data) data = (yield axi_lite.w.data)
strb = (yield axi_lite.w.strb) strb = (yield axi_lite.w.strb)
yield axi_lite.w.ready.eq(1) yield axi_lite.w.ready.eq(1)
yield yield
yield axi_lite.w.ready.eq(0) yield axi_lite.w.ready.eq(0)
yield from self.delay(self.response_latency)
yield axi_lite.b.valid.eq(1) yield axi_lite.b.valid.eq(1)
yield axi_lite.b.resp.eq(RESP_OKAY) yield axi_lite.b.resp.eq(RESP_OKAY)
yield yield
@ -367,11 +369,12 @@ class AXILiteChecker:
def handle_read(self, axi_lite): def handle_read(self, axi_lite):
while not (yield axi_lite.ar.valid): while not (yield axi_lite.ar.valid):
yield yield
yield from self.delay() yield from self.delay(self.ready_latency)
addr = (yield axi_lite.ar.addr) addr = (yield axi_lite.ar.addr)
yield axi_lite.ar.ready.eq(1) yield axi_lite.ar.ready.eq(1)
yield yield
yield axi_lite.ar.ready.eq(0) yield axi_lite.ar.ready.eq(0)
yield from self.delay(self.response_latency)
data = self.rdata_generator(addr) data = self.rdata_generator(addr)
yield axi_lite.r.valid.eq(1) yield axi_lite.r.valid.eq(1)
yield axi_lite.r.resp.eq(RESP_OKAY) yield axi_lite.r.resp.eq(RESP_OKAY)
@ -560,7 +563,7 @@ class TestAXILite(unittest.TestCase):
return _latency return _latency
dut = DUT(width_from=width_from, width_to=width_to) dut = DUT(width_from=width_from, width_to=width_to)
checker = AXILiteChecker(latency, rdata_generator) checker = AXILiteChecker(ready_latency=latency, rdata_generator=rdata_generator)
run_simulation(dut, [generator(dut.master), checker.handler(dut.slave)]) run_simulation(dut, [generator(dut.master), checker.handler(dut.slave)])
self.assertEqual(checker.writes, write_expected) self.assertEqual(checker.writes, write_expected)
self.assertEqual(checker.reads, read_expected) self.assertEqual(checker.reads, read_expected)
@ -644,6 +647,9 @@ class TestAXILite(unittest.TestCase):
self.converter_test(width_from=32, width_to=16, self.converter_test(width_from=32, width_to=16,
write_pattern=write_pattern, write_expected=write_expected) write_pattern=write_pattern, write_expected=write_expected)
# TestAXILiteInterconnet ---------------------------------------------------------------------------
class TestAXILiteInterconnect(unittest.TestCase):
def axilite_pattern_generator(self, axi_lite, pattern): def axilite_pattern_generator(self, axi_lite, pattern):
for rw, addr, data in pattern: for rw, addr, data in pattern:
assert rw in ["w", "r"] assert rw in ["w", "r"]
@ -657,7 +663,7 @@ class TestAXILite(unittest.TestCase):
for _ in range(16): for _ in range(16):
yield yield
def test_axilite_interconnect_p2p(self): def test_interconnect_p2p(self):
class DUT(Module): class DUT(Module):
def __init__(self): def __init__(self):
self.master = master = AXILiteInterface() self.master = master = AXILiteInterface()
@ -687,7 +693,7 @@ class TestAXILite(unittest.TestCase):
self.assertEqual(checker.writes, [(addr, data, 0b1111) for rw, addr, data in pattern if rw == "w"]) self.assertEqual(checker.writes, [(addr, data, 0b1111) for rw, addr, data in pattern if rw == "w"])
self.assertEqual(checker.reads, [(addr, data) for rw, addr, data in pattern if rw == "r"]) self.assertEqual(checker.reads, [(addr, data) for rw, addr, data in pattern if rw == "r"])
def test_axilite_timeout(self): def test_timeout(self):
class DUT(Module): class DUT(Module):
def __init__(self): def __init__(self):
self.master = master = AXILiteInterface() self.master = master = AXILiteInterface()
@ -727,7 +733,7 @@ class TestAXILite(unittest.TestCase):
] ]
run_simulation(dut, generators) run_simulation(dut, generators)
def test_axilite_arbiter(self): def test_arbiter_order(self):
class DUT(Module): class DUT(Module):
def __init__(self, n_masters): def __init__(self, n_masters):
self.masters = [AXILiteInterface() for _ in range(n_masters)] self.masters = [AXILiteInterface() for _ in range(n_masters)]
@ -759,7 +765,7 @@ class TestAXILite(unittest.TestCase):
checker = AXILiteChecker() checker = AXILiteChecker()
generators = [generator(i, master, delay=0) for i, master in enumerate(dut.masters)] generators = [generator(i, master, delay=0) for i, master in enumerate(dut.masters)]
generators += [timeout(300), checker.handler(dut.slave)] generators += [timeout(300), checker.handler(dut.slave)]
run_simulation(dut, generators, vcd_name='sim.vcd') run_simulation(dut, generators)
order = [0, 1, 2, 3, 100, 101, 102, 103, 200, 201, 202, 203] order = [0, 1, 2, 3, 100, 101, 102, 103, 200, 201, 202, 203]
self.assertEqual([addr for addr, data, strb in checker.writes], order) self.assertEqual([addr for addr, data, strb in checker.writes], order)
self.assertEqual([addr for addr, data in checker.reads], order) self.assertEqual([addr for addr, data in checker.reads], order)
@ -774,3 +780,52 @@ class TestAXILite(unittest.TestCase):
order = [0, 100, 200, 1, 101, 201, 2, 102, 202, 3, 103, 203] order = [0, 100, 200, 1, 101, 201, 2, 102, 202, 3, 103, 203]
self.assertEqual([addr for addr, data, strb in checker.writes], order) self.assertEqual([addr for addr, data, strb in checker.writes], order)
self.assertEqual([addr for addr, data in checker.reads], order) self.assertEqual([addr for addr, data in checker.reads], order)
def test_arbiter_holds_grant_until_response(self):
class DUT(Module):
def __init__(self, n_masters):
self.masters = [AXILiteInterface() for _ in range(n_masters)]
self.slave = AXILiteInterface()
self.submodules.arbiter = AXILiteArbiter(self.masters, self.slave)
def generator(n, axi_lite, delay=0):
def gen(i):
return 100*n + i
for i in range(4):
resp = (yield from axi_lite.write(gen(i), gen(i)))
self.assertEqual(resp, RESP_OKAY)
for _ in range(delay):
yield
for i in range(4):
data, resp = (yield from axi_lite.read(gen(i)))
self.assertEqual(resp, RESP_OKAY)
for _ in range(delay):
yield
for _ in range(8):
yield
n_masters = 3
# with no delay each master will do all transfers at once
with self.subTest(delay=0):
dut = DUT(n_masters)
checker = AXILiteChecker(response_latency=lambda: 3)
generators = [generator(i, master, delay=0) for i, master in enumerate(dut.masters)]
generators += [timeout(300), checker.handler(dut.slave)]
run_simulation(dut, generators)
order = [0, 1, 2, 3, 100, 101, 102, 103, 200, 201, 202, 203]
self.assertEqual([addr for addr, data, strb in checker.writes], order)
self.assertEqual([addr for addr, data in checker.reads], order)
# with some delay, the round-robin arbiter will iterate over masters
with self.subTest(delay=1):
dut = DUT(n_masters)
checker = AXILiteChecker(response_latency=lambda: 3)
generators = [generator(i, master, delay=1) for i, master in enumerate(dut.masters)]
generators += [timeout(300), checker.handler(dut.slave)]
run_simulation(dut, generators, vcd_name='sim.vcd')
order = [0, 100, 200, 1, 101, 201, 2, 102, 202, 3, 103, 203]
self.assertEqual([addr for addr, data, strb in checker.writes], order)
self.assertEqual([addr for addr, data in checker.reads], order)