forked from M-Labs/artiq
add channel names to RTIO errors
This commit is contained in:
parent
c591e7e305
commit
1852491102
@ -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
|
||||
-------
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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."""
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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() }
|
||||
|
@ -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
89
artiq/frontend/artiq_rtiomap.py
Executable 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()
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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:
|
||||
|
||||
|
1
setup.py
1
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",
|
||||
|
Loading…
Reference in New Issue
Block a user