diff --git a/litex/soc/interconnect/axi.py b/litex/soc/interconnect/axi.py index 5eb5c3d36..a1fd90b6e 100644 --- a/litex/soc/interconnect/axi.py +++ b/litex/soc/interconnect/axi.py @@ -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) diff --git a/test/test_axi_lite.py b/test/test_axi_lite.py index cc44aeccf..39e1d0287 100644 --- a/test/test_axi_lite.py +++ b/test/test_axi_lite.py @@ -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):