2021-08-11 20:34:44 +08:00
|
|
|
"""Auxiliary controller, common to satellite and master"""
|
|
|
|
|
|
|
|
from migen import *
|
|
|
|
from migen.fhdl.simplify import FullMemoryWE
|
|
|
|
|
|
|
|
from misoc.interconnect.csr import *
|
|
|
|
from migen_axi.interconnect import axi
|
|
|
|
from artiq.gateware.drtio.aux_controller import Transmitter, Receiver
|
|
|
|
|
|
|
|
max_packet = 1024
|
|
|
|
|
2021-08-13 19:06:10 +08:00
|
|
|
OUT_BURST_LEN = 10
|
|
|
|
IN_BURST_LEN = 4
|
|
|
|
|
|
|
|
class SRAM(Module):
|
|
|
|
def __init__(self, mem_or_size, read_only=False, init=None, bus=None):
|
|
|
|
|
|
|
|
# SRAM initialisation
|
|
|
|
|
|
|
|
if bus is None:
|
|
|
|
bus = axi.Interface()
|
|
|
|
self.bus = bus
|
|
|
|
bus_data_width = len(self.bus.r.data)
|
|
|
|
bus_addr_width = len(self.bus.ar.addr)
|
|
|
|
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)
|
|
|
|
|
|
|
|
# memory
|
|
|
|
port = self.mem.get_port(write_capable=not read_only, we_granularity=8)
|
|
|
|
self.specials += self.mem, port
|
|
|
|
|
|
|
|
###
|
|
|
|
|
|
|
|
# probably will get removed
|
|
|
|
self.addr_base = CSRStorage(32)
|
|
|
|
|
|
|
|
self.trigger_stb = Signal()
|
|
|
|
|
|
|
|
# Dout : Data received from CPU, output by SRAM <- port.dat_r
|
|
|
|
# Din : Data driven into SRAM, written into CPU <- port.dat_w
|
|
|
|
# When stb assert, index shows word being read/written, dout/din holds <- will be removed
|
|
|
|
# data
|
|
|
|
#
|
|
|
|
# Cycle:
|
|
|
|
# Then out_burst_len words are strobed out of dout
|
|
|
|
# Then, when din_ready is high, in_burst_len words are strobed in to din
|
|
|
|
self.dout_index = Signal(bus_addr_width) # is this legal?
|
|
|
|
self.din_index = Signal(bus_addr_width)
|
|
|
|
self.din_ready = Signal()
|
|
|
|
self.dout = Signal(64)
|
|
|
|
self.din = Signal(64)
|
|
|
|
|
|
|
|
# probably not correct here
|
|
|
|
self.sync += If(self.trigger_stb, self.trig_count.status.eq(self.trig_count.status+1))
|
|
|
|
|
|
|
|
ar, aw, w, r, b = attrgetter("ar", "aw", "w", "r", "b")(bus)
|
|
|
|
|
|
|
|
### Read
|
|
|
|
self.comb += [
|
2021-08-13 20:14:43 +08:00
|
|
|
port.adr.eq(ar.addr),
|
|
|
|
r.data.eq(port.dat_r),
|
2021-08-13 19:06:10 +08:00
|
|
|
r.ready.eq(1),
|
|
|
|
ar.burst.eq(axi.Burst.incr.value),
|
|
|
|
ar.len.eq(OUT_BURST_LEN-1), # Number of transfers in burst (0->1 transfer, 1->2 transfers...)
|
|
|
|
ar.size.eq(3), # Width of burst: 3 = 8 bytes = 64 bits
|
|
|
|
ar.cache.eq(0xf),
|
|
|
|
]
|
|
|
|
|
|
|
|
# read control
|
|
|
|
self.submodules.read_fsm = read_fsm = FSM(reset_state="IDLE")
|
|
|
|
read_fsm.act("IDLE",
|
2021-08-13 20:14:43 +08:00
|
|
|
If(ar.valid,
|
|
|
|
ar.ready.eq(1),
|
|
|
|
NextState("READ_START"),
|
2021-08-13 19:06:10 +08:00
|
|
|
)
|
|
|
|
)
|
|
|
|
read_fsm.act("READ_START",
|
2021-08-13 20:14:43 +08:00
|
|
|
If(r.valid,
|
|
|
|
r.ready.eq(1),
|
|
|
|
r.data.eq(port.dat_r), # that should be always updated, right?
|
|
|
|
NextState("READ"))
|
2021-08-13 19:06:10 +08:00
|
|
|
)
|
|
|
|
read_fsm.act("READ",
|
2021-08-13 20:14:43 +08:00
|
|
|
If(r.last & r.valid, # that's a smart way of skipping "LAST" state
|
|
|
|
r.data.eq(port.dat_r),
|
2021-08-13 19:06:10 +08:00
|
|
|
NextState("IDLE")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
self.sync += [
|
|
|
|
If(read_fsm.ongoing("IDLE"),
|
2021-08-13 20:14:43 +08:00
|
|
|
self.dout_index.eq(0),
|
|
|
|
r.ready.eq(0), # shall it be reset too on IDLE?
|
|
|
|
ar.ready.eq(0)
|
2021-08-13 19:06:10 +08:00
|
|
|
).Else(If(r.valid & read_fsm.ongoing("READ"),
|
|
|
|
self.dout_index.eq(self.dout_index+1),
|
2021-08-13 20:14:43 +08:00
|
|
|
port.adr.eq(port.adr + self.dout_index), # update address in the port
|
|
|
|
If(self.dout_index==ar.len, r.last.eq(1)) # and update last
|
2021-08-13 19:06:10 +08:00
|
|
|
)
|
|
|
|
)
|
|
|
|
]
|
|
|
|
|
|
|
|
### Write
|
|
|
|
self.comb += [
|
|
|
|
w.data.eq(port.dat_w),
|
2021-08-13 20:14:43 +08:00
|
|
|
port.addr.eq(aw.addr),
|
2021-08-13 19:06:10 +08:00
|
|
|
w.strb.eq(0xff),
|
|
|
|
aw.burst.eq(axi.Burst.incr.value),
|
|
|
|
aw.len.eq(IN_BURST_LEN-1), # Number of transfers in burst minus 1
|
|
|
|
aw.size.eq(3), # Width of burst: 3 = 8 bytes = 64 bits
|
|
|
|
aw.cache.eq(0xf),
|
|
|
|
b.ready.eq(1),
|
|
|
|
]
|
|
|
|
|
|
|
|
self.submodules.write_fsm = write_fsm = FSM(reset_state="IDLE")
|
|
|
|
write_fsm.act("IDLE",
|
|
|
|
w.valid.eq(0),
|
|
|
|
aw.valid.eq(0),
|
|
|
|
If(self.trigger_stb,
|
|
|
|
aw.valid.eq(1),
|
|
|
|
If(aw.ready, # assumes aw.ready is not randomly deasserted
|
|
|
|
NextState("DATA_WAIT")
|
|
|
|
).Else(
|
|
|
|
NextState("AW_READY_WAIT")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
write_fsm.act("AW_READY_WAIT",
|
|
|
|
aw.valid.eq(1),
|
|
|
|
If(aw.ready,
|
|
|
|
NextState("DATA_WAIT"),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
write_fsm.act("DATA_WAIT",
|
|
|
|
aw.valid.eq(0),
|
|
|
|
If(self.din_ready, # probably unnecessary?
|
|
|
|
w.valid.eq(1),
|
|
|
|
NextState("WRITE")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
write_fsm.act("WRITE",
|
|
|
|
w.valid.eq(1),
|
|
|
|
If(w.ready & w.last,
|
|
|
|
NextState("IDLE")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
# refer to port.we instead
|
|
|
|
self.sync += If(w.ready & w.valid, port.we.eq(1))
|
|
|
|
|
|
|
|
self.sync += [
|
|
|
|
If(write_fsm.ongoing("IDLE"),
|
|
|
|
self.din_index.eq(0) # replace with address?
|
|
|
|
), # but need to synchronise the address too
|
|
|
|
If(w.ready & w.valid, self.din_index.eq(self.din_index+1), port.adr.eq(port.addr+self.din_index))
|
|
|
|
]
|
|
|
|
|
|
|
|
self.comb += [
|
|
|
|
w.last.eq(0),
|
|
|
|
If(self.din_index==aw.len, w.last.eq(1))
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
# # generate write enable signal
|
|
|
|
# if not read_only:
|
|
|
|
# # replace with? stb -> w.strb we->w.ready? sel[i]-> r.valid
|
|
|
|
# self.comb += [port.we[i].eq(self.bus.cyc & self.bus.w.strb & self.bus.w.ready & self.bus.r.valid)
|
|
|
|
# for i in range(4)]
|
|
|
|
# # address and data
|
|
|
|
# self.comb += [
|
|
|
|
# self.bus.r.ready.eq(self.bus.r.valid), # AXI handshake?
|
|
|
|
# port.adr.eq(self.bus.ar.addr[:len(port.adr)]),
|
|
|
|
# self.bus.r.data.eq(port.dat_r)
|
|
|
|
# ]
|
|
|
|
# if not read_only:
|
|
|
|
# self.comb += port.dat_w.eq(self.bus.w.data),
|
|
|
|
# # generate ack
|
|
|
|
# self.sync += [
|
|
|
|
# self.bus.ack.eq(0),
|
|
|
|
# If(self.bus.cyc & self.bus.stb & ~self.bus.ack, self.bus.ack.eq(1))
|
|
|
|
# ]
|
|
|
|
|
2021-08-11 20:34:44 +08:00
|
|
|
# TODO: FullMemoryWE should be applied by migen.build
|
|
|
|
@FullMemoryWE()
|
|
|
|
class DRTIOAuxController(Module):
|
|
|
|
def __init__(self, link_layer):
|
|
|
|
self.bus = axi.Interface()
|
2021-08-13 19:06:10 +08:00
|
|
|
self.submodules.transmitter = Transmitter(link_layer, len(self.bus.w.data))
|
|
|
|
self.submodules.receiver = Receiver(link_layer, len(self.bus.w.data))
|
2021-08-11 20:34:44 +08:00
|
|
|
|
2021-08-13 19:06:10 +08:00
|
|
|
# probably will need to make axi.SRAM based on wb code
|
|
|
|
tx_sdram_if = SRAM(self.transmitter.mem, read_only=False)
|
|
|
|
rx_sdram_if = SRAM(self.receiver.mem, read_only=True)
|
|
|
|
wsb = log2_int(len(self.w.data)//8)
|
2021-08-11 20:34:44 +08:00
|
|
|
decoder = axi.AddressDecoder(self.bus,
|
|
|
|
[(lambda a: a[log2_int(max_packet)-wsb] == 0, tx_sdram_if.bus),
|
|
|
|
(lambda a: a[log2_int(max_packet)-wsb] == 1, rx_sdram_if.bus)],
|
|
|
|
register=True)
|
|
|
|
self.submodules += tx_sdram_if, rx_sdram_if, decoder
|
|
|
|
|
|
|
|
def get_csrs(self):
|
|
|
|
return self.transmitter.get_csrs() + self.receiver.get_csrs()
|