add channel names to RTIO errors

This commit is contained in:
Egor Savkin 2022-12-02 16:27:03 +08:00 committed by GitHub
parent c591e7e305
commit 1852491102
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 281 additions and 34 deletions

View File

@ -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
-------

View File

@ -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)

View File

@ -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):
"""

View File

@ -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

View File

@ -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.

View File

@ -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):
"""

View File

@ -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

View File

@ -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.

View File

@ -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."""

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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<u32, String> = 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<u32, String> {
let mut device_map: BTreeMap<u32, String> = 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<u32, String>) -> 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<RefCell<drtio_routing::RoutingTable>>,
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>) {
unsafe { RTIO_DEVICE_MAP = read_device_map(); }
drtio::startup(io, aux_mutex, routing_table, up_destinations);
unsafe {
csr::rtio_core::reset_phy_write(1);

View File

@ -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<Option<eh::eh_artiq::Exception>> = 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() }

View File

@ -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}"] = {{

89
artiq/frontend/artiq_rtiomap.py Executable file
View File

@ -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("<I", len(channel_map)))
for dev_num, dev_name in channel_map.items():
dev_name_bytes = dev_name.encode("utf-8")
outfile.write(struct.pack("<II{}s".format(len(dev_name_bytes)), dev_num, len(dev_name_bytes), dev_name_bytes))
def deserialize_device_map(infile):
result_map = dict()
ch_count, = struct.unpack_from("<I", infile.read(4))
for _ in range(ch_count):
dev_num, dev_name_len = struct.unpack_from("<II", infile.read(8))
dev_name = struct.unpack_from("<{}s".format(dev_name_len), infile.read(dev_name_len))[0].decode("utf-8")
assert dev_num not in result_map
result_map[dev_num] = dev_name
return result_map
def main():
args = get_argparser().parse_args()
common_args.init_logger_from_args(args)
if args.show:
with open(args.file, "rb") as infile:
chan_map = deserialize_device_map(infile)
for chan, device in sorted(chan_map.items(), key=lambda x: x[0]):
print(f"{chan} -> {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()

View File

@ -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"
]
}

View File

@ -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 <rtiomap-tool>` page.

View File

@ -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:

View File

@ -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",