mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
soc/interconnect/axi: minor cleanups.
This commit is contained in:
parent
303d6cca7e
commit
3b293612a8
1 changed files with 90 additions and 90 deletions
|
@ -287,11 +287,11 @@ class AXIBurst2Beat(Module):
|
||||||
beat_offset = Signal(8 + 4)
|
beat_offset = Signal(8 + 4)
|
||||||
beat_wrap = Signal(8 + 4)
|
beat_wrap = Signal(8 + 4)
|
||||||
|
|
||||||
# compute parameters
|
# Compute parameters
|
||||||
self.comb += beat_size.eq(1 << ax_burst.size)
|
self.comb += beat_size.eq(1 << ax_burst.size)
|
||||||
self.comb += beat_wrap.eq(ax_burst.len << ax_burst.size)
|
self.comb += beat_wrap.eq(ax_burst.len << ax_burst.size)
|
||||||
|
|
||||||
# combinatorial logic
|
# Combinatorial logic
|
||||||
self.comb += [
|
self.comb += [
|
||||||
ax_beat.valid.eq(ax_burst.valid | ~ax_beat.first),
|
ax_beat.valid.eq(ax_burst.valid | ~ax_beat.first),
|
||||||
ax_beat.first.eq(beat_count == 0),
|
ax_beat.first.eq(beat_count == 0),
|
||||||
|
@ -305,7 +305,7 @@ class AXIBurst2Beat(Module):
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
# synchronous logic
|
# Synchronous logic
|
||||||
self.sync += [
|
self.sync += [
|
||||||
If(ax_beat.valid & ax_beat.ready,
|
If(ax_beat.valid & ax_beat.ready,
|
||||||
If(ax_beat.last,
|
If(ax_beat.last,
|
||||||
|
@ -374,7 +374,7 @@ class AXI2AXILite(Module):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
fsm.act("READ",
|
fsm.act("READ",
|
||||||
# cmd
|
# ar (read command)
|
||||||
axi_lite.ar.valid.eq(ax_beat.valid & ~_cmd_done),
|
axi_lite.ar.valid.eq(ax_beat.valid & ~_cmd_done),
|
||||||
axi_lite.ar.addr.eq(ax_beat.addr),
|
axi_lite.ar.addr.eq(ax_beat.addr),
|
||||||
ax_beat.ready.eq(axi_lite.ar.ready & ~_cmd_done),
|
ax_beat.ready.eq(axi_lite.ar.ready & ~_cmd_done),
|
||||||
|
@ -384,23 +384,23 @@ class AXI2AXILite(Module):
|
||||||
NextValue(_cmd_done, 1)
|
NextValue(_cmd_done, 1)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
# data
|
# r (read data & response)
|
||||||
axi.r.valid.eq(axi_lite.r.valid),
|
axi.r.valid.eq(axi_lite.r.valid),
|
||||||
axi.r.last.eq(_cmd_done),
|
axi.r.last.eq(_cmd_done),
|
||||||
axi.r.resp.eq(RESP_OKAY),
|
axi.r.resp.eq(RESP_OKAY),
|
||||||
axi.r.id.eq(ax_beat.id),
|
axi.r.id.eq(ax_beat.id),
|
||||||
axi.r.data.eq(axi_lite.r.data),
|
axi.r.data.eq(axi_lite.r.data),
|
||||||
axi_lite.r.ready.eq(axi.r.ready),
|
axi_lite.r.ready.eq(axi.r.ready),
|
||||||
# exit
|
# Exit
|
||||||
If(axi.r.valid & axi.r.last & axi.r.ready,
|
If(axi.r.valid & axi.r.last & axi.r.ready,
|
||||||
ax_beat.ready.eq(1),
|
ax_beat.ready.eq(1),
|
||||||
NextState("IDLE")
|
NextState("IDLE")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# always accept write responses
|
# Always accept write responses.
|
||||||
self.comb += axi_lite.b.ready.eq(1)
|
self.comb += axi_lite.b.ready.eq(1)
|
||||||
fsm.act("WRITE",
|
fsm.act("WRITE",
|
||||||
# cmd
|
# aw (write command)
|
||||||
axi_lite.aw.valid.eq(ax_beat.valid & ~_cmd_done),
|
axi_lite.aw.valid.eq(ax_beat.valid & ~_cmd_done),
|
||||||
axi_lite.aw.addr.eq(ax_beat.addr),
|
axi_lite.aw.addr.eq(ax_beat.addr),
|
||||||
ax_beat.ready.eq(axi_lite.aw.ready & ~_cmd_done),
|
ax_beat.ready.eq(axi_lite.aw.ready & ~_cmd_done),
|
||||||
|
@ -410,12 +410,12 @@ class AXI2AXILite(Module):
|
||||||
NextValue(_cmd_done, 1)
|
NextValue(_cmd_done, 1)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
# data
|
# w (write data)
|
||||||
axi_lite.w.valid.eq(axi.w.valid),
|
axi_lite.w.valid.eq(axi.w.valid),
|
||||||
axi_lite.w.data.eq(axi.w.data),
|
axi_lite.w.data.eq(axi.w.data),
|
||||||
axi_lite.w.strb.eq(axi.w.strb),
|
axi_lite.w.strb.eq(axi.w.strb),
|
||||||
axi.w.ready.eq(axi_lite.w.ready),
|
axi.w.ready.eq(axi_lite.w.ready),
|
||||||
# exit
|
# Exit
|
||||||
If(axi.w.valid & axi.w.last & axi.w.ready,
|
If(axi.w.valid & axi.w.last & axi.w.ready,
|
||||||
NextState("WRITE-RESP")
|
NextState("WRITE-RESP")
|
||||||
)
|
)
|
||||||
|
@ -441,8 +441,8 @@ class AXILite2AXI(Module):
|
||||||
|
|
||||||
# n bytes, encoded as log2(n)
|
# n bytes, encoded as log2(n)
|
||||||
burst_size = log2_int(axi.data_width // 8)
|
burst_size = log2_int(axi.data_width // 8)
|
||||||
# burst type has no meaning as we use burst length of 1, but AXI slaves may require
|
# Burst type has no meaning as we use burst length of 1, but AXI slaves may require certain
|
||||||
# certain type of bursts, so it is probably safest to use INCR in general
|
# type of bursts, so it is probably safest to use INCR in general.
|
||||||
burst_type = {
|
burst_type = {
|
||||||
"FIXED": 0b00,
|
"FIXED": 0b00,
|
||||||
"INCR": 0b01,
|
"INCR": 0b01,
|
||||||
|
@ -450,6 +450,7 @@ class AXILite2AXI(Module):
|
||||||
}[burst_type]
|
}[burst_type]
|
||||||
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
|
# aw (write command)
|
||||||
axi.aw.valid.eq(axi_lite.aw.valid),
|
axi.aw.valid.eq(axi_lite.aw.valid),
|
||||||
axi_lite.aw.ready.eq(axi.aw.ready),
|
axi_lite.aw.ready.eq(axi.aw.ready),
|
||||||
axi.aw.addr.eq(axi_lite.aw.addr),
|
axi.aw.addr.eq(axi_lite.aw.addr),
|
||||||
|
@ -462,16 +463,19 @@ class AXILite2AXI(Module):
|
||||||
axi.aw.qos.eq(0),
|
axi.aw.qos.eq(0),
|
||||||
axi.aw.id.eq(write_id),
|
axi.aw.id.eq(write_id),
|
||||||
|
|
||||||
|
# w (write data)
|
||||||
axi.w.valid.eq(axi_lite.w.valid),
|
axi.w.valid.eq(axi_lite.w.valid),
|
||||||
axi_lite.w.ready.eq(axi.w.ready),
|
axi_lite.w.ready.eq(axi.w.ready),
|
||||||
axi.w.data.eq(axi_lite.w.data),
|
axi.w.data.eq(axi_lite.w.data),
|
||||||
axi.w.strb.eq(axi_lite.w.strb),
|
axi.w.strb.eq(axi_lite.w.strb),
|
||||||
axi.w.last.eq(1),
|
axi.w.last.eq(1),
|
||||||
|
|
||||||
|
# b (write response)
|
||||||
axi_lite.b.valid.eq(axi.b.valid),
|
axi_lite.b.valid.eq(axi.b.valid),
|
||||||
axi_lite.b.resp.eq(axi.b.resp),
|
axi_lite.b.resp.eq(axi.b.resp),
|
||||||
axi.b.ready.eq(axi_lite.b.ready),
|
axi.b.ready.eq(axi_lite.b.ready),
|
||||||
|
|
||||||
|
# ar (read command)
|
||||||
axi.ar.valid.eq(axi_lite.ar.valid),
|
axi.ar.valid.eq(axi_lite.ar.valid),
|
||||||
axi_lite.ar.ready.eq(axi.ar.ready),
|
axi_lite.ar.ready.eq(axi.ar.ready),
|
||||||
axi.ar.addr.eq(axi_lite.ar.addr),
|
axi.ar.addr.eq(axi_lite.ar.addr),
|
||||||
|
@ -484,6 +488,7 @@ class AXILite2AXI(Module):
|
||||||
axi.ar.qos.eq(0),
|
axi.ar.qos.eq(0),
|
||||||
axi.ar.id.eq(read_id),
|
axi.ar.id.eq(read_id),
|
||||||
|
|
||||||
|
# r (read response & data)
|
||||||
axi_lite.r.valid.eq(axi.r.valid),
|
axi_lite.r.valid.eq(axi.r.valid),
|
||||||
axi_lite.r.resp.eq(axi.r.resp),
|
axi_lite.r.resp.eq(axi.r.resp),
|
||||||
axi_lite.r.data.eq(axi.r.data),
|
axi_lite.r.data.eq(axi.r.data),
|
||||||
|
@ -600,20 +605,20 @@ class Wishbone2AXILite(Module):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
fsm.act("WRITE",
|
fsm.act("WRITE",
|
||||||
# cmd
|
# aw (write command)
|
||||||
axi_lite.aw.valid.eq(~_cmd_done),
|
axi_lite.aw.valid.eq(~_cmd_done),
|
||||||
axi_lite.aw.addr[wishbone_adr_shift:].eq(_addr),
|
axi_lite.aw.addr[wishbone_adr_shift:].eq(_addr),
|
||||||
If(axi_lite.aw.valid & axi_lite.aw.ready,
|
If(axi_lite.aw.valid & axi_lite.aw.ready,
|
||||||
NextValue(_cmd_done, 1)
|
NextValue(_cmd_done, 1)
|
||||||
),
|
),
|
||||||
# data
|
# w (write data)
|
||||||
axi_lite.w.valid.eq(~_data_done),
|
axi_lite.w.valid.eq(~_data_done),
|
||||||
axi_lite.w.data.eq(wishbone.dat_w),
|
axi_lite.w.data.eq(wishbone.dat_w),
|
||||||
axi_lite.w.strb.eq(wishbone.sel),
|
axi_lite.w.strb.eq(wishbone.sel),
|
||||||
If(axi_lite.w.valid & axi_lite.w.ready,
|
If(axi_lite.w.valid & axi_lite.w.ready,
|
||||||
NextValue(_data_done, 1),
|
NextValue(_data_done, 1),
|
||||||
),
|
),
|
||||||
# resp
|
# b (write response)
|
||||||
axi_lite.b.ready.eq(_cmd_done & _data_done),
|
axi_lite.b.ready.eq(_cmd_done & _data_done),
|
||||||
If(axi_lite.b.valid & axi_lite.b.ready,
|
If(axi_lite.b.valid & axi_lite.b.ready,
|
||||||
If(axi_lite.b.resp == RESP_OKAY,
|
If(axi_lite.b.resp == RESP_OKAY,
|
||||||
|
@ -625,13 +630,13 @@ class Wishbone2AXILite(Module):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
fsm.act("READ",
|
fsm.act("READ",
|
||||||
# cmd
|
# ar (read command)
|
||||||
axi_lite.ar.valid.eq(~_cmd_done),
|
axi_lite.ar.valid.eq(~_cmd_done),
|
||||||
axi_lite.ar.addr[wishbone_adr_shift:].eq(_addr),
|
axi_lite.ar.addr[wishbone_adr_shift:].eq(_addr),
|
||||||
If(axi_lite.ar.valid & axi_lite.ar.ready,
|
If(axi_lite.ar.valid & axi_lite.ar.ready,
|
||||||
NextValue(_cmd_done, 1)
|
NextValue(_cmd_done, 1)
|
||||||
),
|
),
|
||||||
# data & resp
|
# r (read data & response)
|
||||||
axi_lite.r.ready.eq(_cmd_done),
|
axi_lite.r.ready.eq(_cmd_done),
|
||||||
If(axi_lite.r.valid & axi_lite.r.ready,
|
If(axi_lite.r.valid & axi_lite.r.ready,
|
||||||
If(axi_lite.r.resp == RESP_OKAY,
|
If(axi_lite.r.resp == RESP_OKAY,
|
||||||
|
@ -663,10 +668,10 @@ class Wishbone2AXI(Module):
|
||||||
def axi_lite_to_simple(axi_lite, port_adr, port_dat_r, port_dat_w=None, port_we=None):
|
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"""
|
"""Connection of AXILite to simple bus with 1-cycle latency, such as CSR bus or Memory port"""
|
||||||
bus_data_width = axi_lite.data_width
|
bus_data_width = axi_lite.data_width
|
||||||
adr_shift = log2_int(bus_data_width//8)
|
adr_shift = log2_int(bus_data_width//8)
|
||||||
do_read = Signal()
|
do_read = Signal()
|
||||||
do_write = Signal()
|
do_write = Signal()
|
||||||
last_was_read = Signal()
|
last_was_read = Signal()
|
||||||
|
|
||||||
comb = []
|
comb = []
|
||||||
if port_dat_w is not None:
|
if port_dat_w is not None:
|
||||||
|
@ -680,7 +685,7 @@ def axi_lite_to_simple(axi_lite, port_adr, port_dat_r, port_dat_w=None, port_we=
|
||||||
|
|
||||||
fsm = FSM()
|
fsm = FSM()
|
||||||
fsm.act("START-TRANSACTION",
|
fsm.act("START-TRANSACTION",
|
||||||
# If the last access was a read, do a write, and vice versa
|
# If the last access was a read, do a write, and vice versa.
|
||||||
If(axi_lite.aw.valid & axi_lite.ar.valid,
|
If(axi_lite.aw.valid & axi_lite.ar.valid,
|
||||||
do_write.eq(last_was_read),
|
do_write.eq(last_was_read),
|
||||||
do_read.eq(~last_was_read),
|
do_read.eq(~last_was_read),
|
||||||
|
@ -688,7 +693,7 @@ def axi_lite_to_simple(axi_lite, port_adr, port_dat_r, port_dat_w=None, port_we=
|
||||||
do_write.eq(axi_lite.aw.valid),
|
do_write.eq(axi_lite.aw.valid),
|
||||||
do_read.eq(axi_lite.ar.valid),
|
do_read.eq(axi_lite.ar.valid),
|
||||||
),
|
),
|
||||||
# Start reading/writing immediately not to waste a cycle
|
# Start reading/writing immediately not to waste a cycle.
|
||||||
If(do_write,
|
If(do_write,
|
||||||
port_adr.eq(axi_lite.aw.addr[adr_shift:]),
|
port_adr.eq(axi_lite.aw.addr[adr_shift:]),
|
||||||
If(axi_lite.w.valid,
|
If(axi_lite.w.valid,
|
||||||
|
@ -704,7 +709,7 @@ def axi_lite_to_simple(axi_lite, port_adr, port_dat_r, port_dat_w=None, port_we=
|
||||||
)
|
)
|
||||||
fsm.act("SEND-READ-RESPONSE",
|
fsm.act("SEND-READ-RESPONSE",
|
||||||
NextValue(last_was_read, 1),
|
NextValue(last_was_read, 1),
|
||||||
# As long as we have correct address port.dat_r will be valid
|
# As long as we have correct address port.dat_r will be valid.
|
||||||
port_adr.eq(axi_lite.ar.addr[adr_shift:]),
|
port_adr.eq(axi_lite.ar.addr[adr_shift:]),
|
||||||
axi_lite.r.data.eq(port_dat_r),
|
axi_lite.r.data.eq(port_dat_r),
|
||||||
axi_lite.r.resp.eq(RESP_OKAY),
|
axi_lite.r.resp.eq(RESP_OKAY),
|
||||||
|
@ -731,11 +736,14 @@ class AXILite2CSR(Module):
|
||||||
bus_csr = csr_bus.Interface()
|
bus_csr = csr_bus.Interface()
|
||||||
|
|
||||||
self.axi_lite = axi_lite
|
self.axi_lite = axi_lite
|
||||||
self.csr = bus_csr
|
self.csr = bus_csr
|
||||||
|
|
||||||
fsm, comb = axi_lite_to_simple(self.axi_lite,
|
fsm, comb = axi_lite_to_simple(
|
||||||
port_adr=self.csr.adr, port_dat_r=self.csr.dat_r,
|
axi_lite = self.axi_lite,
|
||||||
port_dat_w=self.csr.dat_w, port_we=self.csr.we)
|
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.submodules.fsm = fsm
|
||||||
self.comb += comb
|
self.comb += comb
|
||||||
|
|
||||||
|
@ -760,7 +768,7 @@ class AXILiteSRAM(Module):
|
||||||
else:
|
else:
|
||||||
read_only = False
|
read_only = False
|
||||||
|
|
||||||
###
|
# # #
|
||||||
|
|
||||||
# Create memory port
|
# Create memory port
|
||||||
port = self.mem.get_port(write_capable=not read_only, we_granularity=8,
|
port = self.mem.get_port(write_capable=not read_only, we_granularity=8,
|
||||||
|
@ -774,10 +782,12 @@ class AXILiteSRAM(Module):
|
||||||
for i in range(bus_data_width//8)]
|
for i in range(bus_data_width//8)]
|
||||||
|
|
||||||
# Transaction logic
|
# Transaction logic
|
||||||
fsm, comb = axi_lite_to_simple(self.bus,
|
fsm, comb = axi_lite_to_simple(
|
||||||
port_adr=port.adr, port_dat_r=port.dat_r,
|
axi_lite = self.bus,
|
||||||
port_dat_w=port.dat_w if not read_only else None,
|
port_adr = port.adr,
|
||||||
port_we=port.we if not read_only else None)
|
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.submodules.fsm = fsm
|
||||||
self.comb += comb
|
self.comb += comb
|
||||||
|
|
||||||
|
@ -818,7 +828,7 @@ class _AXILiteDownConverterWrite(Module):
|
||||||
self.submodules.fsm = fsm
|
self.submodules.fsm = fsm
|
||||||
# Reset the converter state if master breaks a request, we can do that as
|
# Reset the converter state if master breaks a request, we can do that as
|
||||||
# aw.valid and w.valid are kept high in CONVERT and RESPOND-SLAVE, and
|
# aw.valid and w.valid are kept high in CONVERT and RESPOND-SLAVE, and
|
||||||
# acknowledged only when moving to RESPOND-MASTER, and then b.valid is 1
|
# acknowledged only when moving to RESPOND-MASTER, and then b.valid is 1.
|
||||||
self.comb += fsm.reset.eq(~((master.aw.valid | master.w.valid) | master.b.valid))
|
self.comb += fsm.reset.eq(~((master.aw.valid | master.w.valid) | master.b.valid))
|
||||||
|
|
||||||
fsm.act("IDLE",
|
fsm.act("IDLE",
|
||||||
|
@ -838,16 +848,16 @@ class _AXILiteDownConverterWrite(Module):
|
||||||
If(slave.w.ready,
|
If(slave.w.ready,
|
||||||
NextValue(w_ready, 1)
|
NextValue(w_ready, 1)
|
||||||
),
|
),
|
||||||
# When skipping, we just increment the counter
|
# When skipping, we just increment the counter.
|
||||||
If(skip,
|
If(skip,
|
||||||
NextValue(counter, counter + 1),
|
NextValue(counter, counter + 1),
|
||||||
# Corner-case: when the last word is being skipped, we must send the response
|
# Corner-case: when the last word is being skipped, we must send the response.
|
||||||
If(counter == (ratio - 1),
|
If(counter == (ratio - 1),
|
||||||
master.aw.ready.eq(1),
|
master.aw.ready.eq(1),
|
||||||
master.w.ready.eq(1),
|
master.w.ready.eq(1),
|
||||||
NextState("RESPOND-MASTER")
|
NextState("RESPOND-MASTER")
|
||||||
)
|
)
|
||||||
# Write current word and wait for write response
|
# Write current word and wait for write response.
|
||||||
).Elif((slave.aw.ready | aw_ready) & (slave.w.ready | w_ready),
|
).Elif((slave.aw.ready | aw_ready) & (slave.w.ready | w_ready),
|
||||||
NextState("RESPOND-SLAVE")
|
NextState("RESPOND-SLAVE")
|
||||||
)
|
)
|
||||||
|
@ -857,7 +867,7 @@ class _AXILiteDownConverterWrite(Module):
|
||||||
NextValue(w_ready, 0),
|
NextValue(w_ready, 0),
|
||||||
If(slave.b.valid,
|
If(slave.b.valid,
|
||||||
slave.b.ready.eq(1),
|
slave.b.ready.eq(1),
|
||||||
# Errors are sticky, so the first one is always sent
|
# Errors are sticky, so the first one is always sent.
|
||||||
If((resp == RESP_OKAY) & (slave.b.resp != RESP_OKAY),
|
If((resp == RESP_OKAY) & (slave.b.resp != RESP_OKAY),
|
||||||
NextValue(resp, slave.b.resp)
|
NextValue(resp, slave.b.resp)
|
||||||
),
|
),
|
||||||
|
@ -900,11 +910,11 @@ class _AXILiteDownConverterRead(Module):
|
||||||
self.comb += addr_counter[slave_align:].eq(counter)
|
self.comb += addr_counter[slave_align:].eq(counter)
|
||||||
|
|
||||||
# Data path
|
# Data path
|
||||||
# shift the data word
|
# Shift the data word
|
||||||
r_data = Signal(dw_from, reset_less=True)
|
r_data = Signal(dw_from, reset_less=True)
|
||||||
self.sync += If(slave.r.ready, r_data.eq(master.r.data))
|
self.sync += If(slave.r.ready, r_data.eq(master.r.data))
|
||||||
self.comb += master.r.data.eq(Cat(r_data[dw_to:], slave.r.data))
|
self.comb += master.r.data.eq(Cat(r_data[dw_to:], slave.r.data))
|
||||||
# address, resp
|
# Connect address, resp
|
||||||
self.comb += [
|
self.comb += [
|
||||||
slave.ar.addr.eq(Cat(addr_counter, master.ar.addr[master_align:])),
|
slave.ar.addr.eq(Cat(addr_counter, master.ar.addr[master_align:])),
|
||||||
master.r.resp.eq(resp),
|
master.r.resp.eq(resp),
|
||||||
|
@ -915,7 +925,7 @@ class _AXILiteDownConverterRead(Module):
|
||||||
fsm = ResetInserter()(fsm)
|
fsm = ResetInserter()(fsm)
|
||||||
self.submodules.fsm = fsm
|
self.submodules.fsm = fsm
|
||||||
# Reset the converter state if master breaks a request, we can do that as
|
# Reset the converter state if master breaks a request, we can do that as
|
||||||
# ar.valid is high in CONVERT and RESPOND-SLAVE, and r.valid in RESPOND-MASTER
|
# ar.valid is high in CONVERT and RESPOND-SLAVE, and r.valid in RESPOND-MASTER.
|
||||||
self.comb += fsm.reset.eq(~(master.ar.valid | master.r.valid))
|
self.comb += fsm.reset.eq(~(master.ar.valid | master.r.valid))
|
||||||
|
|
||||||
fsm.act("IDLE",
|
fsm.act("IDLE",
|
||||||
|
@ -933,15 +943,15 @@ class _AXILiteDownConverterRead(Module):
|
||||||
)
|
)
|
||||||
fsm.act("RESPOND-SLAVE",
|
fsm.act("RESPOND-SLAVE",
|
||||||
If(slave.r.valid,
|
If(slave.r.valid,
|
||||||
# Errors are sticky, so the first one is always sent
|
# Errors are sticky, so the first one is always sent.
|
||||||
If((resp == RESP_OKAY) & (slave.r.resp != RESP_OKAY),
|
If((resp == RESP_OKAY) & (slave.r.resp != RESP_OKAY),
|
||||||
NextValue(resp, slave.r.resp)
|
NextValue(resp, slave.r.resp)
|
||||||
),
|
),
|
||||||
# On last word acknowledge ar and hold slave.r.valid until we get master.r.ready
|
# On last word acknowledge ar and hold slave.r.valid until we get master.r.ready.
|
||||||
If(counter == (ratio - 1),
|
If(counter == (ratio - 1),
|
||||||
master.ar.ready.eq(1),
|
master.ar.ready.eq(1),
|
||||||
NextState("RESPOND-MASTER")
|
NextState("RESPOND-MASTER")
|
||||||
# Acknowledge the response and continue conversion
|
# Acknowledge the response and continue conversion.
|
||||||
).Else(
|
).Else(
|
||||||
slave.r.ready.eq(1),
|
slave.r.ready.eq(1),
|
||||||
NextValue(counter, counter + 1),
|
NextValue(counter, counter + 1),
|
||||||
|
@ -963,8 +973,8 @@ class AXILiteDownConverter(Module):
|
||||||
self.submodules.read = _AXILiteDownConverterRead(master, slave)
|
self.submodules.read = _AXILiteDownConverterRead(master, slave)
|
||||||
|
|
||||||
class AXILiteUpConverter(Module):
|
class AXILiteUpConverter(Module):
|
||||||
# TODO: we could try joining multiple master accesses into single slave access
|
# TODO: we could try joining multiple master accesses into single slave access would require
|
||||||
# would reuqire checking if address changes and a way to flush on single access
|
# checking if address changes and a way to flush on single access
|
||||||
def __init__(self, master, slave):
|
def __init__(self, master, slave):
|
||||||
assert isinstance(master, AXILiteInterface) and isinstance(slave, AXILiteInterface)
|
assert isinstance(master, AXILiteInterface) and isinstance(slave, AXILiteInterface)
|
||||||
dw_from = len(master.r.data)
|
dw_from = len(master.r.data)
|
||||||
|
@ -1003,7 +1013,7 @@ class AXILiteUpConverter(Module):
|
||||||
master.r.data.eq(slave.r.data[data_from:data_to]),
|
master.r.data.eq(slave.r.data[data_from:data_to]),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Switch current word based on the last valid master address
|
# 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.aw.valid, wr_word_r.eq(wr_word))
|
||||||
self.sync += If(master.ar.valid, rd_word_r.eq(rd_word))
|
self.sync += If(master.ar.valid, rd_word_r.eq(rd_word))
|
||||||
self.comb += [
|
self.comb += [
|
||||||
|
@ -1029,10 +1039,12 @@ class AXILiteConverter(Module):
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
dw_from = len(master.r.data)
|
dw_from = len(master.r.data)
|
||||||
dw_to = len(slave.r.data)
|
dw_to = len(slave.r.data)
|
||||||
if dw_from > dw_to:
|
ratio = dw_from/dw_to
|
||||||
|
|
||||||
|
if ratio > 1:
|
||||||
self.submodules += AXILiteDownConverter(master, slave)
|
self.submodules += AXILiteDownConverter(master, slave)
|
||||||
elif dw_from < dw_to:
|
elif ratio < 1:
|
||||||
self.submodules += AXILiteUpConverter(master, slave)
|
self.submodules += AXILiteUpConverter(master, slave)
|
||||||
else:
|
else:
|
||||||
self.comb += master.connect(slave)
|
self.comb += master.connect(slave)
|
||||||
|
@ -1059,7 +1071,7 @@ class AXILiteTimeout(Module):
|
||||||
fsm.act("WAIT",
|
fsm.act("WAIT",
|
||||||
timer.wait.eq(wait_cond),
|
timer.wait.eq(wait_cond),
|
||||||
# done is updated in `sync`, so we must make sure that `ready` has not been issued
|
# done is updated in `sync`, so we must make sure that `ready` has not been issued
|
||||||
# by slave during that single cycle, by checking `timer.wait`
|
# by slave during that single cycle, by checking `timer.wait`.
|
||||||
If(timer.done & timer.wait,
|
If(timer.done & timer.wait,
|
||||||
error.eq(1),
|
error.eq(1),
|
||||||
NextState("RESPOND")
|
NextState("RESPOND")
|
||||||
|
@ -1129,9 +1141,9 @@ class AXILiteInterconnectPointToPoint(Module):
|
||||||
class AXILiteArbiter(Module):
|
class AXILiteArbiter(Module):
|
||||||
"""AXI Lite arbiter
|
"""AXI Lite arbiter
|
||||||
|
|
||||||
Arbitrate between master interfaces and connect one to the target.
|
Arbitrate between master interfaces and connect one to the target. New master will not be
|
||||||
New master will not be selected until all requests have been responded to.
|
selected until all requests have been responded to. Arbitration for write and read channels is
|
||||||
Arbitration for write and read channels is done separately.
|
done separately.
|
||||||
"""
|
"""
|
||||||
def __init__(self, masters, target):
|
def __init__(self, masters, target):
|
||||||
self.submodules.rr_write = roundrobin.RoundRobin(len(masters), roundrobin.SP_CE)
|
self.submodules.rr_write = roundrobin.RoundRobin(len(masters), roundrobin.SP_CE)
|
||||||
|
@ -1140,14 +1152,14 @@ class AXILiteArbiter(Module):
|
||||||
def get_sig(interface, channel, name):
|
def get_sig(interface, channel, name):
|
||||||
return getattr(getattr(interface, channel), name)
|
return getattr(getattr(interface, channel), name)
|
||||||
|
|
||||||
# mux master->slave signals
|
# Mux master->slave signals
|
||||||
for channel, name, direction in target.layout_flat():
|
for channel, name, direction in target.layout_flat():
|
||||||
rr = self.rr_write if channel in ["aw", "w", "b"] else self.rr_read
|
rr = self.rr_write if channel in ["aw", "w", "b"] else self.rr_read
|
||||||
if direction == DIR_M_TO_S:
|
if direction == DIR_M_TO_S:
|
||||||
choices = Array(get_sig(m, channel, name) for m in masters)
|
choices = Array(get_sig(m, channel, name) for m in masters)
|
||||||
self.comb += get_sig(target, channel, name).eq(choices[rr.grant])
|
self.comb += get_sig(target, channel, name).eq(choices[rr.grant])
|
||||||
|
|
||||||
# connect slave->master signals
|
# Connect slave->master signals
|
||||||
for channel, name, direction in target.layout_flat():
|
for channel, name, direction in target.layout_flat():
|
||||||
rr = self.rr_write if channel in ["aw", "w", "b"] else self.rr_read
|
rr = self.rr_write if channel in ["aw", "w", "b"] else self.rr_read
|
||||||
if direction == DIR_S_TO_M:
|
if direction == DIR_S_TO_M:
|
||||||
|
@ -1159,39 +1171,34 @@ class AXILiteArbiter(Module):
|
||||||
else:
|
else:
|
||||||
self.comb += dest.eq(source)
|
self.comb += dest.eq(source)
|
||||||
|
|
||||||
# allow to change rr.grant only after all requests from a master have been responded to
|
# Allow to change rr.grant only after all requests from a master have been responded to.
|
||||||
self.submodules.wr_lock = wr_lock = _AXILiteRequestCounter(
|
self.submodules.wr_lock = wr_lock = _AXILiteRequestCounter(
|
||||||
request=target.aw.valid & target.aw.ready, response=target.b.valid & target.b.ready)
|
request=target.aw.valid & target.aw.ready, response=target.b.valid & target.b.ready)
|
||||||
self.submodules.rd_lock = rd_lock = _AXILiteRequestCounter(
|
self.submodules.rd_lock = rd_lock = _AXILiteRequestCounter(
|
||||||
request=target.ar.valid & target.ar.ready, response=target.r.valid & target.r.ready)
|
request=target.ar.valid & target.ar.ready, response=target.r.valid & target.r.ready)
|
||||||
|
|
||||||
# switch to next request only if there are no responses pending
|
# Switch to next request only if there are no responses pending.
|
||||||
self.comb += [
|
self.comb += [
|
||||||
self.rr_write.ce.eq(~(target.aw.valid | target.w.valid | target.b.valid) & wr_lock.ready),
|
self.rr_write.ce.eq(~(target.aw.valid | target.w.valid | target.b.valid) & wr_lock.ready),
|
||||||
self.rr_read.ce.eq(~(target.ar.valid | target.r.valid) & rd_lock.ready),
|
self.rr_read.ce.eq(~(target.ar.valid | target.r.valid) & rd_lock.ready),
|
||||||
]
|
]
|
||||||
|
|
||||||
# connect bus requests to round-robin selectors
|
# Connect bus requests to round-robin selectors.
|
||||||
self.comb += [
|
self.comb += [
|
||||||
self.rr_write.request.eq(Cat(*[m.aw.valid | m.w.valid | m.b.valid for m in masters])),
|
self.rr_write.request.eq(Cat(*[m.aw.valid | m.w.valid | m.b.valid for m in masters])),
|
||||||
self.rr_read.request.eq(Cat(*[m.ar.valid | m.r.valid for m in masters])),
|
self.rr_read.request.eq(Cat(*[m.ar.valid | m.r.valid for m in masters])),
|
||||||
]
|
]
|
||||||
|
|
||||||
class AXILiteDecoder(Module):
|
class AXILiteDecoder(Module):
|
||||||
_doc_slaves = """
|
"""AXI Lite decoder
|
||||||
|
|
||||||
|
Decode master access to particular slave based on its decoder function.
|
||||||
|
|
||||||
slaves: [(decoder, slave), ...]
|
slaves: [(decoder, slave), ...]
|
||||||
List of slaves with address decoders, where `decoder` is a function:
|
List of slaves with address decoders, where `decoder` is a function:
|
||||||
decoder(Signal(address_width - log2(data_width//8))) -> Signal(1)
|
decoder(Signal(address_width - log2(data_width//8))) -> Signal(1)
|
||||||
that returns 1 when the slave is selected and 0 otherwise.
|
that returns 1 when the slave is selected and 0 otherwise.
|
||||||
""".strip()
|
"""
|
||||||
|
|
||||||
__doc__ = """AXI Lite decoder
|
|
||||||
|
|
||||||
Decode master access to particular slave based on its decoder function.
|
|
||||||
|
|
||||||
{slaves}
|
|
||||||
""".format(slaves=_doc_slaves)
|
|
||||||
|
|
||||||
def __init__(self, master, slaves, register=False):
|
def __init__(self, master, slaves, register=False):
|
||||||
# TODO: unused register argument
|
# TODO: unused register argument
|
||||||
addr_shift = log2_int(master.data_width//8)
|
addr_shift = log2_int(master.data_width//8)
|
||||||
|
@ -1200,7 +1207,7 @@ class AXILiteDecoder(Module):
|
||||||
"write": {"aw", "w", "b"},
|
"write": {"aw", "w", "b"},
|
||||||
"read": {"ar", "r"},
|
"read": {"ar", "r"},
|
||||||
}
|
}
|
||||||
# reverse mapping: directions[channel] -> "write"/"read"
|
# Reverse mapping: directions[channel] -> "write"/"read".
|
||||||
directions = {ch: d for d, chs in channels.items() for ch in chs}
|
directions = {ch: d for d, chs in channels.items() for ch in chs}
|
||||||
|
|
||||||
def new_slave_sel():
|
def new_slave_sel():
|
||||||
|
@ -1210,7 +1217,7 @@ class AXILiteDecoder(Module):
|
||||||
slave_sel_reg = new_slave_sel()
|
slave_sel_reg = new_slave_sel()
|
||||||
slave_sel = new_slave_sel()
|
slave_sel = new_slave_sel()
|
||||||
|
|
||||||
# we need to hold the slave selected until all responses come back
|
# We need to hold the slave selected until all responses come back.
|
||||||
# TODO: we could reuse arbiter counters
|
# TODO: we could reuse arbiter counters
|
||||||
locks = {
|
locks = {
|
||||||
"write": _AXILiteRequestCounter(
|
"write": _AXILiteRequestCounter(
|
||||||
|
@ -1227,17 +1234,17 @@ class AXILiteDecoder(Module):
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
# decode slave addresses
|
# Decode slave addresses.
|
||||||
for i, (decoder, bus) in enumerate(slaves):
|
for i, (decoder, bus) in enumerate(slaves):
|
||||||
self.comb += [
|
self.comb += [
|
||||||
slave_sel_dec["write"][i].eq(decoder(master.aw.addr[addr_shift:])),
|
slave_sel_dec["write"][i].eq(decoder(master.aw.addr[addr_shift:])),
|
||||||
slave_sel_dec["read"][i].eq(decoder(master.ar.addr[addr_shift:])),
|
slave_sel_dec["read"][i].eq(decoder(master.ar.addr[addr_shift:])),
|
||||||
]
|
]
|
||||||
|
|
||||||
# change the current selection only when we've got all responses
|
# Dhange the current selection only when we've got all responses.
|
||||||
for channel in locks.keys():
|
for channel in locks.keys():
|
||||||
self.sync += If(locks[channel].ready, slave_sel_reg[channel].eq(slave_sel_dec[channel]))
|
self.sync += If(locks[channel].ready, slave_sel_reg[channel].eq(slave_sel_dec[channel]))
|
||||||
# we have to cut the delaying select
|
# We have to cut the delaying select.
|
||||||
for ch, final in slave_sel.items():
|
for ch, final in slave_sel.items():
|
||||||
self.comb += If(locks[ch].ready,
|
self.comb += If(locks[ch].ready,
|
||||||
final.eq(slave_sel_dec[ch])
|
final.eq(slave_sel_dec[ch])
|
||||||
|
@ -1245,35 +1252,31 @@ class AXILiteDecoder(Module):
|
||||||
final.eq(slave_sel_reg[ch])
|
final.eq(slave_sel_reg[ch])
|
||||||
)
|
)
|
||||||
|
|
||||||
# connect master->slaves signals except valid/ready
|
# Connect master->slaves signals except valid/ready.
|
||||||
for i, (_, slave) in enumerate(slaves):
|
for i, (_, slave) in enumerate(slaves):
|
||||||
for channel, name, direction in master.layout_flat():
|
for channel, name, direction in master.layout_flat():
|
||||||
if direction == DIR_M_TO_S:
|
if direction == DIR_M_TO_S:
|
||||||
src = get_sig(master, channel, name)
|
src = get_sig(master, channel, name)
|
||||||
dst = get_sig(slave, channel, name)
|
dst = get_sig(slave, channel, name)
|
||||||
# mask master control signals depending on slave selection
|
# Mask master control signals depending on slave selection.
|
||||||
if name in ["valid", "ready"]:
|
if name in ["valid", "ready"]:
|
||||||
src = src & slave_sel[directions[channel]][i]
|
src = src & slave_sel[directions[channel]][i]
|
||||||
self.comb += dst.eq(src)
|
self.comb += dst.eq(src)
|
||||||
|
|
||||||
# connect slave->master signals masking not selected slaves
|
# Connect slave->master signals masking not selected slaves.
|
||||||
for channel, name, direction in master.layout_flat():
|
for channel, name, direction in master.layout_flat():
|
||||||
if direction == DIR_S_TO_M:
|
if direction == DIR_S_TO_M:
|
||||||
dst = get_sig(master, channel, name)
|
dst = get_sig(master, channel, name)
|
||||||
masked = []
|
masked = []
|
||||||
for i, (_, slave) in enumerate(slaves):
|
for i, (_, slave) in enumerate(slaves):
|
||||||
src = get_sig(slave, channel, name)
|
src = get_sig(slave, channel, name)
|
||||||
# mask depending on channel
|
# Mask depending on channel.
|
||||||
mask = Replicate(slave_sel[directions[channel]][i], len(dst))
|
mask = Replicate(slave_sel[directions[channel]][i], len(dst))
|
||||||
masked.append(src & mask)
|
masked.append(src & mask)
|
||||||
self.comb += dst.eq(reduce(or_, masked))
|
self.comb += dst.eq(reduce(or_, masked))
|
||||||
|
|
||||||
class AXILiteInterconnectShared(Module):
|
class AXILiteInterconnectShared(Module):
|
||||||
__doc__ = """AXI Lite shared interconnect
|
"""AXI Lite shared interconnect"""
|
||||||
|
|
||||||
{slaves}
|
|
||||||
""".format(slaves=AXILiteDecoder._doc_slaves)
|
|
||||||
|
|
||||||
def __init__(self, masters, slaves, register=False, timeout_cycles=1e6):
|
def __init__(self, masters, slaves, register=False, timeout_cycles=1e6):
|
||||||
# TODO: data width
|
# TODO: data width
|
||||||
shared = AXILiteInterface()
|
shared = AXILiteInterface()
|
||||||
|
@ -1283,21 +1286,18 @@ class AXILiteInterconnectShared(Module):
|
||||||
self.submodules.timeout = AXILiteTimeout(shared, timeout_cycles)
|
self.submodules.timeout = AXILiteTimeout(shared, timeout_cycles)
|
||||||
|
|
||||||
class AXILiteCrossbar(Module):
|
class AXILiteCrossbar(Module):
|
||||||
__doc__ = """AXI Lite crossbar
|
"""AXI Lite crossbar
|
||||||
|
|
||||||
MxN crossbar for M masters and N slaves.
|
MxN crossbar for M masters and N slaves.
|
||||||
|
"""
|
||||||
{slaves}
|
|
||||||
""".format(slaves=AXILiteDecoder._doc_slaves)
|
|
||||||
|
|
||||||
def __init__(self, masters, slaves, register=False, timeout_cycles=1e6):
|
def __init__(self, masters, slaves, register=False, timeout_cycles=1e6):
|
||||||
matches, busses = zip(*slaves)
|
matches, busses = zip(*slaves)
|
||||||
access_m_s = [[AXILiteInterface() for j in slaves] for i in masters] # a[master][slave]
|
access_m_s = [[AXILiteInterface() for j in slaves] for i in masters] # a[master][slave]
|
||||||
access_s_m = list(zip(*access_m_s)) # a[slave][master]
|
access_s_m = list(zip(*access_m_s)) # a[slave][master]
|
||||||
# decode each master into its access row
|
# Decode each master into its access row.
|
||||||
for slaves, master in zip(access_m_s, masters):
|
for slaves, master in zip(access_m_s, masters):
|
||||||
slaves = list(zip(matches, slaves))
|
slaves = list(zip(matches, slaves))
|
||||||
self.submodules += AXILiteDecoder(master, slaves, register)
|
self.submodules += AXILiteDecoder(master, slaves, register)
|
||||||
# arbitrate each access column onto its slave
|
# Arbitrate each access column onto its slave.
|
||||||
for masters, bus in zip(access_s_m, busses):
|
for masters, bus in zip(access_s_m, busses):
|
||||||
self.submodules += AXILiteArbiter(masters, bus)
|
self.submodules += AXILiteArbiter(masters, bus)
|
||||||
|
|
Loading…
Reference in a new issue