From 1852491102752c83f6689a316e61b8000454d4fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=81=AB=E7=84=9A=20=E5=AF=8C=E8=89=AF?= Date: Fri, 2 Dec 2022 16:27:03 +0800 Subject: [PATCH] add channel names to RTIO errors --- RELEASE_NOTES.rst | 1 + artiq/coredevice/ad9914.py | 4 ++ artiq/coredevice/adf5356.py | 4 ++ artiq/coredevice/edge_counter.py | 4 ++ artiq/coredevice/fastino.py | 4 ++ artiq/coredevice/grabber.py | 4 ++ artiq/coredevice/phaser.py | 19 +++++- artiq/coredevice/sawg.py | 13 ++++ artiq/coredevice/spi2.py | 4 ++ artiq/coredevice/spline.py | 4 ++ artiq/coredevice/suservo.py | 8 +++ artiq/coredevice/ttl.py | 12 ++++ artiq/firmware/ksupport/lib.rs | 8 +-- artiq/firmware/ksupport/rtio.rs | 20 +++---- artiq/firmware/runtime/main.rs | 2 +- artiq/firmware/runtime/rtio_mgt.rs | 63 ++++++++++++++++---- artiq/firmware/runtime/session.rs | 25 +++++++- artiq/frontend/artiq_ddb_template.py | 5 +- artiq/frontend/artiq_rtiomap.py | 89 ++++++++++++++++++++++++++++ artiq/test/test_frontends.py | 2 +- doc/manual/installing.rst | 10 ++++ doc/manual/utilities.rst | 9 +++ setup.py | 1 + 23 files changed, 281 insertions(+), 34 deletions(-) create mode 100755 artiq/frontend/artiq_rtiomap.py diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index 6c891909a..4c3e1732e 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -15,6 +15,7 @@ Highlights: * Sampler: adjusted ADC MU to Volt conversion base for Sampler since v2.2. For earlier version please explicitly define it as an argument in the device database file (e.g. ``"hw_rev": "v2.1"``). +* Added channel names to RTIO errors. ARTIQ-7 ------- diff --git a/artiq/coredevice/ad9914.py b/artiq/coredevice/ad9914.py index ce7e3b1a2..9466b90d8 100644 --- a/artiq/coredevice/ad9914.py +++ b/artiq/coredevice/ad9914.py @@ -80,6 +80,10 @@ class AD9914: self.set_x_duration_mu = 7 * self.write_duration_mu self.exit_x_duration_mu = 3 * self.write_duration_mu + @staticmethod + def get_rtio_channels(bus_channel, **kwargs): + return [(bus_channel, None)] + @kernel def write(self, addr, data): rtio_output((self.bus_channel << 8) | addr, data) diff --git a/artiq/coredevice/adf5356.py b/artiq/coredevice/adf5356.py index f20ead804..f2b594dbf 100644 --- a/artiq/coredevice/adf5356.py +++ b/artiq/coredevice/adf5356.py @@ -73,6 +73,10 @@ class ADF5356: self._init_registers() + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @kernel def init(self, blind=False): """ diff --git a/artiq/coredevice/edge_counter.py b/artiq/coredevice/edge_counter.py index e7782064e..a3200d3dd 100644 --- a/artiq/coredevice/edge_counter.py +++ b/artiq/coredevice/edge_counter.py @@ -91,6 +91,10 @@ class EdgeCounter: self.channel = channel self.counter_max = (1 << (gateware_width - 1)) - 1 + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @kernel def gate_rising(self, duration): """Count rising edges for the given duration and request the total at diff --git a/artiq/coredevice/fastino.py b/artiq/coredevice/fastino.py index 7d5c1cd34..84cc0500b 100644 --- a/artiq/coredevice/fastino.py +++ b/artiq/coredevice/fastino.py @@ -52,6 +52,10 @@ class Fastino: assert self.core.ref_period == 1*ns self.t_frame = int64(14*7*4) + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @kernel def init(self): """Initialize the device. diff --git a/artiq/coredevice/grabber.py b/artiq/coredevice/grabber.py index 6f29debe3..518a5b12a 100644 --- a/artiq/coredevice/grabber.py +++ b/artiq/coredevice/grabber.py @@ -25,6 +25,10 @@ class Grabber: # ROI engine outputs for one video frame. self.sentinel = int32(int64(2**count_width)) + @staticmethod + def get_rtio_channels(channel_base, **kwargs): + return [(channel_base, "ROI coordinates"), (channel_base + 1, "ROI mask")] + @kernel def setup_roi(self, n, x0, y0, x1, y1): """ diff --git a/artiq/coredevice/phaser.py b/artiq/coredevice/phaser.py index 5ee3124b0..ebe5e047d 100644 --- a/artiq/coredevice/phaser.py +++ b/artiq/coredevice/phaser.py @@ -229,7 +229,7 @@ class Phaser: "dac_mmap"} def __init__(self, dmgr, channel_base, miso_delay=1, tune_fifo_offset=True, - clk_sel=0, sync_dly=0, dac=None, trf0=None, trf1=None, + clk_sel=0, sync_dly=0, dac=None, trf0=None, trf1=None, gw_rev=PHASER_GW_BASE, core_device="core"): self.channel_base = channel_base self.core = dmgr.get(core_device) @@ -243,13 +243,25 @@ class Phaser: self.clk_sel = clk_sel self.tune_fifo_offset = tune_fifo_offset self.sync_dly = sync_dly - self.gw_rev = -1 # discovered in init() + self.gw_rev = gw_rev # verified in init() self.dac_mmap = DAC34H84(dac).get_mmap() self.channel = [PhaserChannel(self, ch, trf) for ch, trf in enumerate([trf0, trf1])] + @staticmethod + def get_rtio_channels(channel_base, gw_rev=PHASER_GW_BASE, **kwargs): + if gw_rev == PHASER_GW_MIQRO: + return [(channel_base, "base"), (channel_base + 1, "ch0"), (channel_base + 2, "ch1")] + elif gw_rev == PHASER_GW_BASE: + return [(channel_base, "base"), + (channel_base + 1, "ch0 frequency"), + (channel_base + 2, "ch0 phase amplitude"), + (channel_base + 3, "ch1 frequency"), + (channel_base + 4, "ch1 phase amplitude")] + raise ValueError("invalid gw_rev `{}`".format(gw_rev)) + @kernel def init(self, debug=False): """Initialize the board. @@ -267,10 +279,11 @@ class Phaser: delay(.1*ms) # slack is_baseband = hw_rev & PHASER_HW_REV_VARIANT - self.gw_rev = self.read8(PHASER_ADDR_GW_REV) + gw_rev = self.read8(PHASER_ADDR_GW_REV) if debug: print("gw_rev:", self.gw_rev) self.core.break_realtime() + assert gw_rev == self.gw_rev delay(.1*ms) # slack # allow a few errors during startup and alignment since boot diff --git a/artiq/coredevice/sawg.py b/artiq/coredevice/sawg.py index 0a5905fa7..0d181ed78 100644 --- a/artiq/coredevice/sawg.py +++ b/artiq/coredevice/sawg.py @@ -334,6 +334,19 @@ class SAWG: self.phase0 = Spline(width, time_width, channel_base + 9, self.core, 1.) + @staticmethod + def get_rtio_channels(channel_base, **kwargs): + return [(channel_base, "base"), + (channel_base+1, "offset"), + (channel_base+2, "amplitude 1"), + (channel_base+3, "frequency 1"), + (channel_base+4, "phase 1"), + (channel_base+5, "amplitude 2"), + (channel_base+6, "frequency 2"), + (channel_base+7, "phase 2"), + (channel_base+8, "frequency 0"), + (channel_base+9, "phase0")] + @kernel def reset(self): """Re-establish initial conditions. diff --git a/artiq/coredevice/spi2.py b/artiq/coredevice/spi2.py index aa1045973..1a788d88f 100644 --- a/artiq/coredevice/spi2.py +++ b/artiq/coredevice/spi2.py @@ -72,6 +72,10 @@ class SPIMaster: self.channel = channel self.update_xfer_duration_mu(div, length) + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @portable def frequency_to_div(self, f): """Convert a SPI clock frequency to the closest SPI clock divider.""" diff --git a/artiq/coredevice/spline.py b/artiq/coredevice/spline.py index 9f8310d1e..4a77e0261 100644 --- a/artiq/coredevice/spline.py +++ b/artiq/coredevice/spline.py @@ -41,6 +41,10 @@ class Spline: self.time_scale = float((1 << time_width) * core_device.coarse_ref_period) + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @portable(flags={"fast-math"}) def to_mu(self, value: TFloat) -> TInt32: """Convert floating point ``value`` from physical units to 32 bit diff --git a/artiq/coredevice/suservo.py b/artiq/coredevice/suservo.py index 26df13532..5214e5a30 100644 --- a/artiq/coredevice/suservo.py +++ b/artiq/coredevice/suservo.py @@ -85,6 +85,10 @@ class SUServo: self.corrected_fs = sampler.Sampler.use_corrected_fs(sampler_hw_rev) assert self.ref_period_mu == self.core.ref_multiplier + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @kernel def init(self): """Initialize the servo, Sampler and both Urukuls. @@ -257,6 +261,10 @@ class Channel: self.servo.channel) self.dds = self.servo.ddses[self.servo_channel // 4] + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @kernel def set(self, en_out, en_iir=0, profile=0): """Operate channel. diff --git a/artiq/coredevice/ttl.py b/artiq/coredevice/ttl.py index 2bc40ed58..eafec1797 100644 --- a/artiq/coredevice/ttl.py +++ b/artiq/coredevice/ttl.py @@ -36,6 +36,10 @@ class TTLOut: self.channel = channel self.target_o = channel << 8 + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @kernel def output(self): pass @@ -128,6 +132,10 @@ class TTLInOut: self.target_sens = (channel << 8) + 2 self.target_sample = (channel << 8) + 3 + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @kernel def set_oe(self, oe): rtio_output(self.target_oe, 1 if oe else 0) @@ -465,6 +473,10 @@ class TTLClockGen: self.acc_width = numpy.int64(acc_width) + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @portable def frequency_to_ftw(self, frequency): """Returns the frequency tuning word corresponding to the given diff --git a/artiq/firmware/ksupport/lib.rs b/artiq/firmware/ksupport/lib.rs index 7bb1e675a..acb461f88 100644 --- a/artiq/firmware/ksupport/lib.rs +++ b/artiq/firmware/ksupport/lib.rs @@ -414,13 +414,13 @@ extern fn dma_playback(timestamp: i64, ptr: i32) { csr::rtio_dma::error_write(1); if error & 1 != 0 { raise!("RTIOUnderflow", - "RTIO underflow at {0} mu, channel {1}", - timestamp as i64, channel as i64, 0); + "RTIO underflow at channel {rtio_channel_info:0}, {1} mu", + channel as i64, timestamp as i64, 0); } if error & 2 != 0 { raise!("RTIODestinationUnreachable", - "RTIO destination unreachable, output, at {0} mu, channel {1}", - timestamp as i64, channel as i64, 0); + "RTIO destination unreachable, output, at channel {rtio_channel_info:0}, {1} mu", + channel as i64, timestamp as i64, 0); } } } diff --git a/artiq/firmware/ksupport/rtio.rs b/artiq/firmware/ksupport/rtio.rs index d9f568f75..b0168ba03 100644 --- a/artiq/firmware/ksupport/rtio.rs +++ b/artiq/firmware/ksupport/rtio.rs @@ -67,13 +67,13 @@ mod imp { } if status & RTIO_O_STATUS_UNDERFLOW != 0 { raise!("RTIOUnderflow", - "RTIO underflow at {0} mu, channel {1}, slack {2} mu", - timestamp, channel as i64, timestamp - get_counter()); + "RTIO underflow at channel {rtio_channel_info:0}, {1} mu, slack {2} mu", + channel as i64, timestamp, timestamp - get_counter()); } if status & RTIO_O_STATUS_DESTINATION_UNREACHABLE != 0 { raise!("RTIODestinationUnreachable", - "RTIO destination unreachable, output, at {0} mu, channel {1}", - timestamp, channel as i64, 0); + "RTIO destination unreachable, output, at channel {rtio_channel_info:0}, {1} mu", + channel as i64, timestamp, 0); } } @@ -115,7 +115,7 @@ mod imp { if status & RTIO_I_STATUS_OVERFLOW != 0 { raise!("RTIOOverflow", - "RTIO input overflow on channel {0}", + "RTIO input overflow on channel {rtio_channel_info:0}", channel as i64, 0, 0); } if status & RTIO_I_STATUS_WAIT_EVENT != 0 { @@ -123,7 +123,7 @@ mod imp { } if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 { raise!("RTIODestinationUnreachable", - "RTIO destination unreachable, input, on channel {0}", + "RTIO destination unreachable, input, on channel {rtio_channel_info:0}", channel as i64, 0, 0); } @@ -143,12 +143,12 @@ mod imp { if status & RTIO_I_STATUS_OVERFLOW != 0 { raise!("RTIOOverflow", - "RTIO input overflow on channel {0}", + "RTIO input overflow on channel {rtio_channel_info:0}", channel as i64, 0, 0); } if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 { raise!("RTIODestinationUnreachable", - "RTIO destination unreachable, input, on channel {0}", + "RTIO destination unreachable, input, on channel {rtio_channel_info:0}", channel as i64, 0, 0); } @@ -168,7 +168,7 @@ mod imp { if status & RTIO_I_STATUS_OVERFLOW != 0 { raise!("RTIOOverflow", - "RTIO input overflow on channel {0}", + "RTIO input overflow on channel {rtio_channel_info:0}", channel as i64, 0, 0); } if status & RTIO_I_STATUS_WAIT_EVENT != 0 { @@ -176,7 +176,7 @@ mod imp { } if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 { raise!("RTIODestinationUnreachable", - "RTIO destination unreachable, input, on channel {0}", + "RTIO destination unreachable, input, on channel {rtio_channel_info:0}", channel as i64, 0, 0); } diff --git a/artiq/firmware/runtime/main.rs b/artiq/firmware/runtime/main.rs index b36fd9251..8715de959 100644 --- a/artiq/firmware/runtime/main.rs +++ b/artiq/firmware/runtime/main.rs @@ -1,4 +1,4 @@ -#![feature(lang_items, panic_info_message)] +#![feature(lang_items, panic_info_message, const_btree_new, iter_advance_by)] #![no_std] extern crate eh; diff --git a/artiq/firmware/runtime/rtio_mgt.rs b/artiq/firmware/runtime/rtio_mgt.rs index c4ca665d4..41dbcb42a 100644 --- a/artiq/firmware/runtime/rtio_mgt.rs +++ b/artiq/firmware/runtime/rtio_mgt.rs @@ -1,15 +1,20 @@ +use alloc::collections::BTreeMap; +use alloc::string::String; use core::cell::RefCell; use urc::Urc; -use board_misoc::csr; +use board_misoc::{csr, config}; #[cfg(has_drtio)] use board_misoc::clock; use board_artiq::drtio_routing; use sched::Io; use sched::Mutex; +use io::{Cursor, ProtoRead}; const ASYNC_ERROR_COLLISION: u8 = 1 << 0; const ASYNC_ERROR_BUSY: u8 = 1 << 1; const ASYNC_ERROR_SEQUENCE_ERROR: u8 = 1 << 2; +static mut RTIO_DEVICE_MAP: BTreeMap = BTreeMap::new(); + #[cfg(has_drtio)] pub mod drtio { use super::*; @@ -215,15 +220,15 @@ pub mod drtio { destination_set_up(routing_table, up_destinations, destination, false), Ok(drtioaux::Packet::DestinationOkReply) => (), Ok(drtioaux::Packet::DestinationSequenceErrorReply { channel }) => { - error!("[DEST#{}] RTIO sequence error involving channel 0x{:04x}", destination, channel); + error!("[DEST#{}] RTIO sequence error involving channel {} 0x{:04x}", destination, resolve_channel_name(channel as u32), channel); unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_SEQUENCE_ERROR }; } Ok(drtioaux::Packet::DestinationCollisionReply { channel }) => { - error!("[DEST#{}] RTIO collision involving channel 0x{:04x}", destination, channel); + error!("[DEST#{}] RTIO collision involving channel {} 0x{:04x}", destination, resolve_channel_name(channel as u32), channel); unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_COLLISION }; } Ok(drtioaux::Packet::DestinationBusyReply { channel }) => { - error!("[DEST#{}] RTIO busy error involving channel 0x{:04x}", destination, channel); + error!("[DEST#{}] RTIO busy error involving channel {} 0x{:04x}", destination, resolve_channel_name(channel as u32), channel); unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_BUSY }; } Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet), @@ -349,16 +354,16 @@ fn async_error_thread(io: Io) { io.until(|| csr::rtio_core::async_error_read() != 0).unwrap(); let errors = csr::rtio_core::async_error_read(); if errors & ASYNC_ERROR_COLLISION != 0 { - error!("RTIO collision involving channel {}", - csr::rtio_core::collision_channel_read()); + let channel = csr::rtio_core::collision_channel_read(); + error!("RTIO collision involving channel {}:{}", channel, resolve_channel_name(channel as u32)); } if errors & ASYNC_ERROR_BUSY != 0 { - error!("RTIO busy error involving channel {}", - csr::rtio_core::busy_channel_read()); + let channel = csr::rtio_core::busy_channel_read(); + error!("RTIO busy error involving channel {}:{}", channel, resolve_channel_name(channel as u32)); } if errors & ASYNC_ERROR_SEQUENCE_ERROR != 0 { - error!("RTIO sequence error involving channel {}", - csr::rtio_core::sequence_error_channel_read()); + let channel = csr::rtio_core::sequence_error_channel_read(); + error!("RTIO sequence error involving channel {}:{}", channel, resolve_channel_name(channel as u32)); } SEEN_ASYNC_ERRORS = errors; csr::rtio_core::async_error_write(errors); @@ -366,9 +371,47 @@ fn async_error_thread(io: Io) { } } +fn read_device_map() -> BTreeMap { + let mut device_map: BTreeMap = BTreeMap::new(); + config::read("device_map", |value: Result<&[u8], config::Error>| { + let mut bytes = match value { + Ok(val) => if val.len() > 0 { Cursor::new(val) } else { + error!("read_device_map: `device_map` was not found in the config"); + return; + }, + Err(err) => { + error!("read_device_map: error reading `device_map` from config: {}", err); + return; + } + }; + let size = bytes.read_u32().unwrap(); + for _ in 0..size { + let channel = bytes.read_u32().unwrap(); + let device_name= bytes.read_string().unwrap(); + if let Some(old_entry) = device_map.insert(channel, device_name.clone()) { + error!("conflicting entries for channel {}: `{}` and `{}`", + channel, old_entry, device_name); + } + } + }); + device_map +} + +fn _resolve_channel_name(channel: u32, device_map: &BTreeMap) -> String { + match device_map.get(&channel) { + Some(val) => val.clone(), + None => String::from("unknown") + } +} + +pub fn resolve_channel_name(channel: u32) -> String { + _resolve_channel_name(channel, unsafe{&RTIO_DEVICE_MAP}) +} + pub fn startup(io: &Io, aux_mutex: &Mutex, routing_table: &Urc>, up_destinations: &Urc>) { + unsafe { RTIO_DEVICE_MAP = read_device_map(); } drtio::startup(io, aux_mutex, routing_table, up_destinations); unsafe { csr::rtio_core::reset_phy_write(1); diff --git a/artiq/firmware/runtime/session.rs b/artiq/firmware/runtime/session.rs index c6d745545..c9d1af4ee 100644 --- a/artiq/firmware/runtime/session.rs +++ b/artiq/firmware/runtime/session.rs @@ -1,4 +1,4 @@ -use core::{mem, str, cell::{Cell, RefCell}, fmt::Write as FmtWrite}; +use core::{mem, str, cell::{Cell, RefCell}, fmt::Write as FmtWrite, slice}; use alloc::{vec::Vec, string::String}; use byteorder::{ByteOrder, NativeEndian}; use cslice::CSlice; @@ -10,7 +10,7 @@ use urc::Urc; use sched::{ThreadHandle, Io, Mutex, TcpListener, TcpStream, Error as SchedError}; use rtio_clocking; use rtio_dma::Manager as DmaManager; -use rtio_mgt::get_async_errors; +use rtio_mgt::{get_async_errors, resolve_channel_name}; use cache::Cache; use kern_hwreq; use board_artiq::drtio_routing; @@ -449,6 +449,25 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex, session.kernel_state = KernelState::Absent; unsafe { session.congress.cache.unborrow() } + let exceptions_with_channel: Vec> = exceptions.iter() + .map(|exception| { + if let Some(exn) = exception { + let msg = str::from_utf8(unsafe{slice::from_raw_parts(exn.message.as_ptr(), exn.message.len())}) + .unwrap() + .replace("{rtio_channel_info:0}", &format!("{}:{}", exn.param[0], resolve_channel_name(exn.param[0] as u32))); + Some(eh::eh_artiq::Exception { + id: exn.id, + file: exn.file, + line: exn.line, + column: exn.column, + function: exn.function, + message: unsafe {CSlice::new(msg.as_ptr(), msg.len())}, + param: exn.param + }) + } else { None } + }) + .collect(); + match stream { None => { error!("exception in flash kernel"); @@ -459,7 +478,7 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex, }, Some(ref mut stream) => { host_write(stream, host::Reply::KernelException { - exceptions: exceptions, + exceptions: &exceptions_with_channel, stack_pointers: stack_pointers, backtrace: backtrace, async_errors: unsafe { get_async_errors() } diff --git a/artiq/frontend/artiq_ddb_template.py b/artiq/frontend/artiq_ddb_template.py index f359b65f0..6d8f13719 100755 --- a/artiq/frontend/artiq_ddb_template.py +++ b/artiq/frontend/artiq_ddb_template.py @@ -8,6 +8,7 @@ from itertools import count from artiq import __version__ as artiq_version from artiq.coredevice import jsondesc +from artiq.coredevice.phaser import PHASER_GW_MIQRO, PHASER_GW_BASE def process_header(output, description): @@ -566,10 +567,10 @@ class PeripheralManager: def process_phaser(self, rtio_offset, peripheral): mode = peripheral.get("mode", "base") if mode == "miqro": - dac = ', "dac": {"pll_m": 16, "pll_n": 3, "interpolation": 2}' + dac = f', "dac": {{"pll_m": 16, "pll_n": 3, "interpolation": 2}}, "gw_rev"={PHASER_GW_MIQRO}' n_channels = 3 else: - dac = "" + dac = f', "gw_rev"={PHASER_GW_BASE}' n_channels = 5 self.gen(""" device_db["{name}"] = {{ diff --git a/artiq/frontend/artiq_rtiomap.py b/artiq/frontend/artiq_rtiomap.py new file mode 100755 index 000000000..21326988d --- /dev/null +++ b/artiq/frontend/artiq_rtiomap.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 + +import argparse +import importlib +import struct + +from sipyco import common_args + +from artiq import __version__ as artiq_version +from artiq.master.databases import DeviceDB + + +def get_argparser(): + parser = argparse.ArgumentParser(description="ARTIQ RTIO channel name map encoder tool") + + parser.add_argument("--version", action="version", + version="ARTIQ v{}".format(artiq_version), + help="print the ARTIQ version number") + + common_args.verbosity_args(parser) + parser.add_argument("--device-db", default="device_db.py", + help="device database file (default: '%(default)s')") + parser.add_argument("file", metavar="FILE", default=None, + help="write the result into the specified file, or read from it to show the map (see `--show`)") + parser.add_argument("--show", default=False, action="store_true", + help="show the channel mapping from the specified file, instead of writing to it") + + return parser + + +def get_rtio_channels(desc): + if desc["type"] == "local": + module = importlib.import_module(desc["module"]) + device_class = getattr(module, desc["class"]) + return getattr(device_class, "get_rtio_channels", lambda **kwargs: [])(**desc.get("arguments", {})) + return [] + + +def get_channel_map(device_db): + reversed_map = {} + for dev_name, device in device_db.items(): + try: + channels = get_rtio_channels(device) + except Exception as e: + raise Exception(f"failed to process the device `{dev_name}`") from e + for chan, suffix in channels: + assert chan not in reversed_map + reversed_map[chan] = dev_name + (" " + suffix if suffix is not None else "") + + return reversed_map + + +def serialize_device_map(channel_map, outfile): + outfile.write(struct.pack(" {device}") + else: + ddb = DeviceDB(args.device_db) + chan_map = get_channel_map(ddb.get_device_db()) + + with open(args.file, "wb") as outfile: + serialize_device_map(chan_map, outfile) + + +if __name__ == "__main__": + main() diff --git a/artiq/test/test_frontends.py b/artiq/test/test_frontends.py index 622c7a010..caef4839c 100644 --- a/artiq/test/test_frontends.py +++ b/artiq/test/test_frontends.py @@ -13,7 +13,7 @@ class TestFrontends(unittest.TestCase): ], "artiq": [ "client", "compile", "coreanalyzer", "coremgmt", - "flash", "master", "mkfs", "route", + "flash", "master", "mkfs", "route", "rtiomap", "rtiomon", "run", "session", "browser", "dashboard" ] } diff --git a/doc/manual/installing.rst b/doc/manual/installing.rst index 485c573f4..ef0106172 100644 --- a/doc/manual/installing.rst +++ b/doc/manual/installing.rst @@ -340,3 +340,13 @@ Other options include: - ``ext0_bypass_125`` and ``ext0_bypass_100`` - explicit aliases for ``ext0_bypass``. Availability of these options depends on the board and their configuration - specific setting may or may not be supported. + +* Setup resolving RTIO channels to their names + +This feature allows you to print the channels' respective names alongside with their numbers in RTIO error messages. To enable it, run the ``artiq_rtiomap`` tool and write its result into the device config at the ``device_map`` key: :: + + $ artiq_rtiomap dev_map.bin + $ artiq_coremgmt config write -f device_map dev_map.bin + +.. note:: You can find more information about how to use the ``artiq_rtiomap`` utility on the :ref:`Utilities ` page. + diff --git a/doc/manual/utilities.rst b/doc/manual/utilities.rst index d66d91342..fb08cb990 100644 --- a/doc/manual/utilities.rst +++ b/doc/manual/utilities.rst @@ -116,6 +116,15 @@ Moninj proxy :ref: artiq.frontend.aqctl_moninj_proxy.get_argparser :prog: aqctl_moninj_proxy +.. _rtiomap-tool: + +RTIO channel name map tool +-------------------------- + +.. argparse:: + :ref: artiq.frontend.artiq_rtiomap.get_argparser + :prog: artiq_rtiomap + .. _core-device-rtio-analyzer-tool: diff --git a/setup.py b/setup.py index b730e6798..93d4d2c35 100755 --- a/setup.py +++ b/setup.py @@ -23,6 +23,7 @@ console_scripts = [ "artiq_compile = artiq.frontend.artiq_compile:main", "artiq_coreanalyzer = artiq.frontend.artiq_coreanalyzer:main", "artiq_coremgmt = artiq.frontend.artiq_coremgmt:main", + "artiq_rtiomap = artiq.frontend.artiq_rtiomap:main", "artiq_ddb_template = artiq.frontend.artiq_ddb_template:main", "artiq_master = artiq.frontend.artiq_master:main", "artiq_mkfs = artiq.frontend.artiq_mkfs:main",