Merge pull request #64 from q3k/q3k/axi4lite

Preliminary AXI4Lite support: CSR bridge
This commit is contained in:
enjoy-digital 2018-02-21 09:47:29 +01:00 committed by GitHub
commit aaf097056a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 277 additions and 0 deletions

View File

@ -0,0 +1,277 @@
"""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 litex.gen import *
from litex.gen.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 litex.gen.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')