forked from M-Labs/artiq
1
0
Fork 0
artiq/artiq/gateware/rtio/cri.py

256 lines
8.4 KiB
Python

"""Common RTIO Interface"""
from migen import *
from migen.genlib.record import *
from migen.genlib.cdc import MultiReg
from misoc.interconnect.csr import *
# CRI write happens in 3 cycles:
# 1. set timestamp and channel
# 2. set other payload elements and issue write command
# 3. check status
commands = {
"nop": 0,
"write": 1,
# i_status should have the "wait for status" bit set until
# an event is available, or timestamp is reached.
"read": 2,
# targets must assert o_buffer_space_valid in response
# to this command
"get_buffer_space": 3
}
layout = [
("cmd", 2, DIR_M_TO_S),
# 8 MSBs of chan_sel = routing destination
# 16 LSBs of chan_sel = channel within the destination
("chan_sel", 24, DIR_M_TO_S),
("o_timestamp", 64, DIR_M_TO_S),
("o_data", 512, DIR_M_TO_S),
("o_address", 8, DIR_M_TO_S),
# o_status bits:
# <0:wait> <1:underflow> <2:destination unreachable>
("o_status", 3, DIR_S_TO_M),
# pessimistic estimate of the number of outputs events that can be
# written without waiting.
# this feature may be omitted on systems without DRTIO.
("o_buffer_space_valid", 1, DIR_S_TO_M),
("o_buffer_space", 16, DIR_S_TO_M),
("i_timeout", 64, DIR_M_TO_S),
("i_data", 32, DIR_S_TO_M),
("i_timestamp", 64, DIR_S_TO_M),
# i_status bits:
# <0:wait for event (command timeout)> <1:overflow> <2:wait for status>
# <3:destination unreachable>
# <0> and <1> are mutually exclusive. <1> has higher priority.
("i_status", 4, DIR_S_TO_M),
]
class Interface(Record):
def __init__(self, **kwargs):
Record.__init__(self, layout, **kwargs)
class KernelInitiator(Module, AutoCSR):
def __init__(self, tsc, cri=None, now64=False):
self.target = CSRStorage(32)
if now64:
self.now = CSRStorage(64)
else:
# not using CSRStorage atomic_write feature here to make storage reset_less
self.now_hi = CSR(32)
self.now_lo = CSR(32)
# Writing target clears o_data. This implements automatic
# zero-extension of output event data by the gateware. When staging an
# output event, always write target before o_data.
self.o_data = CSRStorage(512, write_from_dev=True)
self.o_status = CSRStatus(3)
self.i_timeout = CSRStorage(64)
self.i_data = CSRStatus(32)
self.i_timestamp = CSRStatus(64)
self.i_status = CSRStatus(4)
self.counter = CSRStatus(64)
self.counter_update = CSR()
if cri is None:
cri = Interface()
self.cri = cri
# # #
if now64:
now = self.now.storage
else:
now = Signal(64, reset_less=True)
now_hi_backing = Signal(32)
self.sync += [
If(self.now_hi.re, now_hi_backing.eq(self.now_hi.r)),
If(self.now_lo.re, now.eq(Cat(self.now_lo.r, now_hi_backing)))
]
self.comb += [
self.now_hi.w.eq(now[32:]),
self.now_lo.w.eq(now[:32])
]
self.comb += [
self.cri.cmd.eq(commands["nop"]),
If(self.o_data.re, self.cri.cmd.eq(commands["write"])),
If(self.i_timeout.re, self.cri.cmd.eq(commands["read"])),
self.cri.chan_sel.eq(self.target.storage[8:]),
self.cri.o_timestamp.eq(now),
self.cri.o_data.eq(self.o_data.storage),
self.cri.o_address.eq(self.target.storage[:8]),
self.o_status.status.eq(self.cri.o_status),
self.cri.i_timeout.eq(self.i_timeout.storage),
self.i_data.status.eq(self.cri.i_data),
self.i_timestamp.status.eq(self.cri.i_timestamp),
self.i_status.status.eq(self.cri.i_status),
self.o_data.dat_w.eq(0),
self.o_data.we.eq(self.target.re),
]
self.sync += If(self.counter_update.re, self.counter.status.eq(tsc.full_ts_cri))
class CRIDecoder(Module):
def __init__(self, slaves=2, master=None, mode="async", enable_routing=False):
if isinstance(slaves, int):
slaves = [Interface() for _ in range(slaves)]
if master is None:
master = Interface()
self.slaves = slaves
self.master = master
# # #
# routing
if enable_routing:
destination_unreachable = Interface()
self.comb += [
destination_unreachable.o_status.eq(4),
destination_unreachable.i_status.eq(8)
]
slaves = slaves[:]
slaves.append(destination_unreachable)
target_len = 2**(len(slaves) - 1).bit_length()
slaves += [destination_unreachable]*(target_len - len(slaves))
slave_bits = bits_for(len(slaves)-1)
selected = Signal(slave_bits)
if enable_routing:
self.specials.routing_table = Memory(slave_bits, 256)
if mode == "async":
rtp_decoder = self.routing_table.get_port()
elif mode == "sync":
rtp_decoder = self.routing_table.get_port(clock_domain="rtio")
else:
raise ValueError
self.specials += rtp_decoder
self.comb += [
rtp_decoder.adr.eq(self.master.chan_sel[16:]),
selected.eq(rtp_decoder.dat_r)
]
else:
self.sync += selected.eq(self.master.chan_sel[16:])
# master -> slave
for n, slave in enumerate(slaves):
for name, size, direction in layout:
if direction == DIR_M_TO_S and name != "cmd":
self.comb += getattr(slave, name).eq(getattr(master, name))
self.comb += If(selected == n, slave.cmd.eq(master.cmd))
# slave -> master
cases = dict()
for n, slave in enumerate(slaves):
cases[n] = []
for name, size, direction in layout:
if direction == DIR_S_TO_M:
cases[n].append(getattr(master, name).eq(getattr(slave, name)))
self.comb += Case(selected, cases)
class CRISwitch(Module, AutoCSR):
def __init__(self, masters=2, slave=None, mode="async"):
if isinstance(masters, int):
masters = [Interface() for _ in range(masters)]
if slave is None:
slave = Interface()
self.masters = masters
self.slave = slave
self.selected = CSRStorage(len(masters).bit_length())
# # #
if mode == "async":
selected = self.selected.storage
elif mode == "sync":
self.selected.storage.attr.add("no_retiming")
selected = Signal.like(self.selected.storage)
self.specials += MultiReg(self.selected.storage, selected, "rtio")
else:
raise ValueError
if len(masters) == 1:
self.comb += masters[0].connect(slave)
else:
# mux master->slave signals
for name, size, direction in layout:
if direction == DIR_M_TO_S:
choices = Array(getattr(m, name) for m in masters)
self.comb += getattr(slave, name).eq(choices[selected])
# connect slave->master signals
for name, size, direction in layout:
if direction == DIR_S_TO_M:
source = getattr(slave, name)
for i, m in enumerate(masters):
dest = getattr(m, name)
self.comb += dest.eq(source)
class CRIInterconnectShared(Module):
def __init__(self, masters=2, slaves=2, mode="async", enable_routing=False):
shared = Interface()
self.submodules.switch = CRISwitch(masters, shared, mode)
self.submodules.decoder = CRIDecoder(slaves, shared, mode, enable_routing)
def get_csrs(self):
return self.switch.get_csrs()
class RoutingTableAccess(Module, AutoCSR):
def __init__(self, interconnect):
if isinstance(interconnect, CRIInterconnectShared):
interconnect = interconnect.decoder
rtp_csr = interconnect.routing_table.get_port(write_capable=True)
self.specials += rtp_csr
self.destination = CSRStorage(8)
self.hop = CSR(len(rtp_csr.dat_w))
self.comb += [
rtp_csr.adr.eq(self.destination.storage),
rtp_csr.dat_w.eq(self.hop.r),
rtp_csr.we.eq(self.hop.re),
self.hop.w.eq(rtp_csr.dat_r)
]