Add rtio.cri

This commit is contained in:
Donald Sebastian Leung 2020-10-07 12:01:37 +08:00
parent 8d3c69ea08
commit 85d1523b52
2 changed files with 124 additions and 1 deletions

View File

@ -6,7 +6,7 @@ Formally verified implementation of the ARTIQ RTIO core in nMigen
- Devise a suitable migration strategy for `artiq.gateware.rtio` from Migen to nMigen
- [ ] Implement the core in nMigen
- - [ ] `rtio.cri`
- - [x] `rtio.cri` (`Interface` and `CRIDecoder` only)
- - [ ] `rtio.sed.layouts`
- - [ ] `rtio.sed.output_network`
- - [ ] `rtio.sed.output_driver`

View File

@ -1 +1,124 @@
"""Common RTIO Interface"""
from nmigen import *
from nmigen.utils import *
from nmigen.hdl.rec 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_FANOUT),
# 8 MSBs of chan_sel = routing destination
# 16 LSBs of chan_sel = channel within the destination
("chan_sel", 24, DIR_FANOUT),
("o_timestamp", 64, DIR_FANOUT),
("o_data", 512, DIR_FANOUT),
("o_address", 8, DIR_FANOUT),
# o_status bits:
# <0:wait> <1:underflow> <2:destination unreachable>
("o_status", 3, DIR_FANIN),
# 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_FANIN),
("o_buffer_space", 16, DIR_FANIN),
("i_timeout", 64, DIR_FANOUT),
("i_data", 32, DIR_FANIN),
("i_timestamp", 64, DIR_FANIN),
# 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_FANIN),
]
# We drop the support for keyword arguments here since
# all instances of Interface in this file do not use them
class Interface(Record):
def __init__(self):
super().__init__(layout)
# Skip KernelInitiator for now (depends on AutoCSR)
class CRIDecoder(Elaboratable):
def __init__(self, slaves=2, master=None, mode="async", enable_routing=False):
m = Module()
self.m = m
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()
m.d.comb += destination_unreachable.o_status.eq(4)
m.d.comb += 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:
routing_table = Memory(slave_bits, 256)
if mode == "async":
rtp_decoder = routing_table.read_port()
elif mode == "sync":
rtp_decoder = routing_table.read_port(clock_domain="rtio")
else:
raise ValueError
m.submodules += rtp_decoder
m.d.comb += rtp_decoder.addr.eq(self.master.chan_sel[16:])
m.d.comb += selected.eq(rtp_decoder.data)
else:
m.d.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_FANOUT and name != "cmd":
m.d.comb += getattr(slave, name).eq(getattr(master, name))
with m.If(selected == n):
m.d.comb += slave.cmd.eq(master.cmd)
# slave -> master
with m.Switch(selected):
for n, slave in enumerate(slaves):
with m.Case(n):
for name, size, direction in layout:
if direction == DIR_FANIN:
m.d.comb += getattr(master, name).eq(getattr(slave, name))
def elaborate(self, platform):
return self.m
# Skip CRISwitch for now (depends on AutoCSR)
# Skip CRIInterconnectShared for now (depends on CRISwitch)
# Skip RoutingTableAccess for now (depends on AutoCSR)