Change AXI interface and tidy code

Inspired by parts of https://github.com/peteut/migen-misc/
This commit is contained in:
Sergiusz Bazanski 2018-02-21 00:00:58 +00:00
parent 512ed2b3d6
commit 688f26cc32
1 changed files with 103 additions and 86 deletions

View File

@ -13,33 +13,43 @@ from litex.soc.interconnect import csr_bus
# Layout of AXI4 Lite Bus # Layout of AXI4 Lite Bus
_layout = [ _layout = [
# Write Address # Write Address
("awaddr", "address_width", DIR_M_TO_S), ("aw", [
("awprot", 3, DIR_M_TO_S), ("addr", "address_width", DIR_M_TO_S),
("awvalid", 1, DIR_M_TO_S), ("prot", 3, DIR_M_TO_S),
("awready", 1, DIR_S_TO_M), ("valid", 1, DIR_M_TO_S),
("ready", 1, DIR_S_TO_M),
]),
# Write Data # Write Data
("wdata", "data_width", DIR_M_TO_S), ("w", [
("wstrb", "strb_width", DIR_M_TO_S), ("data", "data_width", DIR_M_TO_S),
("wvalid", 1, DIR_M_TO_S), ("strb", "strb_width", DIR_M_TO_S),
("wready", 1, DIR_S_TO_M), ("valid", 1, DIR_M_TO_S),
("ready", 1, DIR_S_TO_M),
]),
# Write Response # Write Response
("bresp", 2, DIR_S_TO_M), ("b", [
("bvalid", 1, DIR_S_TO_M), ("resp", 2, DIR_S_TO_M),
("bready", 1, DIR_M_TO_S), ("valid", 1, DIR_S_TO_M),
("ready", 1, DIR_M_TO_S),
]),
# Read Address # Read Address
("araddr", "address_width", DIR_M_TO_S), ("ar", [
("arprot", 3, DIR_M_TO_S), ("addr", "address_width", DIR_M_TO_S),
("arvalid", 1, DIR_M_TO_S), ("prot", 3, DIR_M_TO_S),
("arready", 1, DIR_S_TO_M), ("valid", 1, DIR_M_TO_S),
("ready", 1, DIR_S_TO_M),
]),
# Read Data # Read Data
("rdata", "data_width", DIR_S_TO_M), ("r", [
("rresp", 2, DIR_S_TO_M), ("data", "data_width", DIR_S_TO_M),
("rvalid", 1, DIR_S_TO_M), ("resp", 2, DIR_S_TO_M),
("rready", 1, DIR_M_TO_S), ("valid", 1, DIR_S_TO_M),
("ready", 1, DIR_M_TO_S),
]),
] ]
class Interface(Record): class Interface(Record):
@ -66,12 +76,14 @@ class AXILite2CSR(Module):
including writes. including writes.
""" """
def __init__(self, bus_interface_axi, bus_interface_csr): def __init__(self, bus_axi, bus_csr):
self.axi = bus_interface_axi self.axi = axi = bus_axi
self.csr = bus_interface_csr 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. # Machine is currently busy talking to CSR, hold your horses.
busy = Signal() busy = Signal()
@ -80,89 +92,88 @@ class AXILite2CSR(Module):
# A read transaction is happening on the bus. # A read transaction is happening on the bus.
read_transaction = Signal() read_transaction = Signal()
self.comb += [ self.comb += [
write_transaction.eq(self.axi.awvalid & self.axi.awready & self.axi.wvalid & self.axi.wready), write_transaction.eq(aw.valid & aw.ready & w.valid & w.ready),
read_transaction.eq(self.axi.arvalid & self.axi.arready), read_transaction.eq(ar.valid & ar.ready),
] ]
# Write transaction generation. # Write transaction generation.
self.sync += [ self.sync += [
self.axi.awready.eq(0), aw.ready.eq(0),
self.axi.wready.eq(0), w.ready.eq(0),
If(self.axi.awvalid & self.axi.wvalid, If(aw.valid & w.valid,
If(~self.axi.awready & ~busy & ~self.axi.arvalid, If(~aw.ready & ~busy & ~ar.valid,
self.axi.awready.eq(1), aw.ready.eq(1),
self.axi.wready.eq(1) w.ready.eq(1)
) )
) )
] ]
# Write response generation. # Write response generation.
self.sync += [ self.sync += [
self.axi.bvalid.eq(0), b.valid.eq(0),
If(write_transaction, If(write_transaction,
If(self.axi.bready & ~self.axi.bvalid, If(b.ready & ~b.valid,
self.axi.bvalid.eq(1), b.valid.eq(1),
# Response 0 -> OKAY # Response 0 -> OKAY
self.axi.bresp.eq(0), b.resp.eq(0),
) )
) )
] ]
# Read transaction generation. # Read transaction generation.
self.sync += [ self.sync += [
self.axi.arready.eq(0), ar.ready.eq(0),
If(self.axi.arvalid & ~self.axi.arready & ~busy, If(ar.valid & ~ar.ready & ~busy,
self.axi.arready.eq(1), ar.ready.eq(1),
) )
] ]
# Registered data to be written to CSR, set by FSM. # Registered data to be written to CSR, set by FSM.
wdata = Signal(self.csr.dat_w.nbits) wdata = Signal(csr.dat_w.nbits)
# Combinatorial byte address to assert on CSR bus, driven by FSM. # Combinatorial byte address to assert on CSR bus, driven by FSM.
addr = Signal(self.axi.araddr.nbits) addr = Signal(ar.addr.nbits)
# Drive AXI & CSR combinatorial signals. # Drive AXI & CSR combinatorial signals.
self.comb += [ self.comb += [
self.csr.adr.eq(addr >> csr.adr.eq(addr >> int(math.log(r.data.nbits//8, 2.0))),
int(math.log(self.axi.rdata.nbits//8, 2.0))), csr.dat_w.eq(wdata),
self.csr.dat_w.eq(wdata),
self.axi.rdata.eq(self.csr.dat_r), r.data.eq(csr.dat_r),
self.axi.rresp.eq(0), r.resp.eq(0),
] ]
# CSR interaction FSM. # CSR interaction FSM.
self.submodules.fsm = FSM(reset_state='IDLE') self.submodules.fsm = fsm = FSM(reset_state='IDLE')
self.comb += [ self.comb += [
busy.eq(~self.fsm.ongoing('IDLE')), busy.eq(~fsm.ongoing('IDLE')),
self.axi.rvalid.eq(self.fsm.ongoing('READING')), r.valid.eq(fsm.ongoing('READING')),
self.csr.we.eq(self.fsm.ongoing('WRITING')), csr.we.eq(fsm.ongoing('WRITING')),
] ]
# Idle state - wait for a transaction to happen on AXI. Immediately # Idle state - wait for a transaction to happen on AXI. Immediately
# assert read/write address on CSR if such an transaction is occuring. # assert read/write address on CSR if such an transaction is occuring.
self.fsm.act('IDLE', fsm.act('IDLE',
If(read_transaction, If(read_transaction,
addr.eq(self.axi.araddr), addr.eq(ar.addr),
NextState('READING'), NextState('READING'),
).Elif(write_transaction, ).Elif(write_transaction,
addr.eq(self.axi.awaddr), addr.eq(aw.addr),
# Register data from AXI. # Register data from AXI.
NextValue(wdata, self.axi.wdata), NextValue(wdata, w.data),
NextState('WRITING'), NextState('WRITING'),
) )
) )
# Perform write to CSR. # Perform write to CSR.
self.fsm.act('WRITING', fsm.act('WRITING',
addr.eq(self.axi.awaddr), addr.eq(aw.addr),
# CSR writes are single cycle, go back to IDLE. # CSR writes are single cycle, go back to IDLE.
NextState('IDLE'), NextState('IDLE'),
) )
# Respond to read to AXI. # Respond to read to AXI.
self.fsm.act('READING', fsm.act('READING',
addr.eq(self.axi.araddr), addr.eq(ar.addr),
# If AXI master is ready to receive data, go back to IDLE. # If AXI master is ready to receive data, go back to IDLE.
If(self.axi.rready, If(r.ready,
NextState('IDLE'), NextState('IDLE'),
) )
) )
@ -183,8 +194,10 @@ def test_axilite2csr():
self.axi = Interface(data_width=32, address_width=14) self.axi = Interface(data_width=32, address_width=14)
self.submodules.holder = CSRHolder() self.submodules.holder = CSRHolder()
self.submodules.dut = AXILite2CSR(self.axi, self.csr) 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.csrbankarray = csr_bus.CSRBankArray(
self.submodules.csrcon = csr_bus.Interconnect(self.csr, self.csrbankarray.get_buses()) 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): def map_csr(self, name, memory):
return { return {
@ -192,64 +205,68 @@ def test_axilite2csr():
}[name] }[name]
def testbench_write_read(dut): def testbench_write_read(dut):
axi = dut.axi
for _ in range(8): for _ in range(8):
yield yield
# Write test # Write test
yield dut.axi.awvalid.eq(1) yield axi.aw.valid.eq(1)
yield dut.axi.awaddr.eq(4) yield axi.aw.addr.eq(4)
yield dut.axi.wvalid.eq(1) yield axi.w.valid.eq(1)
yield dut.axi.bready.eq(1) yield axi.b.ready.eq(1)
yield dut.axi.wdata.eq(0x2137) yield axi.w.data.eq(0x2137)
while (yield dut.axi.awready) != 1: while (yield axi.aw.ready) != 1:
yield yield
while (yield dut.axi.wready) != 1: while (yield axi.w.ready) != 1:
yield yield
yield dut.axi.awvalid.eq(0) yield axi.aw.valid.eq(0)
yield dut.axi.wvalid.eq(0) yield axi.w.valid.eq(0)
for _ in range(8): for _ in range(8):
yield yield
# Read test # Read test
yield dut.axi.arvalid.eq(1) yield axi.ar.valid.eq(1)
yield dut.axi.rready.eq(1) yield axi.r.ready.eq(1)
yield dut.axi.araddr.eq(4) yield axi.ar.addr.eq(4)
while (yield dut.axi.arready != 1): while (yield axi.ar.ready != 1):
yield yield
yield dut.axi.arvalid.eq(0) yield axi.ar.valid.eq(0)
while (yield dut.axi.rvalid != 1): while (yield axi.r.valid != 1):
yield yield
yield dut.axi.rready.eq(0) yield axi.r.ready.eq(0)
read = yield dut.axi.rdata read = yield axi.r.data
assert read == 0x2137 assert read == 0x2137
for _ in range(8): for _ in range(8):
yield yield
def testbench_simultaneous(dut): def testbench_simultaneous(dut):
axi = dut.axi
for _ in range(8): for _ in range(8):
yield yield
# Write # Write
yield dut.axi.awvalid.eq(1) yield axi.aw.valid.eq(1)
yield dut.axi.awaddr.eq(2) yield axi.aw.addr.eq(2)
yield dut.axi.wvalid.eq(1) yield axi.w.valid.eq(1)
yield dut.axi.bready.eq(1) yield axi.b.ready.eq(1)
yield dut.axi.wdata.eq(0x2137) yield axi.w.data.eq(0x2137)
# Read # Read
yield dut.axi.arvalid.eq(1) yield axi.ar.valid.eq(1)
yield dut.axi.rready.eq(1) yield axi.r.ready.eq(1)
yield dut.axi.araddr.eq(2) yield axi.ar.addr.eq(2)
yield yield
yield yield
is_reading = yield dut.axi.arready is_reading = yield axi.ar.ready
is_writing = yield dut.axi.awready is_writing = yield axi.aw.ready
assert is_reading assert is_reading
assert not is_writing assert not is_writing