soc/interconnect/axi: add basic AXI Lite up-converter
This commit is contained in:
parent
32160e615f
commit
879e6ffe73
|
@ -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)
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue