From: Florent Kermarrec Date: Wed, 27 Feb 2019 21:11:09 +0000 (+0100) Subject: soc/interconnect: rename axi to axi_lite X-Git-Tag: 24jan2021_ls180~1381 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=4aa07f2ae999fac79c2c940f663a9075fcfe0c1c;p=litex.git soc/interconnect: rename axi to axi_lite --- diff --git a/litex/soc/interconnect/axi.py b/litex/soc/interconnect/axi.py deleted file mode 100644 index 27c45fde..00000000 --- a/litex/soc/interconnect/axi.py +++ /dev/null @@ -1,278 +0,0 @@ -"""AXI4Lite support for LiteX""" - -# Copyright (C) 2018 by Sergiusz Bazanski -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted. - -import math - -from migen import * -from migen.genlib.record import * - -from litex.soc.interconnect import csr_bus - -# Layout of AXI4 Lite Bus -_layout = [ - # Write Address - ("aw", [ - ("addr", "address_width", DIR_M_TO_S), - ("prot", 3, DIR_M_TO_S), - ("valid", 1, DIR_M_TO_S), - ("ready", 1, DIR_S_TO_M), - ]), - - # Write Data - ("w", [ - ("data", "data_width", DIR_M_TO_S), - ("strb", "strb_width", DIR_M_TO_S), - ("valid", 1, DIR_M_TO_S), - ("ready", 1, DIR_S_TO_M), - ]), - - # Write Response - ("b", [ - ("resp", 2, DIR_S_TO_M), - ("valid", 1, DIR_S_TO_M), - ("ready", 1, DIR_M_TO_S), - ]), - - # Read Address - ("ar", [ - ("addr", "address_width", DIR_M_TO_S), - ("prot", 3, DIR_M_TO_S), - ("valid", 1, DIR_M_TO_S), - ("ready", 1, DIR_S_TO_M), - ]), - - # Read Data - ("r", [ - ("data", "data_width", DIR_S_TO_M), - ("resp", 2, DIR_S_TO_M), - ("valid", 1, DIR_S_TO_M), - ("ready", 1, DIR_M_TO_S), - ]), -] - -class Interface(Record): - """AXI4Lite Bus Interface""" - def __init__(self, data_width=32, address_width=6): - super().__init__(set_layout_parameters(_layout, - data_width=data_width, - address_width=address_width, - strb_width=data_width//8)) - - -class AXILite2CSR(Module): - """ - A bridge between AXI4Lite and a CSR bus. - - This bridge will let you connect an CSR bus to an AXI4 Lite master. Please - bear in mind that CSR is word-addressed but AXI4 is byte-addressed. This - bridge performs translation, so your AXI bus should be at least two bits - wider then your CSR bus. - - The bridge does not support unaligned reads/writes - it will round down - every access to the nearest word. If it tries to access unmapped memory, - it will return whaterver word is currently active on the CSR bus - - including writes. - """ - - def __init__(self, bus_axi, bus_csr): - self.axi = axi = bus_axi - self.csr = csr = bus_csr - - ### - - ar, r, aw, w, b = axi.ar, axi.r, axi.aw, axi.w, axi.b - - # Machine is currently busy talking to CSR, hold your horses. - busy = Signal() - - # A write transaction is happening on the bus. - write_transaction = Signal() - # A read transaction is happening on the bus. - read_transaction = Signal() - self.comb += [ - write_transaction.eq(aw.valid & aw.ready & w.valid & w.ready), - read_transaction.eq(ar.valid & ar.ready), - ] - - # Write transaction generation. - self.sync += [ - aw.ready.eq(0), - w.ready.eq(0), - If(aw.valid & w.valid, - If(~aw.ready & ~busy & ~ar.valid, - aw.ready.eq(1), - w.ready.eq(1) - ) - ) - ] - # Write response generation. - self.sync += [ - b.valid.eq(0), - If(write_transaction, - If(b.ready & ~b.valid, - b.valid.eq(1), - # Response 0 -> OKAY - b.resp.eq(0), - ) - ) - ] - # Read transaction generation. - self.sync += [ - ar.ready.eq(0), - If(ar.valid & ~ar.ready & ~busy, - ar.ready.eq(1), - ) - ] - - - # Registered data to be written to CSR, set by FSM. - wdata = Signal(csr.dat_w.nbits) - # Combinatorial byte address to assert on CSR bus, driven by FSM. - addr = Signal(ar.addr.nbits) - # Drive AXI & CSR combinatorial signals. - self.comb += [ - csr.adr.eq(addr >> int(math.log(r.data.nbits//8, 2.0))), - csr.dat_w.eq(wdata), - - r.data.eq(csr.dat_r), - r.resp.eq(0), - ] - - # CSR interaction FSM. - self.submodules.fsm = fsm = FSM(reset_state='IDLE') - self.comb += [ - busy.eq(~fsm.ongoing('IDLE')), - r.valid.eq(fsm.ongoing('READING')), - csr.we.eq(fsm.ongoing('WRITING')), - ] - - # Idle state - wait for a transaction to happen on AXI. Immediately - # assert read/write address on CSR if such an transaction is occuring. - fsm.act('IDLE', - If(read_transaction, - addr.eq(ar.addr), - NextState('READING'), - ).Elif(write_transaction, - addr.eq(aw.addr), - # Register data from AXI. - NextValue(wdata, w.data), - NextState('WRITING'), - ) - ) - - # Perform write to CSR. - fsm.act('WRITING', - addr.eq(aw.addr), - # CSR writes are single cycle, go back to IDLE. - NextState('IDLE'), - ) - - # Respond to read to AXI. - fsm.act('READING', - addr.eq(ar.addr), - # If AXI master is ready to receive data, go back to IDLE. - If(r.ready, - NextState('IDLE'), - ) - ) - - -from migen.sim import run_simulation -from litex.soc.interconnect import csr, csr_bus - -def test_axilite2csr(): - class CSRHolder(Module, csr.AutoCSR): - def __init__(self): - self.foo = csr.CSRStorage(32, reset=1) - self.bar = csr.CSRStorage(32, reset=1) - - class Fixture(Module): - def __init__(self): - self.csr = csr_bus.Interface(data_width=32, address_width=12) - self.axi = Interface(data_width=32, address_width=14) - self.submodules.holder = CSRHolder() - self.submodules.dut = AXILite2CSR(self.axi, self.csr) - self.submodules.csrbankarray = csr_bus.CSRBankArray( - self, self.map_csr, data_width=32, address_width=12) - self.submodules.csrcon = csr_bus.Interconnect( - self.csr, self.csrbankarray.get_buses()) - - def map_csr(self, name, memory): - return { - 'holder': 0, - }[name] - - def testbench_write_read(dut): - axi = dut.axi - - for _ in range(8): - yield - - # Write test - yield axi.aw.valid.eq(1) - yield axi.aw.addr.eq(4) - yield axi.w.valid.eq(1) - yield axi.b.ready.eq(1) - yield axi.w.data.eq(0x2137) - - while (yield axi.aw.ready) != 1: - yield - while (yield axi.w.ready) != 1: - yield - yield axi.aw.valid.eq(0) - yield axi.w.valid.eq(0) - - for _ in range(8): - yield - - # Read test - yield axi.ar.valid.eq(1) - yield axi.r.ready.eq(1) - yield axi.ar.addr.eq(4) - - while (yield axi.ar.ready != 1): - yield - yield axi.ar.valid.eq(0) - while (yield axi.r.valid != 1): - yield - yield axi.r.ready.eq(0) - - read = yield axi.r.data - assert read == 0x2137 - - for _ in range(8): - yield - - def testbench_simultaneous(dut): - axi = dut.axi - - for _ in range(8): - yield - - # Write - yield axi.aw.valid.eq(1) - yield axi.aw.addr.eq(2) - yield axi.w.valid.eq(1) - yield axi.b.ready.eq(1) - yield axi.w.data.eq(0x2137) - # Read - yield axi.ar.valid.eq(1) - yield axi.r.ready.eq(1) - yield axi.ar.addr.eq(2) - - yield - yield - - is_reading = yield axi.ar.ready - is_writing = yield axi.aw.ready - - assert is_reading - assert not is_writing - - fixture = Fixture() - run_simulation(fixture, testbench_write_read(fixture.dut), vcd_name='axi-write-read.vcd') - fixture = Fixture() - run_simulation(fixture, testbench_simultaneous(fixture.dut), vcd_name='axi-simultaneous.vcd') diff --git a/litex/soc/interconnect/axi_lite.py b/litex/soc/interconnect/axi_lite.py new file mode 100644 index 00000000..27c45fde --- /dev/null +++ b/litex/soc/interconnect/axi_lite.py @@ -0,0 +1,278 @@ +"""AXI4Lite support for LiteX""" + +# Copyright (C) 2018 by Sergiusz Bazanski +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted. + +import math + +from migen import * +from migen.genlib.record import * + +from litex.soc.interconnect import csr_bus + +# Layout of AXI4 Lite Bus +_layout = [ + # Write Address + ("aw", [ + ("addr", "address_width", DIR_M_TO_S), + ("prot", 3, DIR_M_TO_S), + ("valid", 1, DIR_M_TO_S), + ("ready", 1, DIR_S_TO_M), + ]), + + # Write Data + ("w", [ + ("data", "data_width", DIR_M_TO_S), + ("strb", "strb_width", DIR_M_TO_S), + ("valid", 1, DIR_M_TO_S), + ("ready", 1, DIR_S_TO_M), + ]), + + # Write Response + ("b", [ + ("resp", 2, DIR_S_TO_M), + ("valid", 1, DIR_S_TO_M), + ("ready", 1, DIR_M_TO_S), + ]), + + # Read Address + ("ar", [ + ("addr", "address_width", DIR_M_TO_S), + ("prot", 3, DIR_M_TO_S), + ("valid", 1, DIR_M_TO_S), + ("ready", 1, DIR_S_TO_M), + ]), + + # Read Data + ("r", [ + ("data", "data_width", DIR_S_TO_M), + ("resp", 2, DIR_S_TO_M), + ("valid", 1, DIR_S_TO_M), + ("ready", 1, DIR_M_TO_S), + ]), +] + +class Interface(Record): + """AXI4Lite Bus Interface""" + def __init__(self, data_width=32, address_width=6): + super().__init__(set_layout_parameters(_layout, + data_width=data_width, + address_width=address_width, + strb_width=data_width//8)) + + +class AXILite2CSR(Module): + """ + A bridge between AXI4Lite and a CSR bus. + + This bridge will let you connect an CSR bus to an AXI4 Lite master. Please + bear in mind that CSR is word-addressed but AXI4 is byte-addressed. This + bridge performs translation, so your AXI bus should be at least two bits + wider then your CSR bus. + + The bridge does not support unaligned reads/writes - it will round down + every access to the nearest word. If it tries to access unmapped memory, + it will return whaterver word is currently active on the CSR bus - + including writes. + """ + + def __init__(self, bus_axi, bus_csr): + self.axi = axi = bus_axi + self.csr = csr = bus_csr + + ### + + ar, r, aw, w, b = axi.ar, axi.r, axi.aw, axi.w, axi.b + + # Machine is currently busy talking to CSR, hold your horses. + busy = Signal() + + # A write transaction is happening on the bus. + write_transaction = Signal() + # A read transaction is happening on the bus. + read_transaction = Signal() + self.comb += [ + write_transaction.eq(aw.valid & aw.ready & w.valid & w.ready), + read_transaction.eq(ar.valid & ar.ready), + ] + + # Write transaction generation. + self.sync += [ + aw.ready.eq(0), + w.ready.eq(0), + If(aw.valid & w.valid, + If(~aw.ready & ~busy & ~ar.valid, + aw.ready.eq(1), + w.ready.eq(1) + ) + ) + ] + # Write response generation. + self.sync += [ + b.valid.eq(0), + If(write_transaction, + If(b.ready & ~b.valid, + b.valid.eq(1), + # Response 0 -> OKAY + b.resp.eq(0), + ) + ) + ] + # Read transaction generation. + self.sync += [ + ar.ready.eq(0), + If(ar.valid & ~ar.ready & ~busy, + ar.ready.eq(1), + ) + ] + + + # Registered data to be written to CSR, set by FSM. + wdata = Signal(csr.dat_w.nbits) + # Combinatorial byte address to assert on CSR bus, driven by FSM. + addr = Signal(ar.addr.nbits) + # Drive AXI & CSR combinatorial signals. + self.comb += [ + csr.adr.eq(addr >> int(math.log(r.data.nbits//8, 2.0))), + csr.dat_w.eq(wdata), + + r.data.eq(csr.dat_r), + r.resp.eq(0), + ] + + # CSR interaction FSM. + self.submodules.fsm = fsm = FSM(reset_state='IDLE') + self.comb += [ + busy.eq(~fsm.ongoing('IDLE')), + r.valid.eq(fsm.ongoing('READING')), + csr.we.eq(fsm.ongoing('WRITING')), + ] + + # Idle state - wait for a transaction to happen on AXI. Immediately + # assert read/write address on CSR if such an transaction is occuring. + fsm.act('IDLE', + If(read_transaction, + addr.eq(ar.addr), + NextState('READING'), + ).Elif(write_transaction, + addr.eq(aw.addr), + # Register data from AXI. + NextValue(wdata, w.data), + NextState('WRITING'), + ) + ) + + # Perform write to CSR. + fsm.act('WRITING', + addr.eq(aw.addr), + # CSR writes are single cycle, go back to IDLE. + NextState('IDLE'), + ) + + # Respond to read to AXI. + fsm.act('READING', + addr.eq(ar.addr), + # If AXI master is ready to receive data, go back to IDLE. + If(r.ready, + NextState('IDLE'), + ) + ) + + +from migen.sim import run_simulation +from litex.soc.interconnect import csr, csr_bus + +def test_axilite2csr(): + class CSRHolder(Module, csr.AutoCSR): + def __init__(self): + self.foo = csr.CSRStorage(32, reset=1) + self.bar = csr.CSRStorage(32, reset=1) + + class Fixture(Module): + def __init__(self): + self.csr = csr_bus.Interface(data_width=32, address_width=12) + self.axi = Interface(data_width=32, address_width=14) + self.submodules.holder = CSRHolder() + self.submodules.dut = AXILite2CSR(self.axi, self.csr) + self.submodules.csrbankarray = csr_bus.CSRBankArray( + self, self.map_csr, data_width=32, address_width=12) + self.submodules.csrcon = csr_bus.Interconnect( + self.csr, self.csrbankarray.get_buses()) + + def map_csr(self, name, memory): + return { + 'holder': 0, + }[name] + + def testbench_write_read(dut): + axi = dut.axi + + for _ in range(8): + yield + + # Write test + yield axi.aw.valid.eq(1) + yield axi.aw.addr.eq(4) + yield axi.w.valid.eq(1) + yield axi.b.ready.eq(1) + yield axi.w.data.eq(0x2137) + + while (yield axi.aw.ready) != 1: + yield + while (yield axi.w.ready) != 1: + yield + yield axi.aw.valid.eq(0) + yield axi.w.valid.eq(0) + + for _ in range(8): + yield + + # Read test + yield axi.ar.valid.eq(1) + yield axi.r.ready.eq(1) + yield axi.ar.addr.eq(4) + + while (yield axi.ar.ready != 1): + yield + yield axi.ar.valid.eq(0) + while (yield axi.r.valid != 1): + yield + yield axi.r.ready.eq(0) + + read = yield axi.r.data + assert read == 0x2137 + + for _ in range(8): + yield + + def testbench_simultaneous(dut): + axi = dut.axi + + for _ in range(8): + yield + + # Write + yield axi.aw.valid.eq(1) + yield axi.aw.addr.eq(2) + yield axi.w.valid.eq(1) + yield axi.b.ready.eq(1) + yield axi.w.data.eq(0x2137) + # Read + yield axi.ar.valid.eq(1) + yield axi.r.ready.eq(1) + yield axi.ar.addr.eq(2) + + yield + yield + + is_reading = yield axi.ar.ready + is_writing = yield axi.aw.ready + + assert is_reading + assert not is_writing + + fixture = Fixture() + run_simulation(fixture, testbench_write_read(fixture.dut), vcd_name='axi-write-read.vcd') + fixture = Fixture() + run_simulation(fixture, testbench_simultaneous(fixture.dut), vcd_name='axi-simultaneous.vcd')