From 35149c4e808df4db4d38e9f7d4d9c055b745b77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Tue, 14 Jul 2020 14:44:28 +0200 Subject: [PATCH 1/8] soc/integration: update add_adapter to convert between AXILite/Wishbone --- litex/soc/integration/soc.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/litex/soc/integration/soc.py b/litex/soc/integration/soc.py index 8980ea4c7..7111921cd 100644 --- a/litex/soc/integration/soc.py +++ b/litex/soc/integration/soc.py @@ -280,6 +280,21 @@ class SoCBusHandler(Module): # Add Master/Slave ----------------------------------------------------------------------------- def add_adapter(self, name, interface, direction="m2s"): assert direction in ["m2s", "s2m"] + + if isinstance(interface, axi.AXILiteInterface): + self.logger.info("{} Bus {} from {} to {}.".format( + colorer(name), + colorer("converted", color="cyan"), + colorer("AXILite"), + colorer("Wishbone"))) + new_interface = wishbone.Interface(data_width=interface.data_width) + if direction == "m2s": + converter = axi.AXILite2Wishbone(axi_lite=interface, wishbone=new_interface) + elif direction == "s2m": + converter = axi.Wishbone2AXILite(wishbone=new_interface, axi_lite=interface) + self.submodules += converter + interface = new_interface + if interface.data_width != self.data_width: self.logger.info("{} Bus {} from {}-bit to {}-bit.".format( colorer(name), From b692b2a3f126a0ae38a40c95ed9153fa77e44bfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Tue, 14 Jul 2020 16:28:29 +0200 Subject: [PATCH 2/8] soc/interconnect: add AXILite2CSR bridge --- litex/soc/interconnect/axi.py | 71 +++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/litex/soc/interconnect/axi.py b/litex/soc/interconnect/axi.py index e094a3d15..83f5044bf 100644 --- a/litex/soc/interconnect/axi.py +++ b/litex/soc/interconnect/axi.py @@ -494,3 +494,74 @@ class Wishbone2AXILite(Module): wishbone.err.eq(1), NextState("IDLE") ) + +# AXILite to CSR ----------------------------------------------------------------------------------- + +class AXILite2CSR(Module): + def __init__(self, axi_lite=None, csr=None): + if axi_lite is None: + axi_lite = AXILiteInterface() + if csr is None: + csr = csr.bus.Interface() + + self.axi_lite = axi_lite + self.csr = csr + + adr_shift = log2_int(self.axi_lite.data_width//8) + rdata = Signal.like(self.csr.dat_r) + do_read = Signal() + do_write = Signal() + last_was_read = Signal() + + # # # + + self.submodules.fsm = fsm = FSM() + fsm.act("IDLE", + # if last access was a read, do a write, and vice versa + If(self.axi_lite.aw.valid & self.axi_lite.ar.valid, + do_write.eq(last_was_read), + do_read.eq(~last_was_read), + ).Else( + do_write.eq(self.axi_lite.aw.valid), + do_read.eq(self.axi_lite.ar.valid), + ), + If(do_write, + NextValue(last_was_read, 0), + NextState("DO-WRITE"), + ).Elif(do_read, + self.csr.adr.eq(self.axi_lite.ar.addr[adr_shift:]), + self.csr.we.eq(0), + NextValue(last_was_read, 1), + NextState("DO-READ"), + ) + ) + fsm.act("DO-READ", + self.axi_lite.ar.ready.eq(1), + NextValue(rdata, self.csr.dat_r), + NextState("SEND-READ-RESPONSE"), + ) + fsm.act("SEND-READ-RESPONSE", + self.axi_lite.r.valid.eq(1), + self.axi_lite.r.resp.eq(RESP_OKAY), + self.axi_lite.r.data.eq(rdata), + If(self.axi_lite.r.ready, + NextState("IDLE") + ) + ) + fsm.act("DO-WRITE", + self.csr.adr.eq(self.axi_lite.aw.addr[adr_shift:]), + self.csr.dat_w.eq(self.axi_lite.w.data), + If(self.axi_lite.w.valid, + self.csr.we.eq(1 & (self.axi_lite.w.strb != 0)), + self.axi_lite.aw.ready.eq(1), + self.axi_lite.w.ready.eq(1), + NextState("SEND-WRITE-RESPONSE") + ) + ) + fsm.act("SEND-WRITE-RESPONSE", + self.axi_lite.b.valid.eq(1), + self.axi_lite.b.resp.eq(RESP_OKAY), + If(self.axi_lite.b.ready, + NextState("IDLE") + ) + ) From d8a242d86f56ff02c676f5678e060966840e0749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Wed, 15 Jul 2020 10:58:34 +0200 Subject: [PATCH 3/8] soc/interconnect: add AXILite SRAM --- litex/soc/integration/soc.py | 21 ++++++--- litex/soc/interconnect/axi.py | 89 +++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 6 deletions(-) diff --git a/litex/soc/integration/soc.py b/litex/soc/integration/soc.py index 7111921cd..28ee0cc22 100644 --- a/litex/soc/integration/soc.py +++ b/litex/soc/integration/soc.py @@ -743,19 +743,28 @@ class SoC(Module): setattr(self.submodules, name, SoCController(**kwargs)) self.csr.add(name, use_loc_if_exists=True) - def add_ram(self, name, origin, size, contents=[], mode="rw"): - ram_bus = wishbone.Interface(data_width=self.bus.data_width) - ram = wishbone.SRAM(size, bus=ram_bus, init=contents, read_only=(mode == "r")) + def add_ram(self, name, origin, size, contents=[], mode="rw", bus=None): + if bus is None: + bus = wishbone.Interface(data_width=self.bus.data_width) + + if isinstance(bus, wishbone.Interface): + ram = wishbone.SRAM(size, bus=bus, init=contents, read_only=(mode == "r")) + elif isinstance(bus, axi.AXILiteInterface): + ram = axi.AXILiteSRAM(size, bus=bus, init=contents, read_only=(mode == "r")) + else: + raise TypeError(bus) + self.bus.add_slave(name, ram.bus, SoCRegion(origin=origin, size=size, mode=mode)) self.check_if_exists(name) - self.logger.info("RAM {} {} {}.".format( + self.logger.info("{} RAM {} {} {}.".format( + colorer("Wishbone" if isinstance(bus, wishbone.Interface) else "AXILite"), colorer(name), colorer("added", color="green"), self.bus.regions[name])) setattr(self.submodules, name, ram) - def add_rom(self, name, origin, size, contents=[]): - self.add_ram(name, origin, size, contents, mode="r") + def add_rom(self, name, origin, size, contents=[], bus=None): + self.add_ram(name, origin, size, contents, mode="r", bus=bus) def add_csr_bridge(self, origin): self.submodules.csr_bridge = wishbone.Wishbone2CSR( diff --git a/litex/soc/interconnect/axi.py b/litex/soc/interconnect/axi.py index 83f5044bf..a9ee719ca 100644 --- a/litex/soc/interconnect/axi.py +++ b/litex/soc/interconnect/axi.py @@ -565,3 +565,92 @@ class AXILite2CSR(Module): NextState("IDLE") ) ) + +# AXILite SRAM ------------------------------------------------------------------------------------- + +class AXILiteSRAM(Module): + def __init__(self, mem_or_size, read_only=None, init=None, bus=None): + if bus is None: + bus = AXILiteInterface() + self.bus = bus + + bus_data_width = len(self.bus.r.data) + if isinstance(mem_or_size, Memory): + assert(mem_or_size.width <= bus_data_width) + self.mem = mem_or_size + else: + self.mem = Memory(bus_data_width, mem_or_size//(bus_data_width//8), init=init) + + if read_only is None: + if hasattr(self.mem, "bus_read_only"): + read_only = self.mem.bus_read_only + else: + read_only = False + + ### + + # Create memory port + port = self.mem.get_port(write_capable=not read_only, we_granularity=8, + mode=READ_FIRST if read_only else WRITE_FIRST) + self.specials += self.mem, port + + # Generate write enable signal + if not read_only: + self.comb += port.dat_w.eq(self.bus.w.data), + self.comb += [port.we[i].eq(self.bus.w.valid & self.bus.w.ready & self.bus.w.strb[i]) + for i in range(bus_data_width//8)] + + # Access logic + adr_shift = log2_int(self.bus.data_width//8) + rdata = Signal.like(port.dat_r) + do_read = Signal() + do_write = Signal() + last_was_read = Signal() + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + # if last access was a read, do a write, and vice versa + If(self.bus.aw.valid & self.bus.ar.valid, + do_write.eq(last_was_read), + do_read.eq(~last_was_read), + ).Else( + do_write.eq(self.bus.aw.valid), + do_read.eq(self.bus.ar.valid), + ), + If(do_write, + NextValue(last_was_read, 0), + NextState("DO-WRITE"), + ).Elif(do_read, + port.adr.eq(self.bus.ar.addr[adr_shift:]), + NextValue(last_was_read, 1), + NextState("DO-READ"), + ) + ) + fsm.act("DO-READ", + self.bus.ar.ready.eq(1), + NextValue(rdata, port.dat_r), + NextState("SEND-READ-RESPONSE"), + ) + fsm.act("SEND-READ-RESPONSE", + self.bus.r.valid.eq(1), + self.bus.r.resp.eq(RESP_OKAY), + self.bus.r.data.eq(rdata), + If(self.bus.r.ready, + NextState("IDLE") + ) + ) + fsm.act("DO-WRITE", + port.adr.eq(self.bus.aw.addr[adr_shift:]), + If(self.bus.w.valid, + self.bus.aw.ready.eq(1), + self.bus.w.ready.eq(1), + NextState("SEND-WRITE-RESPONSE") + ) + ) + fsm.act("SEND-WRITE-RESPONSE", + self.bus.b.valid.eq(1), + self.bus.b.resp.eq(RESP_OKAY), + If(self.bus.b.ready, + NextState("IDLE") + ) + ) From a5be2cd2576a0d2ba5e5c1bd3a5a609bef7431eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Wed, 15 Jul 2020 11:28:21 +0200 Subject: [PATCH 4/8] soc/interconnect/axi: improve SRAM/CSR access speed --- litex/soc/interconnect/axi.py | 187 ++++++++++++++-------------------- 1 file changed, 75 insertions(+), 112 deletions(-) diff --git a/litex/soc/interconnect/axi.py b/litex/soc/interconnect/axi.py index a9ee719ca..1fc7af8aa 100644 --- a/litex/soc/interconnect/axi.py +++ b/litex/soc/interconnect/axi.py @@ -497,6 +497,69 @@ class Wishbone2AXILite(Module): # AXILite to CSR ----------------------------------------------------------------------------------- +def axi_lite_to_simple(axi_lite, port_adr, port_dat_r, port_dat_w=None, port_we=None): + """Connection of AXILite to simple bus with 1-cycle latency, such as CSR bus or Memory port""" + bus_data_width = axi_lite.data_width + adr_shift = log2_int(bus_data_width//8) + do_read = Signal() + do_write = Signal() + last_was_read = Signal() + + comb = [] + if port_dat_w is not None: + comb.append(port_dat_w.eq(axi_lite.w.data)) + if port_we is not None: + if len(port_we) > 1: + for i in range(bus_data_width//8): + comb.append(port_we[i].eq(axi_lite.w.valid & axi_lite.w.ready & axi_lite.w.strb[i])) + else: + comb.append(port_we.eq(axi_lite.w.valid & axi_lite.w.ready & (axi_lite.w.strb != 0))) + + fsm = FSM() + fsm.act("START-TRANSACTION", + # If the last access was a read, do a write, and vice versa + If(axi_lite.aw.valid & axi_lite.ar.valid, + do_write.eq(last_was_read), + do_read.eq(~last_was_read), + ).Else( + do_write.eq(axi_lite.aw.valid), + do_read.eq(axi_lite.ar.valid), + ), + # Start reading/writing immediately not to waste a cycle + If(do_write, + port_adr.eq(axi_lite.aw.addr[adr_shift:]), + If(axi_lite.w.valid, + axi_lite.aw.ready.eq(1), + axi_lite.w.ready.eq(1), + NextState("SEND-WRITE-RESPONSE") + ) + ).Elif(do_read, + port_adr.eq(axi_lite.ar.addr[adr_shift:]), + axi_lite.ar.ready.eq(1), + NextState("SEND-READ-RESPONSE"), + ) + ) + fsm.act("SEND-READ-RESPONSE", + NextValue(last_was_read, 1), + # As long as we have correct address port.dat_r will be valid + port_adr.eq(axi_lite.ar.addr[adr_shift:]), + axi_lite.r.data.eq(port_dat_r), + axi_lite.r.resp.eq(RESP_OKAY), + axi_lite.r.valid.eq(1), + If(axi_lite.r.ready, + NextState("START-TRANSACTION") + ) + ) + fsm.act("SEND-WRITE-RESPONSE", + NextValue(last_was_read, 0), + axi_lite.b.valid.eq(1), + axi_lite.b.resp.eq(RESP_OKAY), + If(axi_lite.b.ready, + NextState("START-TRANSACTION") + ) + ) + return fsm, comb + class AXILite2CSR(Module): def __init__(self, axi_lite=None, csr=None): if axi_lite is None: @@ -507,64 +570,11 @@ class AXILite2CSR(Module): self.axi_lite = axi_lite self.csr = csr - adr_shift = log2_int(self.axi_lite.data_width//8) - rdata = Signal.like(self.csr.dat_r) - do_read = Signal() - do_write = Signal() - last_was_read = Signal() - - # # # - - self.submodules.fsm = fsm = FSM() - fsm.act("IDLE", - # if last access was a read, do a write, and vice versa - If(self.axi_lite.aw.valid & self.axi_lite.ar.valid, - do_write.eq(last_was_read), - do_read.eq(~last_was_read), - ).Else( - do_write.eq(self.axi_lite.aw.valid), - do_read.eq(self.axi_lite.ar.valid), - ), - If(do_write, - NextValue(last_was_read, 0), - NextState("DO-WRITE"), - ).Elif(do_read, - self.csr.adr.eq(self.axi_lite.ar.addr[adr_shift:]), - self.csr.we.eq(0), - NextValue(last_was_read, 1), - NextState("DO-READ"), - ) - ) - fsm.act("DO-READ", - self.axi_lite.ar.ready.eq(1), - NextValue(rdata, self.csr.dat_r), - NextState("SEND-READ-RESPONSE"), - ) - fsm.act("SEND-READ-RESPONSE", - self.axi_lite.r.valid.eq(1), - self.axi_lite.r.resp.eq(RESP_OKAY), - self.axi_lite.r.data.eq(rdata), - If(self.axi_lite.r.ready, - NextState("IDLE") - ) - ) - fsm.act("DO-WRITE", - self.csr.adr.eq(self.axi_lite.aw.addr[adr_shift:]), - self.csr.dat_w.eq(self.axi_lite.w.data), - If(self.axi_lite.w.valid, - self.csr.we.eq(1 & (self.axi_lite.w.strb != 0)), - self.axi_lite.aw.ready.eq(1), - self.axi_lite.w.ready.eq(1), - NextState("SEND-WRITE-RESPONSE") - ) - ) - fsm.act("SEND-WRITE-RESPONSE", - self.axi_lite.b.valid.eq(1), - self.axi_lite.b.resp.eq(RESP_OKAY), - If(self.axi_lite.b.ready, - NextState("IDLE") - ) - ) + fsm, comb = axi_lite_to_simple(self.axi_lite, + port_adr=self.csr.adr, port_dat_r=self.csr.dat_r, + port_dat_w=self.csr.dat_w, port_we=self.csr.we) + self.submodules.fsm = fsm + self.comb += comb # AXILite SRAM ------------------------------------------------------------------------------------- @@ -600,57 +610,10 @@ class AXILiteSRAM(Module): self.comb += [port.we[i].eq(self.bus.w.valid & self.bus.w.ready & self.bus.w.strb[i]) for i in range(bus_data_width//8)] - # Access logic - adr_shift = log2_int(self.bus.data_width//8) - rdata = Signal.like(port.dat_r) - do_read = Signal() - do_write = Signal() - last_was_read = Signal() - - self.submodules.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - # if last access was a read, do a write, and vice versa - If(self.bus.aw.valid & self.bus.ar.valid, - do_write.eq(last_was_read), - do_read.eq(~last_was_read), - ).Else( - do_write.eq(self.bus.aw.valid), - do_read.eq(self.bus.ar.valid), - ), - If(do_write, - NextValue(last_was_read, 0), - NextState("DO-WRITE"), - ).Elif(do_read, - port.adr.eq(self.bus.ar.addr[adr_shift:]), - NextValue(last_was_read, 1), - NextState("DO-READ"), - ) - ) - fsm.act("DO-READ", - self.bus.ar.ready.eq(1), - NextValue(rdata, port.dat_r), - NextState("SEND-READ-RESPONSE"), - ) - fsm.act("SEND-READ-RESPONSE", - self.bus.r.valid.eq(1), - self.bus.r.resp.eq(RESP_OKAY), - self.bus.r.data.eq(rdata), - If(self.bus.r.ready, - NextState("IDLE") - ) - ) - fsm.act("DO-WRITE", - port.adr.eq(self.bus.aw.addr[adr_shift:]), - If(self.bus.w.valid, - self.bus.aw.ready.eq(1), - self.bus.w.ready.eq(1), - NextState("SEND-WRITE-RESPONSE") - ) - ) - fsm.act("SEND-WRITE-RESPONSE", - self.bus.b.valid.eq(1), - self.bus.b.resp.eq(RESP_OKAY), - If(self.bus.b.ready, - NextState("IDLE") - ) - ) + # Transaction logic + fsm, comb = axi_lite_to_simple(self.bus, + port_adr=port.adr, port_dat_r=port.dat_r, + port_dat_w=port.dat_w if not read_only else None, + port_we=port.we if not read_only else None) + self.submodules.fsm = fsm + self.comb += comb From 78a631f39262aebfe12c67438d7a19352a6dafb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Wed, 15 Jul 2020 12:30:28 +0200 Subject: [PATCH 5/8] test/axi: add AXILite2CSR and AXILiteSRAM tests --- litex/soc/interconnect/axi.py | 36 +++++++++++++++ test/test_axi.py | 83 ++++++++++++++++++++++++++++++++++- 2 files changed, 117 insertions(+), 2 deletions(-) diff --git a/litex/soc/interconnect/axi.py b/litex/soc/interconnect/axi.py index 1fc7af8aa..d0886c1fb 100644 --- a/litex/soc/interconnect/axi.py +++ b/litex/soc/interconnect/axi.py @@ -138,6 +138,42 @@ class AXILiteInterface: r.append(pad.eq(sig)) return r + def write(self, addr, data, strb=None): + if strb is None: + strb = 2**len(self.w.strb) - 1 + yield self.aw.valid.eq(1) + yield self.aw.addr.eq(addr) + yield self.w.data.eq(data) + yield self.w.valid.eq(1) + yield self.w.strb.eq(strb) + yield + while not (yield self.aw.ready): + yield + while not (yield self.w.ready): + yield + while not (yield self.b.valid): + yield + yield self.b.ready.eq(1) + resp = (yield self.b.resp) + yield + yield self.b.ready.eq(0) + return resp + + def read(self, addr): + yield self.ar.valid.eq(1) + yield self.ar.addr.eq(addr) + yield + while not (yield self.ar.ready): + yield + while not (yield self.r.valid): + yield + yield self.r.ready.eq(1) + data = (yield self.r.data) + resp = (yield self.r.resp) + yield + yield self.r.ready.eq(0) + return (data, resp) + # AXI Stream Definition ---------------------------------------------------------------------------- class AXIStreamInterface(stream.Endpoint): diff --git a/test/test_axi.py b/test/test_axi.py index 3152a4134..fa694e412 100644 --- a/test/test_axi.py +++ b/test/test_axi.py @@ -7,7 +7,7 @@ import random from migen import * from litex.soc.interconnect.axi import * -from litex.soc.interconnect import wishbone +from litex.soc.interconnect import wishbone, csr_bus # Software Models ---------------------------------------------------------------------------------- @@ -358,5 +358,84 @@ class TestAXI(unittest.TestCase): dut.errors += 1 dut = DUT() - run_simulation(dut, [generator(dut)], vcd_name="toto.vcd") + run_simulation(dut, [generator(dut)]) + self.assertEqual(dut.errors, 0) + + def test_axilite2csr(self): + @passive + def csr_mem_handler(csr, mem): + while True: + adr = (yield csr.adr) + yield csr.dat_r.eq(mem[adr]) + if (yield csr.we): + mem[adr] = (yield csr.dat_w) + yield + + class DUT(Module): + def __init__(self): + self.axi_lite = AXILiteInterface() + self.csr = csr_bus.Interface() + self.submodules.axilite2csr = AXILite2CSR(self.axi_lite, self.csr) + self.errors = 0 + + prng = random.Random(42) + mem_ref = [prng.randrange(255) for i in range(100)] + + def generator(dut): + dut.errors = 0 + + for adr, ref in enumerate(mem_ref): + adr = adr << 2 + data, resp = (yield from dut.axi_lite.read(adr)) + self.assertEqual(resp, 0b00) + if data != ref: + dut.errors += 1 + + write_data = [prng.randrange(255) for _ in mem_ref] + + for adr, wdata in enumerate(write_data): + adr = adr << 2 + resp = (yield from dut.axi_lite.write(adr, wdata)) + self.assertEqual(resp, 0b00) + rdata, resp = (yield from dut.axi_lite.read(adr)) + self.assertEqual(resp, 0b00) + if rdata != wdata: + dut.errors += 1 + + dut = DUT() + mem = [v for v in mem_ref] + run_simulation(dut, [generator(dut), csr_mem_handler(dut.csr, mem)]) + self.assertEqual(dut.errors, 0) + + def test_axilite_sram(self): + class DUT(Module): + def __init__(self, size, init): + self.axi_lite = AXILiteInterface() + self.submodules.sram = AXILiteSRAM(size, init=init, bus=self.axi_lite) + self.errors = 0 + + def generator(dut, ref_init): + for adr, ref in enumerate(ref_init): + adr = adr << 2 + data, resp = (yield from dut.axi_lite.read(adr)) + self.assertEqual(resp, 0b00) + if data != ref: + dut.errors += 1 + + write_data = [prng.randrange(255) for _ in ref_init] + + for adr, wdata in enumerate(write_data): + adr = adr << 2 + resp = (yield from dut.axi_lite.write(adr, wdata)) + self.assertEqual(resp, 0b00) + rdata, resp = (yield from dut.axi_lite.read(adr)) + self.assertEqual(resp, 0b00) + if rdata != wdata: + dut.errors += 1 + + prng = random.Random(42) + init = [prng.randrange(2**32) for i in range(100)] + + dut = DUT(size=len(init)*4, init=[v for v in init]) + run_simulation(dut, [generator(dut, init)]) self.assertEqual(dut.errors, 0) From f3072d498421280ed5b22f2bf016b4fbb4994cea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Wed, 15 Jul 2020 15:47:06 +0200 Subject: [PATCH 6/8] soc/interconnect/axi: add connect methods for convenience --- litex/soc/interconnect/axi.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/litex/soc/interconnect/axi.py b/litex/soc/interconnect/axi.py index d0886c1fb..cb9c9ad85 100644 --- a/litex/soc/interconnect/axi.py +++ b/litex/soc/interconnect/axi.py @@ -55,6 +55,23 @@ def r_description(data_width, id_width): ("id", id_width) ] +def _connect_axi(master, slave): + channel_modes = { + "aw": "master", + "w" : "master", + "b" : "slave", + "ar": "master", + "r" : "slave", + } + r = [] + for channel, mode in channel_modes.items(): + if mode == "master": + m, s = getattr(master, channel), getattr(slave, channel) + else: + s, m = getattr(master, channel), getattr(slave, channel) + r.extend(m.connect(s)) + return r + class AXIInterface: def __init__(self, data_width=32, address_width=32, id_width=1, clock_domain="sys"): self.data_width = data_width @@ -68,6 +85,9 @@ 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) + # AXI Lite Definition ------------------------------------------------------------------------------ def ax_lite_description(address_width): @@ -138,6 +158,9 @@ class AXILiteInterface: r.append(pad.eq(sig)) return r + def connect(self, slave): + return _connect_axi(self, slave) + def write(self, addr, data, strb=None): if strb is None: strb = 2**len(self.w.strb) - 1 From 2700ec3ce515670a388e2a46a2b87a5b602cc9c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Wed, 15 Jul 2020 15:59:16 +0200 Subject: [PATCH 7/8] soc/integration: use AXILiteConverter (dummy implementation) in add_adapter() --- litex/soc/integration/soc.py | 54 +++++++++++++++++++++-------------- litex/soc/interconnect/axi.py | 19 ++++++++++++ 2 files changed, 51 insertions(+), 22 deletions(-) diff --git a/litex/soc/integration/soc.py b/litex/soc/integration/soc.py index 28ee0cc22..d52402c95 100644 --- a/litex/soc/integration/soc.py +++ b/litex/soc/integration/soc.py @@ -281,35 +281,45 @@ class SoCBusHandler(Module): def add_adapter(self, name, interface, direction="m2s"): assert direction in ["m2s", "s2m"] - if isinstance(interface, axi.AXILiteInterface): - self.logger.info("{} Bus {} from {} to {}.".format( - colorer(name), - colorer("converted", color="cyan"), - colorer("AXILite"), - colorer("Wishbone"))) - new_interface = wishbone.Interface(data_width=interface.data_width) - if direction == "m2s": - converter = axi.AXILite2Wishbone(axi_lite=interface, wishbone=new_interface) - elif direction == "s2m": - converter = axi.Wishbone2AXILite(wishbone=new_interface, axi_lite=interface) - self.submodules += converter - interface = new_interface - - if interface.data_width != self.data_width: - self.logger.info("{} Bus {} from {}-bit to {}-bit.".format( - colorer(name), - colorer("converted", color="cyan"), - colorer(interface.data_width), - colorer(self.data_width))) + if isinstance(interface, wishbone.Interface): new_interface = wishbone.Interface(data_width=self.data_width) if direction == "m2s": converter = wishbone.Converter(master=interface, slave=new_interface) if direction == "s2m": converter = wishbone.Converter(master=new_interface, slave=interface) self.submodules += converter - return new_interface + elif isinstance(interface, axi.AXILiteInterface): + # Data width conversion + intermediate = axi.AXILiteInterface(data_width=self.data_width) + if direction == "m2s": + converter = axi.AXILiteConverter(master=interface, slave=intermediate) + if direction == "s2m": + converter = axi.AXILiteConverter(master=intermediate, slave=interface) + self.submodules += converter + # Bus type conversion + new_interface = wishbone.Interface(data_width=self.data_width) + if direction == "m2s": + converter = axi.AXILite2Wishbone(axi_lite=intermediate, wishbone=new_interface) + elif direction == "s2m": + converter = axi.Wishbone2AXILite(wishbone=new_interface, axi_lite=intermediate) + self.submodules += converter else: - return interface + raise TypeError(interface) + + fmt = "{name} Bus {converted} from {frombus} {frombits}-bit to {tobus} {tobits}-bit." + frombus = "Wishbone" if isinstance(interface, wishbone.Interface) else "AXILite" + tobus = "Wishbone" if isinstance(new_interface, wishbone.Interface) else "AXILite" + frombits = interface.data_width + tobits = new_interface.data_width + if frombus != tobus or frombits != tobits: + self.logger.info(fmt.format( + name = colorer(name), + converted = colorer("converted", color="cyan"), + frombus = colorer("Wishbone" if isinstance(interface, wishbone.Interface) else "AXILite"), + frombits = colorer(interface.data_width), + tobus = colorer("Wishbone" if isinstance(new_interface, wishbone.Interface) else "AXILite"), + tobits = colorer(new_interface.data_width))) + return new_interface def add_master(self, name=None, master=None): if name is None: diff --git a/litex/soc/interconnect/axi.py b/litex/soc/interconnect/axi.py index cb9c9ad85..02002a58e 100644 --- a/litex/soc/interconnect/axi.py +++ b/litex/soc/interconnect/axi.py @@ -676,3 +676,22 @@ class AXILiteSRAM(Module): port_we=port.we if not read_only else None) self.submodules.fsm = fsm self.comb += comb + +# AXILite Data Width Converter --------------------------------------------------------------------- + +class AXILiteConverter(Module): + """AXILite data width converter""" + def __init__(self, master, slave): + self.master = master + self.slave = slave + + # # # + + dw_from = len(master.r.data) + dw_to = len(slave.r.data) + if dw_from > dw_to: + raise NotImplementedError + elif dw_from < dw_to: + raise NotImplementedError + else: + self.comb += master.connect(slave) From 0be607dad95582e902066b990aa11d69e9fb965e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Thu, 16 Jul 2020 10:26:12 +0200 Subject: [PATCH 8/8] soc/integration: revert `bus` argument for add_ram/add_rom --- litex/soc/integration/soc.py | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/litex/soc/integration/soc.py b/litex/soc/integration/soc.py index d52402c95..aee695c10 100644 --- a/litex/soc/integration/soc.py +++ b/litex/soc/integration/soc.py @@ -753,28 +753,19 @@ class SoC(Module): setattr(self.submodules, name, SoCController(**kwargs)) self.csr.add(name, use_loc_if_exists=True) - def add_ram(self, name, origin, size, contents=[], mode="rw", bus=None): - if bus is None: - bus = wishbone.Interface(data_width=self.bus.data_width) - - if isinstance(bus, wishbone.Interface): - ram = wishbone.SRAM(size, bus=bus, init=contents, read_only=(mode == "r")) - elif isinstance(bus, axi.AXILiteInterface): - ram = axi.AXILiteSRAM(size, bus=bus, init=contents, read_only=(mode == "r")) - else: - raise TypeError(bus) - + def add_ram(self, name, origin, size, contents=[], mode="rw"): + ram_bus = wishbone.Interface(data_width=self.bus.data_width) + ram = wishbone.SRAM(size, bus=ram_bus, init=contents, read_only=(mode == "r")) self.bus.add_slave(name, ram.bus, SoCRegion(origin=origin, size=size, mode=mode)) self.check_if_exists(name) - self.logger.info("{} RAM {} {} {}.".format( - colorer("Wishbone" if isinstance(bus, wishbone.Interface) else "AXILite"), + self.logger.info("RAM {} {} {}.".format( colorer(name), colorer("added", color="green"), self.bus.regions[name])) setattr(self.submodules, name, ram) - def add_rom(self, name, origin, size, contents=[], bus=None): - self.add_ram(name, origin, size, contents, mode="r", bus=bus) + def add_rom(self, name, origin, size, contents=[]): + self.add_ram(name, origin, size, contents, mode="r") def add_csr_bridge(self, origin): self.submodules.csr_bridge = wishbone.Wishbone2CSR(