From e78d950a31099d62ce51d0620b842f4e2c89d5d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Thu, 30 Jul 2020 13:38:17 +0200 Subject: [PATCH] soc/interconnect/axi: add AXILite -> AXI converter --- litex/soc/interconnect/axi.py | 60 +++++++++++++++++++++++++++++++++++ test/test_axi_lite.py | 49 ++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/litex/soc/interconnect/axi.py b/litex/soc/interconnect/axi.py index a1fd90b6e..958061a9b 100644 --- a/litex/soc/interconnect/axi.py +++ b/litex/soc/interconnect/axi.py @@ -430,6 +430,66 @@ class AXI2AXILite(Module): ) ) +# AXI Lite to AXI ---------------------------------------------------------------------------------- + +class AXILite2AXI(Module): + def __init__(self, axi_lite, axi, write_id=0, read_id=0, prot=0, burst_type="INCR"): + assert isinstance(axi_lite, AXILiteInterface) + assert isinstance(axi, AXIInterface) + assert axi_lite.data_width == axi.data_width + assert axi_lite.address_width == axi.address_width + + # n bytes, encoded as log2(n) + burst_size = log2_int(axi.data_width // 8) + # burst type has no meaning as we use burst length of 1, but AXI slaves may require + # certain type of bursts, so it is probably safest to use INCR in general + burst_type = { + "FIXED": 0b00, + "INCR": 0b01, + "WRAP": 0b10, + }[burst_type] + + self.comb += [ + axi.aw.valid.eq(axi_lite.aw.valid), + axi_lite.aw.ready.eq(axi.aw.ready), + axi.aw.addr.eq(axi_lite.aw.addr), + axi.aw.burst.eq(burst_type), + axi.aw.len.eq(0), # 1 transfer per burst + axi.aw.size.eq(burst_size), + axi.aw.lock.eq(0), # Normal access + axi.aw.prot.eq(prot), + axi.aw.cache.eq(0b0011), # Normal Non-cacheable Bufferable + axi.aw.qos.eq(0), + axi.aw.id.eq(write_id), + + axi.w.valid.eq(axi_lite.w.valid), + axi_lite.w.ready.eq(axi.w.ready), + axi.w.data.eq(axi_lite.w.data), + axi.w.strb.eq(axi_lite.w.strb), + axi.w.last.eq(1), + + axi_lite.b.valid.eq(axi.b.valid), + axi_lite.b.resp.eq(axi.b.resp), + axi.b.ready.eq(axi_lite.b.ready), + + axi.ar.valid.eq(axi_lite.ar.valid), + axi_lite.ar.ready.eq(axi.ar.ready), + axi.ar.addr.eq(axi_lite.ar.addr), + axi.ar.burst.eq(burst_type), + axi.ar.len.eq(0), + axi.ar.size.eq(burst_size), + axi.ar.lock.eq(0), + axi.ar.prot.eq(prot), + axi.ar.cache.eq(0b0011), + axi.ar.qos.eq(0), + axi.ar.id.eq(read_id), + + axi_lite.r.valid.eq(axi.r.valid), + axi_lite.r.resp.eq(axi.r.resp), + axi_lite.r.data.eq(axi.r.data), + axi.r.ready.eq(axi_lite.r.ready), + ] + # AXI Lite to Wishbone ----------------------------------------------------------------------------- class AXILite2Wishbone(Module): diff --git a/test/test_axi_lite.py b/test/test_axi_lite.py index 39e1d0287..160f7e239 100644 --- a/test/test_axi_lite.py +++ b/test/test_axi_lite.py @@ -177,6 +177,55 @@ class TestAXILite(unittest.TestCase): run_simulation(dut, [generator(dut)]) self.assertEqual(dut.errors, 0) + def test_axilite2axi2mem(self): + class DUT(Module): + def __init__(self, mem_bus="wishbone"): + self.axi_lite = AXILiteInterface() + + axi = AXIInterface() + self.submodules.axil2axi = AXILite2AXI(self.axi_lite, axi) + + interface_cls, converter_cls, sram_cls = { + "wishbone": (wishbone.Interface, AXI2Wishbone, wishbone.SRAM), + "axi_lite": (AXILiteInterface, AXI2AXILite, AXILiteSRAM), + }[mem_bus] + + bus = interface_cls() + self.submodules += converter_cls(axi, bus) + sram = sram_cls(1024, init=[0x12345678, 0xa55aa55a]) + self.submodules += sram + self.comb += bus.connect(sram.bus) + + def generator(axi_lite, datas, resps): + data, resp = (yield from axi_lite.read(0x00)) + resps.append((resp, RESP_OKAY)) + datas.append((data, 0x12345678)) + data, resp = (yield from axi_lite.read(0x04)) + resps.append((resp, RESP_OKAY)) + datas.append((data, 0xa55aa55a)) + for i in range(32): + resp = (yield from axi_lite.write(4*i, i)) + resps.append((resp, RESP_OKAY)) + for i in range(32): + data, resp = (yield from axi_lite.read(4*i)) + resps.append((resp, RESP_OKAY)) + datas.append((data, i)) + + for mem_bus in ["wishbone", "axi_lite"]: + with self.subTest(mem_bus=mem_bus): + # to have more verbose error messages store errors in list((actual, expected)) + datas = [] + resps = [] + + def actual_expected(results): # split into (list(actual), list(expected)) + return list(zip(*results)) + + dut = DUT(mem_bus) + run_simulation(dut, [generator(dut.axi_lite, datas, resps)]) + self.assertEqual(*actual_expected(resps)) + msg = "\n".join("0x{:08x} vs 0x{:08x}".format(actual, expected) for actual, expected in datas) + self.assertEqual(*actual_expected(datas), msg="actual vs expected:\n" + msg) + def test_axilite2csr(self): @passive def csr_mem_handler(csr, mem):