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)