From 85d1523b522a421863ba238b844a25d6eee75547 Mon Sep 17 00:00:00 2001
From: Donald Sebastian Leung
Date: Wed, 7 Oct 2020 12:01:37 +0800
Subject: [PATCH] Add rtio.cri
---
README.md | 2 +-
rtio/cri.py | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 124 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 087f0d5..51d09a9 100644
--- a/README.md
+++ b/README.md
@@ -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`
diff --git a/rtio/cri.py b/rtio/cri.py
index 8b13789..1d6bb0e 100644
--- a/rtio/cri.py
+++ b/rtio/cri.py
@@ -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)