Master: DDMA support

Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
This commit is contained in:
mwojcik 2023-03-27 15:47:54 +08:00 committed by sb10q
parent 908dfc780e
commit 90071f7620
8 changed files with 584 additions and 52 deletions

18
flake.lock generated
View File

@ -11,11 +11,11 @@
"src-pythonparser": "src-pythonparser" "src-pythonparser": "src-pythonparser"
}, },
"locked": { "locked": {
"lastModified": 1677033865, "lastModified": 1679454985,
"narHash": "sha256-9w9V+B6vMl4I2uX5k6lJc1FXhf4PeTCZXrq5Tdq2qHc=", "narHash": "sha256-ObDDVR8hKh4lbT42qBdMxc+kIGh5/ORGM0YWGTyO4Sw=",
"ref": "refs/heads/master", "ref": "master",
"rev": "d0437f5672b7dd5f898a73469d730bc46f058ecc", "rev": "e9a153b985d1c703338ad49bb0ac986ade29d21a",
"revCount": 8310, "revCount": 8319,
"type": "git", "type": "git",
"url": "https://github.com/m-labs/artiq.git" "url": "https://github.com/m-labs/artiq.git"
}, },
@ -84,11 +84,11 @@
"mozilla-overlay_2": { "mozilla-overlay_2": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1675354105, "lastModified": 1677493379,
"narHash": "sha256-ZAJGIZ7TjOCU7302lSUabNDz+rxM4If0l8/ZbE/7R5U=", "narHash": "sha256-A1gO8zlWLv3+tZ3cGVB1WYvvoN9pbFyv0xIJHcTsckw=",
"owner": "mozilla", "owner": "mozilla",
"repo": "nixpkgs-mozilla", "repo": "nixpkgs-mozilla",
"rev": "85eb0ba7d8e5d6d4b79e5b0180aadbdd25d76404", "rev": "78e723925daf5c9e8d0a1837ec27059e61649cb6",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -218,7 +218,7 @@
"locked": { "locked": {
"lastModified": 1669819016, "lastModified": 1669819016,
"narHash": "sha256-WvNMUekL4Elc55RdqX8XP43QPnBrK8Rbd0bsoI61E5U=", "narHash": "sha256-WvNMUekL4Elc55RdqX8XP43QPnBrK8Rbd0bsoI61E5U=",
"ref": "refs/heads/master", "ref": "master",
"rev": "67dbb5932fa8ff5f143983476f741f945871d286", "rev": "67dbb5932fa8ff5f143983476f741f945871d286",
"revCount": 624, "revCount": 624,
"type": "git", "type": "git",

View File

@ -27,7 +27,8 @@ use crate::pl;
use crate::{analyzer, kernel, mgmt, moninj, use crate::{analyzer, kernel, mgmt, moninj,
proto_async::*, proto_async::*,
rpc, rpc,
rtio_mgt::{self, resolve_channel_name}}; rtio_mgt::{self, resolve_channel_name},
rtio_dma};
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error { pub enum Error {
@ -79,7 +80,7 @@ enum Reply {
} }
static CACHE_STORE: Mutex<BTreeMap<String, Vec<i32>>> = Mutex::new(BTreeMap::new()); static CACHE_STORE: Mutex<BTreeMap<String, Vec<i32>>> = Mutex::new(BTreeMap::new());
static DMA_RECORD_STORE: Mutex<BTreeMap<String, (Vec<u8>, i64)>> = Mutex::new(BTreeMap::new());
async fn write_header(stream: &TcpStream, reply: Reply) -> Result<()> { async fn write_header(stream: &TcpStream, reply: Reply) -> Result<()> {
stream stream
@ -157,6 +158,9 @@ async fn handle_run_kernel(
stream: Option<&TcpStream>, stream: Option<&TcpStream>,
control: &Rc<RefCell<kernel::Control>>, control: &Rc<RefCell<kernel::Control>>,
_up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>, _up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &drtio_routing::RoutingTable,
timer: GlobalTimer
) -> Result<()> { ) -> Result<()> {
control.borrow_mut().tx.async_send(kernel::Message::StartRequest).await; control.borrow_mut().tx.async_send(kernel::Message::StartRequest).await;
loop { loop {
@ -319,16 +323,16 @@ async fn handle_run_kernel(
.await; .await;
} }
kernel::Message::DmaPutRequest(recorder) => { kernel::Message::DmaPutRequest(recorder) => {
DMA_RECORD_STORE let _id = rtio_dma::put_record(aux_mutex, routing_table, timer, recorder).await;
.lock() #[cfg(has_drtio)]
.insert(recorder.name, (recorder.buffer, recorder.duration)); rtio_dma::remote_dma::upload_traces(aux_mutex, routing_table, timer, _id).await;
} }
kernel::Message::DmaEraseRequest(name) => { kernel::Message::DmaEraseRequest(name) => {
// prevent possible OOM when we have large DMA record replacement. // prevent possible OOM when we have large DMA record replacement.
DMA_RECORD_STORE.lock().remove(&name); rtio_dma::erase(name, aux_mutex, routing_table, timer).await;
} }
kernel::Message::DmaGetRequest(name) => { kernel::Message::DmaGetRequest(name) => {
let result = DMA_RECORD_STORE.lock().get(&name).map(|v| v.clone()); let result = rtio_dma::retrieve(name);
control control
.borrow_mut() .borrow_mut()
.tx .tx
@ -336,6 +340,33 @@ async fn handle_run_kernel(
.await; .await;
} }
#[cfg(has_drtio)] #[cfg(has_drtio)]
kernel::Message::DmaStartRemoteRequest { id, timestamp } => {
rtio_dma::remote_dma::playback(aux_mutex, routing_table, timer, id as u32, timestamp as u64).await;
}
#[cfg(has_drtio)]
kernel::Message::DmaAwaitRemoteRequest(id) => {
let result = rtio_dma::remote_dma::await_done(id as u32, Some(10_000), timer).await;
let reply = match result {
Ok(rtio_dma::remote_dma::RemoteState::PlaybackEnded {
error,
channel,
timestamp
}) => kernel::Message::DmaAwaitRemoteReply {
timeout: false,
error: error,
channel: channel,
timestamp: timestamp
},
_ => kernel::Message::DmaAwaitRemoteReply {
timeout: true,
error: 0,
channel: 0,
timestamp: 0
}
};
control.borrow_mut().tx.async_send(reply).await;
}
#[cfg(has_drtio)]
kernel::Message::UpDestinationsRequest(destination) => { kernel::Message::UpDestinationsRequest(destination) => {
let result = _up_destinations.borrow()[destination as usize]; let result = _up_destinations.borrow()[destination as usize];
control control
@ -395,6 +426,9 @@ async fn handle_connection(
stream: &mut TcpStream, stream: &mut TcpStream,
control: Rc<RefCell<kernel::Control>>, control: Rc<RefCell<kernel::Control>>,
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>, up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &drtio_routing::RoutingTable,
timer: GlobalTimer
) -> Result<()> { ) -> Result<()> {
stream.set_ack_delay(None); stream.set_ack_delay(None);
@ -418,7 +452,7 @@ async fn handle_connection(
load_kernel(&buffer, &control, Some(stream)).await?; load_kernel(&buffer, &control, Some(stream)).await?;
} }
Request::RunKernel => { Request::RunKernel => {
handle_run_kernel(Some(stream), &control, &up_destinations).await?; handle_run_kernel(Some(stream), &control, &up_destinations, aux_mutex, routing_table, timer).await?;
} }
_ => { _ => {
error!("unexpected request from host: {:?}", request); error!("unexpected request from host: {:?}", request);
@ -485,7 +519,7 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
rtio_mgt::startup(&aux_mutex, &drtio_routing_table, &up_destinations, timer, &cfg); rtio_mgt::startup(&aux_mutex, &drtio_routing_table, &up_destinations, timer, &cfg);
analyzer::start(); analyzer::start();
moninj::start(timer, aux_mutex, drtio_routing_table); moninj::start(timer, &aux_mutex, &drtio_routing_table);
let control: Rc<RefCell<kernel::Control>> = Rc::new(RefCell::new(kernel::Control::start())); let control: Rc<RefCell<kernel::Control>> = Rc::new(RefCell::new(kernel::Control::start()));
let idle_kernel = Rc::new(cfg.read("idle_kernel").ok()); let idle_kernel = Rc::new(cfg.read("idle_kernel").ok());
@ -493,7 +527,8 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
info!("Loading startup kernel..."); info!("Loading startup kernel...");
if let Ok(()) = task::block_on(load_kernel(&buffer, &control, None)) { if let Ok(()) = task::block_on(load_kernel(&buffer, &control, None)) {
info!("Starting startup kernel..."); info!("Starting startup kernel...");
let _ = task::block_on(handle_run_kernel(None, &control, &up_destinations)); let routing_table = drtio_routing_table.borrow();
let _ = task::block_on(handle_run_kernel(None, &control, &up_destinations, &aux_mutex, &routing_table, timer));
info!("Startup kernel finished!"); info!("Startup kernel finished!");
} else { } else {
error!("Error loading startup kernel!"); error!("Error loading startup kernel!");
@ -519,13 +554,16 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
let connection = connection.clone(); let connection = connection.clone();
let terminate = terminate.clone(); let terminate = terminate.clone();
let up_destinations = up_destinations.clone(); let up_destinations = up_destinations.clone();
let aux_mutex = aux_mutex.clone();
let routing_table = drtio_routing_table.clone();
// we make sure the value of terminate is 0 before we start // we make sure the value of terminate is 0 before we start
let _ = terminate.try_wait(); let _ = terminate.try_wait();
task::spawn(async move { task::spawn(async move {
let routing_table = routing_table.borrow();
select_biased! { select_biased! {
_ = (async { _ = (async {
let _ = handle_connection(&mut stream, control.clone(), &up_destinations) let _ = handle_connection(&mut stream, control.clone(), &up_destinations, &aux_mutex, &routing_table, timer)
.await .await
.map_err(|e| warn!("connection terminated: {}", e)); .map_err(|e| warn!("connection terminated: {}", e));
if let Some(buffer) = &*idle_kernel { if let Some(buffer) = &*idle_kernel {
@ -533,7 +571,7 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
let _ = load_kernel(&buffer, &control, None) let _ = load_kernel(&buffer, &control, None)
.await.map_err(|_| warn!("error loading idle kernel")); .await.map_err(|_| warn!("error loading idle kernel"));
info!("Running idle kernel"); info!("Running idle kernel");
let _ = handle_run_kernel(None, &control, &up_destinations) let _ = handle_run_kernel(None, &control, &up_destinations, &aux_mutex, &routing_table, timer)
.await.map_err(|_| warn!("error running idle kernel")); .await.map_err(|_| warn!("error running idle kernel"));
info!("Idle kernel terminated"); info!("Idle kernel terminated");
} }

View File

@ -1,14 +1,11 @@
use alloc::{boxed::Box, string::String, vec::Vec}; use alloc::{string::String, vec::Vec};
use core::mem; use core::mem;
use cslice::CSlice; use cslice::CSlice;
use libcortex_a9::cache::dcci_slice;
use super::{Message, KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0, KERNEL_IMAGE}; use super::{Message, KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0, KERNEL_IMAGE};
use crate::{artiq_raise, pl::csr, rtio}; use crate::{artiq_raise, pl::csr, rtio};
const ALIGNMENT: usize = 16 * 8;
#[repr(C)] #[repr(C)]
pub struct DmaTrace { pub struct DmaTrace {
duration: i64, duration: i64,
@ -20,6 +17,7 @@ pub struct DmaRecorder {
pub name: String, pub name: String,
pub buffer: Vec<u8>, pub buffer: Vec<u8>,
pub duration: i64, pub duration: i64,
pub enable_ddma: bool
} }
static mut RECORDER: Option<DmaRecorder> = None; static mut RECORDER: Option<DmaRecorder> = None;
@ -53,11 +51,12 @@ pub extern "C" fn dma_record_start(name: CSlice<u8>) {
name, name,
buffer: Vec::new(), buffer: Vec::new(),
duration: 0, duration: 0,
enable_ddma: false
}); });
} }
} }
pub extern "C" fn dma_record_stop(duration: i64) { pub extern "C" fn dma_record_stop(duration: i64, enable_ddma: bool) {
unsafe { unsafe {
if RECORDER.is_none() { if RECORDER.is_none() {
artiq_raise!("DMAError", "DMA is not recording") artiq_raise!("DMAError", "DMA is not recording")
@ -71,6 +70,7 @@ pub extern "C" fn dma_record_stop(duration: i64) {
let mut recorder = RECORDER.take().unwrap(); let mut recorder = RECORDER.take().unwrap();
recorder.duration = duration; recorder.duration = duration;
recorder.enable_ddma = enable_ddma;
KERNEL_CHANNEL_1TO0 KERNEL_CHANNEL_1TO0
.as_mut() .as_mut()
.unwrap() .unwrap()
@ -151,20 +151,7 @@ pub extern "C" fn dma_retrieve(name: CSlice<u8>) -> DmaTrace {
} }
match unsafe { KERNEL_CHANNEL_0TO1.as_mut().unwrap() }.recv() { match unsafe { KERNEL_CHANNEL_0TO1.as_mut().unwrap() }.recv() {
Message::DmaGetReply(None) => (), Message::DmaGetReply(None) => (),
Message::DmaGetReply(Some((mut v, duration))) => { Message::DmaGetReply(Some((address, duration))) => {
v.reserve(ALIGNMENT - 1);
let original_length = v.len();
let padding = ALIGNMENT - v.as_ptr() as usize % ALIGNMENT;
let padding = if padding == ALIGNMENT { 0 } else { padding };
for _ in 0..padding {
v.push(0);
}
// trailing zero to indicate end of buffer
v.push(0);
v.copy_within(0..original_length, padding);
dcci_slice(&v);
let v = Box::new(v);
let address = Box::into_raw(v) as *mut Vec<u8> as i32;
return DmaTrace { address, duration }; return DmaTrace { address, duration };
} }
_ => panic!("Expected DmaGetReply after DmaGetRequest!"), _ => panic!("Expected DmaGetReply after DmaGetRequest!"),
@ -175,22 +162,17 @@ pub extern "C" fn dma_retrieve(name: CSlice<u8>) -> DmaTrace {
pub extern "C" fn dma_playback(timestamp: i64, ptr: i32) { pub extern "C" fn dma_playback(timestamp: i64, ptr: i32) {
unsafe { unsafe {
let v = Box::from_raw(ptr as *mut Vec<u8>);
let padding = ALIGNMENT - v.as_ptr() as usize % ALIGNMENT;
let padding = if padding == ALIGNMENT { 0 } else { padding };
let ptr = v.as_ptr().add(padding) as i32;
csr::rtio_dma::base_address_write(ptr as u32); csr::rtio_dma::base_address_write(ptr as u32);
csr::rtio_dma::time_offset_write(timestamp as u64); csr::rtio_dma::time_offset_write(timestamp as u64);
csr::cri_con::selected_write(1); csr::cri_con::selected_write(1);
csr::rtio_dma::enable_write(1); csr::rtio_dma::enable_write(1);
#[cfg(has_drtio)]
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(
Message::DmaStartRemoteRequest{ id: ptr, timestamp: timestamp });
while csr::rtio_dma::enable_read() != 0 {} while csr::rtio_dma::enable_read() != 0 {}
csr::cri_con::selected_write(0); csr::cri_con::selected_write(0);
// leave the handle as we may try to do playback for another time.
mem::forget(v);
let error = csr::rtio_dma::error_read(); let error = csr::rtio_dma::error_read();
if error != 0 { if error != 0 {
let timestamp = csr::rtio_dma::error_timestamp_read(); let timestamp = csr::rtio_dma::error_timestamp_read();
@ -215,5 +197,39 @@ pub extern "C" fn dma_playback(timestamp: i64, ptr: i32) {
); );
} }
} }
#[cfg(has_drtio)]
{
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(
Message::DmaAwaitRemoteRequest(ptr));
match KERNEL_CHANNEL_0TO1.as_mut().unwrap().recv() {
Message::DmaAwaitRemoteReply { timeout, error, channel, timestamp } => {
if timeout {
artiq_raise!(
"DMAError",
"Error running DMA on satellite device, timed out waiting for results"
);
}
if error & 1 != 0 {
artiq_raise!(
"RTIOUnderflow",
"RTIO underflow at {1} mu, channel {rtio_channel_info:0}",
channel as i64,
timestamp as i64,
0
);
}
if error & 2 != 0 {
artiq_raise!(
"RTIODestinationUnreachable",
"RTIO destination unreachable, output, at {1} mu, channel {rtio_channel_info:0}",
channel as i64,
timestamp as i64,
0
);
}
}
_ => panic!("Expected DmaAwaitRemoteReply after DmaAwaitRemoteRequest!"),
}
}
} }
} }

View File

@ -52,7 +52,21 @@ pub enum Message {
DmaPutRequest(DmaRecorder), DmaPutRequest(DmaRecorder),
DmaEraseRequest(String), DmaEraseRequest(String),
DmaGetRequest(String), DmaGetRequest(String),
DmaGetReply(Option<(Vec<u8>, i64)>), DmaGetReply(Option<(i32, i64)>),
#[cfg(has_drtio)]
DmaStartRemoteRequest {
id: i32,
timestamp: i64
},
#[cfg(has_drtio)]
DmaAwaitRemoteRequest(i32),
#[cfg(has_drtio)]
DmaAwaitRemoteReply {
timeout: bool,
error: u8,
channel: u32,
timestamp: u64
},
#[cfg(has_drtio)] #[cfg(has_drtio)]
UpDestinationsRequest(i32), UpDestinationsRequest(i32),

View File

@ -47,6 +47,7 @@ mod rtio;
mod rtio; mod rtio;
mod rtio_clocking; mod rtio_clocking;
mod rtio_mgt; mod rtio_mgt;
mod rtio_dma;
static mut SEEN_ASYNC_ERRORS: u8 = 0; static mut SEEN_ASYNC_ERRORS: u8 = 0;

View File

@ -298,7 +298,9 @@ async fn handle_connection(
} }
} }
pub fn start(timer: GlobalTimer, aux_mutex: Rc<Mutex<bool>>, routing_table: Rc<RefCell<drtio_routing::RoutingTable>>) { pub fn start(timer: GlobalTimer, aux_mutex: &Rc<Mutex<bool>>, routing_table: &Rc<RefCell<drtio_routing::RoutingTable>>) {
let aux_mutex = aux_mutex.clone();
let routing_table = routing_table.clone();
task::spawn(async move { task::spawn(async move {
loop { loop {
let aux_mutex = aux_mutex.clone(); let aux_mutex = aux_mutex.clone();

373
src/runtime/src/rtio_dma.rs Normal file
View File

@ -0,0 +1,373 @@
use alloc::{collections::BTreeMap, string::String, vec::Vec, rc::Rc};
use libcortex_a9::{mutex::Mutex, cache::dcci_slice};
use libboard_zynq::timer::GlobalTimer;
use libboard_artiq::drtio_routing::RoutingTable;
#[cfg(has_drtio)]
use libasync::task;
#[cfg(has_drtio)]
use core::mem;
use crate::kernel::DmaRecorder;
const ALIGNMENT: usize = 16 * 8;
static DMA_RECORD_STORE: Mutex<BTreeMap<String, (u32, Vec<u8>, i64)>> = Mutex::new(BTreeMap::new());
#[cfg(has_drtio)]
pub mod remote_dma {
use super::*;
use libboard_zynq::time::Milliseconds;
use log::error;
use crate::rtio_mgt::drtio;
#[derive(Debug, PartialEq, Clone)]
pub enum RemoteState {
NotLoaded,
Loaded,
PlaybackEnded { error: u8, channel: u32, timestamp: u64 }
}
#[derive(Debug, Clone)]
struct RemoteTrace {
trace: Vec<u8>,
pub state: RemoteState
}
impl From<Vec<u8>> for RemoteTrace {
fn from(trace: Vec<u8>) -> Self {
RemoteTrace {
trace: trace,
state: RemoteState::NotLoaded
}
}
}
impl RemoteTrace {
pub fn get_trace(&self) -> &Vec<u8> {
&self.trace
}
}
// represents all traces for a given ID
struct TraceSet {
id: u32,
done_count: Mutex<usize>,
traces: Mutex<BTreeMap<u8, RemoteTrace>>
}
impl TraceSet {
pub fn new(id: u32, traces: BTreeMap<u8, Vec<u8>>) -> TraceSet {
let mut trace_map: BTreeMap<u8, RemoteTrace> = BTreeMap::new();
for (destination, trace) in traces {
trace_map.insert(destination, trace.into());
}
TraceSet {
id: id,
done_count: Mutex::new(0),
traces: Mutex::new(trace_map)
}
}
pub async fn await_done(
&self,
timeout: Option<u64>,
timer: GlobalTimer
) -> Result<RemoteState, &'static str> {
let timeout_ms = Milliseconds(timeout.unwrap_or(10_000));
let limit = timer.get_time() + timeout_ms;
while (timer.get_time() < limit) &
(*(self.done_count.async_lock().await) < self.traces.async_lock().await.len()) {
task::r#yield().await;
}
if timer.get_time() >= limit {
error!("Remote DMA await done timed out");
return Err("Timed out waiting for results.");
}
let mut playback_state: RemoteState = RemoteState::PlaybackEnded { error: 0, channel: 0, timestamp: 0 };
let mut lock = self.traces.async_lock().await;
let trace_iter = lock.iter_mut();
for (_dest, trace) in trace_iter {
match trace.state {
RemoteState::PlaybackEnded {
error: e,
channel: _c,
timestamp: _ts
} => if e != 0 { playback_state = trace.state.clone(); },
_ => (),
}
trace.state = RemoteState::Loaded;
}
Ok(playback_state)
}
pub async fn upload_traces(
&mut self,
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &RoutingTable,
timer: GlobalTimer
) {
let mut lock = self.traces.async_lock().await;
let trace_iter = lock.iter_mut();
for (destination, trace) in trace_iter {
match drtio::ddma_upload_trace(
aux_mutex,
routing_table,
timer,
self.id,
*destination,
trace.get_trace()
).await {
Ok(_) => trace.state = RemoteState::Loaded,
Err(e) => error!("Error adding DMA trace on destination {}: {}", destination, e)
}
}
*(self.done_count.async_lock().await) = 0;
}
pub async fn erase(
&mut self,
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &RoutingTable,
timer: GlobalTimer
) {
let lock = self.traces.async_lock().await;
let trace_iter = lock.keys();
for destination in trace_iter {
match drtio::ddma_send_erase(
aux_mutex,
routing_table,
timer,
self.id,
*destination
).await {
Ok(_) => (),
Err(e) => error!("Error adding DMA trace on destination {}: {}", destination, e)
}
}
}
pub async fn playback_done(&mut self, destination: u8, error: u8, channel: u32, timestamp: u64) {
let mut traces_locked = self.traces.async_lock().await;
let mut trace = traces_locked.get_mut(&destination).unwrap();
trace.state = RemoteState::PlaybackEnded {
error: error,
channel: channel,
timestamp: timestamp
};
*(self.done_count.async_lock().await) += 1;
}
pub async fn playback(
&self,
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &RoutingTable,
timer: GlobalTimer,
timestamp: u64
) {
let mut dest_list: Vec<u8> = Vec::new();
{
let lock = self.traces.async_lock().await;
let trace_iter = lock.iter();
for (dest, trace) in trace_iter {
if trace.state != RemoteState::Loaded {
error!("Destination {} not ready for DMA, state: {:?}", dest, trace.state);
continue;
}
dest_list.push(dest.clone());
}
}
// mutex lock must be dropped before sending a playback request to avoid a deadlock,
// if PlaybackStatus is sent from another satellite and the state must be updated.
for destination in dest_list {
match drtio::ddma_send_playback(
aux_mutex,
routing_table,
timer,
self.id,
destination,
timestamp
).await {
Ok(_) => (),
Err(e) => error!("Error during remote DMA playback: {}", e)
}
}
}
pub async fn destination_changed(
&mut self,
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &RoutingTable,
timer: GlobalTimer,
destination: u8, up: bool
) {
// update state of the destination, resend traces if it's up
if let Some(trace) = self.traces.lock().get_mut(&destination) {
if up {
match drtio::ddma_upload_trace(
aux_mutex,
routing_table,
timer,
self.id,
destination,
trace.get_trace()
).await {
Ok(_) => trace.state = RemoteState::Loaded,
Err(e) => error!("Error adding DMA trace on destination {}: {}", destination, e)
}
} else {
trace.state = RemoteState::NotLoaded;
}
}
}
}
static mut TRACES: BTreeMap<u32, TraceSet> = BTreeMap::new();
pub fn add_traces(id: u32, traces: BTreeMap<u8, Vec<u8>>) {
unsafe { TRACES.insert(id, TraceSet::new(id, traces)) };
}
pub async fn await_done(
id: u32,
timeout: Option<u64>,
timer: GlobalTimer
) -> Result<RemoteState, &'static str> {
let trace_set = unsafe { TRACES.get_mut(&id).unwrap() };
trace_set.await_done(timeout, timer).await
}
pub async fn erase(
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &RoutingTable,
timer: GlobalTimer,
id: u32
) {
let trace_set = unsafe { TRACES.get_mut(&id).unwrap() };
trace_set.erase(aux_mutex, routing_table, timer).await;
unsafe { TRACES.remove(&id); }
}
pub async fn upload_traces(
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &RoutingTable,
timer: GlobalTimer,
id: u32
) {
let trace_set = unsafe { TRACES.get_mut(&id).unwrap() };
trace_set.upload_traces(aux_mutex, routing_table, timer).await;
}
pub async fn playback(
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &RoutingTable,
timer: GlobalTimer,
id: u32,
timestamp: u64
) {
let trace_set = unsafe { TRACES.get_mut(&id).unwrap() };
trace_set.playback(aux_mutex, routing_table, timer, timestamp).await;
}
pub async fn playback_done(
id: u32,
destination: u8,
error: u8,
channel: u32,
timestamp: u64
) {
let trace_set = unsafe { TRACES.get_mut(&id).unwrap() };
trace_set.playback_done(destination, error, channel, timestamp).await;
}
pub async fn destination_changed(
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &RoutingTable,
timer: GlobalTimer,
destination: u8,
up: bool
) {
let trace_iter = unsafe { TRACES.values_mut() };
for trace_set in trace_iter {
trace_set.destination_changed(aux_mutex, routing_table, timer, destination, up).await;
}
}
}
pub async fn put_record(_aux_mutex: &Rc<Mutex<bool>>,
_routing_table: &RoutingTable,
_timer: GlobalTimer,
mut recorder: DmaRecorder,
) -> u32 {
#[cfg(has_drtio)]
let mut remote_traces: BTreeMap<u8, Vec<u8>> = BTreeMap::new();
#[cfg(has_drtio)]
if recorder.enable_ddma {
let mut local_trace: Vec<u8> = Vec::new();
// analyze each entry and put in proper buckets, as the kernel core
// sends whole chunks, to limit comms/kernel CPU communication,
// and as only comms core has access to varios DMA buffers.
let mut ptr = 0;
recorder.buffer.push(0);
while recorder.buffer[ptr] != 0 {
// ptr + 3 = tgt >> 24 (destination)
let len = recorder.buffer[ptr] as usize;
let destination = recorder.buffer[ptr+3];
if destination == 0 {
local_trace.extend(&recorder.buffer[ptr..ptr+len]);
}
else {
if let Some(remote_trace) = remote_traces.get_mut(&destination) {
remote_trace.extend(&recorder.buffer[ptr..ptr+len]);
} else {
remote_traces.insert(destination, recorder.buffer[ptr..ptr+len].to_vec());
}
}
// and jump to the next event
ptr += len;
}
mem::swap(&mut recorder.buffer, &mut local_trace);
}
// trailing zero to indicate end of buffer
recorder.buffer.push(0);
recorder.buffer.reserve(ALIGNMENT - 1);
let original_length = recorder.buffer.len();
let padding = ALIGNMENT - recorder.buffer.as_ptr() as usize % ALIGNMENT;
let padding = if padding == ALIGNMENT { 0 } else { padding };
for _ in 0..padding {
recorder.buffer.push(0);
}
recorder.buffer.copy_within(0..original_length, padding);
dcci_slice(&recorder.buffer);
let ptr = recorder.buffer[padding..].as_ptr() as u32;
let _old_record = DMA_RECORD_STORE
.lock()
.insert(recorder.name, (ptr, recorder.buffer, recorder.duration));
#[cfg(has_drtio)]
{
if let Some((old_id, _v, _d)) = _old_record {
remote_dma::erase(_aux_mutex, _routing_table, _timer, old_id).await;
}
remote_dma::add_traces(ptr, remote_traces);
}
ptr
}
pub async fn erase(name: String, _aux_mutex: &Rc<Mutex<bool>>,
_routing_table: &RoutingTable, _timer: GlobalTimer
) {
let _entry = DMA_RECORD_STORE.lock().remove(&name);
#[cfg(has_drtio)]
if let Some((id, _v, _d)) = _entry {
remote_dma::erase(_aux_mutex, _routing_table, _timer, id).await;
}
}
pub fn retrieve(name: String) -> Option<(i32, i64)> {
let (ptr, _v, duration) = DMA_RECORD_STORE.lock().get(&name)?.clone();
Some((ptr as i32, duration))
}

View File

@ -13,13 +13,16 @@ static mut RTIO_DEVICE_MAP: BTreeMap<u32, String> = BTreeMap::new();
#[cfg(has_drtio)] #[cfg(has_drtio)]
pub mod drtio { pub mod drtio {
use embedded_hal::blocking::delay::DelayMs; use embedded_hal::blocking::delay::DelayMs;
use alloc::vec::Vec;
use libasync::{delay, task}; use libasync::{delay, task};
use libboard_artiq::{drtioaux::Error, drtioaux_async, drtioaux_async::Packet}; use libboard_artiq::{drtioaux::Error, drtioaux_async, drtioaux_async::Packet};
use libboard_artiq::drtioaux_proto::DMA_TRACE_MAX_SIZE;
use libboard_zynq::time::Milliseconds; use libboard_zynq::time::Milliseconds;
use log::{error, info, warn}; use log::{error, info, warn};
use super::*; use super::*;
use crate::{ASYNC_ERROR_BUSY, ASYNC_ERROR_COLLISION, ASYNC_ERROR_SEQUENCE_ERROR, SEEN_ASYNC_ERRORS}; use crate::{ASYNC_ERROR_BUSY, ASYNC_ERROR_COLLISION, ASYNC_ERROR_SEQUENCE_ERROR, SEEN_ASYNC_ERRORS};
use crate::rtio_dma::remote_dma;
pub fn startup( pub fn startup(
aux_mutex: &Rc<Mutex<bool>>, aux_mutex: &Rc<Mutex<bool>>,
@ -63,7 +66,16 @@ pub mod drtio {
} }
let _lock = aux_mutex.async_lock().await; let _lock = aux_mutex.async_lock().await;
drtioaux_async::send(linkno, request).await.unwrap(); drtioaux_async::send(linkno, request).await.unwrap();
recv_aux_timeout(linkno, 200, timer).await loop {
let reply = recv_aux_timeout(linkno, 200, timer).await;
match reply {
Ok(Packet::DmaPlaybackStatus { id, destination, error, channel, timestamp }) => {
remote_dma::playback_done(id, destination, error, channel, timestamp).await;
},
Ok(packet) => return Ok(packet),
Err(e) => return Err(e)
}
}
} }
async fn drain_buffer(linkno: u8, draining_time: Milliseconds, timer: GlobalTimer) { async fn drain_buffer(linkno: u8, draining_time: Milliseconds, timer: GlobalTimer) {
@ -172,6 +184,8 @@ pub mod drtio {
async fn process_unsolicited_aux(aux_mutex: &Rc<Mutex<bool>>, linkno: u8) { async fn process_unsolicited_aux(aux_mutex: &Rc<Mutex<bool>>, linkno: u8) {
let _lock = aux_mutex.async_lock().await; let _lock = aux_mutex.async_lock().await;
match drtioaux_async::recv(linkno).await { match drtioaux_async::recv(linkno).await {
Ok(Some(Packet::DmaPlaybackStatus { id, destination, error, channel, timestamp })
) => remote_dma::playback_done(id, destination, error, channel, timestamp).await,
Ok(Some(packet)) => warn!("[LINK#{}] unsolicited aux packet: {:?}", linkno, packet), Ok(Some(packet)) => warn!("[LINK#{}] unsolicited aux packet: {:?}", linkno, packet),
Ok(None) => (), Ok(None) => (),
Err(_) => warn!("[LINK#{}] aux packet error", linkno), Err(_) => warn!("[LINK#{}] aux packet error", linkno),
@ -252,7 +266,8 @@ pub mod drtio {
.await; .await;
match reply { match reply {
Ok(Packet::DestinationDownReply) => { Ok(Packet::DestinationDownReply) => {
destination_set_up(routing_table, up_destinations, destination, false).await destination_set_up(routing_table, up_destinations, destination, false).await;
remote_dma::destination_changed(aux_mutex, routing_table, timer, destination, false).await;
} }
Ok(Packet::DestinationOkReply) => (), Ok(Packet::DestinationOkReply) => (),
Ok(Packet::DestinationSequenceErrorReply { channel }) => { Ok(Packet::DestinationSequenceErrorReply { channel }) => {
@ -287,6 +302,7 @@ pub mod drtio {
} }
} else { } else {
destination_set_up(routing_table, up_destinations, destination, false).await; destination_set_up(routing_table, up_destinations, destination, false).await;
remote_dma::destination_changed(aux_mutex, routing_table, timer, destination, false).await;
} }
} else { } else {
if up_links[linkno as usize] { if up_links[linkno as usize] {
@ -304,6 +320,7 @@ pub mod drtio {
Ok(Packet::DestinationOkReply) => { Ok(Packet::DestinationOkReply) => {
destination_set_up(routing_table, up_destinations, destination, true).await; destination_set_up(routing_table, up_destinations, destination, true).await;
init_buffer_space(destination as u8, linkno).await; init_buffer_space(destination as u8, linkno).await;
remote_dma::destination_changed(aux_mutex, routing_table, timer, destination, true).await;
} }
Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet), Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet),
Err(e) => error!("[DEST#{}] communication failed ({})", destination, e), Err(e) => error!("[DEST#{}] communication failed ({})", destination, e),
@ -389,6 +406,77 @@ pub mod drtio {
} }
} }
} }
pub async fn ddma_upload_trace(
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &drtio_routing::RoutingTable,
timer: GlobalTimer,
id: u32,
destination: u8,
trace: &Vec<u8>
) -> Result<(), &'static str> {
let linkno = routing_table.0[destination as usize][0] - 1;
let mut i = 0;
while i < trace.len() {
let mut trace_slice: [u8; DMA_TRACE_MAX_SIZE] = [0; DMA_TRACE_MAX_SIZE];
let len: usize = if i + DMA_TRACE_MAX_SIZE < trace.len() { DMA_TRACE_MAX_SIZE } else { trace.len() - i } as usize;
let last = i + len == trace.len();
trace_slice[..len].clone_from_slice(&trace[i..i+len]);
i += len;
let reply = aux_transact(aux_mutex, linkno,
&Packet::DmaAddTraceRequest {
id: id, destination: destination, last: last, length: len as u16, trace: trace_slice},
timer).await;
match reply {
Ok(Packet::DmaAddTraceReply { succeeded: true }) => (),
Ok(Packet::DmaAddTraceReply { succeeded: false }) => {
return Err("error adding trace on satellite"); },
Ok(_) => { return Err("adding DMA trace failed, unexpected aux packet"); },
Err(_) => { return Err("adding DMA trace failed, aux error"); }
}
}
Ok(())
}
pub async fn ddma_send_erase(
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &drtio_routing::RoutingTable,
timer: GlobalTimer,
id: u32,
destination: u8
) -> Result<(), &'static str> {
let linkno = routing_table.0[destination as usize][0] - 1;
let reply = aux_transact(aux_mutex, linkno,
&Packet::DmaRemoveTraceRequest { id: id, destination: destination },
timer).await;
match reply {
Ok(Packet::DmaRemoveTraceReply { succeeded: true }) => Ok(()),
Ok(Packet::DmaRemoveTraceReply { succeeded: false }) => Err("satellite DMA erase error"),
Ok(_) => Err("adding trace failed, unexpected aux packet"),
Err(_) => Err("erasing trace failed, aux error")
}
}
pub async fn ddma_send_playback(
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &drtio_routing::RoutingTable,
timer: GlobalTimer,
id: u32,
destination: u8,
timestamp: u64
) -> Result<(), &'static str> {
let linkno = routing_table.0[destination as usize][0] - 1;
let reply = aux_transact(aux_mutex, linkno, &Packet::DmaPlaybackRequest{
id: id, destination: destination, timestamp: timestamp }, timer).await;
match reply {
Ok(Packet::DmaPlaybackReply { succeeded: true }) => Ok(()),
Ok(Packet::DmaPlaybackReply { succeeded: false }) => Err("error on DMA playback request"),
Ok(_) => Err("received unexpected aux packet during DMA playback"),
Err(_) => Err("aux error on DMA playback")
}
}
} }
fn read_device_map(cfg: &Config) -> BTreeMap<u32, String> { fn read_device_map(cfg: &Config) -> BTreeMap<u32, String> {