soc/interconnect/axi: add AXILite -> AXI converter
authorJędrzej Boczar <jboczar@antmicro.com>
Thu, 30 Jul 2020 11:38:17 +0000 (13:38 +0200)
committerJędrzej Boczar <jboczar@antmicro.com>
Thu, 30 Jul 2020 11:50:34 +0000 (13:50 +0200)
litex/soc/interconnect/axi.py
test/test_axi_lite.py

index a1fd90b6ed4200dc9ce7faea90231901749c04f1..958061a9bbee575280e12e9c9845285a6affbde9 100644 (file)
@@ -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):
index 39e1d02870c74abbf2ce9cc4c9293b13601ddce8..160f7e239043e788b8006446f7c85592ee5cebc4 100644 (file)
@@ -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):