soc/interconnect/axi: add basic AXI Lite up-converter
authorJędrzej Boczar <jboczar@antmicro.com>
Fri, 24 Jul 2020 11:46:51 +0000 (13:46 +0200)
committerJędrzej Boczar <jboczar@antmicro.com>
Fri, 24 Jul 2020 11:47:18 +0000 (13:47 +0200)
litex/soc/interconnect/axi.py
test/test_axi_lite.py

index 5eb5c3d36e6809de0daae40b16224a78ba72f7db..a1fd90b6ed4200dc9ce7faea90231901749c04f1 100644 (file)
@@ -59,7 +59,7 @@ def r_description(data_width, id_width):
         ("id",   id_width)
     ]
 
-def _connect_axi(master, slave):
+def _connect_axi(master, slave, keep=None, omit=None):
     channel_modes = {
         "aw": "master",
         "w" : "master",
@@ -73,7 +73,7 @@ def _connect_axi(master, slave):
             m, s = getattr(master, channel), getattr(slave, channel)
         else:
             s, m = getattr(master, channel), getattr(slave, channel)
-        r.extend(m.connect(s))
+        r.extend(m.connect(s, keep=keep, omit=omit))
     return r
 
 def _axi_layout_flat(axi):
@@ -107,8 +107,8 @@ class AXIInterface:
         self.ar = stream.Endpoint(ax_description(address_width, id_width))
         self.r  = stream.Endpoint(r_description(data_width, id_width))
 
-    def connect(self, slave):
-        return _connect_axi(self, slave)
+    def connect(self, slave, **kwargs):
+        return _connect_axi(self, slave, **kwargs)
 
     def layout_flat(self):
         return list(_axi_layout_flat(self))
@@ -183,8 +183,8 @@ class AXILiteInterface:
                     r.append(pad.eq(sig))
         return r
 
-    def connect(self, slave):
-        return _connect_axi(self, slave)
+    def connect(self, slave, **kwargs):
+        return _connect_axi(self, slave, **kwargs)
 
     def layout_flat(self):
         return list(_axi_layout_flat(self))
@@ -893,6 +893,64 @@ class AXILiteDownConverter(Module):
         self.submodules.write = _AXILiteDownConverterWrite(master, slave)
         self.submodules.read  = _AXILiteDownConverterRead(master, slave)
 
+class AXILiteUpConverter(Module):
+    # TODO: we could try joining multiple master accesses into single slave access
+    # would reuqire checking if address changes and a way to flush on single access
+    def __init__(self, master, slave):
+        assert isinstance(master, AXILiteInterface) and isinstance(slave, AXILiteInterface)
+        dw_from      = len(master.r.data)
+        dw_to        = len(slave.r.data)
+        ratio        = dw_to//dw_from
+        master_align = log2_int(master.data_width//8)
+        slave_align  = log2_int(slave.data_width//8)
+
+        wr_word   = Signal(log2_int(ratio))
+        rd_word   = Signal(log2_int(ratio))
+        wr_word_r = Signal(log2_int(ratio))
+        rd_word_r = Signal(log2_int(ratio))
+
+        # # #
+
+        self.comb += master.connect(slave, omit={"addr", "strb", "data"})
+
+        # Address
+        self.comb += [
+            slave.aw.addr[slave_align:].eq(master.aw.addr[slave_align:]),
+            slave.ar.addr[slave_align:].eq(master.ar.addr[slave_align:]),
+        ]
+
+        # Data path
+        wr_cases, rd_cases = {}, {}
+        for i in range(ratio):
+            strb_from = i     * dw_from//8
+            strb_to   = (i+1) * dw_from//8
+            data_from = i     * dw_from
+            data_to   = (i+1) * dw_from
+            wr_cases[i] = [
+                slave.w.strb[strb_from:strb_to].eq(master.w.strb),
+                slave.w.data[data_from:data_to].eq(master.w.data),
+            ]
+            rd_cases[i] = [
+                master.r.data.eq(slave.r.data[data_from:data_to]),
+            ]
+
+        # Switch current word based on the last valid master address
+        self.sync += If(master.aw.valid, wr_word_r.eq(wr_word))
+        self.sync += If(master.ar.valid, rd_word_r.eq(rd_word))
+        self.comb += [
+            Case(master.aw.valid, {
+                0: wr_word.eq(wr_word_r),
+                1: wr_word.eq(master.aw.addr[master_align:slave_align]),
+            }),
+            Case(master.ar.valid, {
+                0: rd_word.eq(rd_word_r),
+                1: rd_word.eq(master.ar.addr[master_align:slave_align]),
+            }),
+        ]
+
+        self.comb += Case(wr_word, wr_cases)
+        self.comb += Case(rd_word, rd_cases)
+
 class AXILiteConverter(Module):
     """AXILite data width converter"""
     def __init__(self, master, slave):
@@ -906,7 +964,7 @@ class AXILiteConverter(Module):
         if dw_from > dw_to:
             self.submodules += AXILiteDownConverter(master, slave)
         elif dw_from < dw_to:
-            raise NotImplementedError("AXILiteUpConverter")
+            self.submodules += AXILiteUpConverter(master, slave)
         else:
             self.comb += master.connect(slave)
 
index cc44aeccfb3de528d08d361e9ffed678dae7574d..39e1d02870c74abbf2ce9cc4c9293b13601ddce8 100644 (file)
@@ -411,6 +411,82 @@ class TestAXILite(unittest.TestCase):
         self.converter_test(width_from=32, width_to=16,
                             write_pattern=write_pattern, write_expected=write_expected)
 
+    def test_axilite_up_converter_16to32(self):
+        write_pattern = [
+            (0x00000000, 0x1111),
+            (0x00000002, 0x2222),
+            (0x00000006, 0x3333),
+            (0x00000004, 0x4444),
+            (0x00000102, 0x5555),
+        ]
+        write_expected = [
+            (0x00000000, 0x00001111, 0b0011),
+            (0x00000000, 0x22220000, 0b1100),
+            (0x00000004, 0x33330000, 0b1100),
+            (0x00000004, 0x00004444, 0b0011),
+            (0x00000100, 0x55550000, 0b1100),
+        ]
+        read_pattern = write_pattern
+        read_expected = [
+            (0x00000000, 0x22221111),
+            (0x00000000, 0x22221111),
+            (0x00000004, 0x33334444),
+            (0x00000004, 0x33334444),
+            (0x00000100, 0x55550000),
+        ]
+        for parallel in [False, True]:
+            with self.subTest(parallel=parallel):
+                self.converter_test(width_from=16, width_to=32, parallel_rw=parallel,
+                                    write_pattern=write_pattern, write_expected=write_expected,
+                                    read_pattern=read_pattern, read_expected=read_expected)
+
+    def test_axilite_up_converter_8to32(self):
+        write_pattern = [
+            (0x00000000, 0x11),
+            (0x00000001, 0x22),
+            (0x00000003, 0x33),
+            (0x00000002, 0x44),
+            (0x00000101, 0x55),
+        ]
+        write_expected = [
+            (0x00000000, 0x00000011, 0b0001),
+            (0x00000000, 0x00002200, 0b0010),
+            (0x00000000, 0x33000000, 0b1000),
+            (0x00000000, 0x00440000, 0b0100),
+            (0x00000100, 0x00005500, 0b0010),
+        ]
+        read_pattern = write_pattern
+        read_expected = [
+            (0x00000000, 0x33442211),
+            (0x00000000, 0x33442211),
+            (0x00000000, 0x33442211),
+            (0x00000000, 0x33442211),
+            (0x00000100, 0x00005500),
+        ]
+        for parallel in [False, True]:
+            with self.subTest(parallel=parallel):
+                self.converter_test(width_from=8, width_to=32, parallel_rw=parallel,
+                                    write_pattern=write_pattern, write_expected=write_expected,
+                                    read_pattern=read_pattern, read_expected=read_expected)
+
+    def test_axilite_up_converter_strb(self):
+        write_pattern = [
+            (0x00000000, 0x1111, 0b10),
+            (0x00000002, 0x2222, 0b11),
+            (0x00000006, 0x3333, 0b11),
+            (0x00000004, 0x4444, 0b01),
+            (0x00000102, 0x5555, 0b01),
+        ]
+        write_expected = [
+            (0x00000000, 0x00001111, 0b0010),
+            (0x00000000, 0x22220000, 0b1100),
+            (0x00000004, 0x33330000, 0b1100),
+            (0x00000004, 0x00004444, 0b0001),
+            (0x00000100, 0x55550000, 0b0100),
+        ]
+        self.converter_test(width_from=16, width_to=32,
+                            write_pattern=write_pattern, write_expected=write_expected)
+
 # TestAXILiteInterconnet ---------------------------------------------------------------------------
 
 class TestAXILiteInterconnect(unittest.TestCase):