mirror of https://github.com/m-labs/artiq.git
runtime: implement distributed DMA
This commit is contained in:
parent
8b1f38b015
commit
e9a153b985
|
@ -6,7 +6,7 @@ alone could achieve.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from artiq.language.core import syscall, kernel
|
from artiq.language.core import syscall, kernel
|
||||||
from artiq.language.types import TInt32, TInt64, TStr, TNone, TTuple
|
from artiq.language.types import TInt32, TInt64, TStr, TNone, TTuple, TBool
|
||||||
from artiq.coredevice.exceptions import DMAError
|
from artiq.coredevice.exceptions import DMAError
|
||||||
|
|
||||||
from numpy import int64
|
from numpy import int64
|
||||||
|
@ -17,7 +17,7 @@ def dma_record_start(name: TStr) -> TNone:
|
||||||
raise NotImplementedError("syscall not simulated")
|
raise NotImplementedError("syscall not simulated")
|
||||||
|
|
||||||
@syscall
|
@syscall
|
||||||
def dma_record_stop(duration: TInt64) -> TNone:
|
def dma_record_stop(duration: TInt64, enable_ddma: TBool) -> TNone:
|
||||||
raise NotImplementedError("syscall not simulated")
|
raise NotImplementedError("syscall not simulated")
|
||||||
|
|
||||||
@syscall
|
@syscall
|
||||||
|
@ -47,6 +47,7 @@ class DMARecordContextManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.name = ""
|
self.name = ""
|
||||||
self.saved_now_mu = int64(0)
|
self.saved_now_mu = int64(0)
|
||||||
|
self.enable_ddma = False
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
|
@ -56,7 +57,7 @@ class DMARecordContextManager:
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def __exit__(self, type, value, traceback):
|
def __exit__(self, type, value, traceback):
|
||||||
dma_record_stop(now_mu()) # see above
|
dma_record_stop(now_mu(), self.enable_ddma) # see above
|
||||||
at_mu(self.saved_now_mu)
|
at_mu(self.saved_now_mu)
|
||||||
|
|
||||||
|
|
||||||
|
@ -74,12 +75,18 @@ class CoreDMA:
|
||||||
self.epoch = 0
|
self.epoch = 0
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def record(self, name):
|
def record(self, name, enable_ddma=False):
|
||||||
"""Returns a context manager that will record a DMA trace called ``name``.
|
"""Returns a context manager that will record a DMA trace called ``name``.
|
||||||
Any previously recorded trace with the same name is overwritten.
|
Any previously recorded trace with the same name is overwritten.
|
||||||
The trace will persist across kernel switches."""
|
The trace will persist across kernel switches.
|
||||||
|
In DRTIO context, you can toggle distributed DMA with ``enable_ddma``.
|
||||||
|
Enabling it allows running DMA on satellites, rather than sending all
|
||||||
|
events from the master.
|
||||||
|
Disabling it may improve performance in some scenarios,
|
||||||
|
e.g. when there are many small satellite buffers."""
|
||||||
self.epoch += 1
|
self.epoch += 1
|
||||||
self.recorder.name = name
|
self.recorder.name = name
|
||||||
|
self.recorder.enable_ddma = enable_ddma
|
||||||
return self.recorder
|
return self.recorder
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
|
|
|
@ -269,7 +269,7 @@ extern fn dma_record_start(name: &CSlice<u8>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unwind(allowed)]
|
#[unwind(allowed)]
|
||||||
extern fn dma_record_stop(duration: i64) {
|
extern fn dma_record_stop(duration: i64, enable_ddma: bool) {
|
||||||
unsafe {
|
unsafe {
|
||||||
dma_record_flush();
|
dma_record_flush();
|
||||||
|
|
||||||
|
@ -285,7 +285,8 @@ extern fn dma_record_stop(duration: i64) {
|
||||||
|
|
||||||
DMA_RECORDER.active = false;
|
DMA_RECORDER.active = false;
|
||||||
send(&DmaRecordStop {
|
send(&DmaRecordStop {
|
||||||
duration: duration as u64
|
duration: duration as u64,
|
||||||
|
enable_ddma: enable_ddma
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -370,7 +371,7 @@ extern fn dma_erase(name: &CSlice<u8>) {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct DmaTrace {
|
struct DmaTrace {
|
||||||
duration: i64,
|
duration: i64,
|
||||||
address: i32,
|
address: i32
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unwind(allowed)]
|
#[unwind(allowed)]
|
||||||
|
@ -404,6 +405,7 @@ extern fn dma_playback(timestamp: i64, ptr: i32) {
|
||||||
|
|
||||||
csr::cri_con::selected_write(1);
|
csr::cri_con::selected_write(1);
|
||||||
csr::rtio_dma::enable_write(1);
|
csr::rtio_dma::enable_write(1);
|
||||||
|
send(&DmaStartRemoteRequest { id: ptr as i32, 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);
|
||||||
|
|
||||||
|
@ -424,6 +426,24 @@ extern fn dma_playback(timestamp: i64, ptr: i32) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
send(&DmaAwaitRemoteRequest { id: ptr as i32 });
|
||||||
|
recv!(&DmaAwaitRemoteReply { timeout, error, channel, timestamp } => {
|
||||||
|
if timeout {
|
||||||
|
raise!("DMAError",
|
||||||
|
"Error running DMA on satellite device, timed out waiting for results");
|
||||||
|
}
|
||||||
|
if error & 1 != 0 {
|
||||||
|
raise!("RTIOUnderflow",
|
||||||
|
"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 channel {rtio_channel_info:0}, {1} mu",
|
||||||
|
channel as i64, timestamp as i64, 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(has_rtio_dma))]
|
#[cfg(not(has_rtio_dma))]
|
||||||
|
|
|
@ -14,8 +14,8 @@ impl<T> From<IoError<T>> for Error<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 512 (max size) - 4 (CRC) - 1 (packet ID) - 4 (trace ID) - 1 (last) - 2 (length) */
|
/* 512 (max size) - 4 (CRC) - 1 (packet ID) - 1 (destination) - 4 (trace ID) - 1 (last) - 2 (length) */
|
||||||
const DMA_TRACE_MAX_SIZE: usize = 500;
|
pub const DMA_TRACE_MAX_SIZE: usize = 499;
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub enum Packet {
|
pub enum Packet {
|
||||||
|
@ -58,13 +58,13 @@ pub enum Packet {
|
||||||
SpiReadReply { succeeded: bool, data: u32 },
|
SpiReadReply { succeeded: bool, data: u32 },
|
||||||
SpiBasicReply { succeeded: bool },
|
SpiBasicReply { succeeded: bool },
|
||||||
|
|
||||||
DmaAddTraceRequest { id: u32, last: bool, length: u16, trace: [u8; DMA_TRACE_MAX_SIZE] },
|
DmaAddTraceRequest { destination: u8, id: u32, last: bool, length: u16, trace: [u8; DMA_TRACE_MAX_SIZE] },
|
||||||
DmaAddTraceReply { succeeded: bool },
|
DmaAddTraceReply { succeeded: bool },
|
||||||
DmaRemoveTraceRequest { id: u32 },
|
DmaRemoveTraceRequest { destination: u8, id: u32 },
|
||||||
DmaRemoveTraceReply { succeeded: bool },
|
DmaRemoveTraceReply { succeeded: bool },
|
||||||
DmaPlaybackRequest { id: u32, timestamp: u64 },
|
DmaPlaybackRequest { destination: u8, id: u32, timestamp: u64 },
|
||||||
DmaPlaybackReply { succeeded: bool },
|
DmaPlaybackReply { succeeded: bool },
|
||||||
DmaPlaybackStatus { id: u32, error: u8, channel: u32, timestamp: u64 }
|
DmaPlaybackStatus { destination: u8, id: u32, error: u8, channel: u32, timestamp: u64 }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,12 +198,14 @@ impl Packet {
|
||||||
},
|
},
|
||||||
|
|
||||||
0xb0 => {
|
0xb0 => {
|
||||||
|
let destination = reader.read_u8()?;
|
||||||
let id = reader.read_u32()?;
|
let id = reader.read_u32()?;
|
||||||
let last = reader.read_bool()?;
|
let last = reader.read_bool()?;
|
||||||
let length = reader.read_u16()?;
|
let length = reader.read_u16()?;
|
||||||
let mut trace: [u8; DMA_TRACE_MAX_SIZE] = [0; DMA_TRACE_MAX_SIZE];
|
let mut trace: [u8; DMA_TRACE_MAX_SIZE] = [0; DMA_TRACE_MAX_SIZE];
|
||||||
reader.read_exact(&mut trace[0..length as usize])?;
|
reader.read_exact(&mut trace[0..length as usize])?;
|
||||||
Packet::DmaAddTraceRequest {
|
Packet::DmaAddTraceRequest {
|
||||||
|
destination: destination,
|
||||||
id: id,
|
id: id,
|
||||||
last: last,
|
last: last,
|
||||||
length: length as u16,
|
length: length as u16,
|
||||||
|
@ -214,12 +216,14 @@ impl Packet {
|
||||||
succeeded: reader.read_bool()?
|
succeeded: reader.read_bool()?
|
||||||
},
|
},
|
||||||
0xb2 => Packet::DmaRemoveTraceRequest {
|
0xb2 => Packet::DmaRemoveTraceRequest {
|
||||||
|
destination: reader.read_u8()?,
|
||||||
id: reader.read_u32()?
|
id: reader.read_u32()?
|
||||||
},
|
},
|
||||||
0xb3 => Packet::DmaRemoveTraceReply {
|
0xb3 => Packet::DmaRemoveTraceReply {
|
||||||
succeeded: reader.read_bool()?
|
succeeded: reader.read_bool()?
|
||||||
},
|
},
|
||||||
0xb4 => Packet::DmaPlaybackRequest {
|
0xb4 => Packet::DmaPlaybackRequest {
|
||||||
|
destination: reader.read_u8()?,
|
||||||
id: reader.read_u32()?,
|
id: reader.read_u32()?,
|
||||||
timestamp: reader.read_u64()?
|
timestamp: reader.read_u64()?
|
||||||
},
|
},
|
||||||
|
@ -227,6 +231,7 @@ impl Packet {
|
||||||
succeeded: reader.read_bool()?
|
succeeded: reader.read_bool()?
|
||||||
},
|
},
|
||||||
0xb6 => Packet::DmaPlaybackStatus {
|
0xb6 => Packet::DmaPlaybackStatus {
|
||||||
|
destination: reader.read_u8()?,
|
||||||
id: reader.read_u32()?,
|
id: reader.read_u32()?,
|
||||||
error: reader.read_u8()?,
|
error: reader.read_u8()?,
|
||||||
channel: reader.read_u32()?,
|
channel: reader.read_u32()?,
|
||||||
|
@ -392,8 +397,9 @@ impl Packet {
|
||||||
writer.write_bool(succeeded)?;
|
writer.write_bool(succeeded)?;
|
||||||
},
|
},
|
||||||
|
|
||||||
Packet::DmaAddTraceRequest { id, last, trace, length } => {
|
Packet::DmaAddTraceRequest { destination, id, last, trace, length } => {
|
||||||
writer.write_u8(0xb0)?;
|
writer.write_u8(0xb0)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
writer.write_u32(id)?;
|
writer.write_u32(id)?;
|
||||||
writer.write_bool(last)?;
|
writer.write_bool(last)?;
|
||||||
// trace may be broken down to fit within drtio aux memory limit
|
// trace may be broken down to fit within drtio aux memory limit
|
||||||
|
@ -405,16 +411,18 @@ impl Packet {
|
||||||
writer.write_u8(0xb1)?;
|
writer.write_u8(0xb1)?;
|
||||||
writer.write_bool(succeeded)?;
|
writer.write_bool(succeeded)?;
|
||||||
},
|
},
|
||||||
Packet::DmaRemoveTraceRequest { id } => {
|
Packet::DmaRemoveTraceRequest { destination, id } => {
|
||||||
writer.write_u8(0xb2)?;
|
writer.write_u8(0xb2)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
writer.write_u32(id)?;
|
writer.write_u32(id)?;
|
||||||
},
|
},
|
||||||
Packet::DmaRemoveTraceReply { succeeded } => {
|
Packet::DmaRemoveTraceReply { succeeded } => {
|
||||||
writer.write_u8(0xb3)?;
|
writer.write_u8(0xb3)?;
|
||||||
writer.write_bool(succeeded)?;
|
writer.write_bool(succeeded)?;
|
||||||
},
|
},
|
||||||
Packet::DmaPlaybackRequest { id, timestamp } => {
|
Packet::DmaPlaybackRequest { destination, id, timestamp } => {
|
||||||
writer.write_u8(0xb4)?;
|
writer.write_u8(0xb4)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
writer.write_u32(id)?;
|
writer.write_u32(id)?;
|
||||||
writer.write_u64(timestamp)?;
|
writer.write_u64(timestamp)?;
|
||||||
},
|
},
|
||||||
|
@ -422,8 +430,9 @@ impl Packet {
|
||||||
writer.write_u8(0xb5)?;
|
writer.write_u8(0xb5)?;
|
||||||
writer.write_bool(succeeded)?;
|
writer.write_bool(succeeded)?;
|
||||||
},
|
},
|
||||||
Packet::DmaPlaybackStatus { id, error, channel, timestamp } => {
|
Packet::DmaPlaybackStatus { destination, id, error, channel, timestamp } => {
|
||||||
writer.write_u8(0xb6)?;
|
writer.write_u8(0xb6)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
writer.write_u32(id)?;
|
writer.write_u32(id)?;
|
||||||
writer.write_u8(error)?;
|
writer.write_u8(error)?;
|
||||||
writer.write_u32(channel)?;
|
writer.write_u32(channel)?;
|
||||||
|
|
|
@ -20,7 +20,8 @@ pub enum Message<'a> {
|
||||||
DmaRecordStart(&'a str),
|
DmaRecordStart(&'a str),
|
||||||
DmaRecordAppend(&'a [u8]),
|
DmaRecordAppend(&'a [u8]),
|
||||||
DmaRecordStop {
|
DmaRecordStop {
|
||||||
duration: u64
|
duration: u64,
|
||||||
|
enable_ddma: bool
|
||||||
},
|
},
|
||||||
|
|
||||||
DmaEraseRequest {
|
DmaEraseRequest {
|
||||||
|
@ -32,9 +33,24 @@ pub enum Message<'a> {
|
||||||
},
|
},
|
||||||
DmaRetrieveReply {
|
DmaRetrieveReply {
|
||||||
trace: Option<&'a [u8]>,
|
trace: Option<&'a [u8]>,
|
||||||
duration: u64
|
duration: u64,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
DmaStartRemoteRequest {
|
||||||
|
id: i32,
|
||||||
|
timestamp: i64,
|
||||||
|
},
|
||||||
|
DmaAwaitRemoteRequest {
|
||||||
|
id: i32
|
||||||
|
},
|
||||||
|
DmaAwaitRemoteReply {
|
||||||
|
timeout: bool,
|
||||||
|
error: u8,
|
||||||
|
channel: u32,
|
||||||
|
timestamp: u64
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
RunFinished,
|
RunFinished,
|
||||||
RunException {
|
RunException {
|
||||||
exceptions: &'a [Option<eh::eh_artiq::Exception<'a>>],
|
exceptions: &'a [Option<eh::eh_artiq::Exception<'a>>],
|
||||||
|
|
|
@ -182,6 +182,8 @@ fn startup() {
|
||||||
drtio_routing::interconnect_disable_all();
|
drtio_routing::interconnect_disable_all();
|
||||||
let aux_mutex = sched::Mutex::new();
|
let aux_mutex = sched::Mutex::new();
|
||||||
|
|
||||||
|
let ddma_mutex = sched::Mutex::new();
|
||||||
|
|
||||||
let mut scheduler = sched::Scheduler::new(interface);
|
let mut scheduler = sched::Scheduler::new(interface);
|
||||||
let io = scheduler.io();
|
let io = scheduler.io();
|
||||||
|
|
||||||
|
@ -189,14 +191,15 @@ fn startup() {
|
||||||
io.spawn(4096, dhcp::dhcp_thread);
|
io.spawn(4096, dhcp::dhcp_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
rtio_mgt::startup(&io, &aux_mutex, &drtio_routing_table, &up_destinations);
|
rtio_mgt::startup(&io, &aux_mutex, &drtio_routing_table, &up_destinations, &ddma_mutex);
|
||||||
|
|
||||||
io.spawn(4096, mgmt::thread);
|
io.spawn(4096, mgmt::thread);
|
||||||
{
|
{
|
||||||
let aux_mutex = aux_mutex.clone();
|
let aux_mutex = aux_mutex.clone();
|
||||||
let drtio_routing_table = drtio_routing_table.clone();
|
let drtio_routing_table = drtio_routing_table.clone();
|
||||||
let up_destinations = up_destinations.clone();
|
let up_destinations = up_destinations.clone();
|
||||||
io.spawn(16384, move |io| { session::thread(io, &aux_mutex, &drtio_routing_table, &up_destinations) });
|
let ddma_mutex = ddma_mutex.clone();
|
||||||
|
io.spawn(16384, move |io| { session::thread(io, &aux_mutex, &drtio_routing_table, &up_destinations, &ddma_mutex) });
|
||||||
}
|
}
|
||||||
#[cfg(any(has_rtio_moninj, has_drtio))]
|
#[cfg(any(has_rtio_moninj, has_drtio))]
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,10 +1,182 @@
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use alloc::{vec::Vec, string::String, collections::btree_map::BTreeMap};
|
use alloc::{vec::Vec, string::String, collections::btree_map::BTreeMap};
|
||||||
|
use sched::{Io, Mutex};
|
||||||
|
|
||||||
const ALIGNMENT: usize = 64;
|
const ALIGNMENT: usize = 64;
|
||||||
|
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
pub mod remote_dma {
|
||||||
|
use super::*;
|
||||||
|
use board_artiq::drtio_routing::RoutingTable;
|
||||||
|
use rtio_mgt::drtio;
|
||||||
|
use board_misoc::clock;
|
||||||
|
|
||||||
|
#[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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remote traces map. ID -> destination, trace pair
|
||||||
|
static mut TRACES: BTreeMap<u32, BTreeMap<u8, RemoteTrace>> = BTreeMap::new();
|
||||||
|
|
||||||
|
pub fn add_traces(io: &Io, ddma_mutex: &Mutex, id: u32, traces: BTreeMap<u8, Vec<u8>>) {
|
||||||
|
let _lock = ddma_mutex.lock(io);
|
||||||
|
let mut trace_map: BTreeMap<u8, RemoteTrace> = BTreeMap::new();
|
||||||
|
for (destination, trace) in traces {
|
||||||
|
trace_map.insert(destination, trace.into());
|
||||||
|
}
|
||||||
|
unsafe { TRACES.insert(id, trace_map); }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn await_done(io: &Io, ddma_mutex: &Mutex, id: u32, timeout: u64) -> Result<RemoteState, &'static str> {
|
||||||
|
let max_time = clock::get_ms() + timeout as u64;
|
||||||
|
io.until(|| {
|
||||||
|
if clock::get_ms() > max_time {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ddma_mutex.test_lock() {
|
||||||
|
// cannot lock again within io.until - scheduler guarantees
|
||||||
|
// that it will not be interrupted - so only test the lock
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let traces = unsafe { TRACES.get(&id).unwrap() };
|
||||||
|
for (_dest, trace) in traces {
|
||||||
|
match trace.state {
|
||||||
|
RemoteState::PlaybackEnded {error: _, channel: _, timestamp: _} => (),
|
||||||
|
_ => return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}).unwrap();
|
||||||
|
if clock::get_ms() > max_time {
|
||||||
|
error!("Remote DMA await done timed out");
|
||||||
|
return Err("Timed out waiting for results.");
|
||||||
|
}
|
||||||
|
// clear the internal state, and if there have been any errors, return one of them
|
||||||
|
let mut playback_state: RemoteState = RemoteState::PlaybackEnded { error: 0, channel: 0, timestamp: 0 };
|
||||||
|
{
|
||||||
|
let _lock = ddma_mutex.lock(io).unwrap();
|
||||||
|
let traces = unsafe { TRACES.get_mut(&id).unwrap() };
|
||||||
|
for (_dest, trace) in traces {
|
||||||
|
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 fn erase(io: &Io, aux_mutex: &Mutex, routing_table: &RoutingTable,
|
||||||
|
ddma_mutex: &Mutex, id: u32) {
|
||||||
|
let _lock = ddma_mutex.lock(io).unwrap();
|
||||||
|
let destinations = unsafe { TRACES.get(&id).unwrap() };
|
||||||
|
for destination in destinations.keys() {
|
||||||
|
match drtio::ddma_send_erase(io, aux_mutex, routing_table, id, *destination) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => error!("Error erasing trace on DMA: {}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe { TRACES.remove(&id); }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn upload_traces(io: &Io, aux_mutex: &Mutex, routing_table: &RoutingTable,
|
||||||
|
ddma_mutex: &Mutex, id: u32) {
|
||||||
|
let _lock = ddma_mutex.lock(io);
|
||||||
|
let traces = unsafe { TRACES.get_mut(&id).unwrap() };
|
||||||
|
for (destination, mut trace) in traces {
|
||||||
|
match drtio::ddma_upload_trace(io, aux_mutex, routing_table, id, *destination, trace.get_trace())
|
||||||
|
{
|
||||||
|
Ok(_) => trace.state = RemoteState::Loaded,
|
||||||
|
Err(e) => error!("Error adding DMA trace on destination {}: {}", destination, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn playback(io: &Io, aux_mutex: &Mutex, routing_table: &RoutingTable,
|
||||||
|
ddma_mutex: &Mutex, id: u32, timestamp: u64) {
|
||||||
|
// triggers playback on satellites
|
||||||
|
let destinations = unsafe {
|
||||||
|
let _lock = ddma_mutex.lock(io).unwrap();
|
||||||
|
TRACES.get(&id).unwrap() };
|
||||||
|
for (destination, trace) in destinations {
|
||||||
|
{
|
||||||
|
// need to drop the lock before sending the playback request to avoid a deadlock
|
||||||
|
// if a PlaybackStatus is returned from another satellite in the meanwhile.
|
||||||
|
let _lock = ddma_mutex.lock(io).unwrap();
|
||||||
|
if trace.state != RemoteState::Loaded {
|
||||||
|
error!("Destination {} not ready for DMA, state: {:?}", *destination, trace.state);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match drtio::ddma_send_playback(io, aux_mutex, routing_table, ddma_mutex, id, *destination, timestamp) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => error!("Error during remote DMA playback: {}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn playback_done(io: &Io, ddma_mutex: &Mutex,
|
||||||
|
id: u32, destination: u8, error: u8, channel: u32, timestamp: u64) {
|
||||||
|
// called upon receiving PlaybackDone aux packet
|
||||||
|
let _lock = ddma_mutex.lock(io).unwrap();
|
||||||
|
let mut trace = unsafe { TRACES.get_mut(&id).unwrap().get_mut(&destination).unwrap() };
|
||||||
|
trace.state = RemoteState::PlaybackEnded {
|
||||||
|
error: error,
|
||||||
|
channel: channel,
|
||||||
|
timestamp: timestamp
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destination_changed(io: &Io, aux_mutex: &Mutex, routing_table: &RoutingTable,
|
||||||
|
ddma_mutex: &Mutex, destination: u8, up: bool) {
|
||||||
|
// update state of the destination, resend traces if it's up
|
||||||
|
let _lock = ddma_mutex.lock(io).unwrap();
|
||||||
|
let traces_iter = unsafe { TRACES.iter_mut() };
|
||||||
|
for (id, dest_traces) in traces_iter {
|
||||||
|
if let Some(trace) = dest_traces.get_mut(&destination) {
|
||||||
|
if up {
|
||||||
|
match drtio::ddma_upload_trace(io, aux_mutex, routing_table, *id, destination, trace.get_trace())
|
||||||
|
{
|
||||||
|
Ok(_) => trace.state = RemoteState::Loaded,
|
||||||
|
Err(e) => error!("Error adding DMA trace on destination {}: {}", destination, e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
trace.state = RemoteState::NotLoaded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Entry {
|
struct LocalEntry {
|
||||||
trace: Vec<u8>,
|
trace: Vec<u8>,
|
||||||
padding_len: usize,
|
padding_len: usize,
|
||||||
duration: u64
|
duration: u64
|
||||||
|
@ -12,7 +184,8 @@ struct Entry {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Manager {
|
pub struct Manager {
|
||||||
entries: BTreeMap<String, Entry>,
|
entries: BTreeMap<u32, LocalEntry>,
|
||||||
|
name_map: BTreeMap<String, u32>,
|
||||||
recording_name: String,
|
recording_name: String,
|
||||||
recording_trace: Vec<u8>
|
recording_trace: Vec<u8>
|
||||||
}
|
}
|
||||||
|
@ -21,59 +194,116 @@ impl Manager {
|
||||||
pub fn new() -> Manager {
|
pub fn new() -> Manager {
|
||||||
Manager {
|
Manager {
|
||||||
entries: BTreeMap::new(),
|
entries: BTreeMap::new(),
|
||||||
recording_name: String::new(),
|
name_map: BTreeMap::new(),
|
||||||
recording_trace: Vec::new(),
|
recording_trace: Vec::new(),
|
||||||
|
recording_name: String::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn record_start(&mut self, name: &str) {
|
pub fn record_start(&mut self, name: &str) -> Option<u32> {
|
||||||
self.recording_name = String::from(name);
|
self.recording_name = String::from(name);
|
||||||
self.recording_trace = Vec::new();
|
self.recording_trace = Vec::new();
|
||||||
|
if let Some(id) = self.name_map.get(&self.recording_name) {
|
||||||
// or we could needlessly OOM replacing a large trace
|
// replacing a trace
|
||||||
self.entries.remove(name);
|
let old_id = id.clone();
|
||||||
|
self.entries.remove(&id);
|
||||||
|
self.name_map.remove(&self.recording_name);
|
||||||
|
// return old ID
|
||||||
|
return Some(old_id);
|
||||||
|
}
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn record_append(&mut self, data: &[u8]) {
|
pub fn record_append(&mut self, data: &[u8]) {
|
||||||
self.recording_trace.extend_from_slice(data)
|
self.recording_trace.extend_from_slice(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn record_stop(&mut self, duration: u64) {
|
pub fn record_stop(&mut self, duration: u64, enable_ddma: bool,
|
||||||
let mut trace = Vec::new();
|
_io: &Io, _ddma_mutex: &Mutex) -> u32 {
|
||||||
mem::swap(&mut self.recording_trace, &mut trace);
|
let mut local_trace = Vec::new();
|
||||||
trace.push(0);
|
let mut remote_traces: BTreeMap<u8, Vec<u8>> = BTreeMap::new();
|
||||||
let data_len = trace.len();
|
|
||||||
|
|
||||||
// Realign.
|
if enable_ddma {
|
||||||
trace.reserve(ALIGNMENT - 1);
|
let mut trace = Vec::new();
|
||||||
let padding = ALIGNMENT - trace.as_ptr() as usize % ALIGNMENT;
|
mem::swap(&mut self.recording_trace, &mut trace);
|
||||||
|
trace.push(0);
|
||||||
|
// 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;
|
||||||
|
while trace[ptr] != 0 {
|
||||||
|
// ptr + 3 = tgt >> 24 (destination)
|
||||||
|
let len = trace[ptr] as usize;
|
||||||
|
let destination = trace[ptr+3];
|
||||||
|
if destination == 0 {
|
||||||
|
local_trace.extend(&trace[ptr..ptr+len]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if let Some(remote_trace) = remote_traces.get_mut(&destination) {
|
||||||
|
remote_trace.extend(&trace[ptr..ptr+len]);
|
||||||
|
} else {
|
||||||
|
remote_traces.insert(destination, trace[ptr..ptr+len].to_vec());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// and jump to the next event
|
||||||
|
ptr += len;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// with disabled DDMA, move the whole trace to local
|
||||||
|
mem::swap(&mut self.recording_trace, &mut local_trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
local_trace.push(0);
|
||||||
|
let data_len = local_trace.len();
|
||||||
|
// Realign the local entry.
|
||||||
|
local_trace.reserve(ALIGNMENT - 1);
|
||||||
|
let padding = ALIGNMENT - local_trace.as_ptr() as usize % ALIGNMENT;
|
||||||
let padding = if padding == ALIGNMENT { 0 } else { padding };
|
let padding = if padding == ALIGNMENT { 0 } else { padding };
|
||||||
for _ in 0..padding {
|
for _ in 0..padding {
|
||||||
// Vec guarantees that this will not reallocate
|
// Vec guarantees that this will not reallocate
|
||||||
trace.push(0)
|
local_trace.push(0)
|
||||||
}
|
}
|
||||||
for i in 1..data_len + 1 {
|
for i in 1..data_len + 1 {
|
||||||
trace[data_len + padding - i] = trace[data_len - i]
|
local_trace[data_len + padding - i] = local_trace[data_len - i]
|
||||||
}
|
}
|
||||||
|
// trace ID is its pointer
|
||||||
|
let id = local_trace[padding..].as_ptr() as u32;
|
||||||
|
self.entries.insert(id, LocalEntry {
|
||||||
|
trace: local_trace,
|
||||||
|
padding_len: padding,
|
||||||
|
duration: duration,
|
||||||
|
});
|
||||||
let mut name = String::new();
|
let mut name = String::new();
|
||||||
mem::swap(&mut self.recording_name, &mut name);
|
mem::swap(&mut self.recording_name, &mut name);
|
||||||
self.entries.insert(name, Entry {
|
self.name_map.insert(name, id);
|
||||||
trace: trace,
|
|
||||||
padding_len: padding,
|
#[cfg(has_drtio)]
|
||||||
duration: duration
|
remote_dma::add_traces(_io, _ddma_mutex, id, remote_traces);
|
||||||
});
|
|
||||||
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn erase(&mut self, name: &str) {
|
pub fn erase(&mut self, name: &str) {
|
||||||
self.entries.remove(name);
|
if let Some(id) = self.name_map.get(name) {
|
||||||
|
self.entries.remove(&id);
|
||||||
|
}
|
||||||
|
self.name_map.remove(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
pub fn get_id(&mut self, name: &str) -> Option<&u32> {
|
||||||
|
self.name_map.get(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_trace<F, R>(&self, name: &str, f: F) -> R
|
pub fn with_trace<F, R>(&self, name: &str, f: F) -> R
|
||||||
where F: FnOnce(Option<&[u8]>, u64) -> R {
|
where F: FnOnce(Option<&[u8]>, u64) -> R {
|
||||||
match self.entries.get(name) {
|
if let Some(ptr) = self.name_map.get(name) {
|
||||||
Some(entry) => f(Some(&entry.trace[entry.padding_len..]), entry.duration),
|
match self.entries.get(ptr) {
|
||||||
None => f(None, 0)
|
Some(entry) => f(Some(&entry.trace[entry.padding_len..]), entry.duration),
|
||||||
|
None => f(None, 0)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
f(None, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,17 +18,22 @@ static mut RTIO_DEVICE_MAP: BTreeMap<u32, String> = BTreeMap::new();
|
||||||
#[cfg(has_drtio)]
|
#[cfg(has_drtio)]
|
||||||
pub mod drtio {
|
pub mod drtio {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use alloc::vec::Vec;
|
||||||
use drtioaux;
|
use drtioaux;
|
||||||
|
use proto_artiq::drtioaux_proto::DMA_TRACE_MAX_SIZE;
|
||||||
|
use rtio_dma::remote_dma;
|
||||||
|
|
||||||
pub fn startup(io: &Io, aux_mutex: &Mutex,
|
pub fn startup(io: &Io, aux_mutex: &Mutex,
|
||||||
routing_table: &Urc<RefCell<drtio_routing::RoutingTable>>,
|
routing_table: &Urc<RefCell<drtio_routing::RoutingTable>>,
|
||||||
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>) {
|
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
||||||
|
ddma_mutex: &Mutex) {
|
||||||
let aux_mutex = aux_mutex.clone();
|
let aux_mutex = aux_mutex.clone();
|
||||||
let routing_table = routing_table.clone();
|
let routing_table = routing_table.clone();
|
||||||
let up_destinations = up_destinations.clone();
|
let up_destinations = up_destinations.clone();
|
||||||
io.spawn(4096, move |io| {
|
let ddma_mutex = ddma_mutex.clone();
|
||||||
|
io.spawn(8192, move |io| {
|
||||||
let routing_table = routing_table.borrow();
|
let routing_table = routing_table.borrow();
|
||||||
link_thread(io, &aux_mutex, &routing_table, &up_destinations);
|
link_thread(io, &aux_mutex, &routing_table, &up_destinations, &ddma_mutex);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,9 +152,12 @@ pub mod drtio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_unsolicited_aux(io: &Io, aux_mutex: &Mutex, linkno: u8) {
|
fn process_unsolicited_aux(io: &Io, aux_mutex: &Mutex, linkno: u8, ddma_mutex: &Mutex) {
|
||||||
let _lock = aux_mutex.lock(io).unwrap();
|
let _lock = aux_mutex.lock(io).unwrap();
|
||||||
match drtioaux::recv(linkno) {
|
match drtioaux::recv(linkno) {
|
||||||
|
Ok(Some(drtioaux::Packet::DmaPlaybackStatus { id, destination, error, channel, timestamp })) => {
|
||||||
|
remote_dma::playback_done(io, ddma_mutex, id, destination, error, channel, timestamp);
|
||||||
|
}
|
||||||
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)
|
||||||
|
@ -198,7 +206,8 @@ pub mod drtio {
|
||||||
|
|
||||||
fn destination_survey(io: &Io, aux_mutex: &Mutex, routing_table: &drtio_routing::RoutingTable,
|
fn destination_survey(io: &Io, aux_mutex: &Mutex, routing_table: &drtio_routing::RoutingTable,
|
||||||
up_links: &[bool],
|
up_links: &[bool],
|
||||||
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>) {
|
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
||||||
|
ddma_mutex: &Mutex) {
|
||||||
for destination in 0..drtio_routing::DEST_COUNT {
|
for destination in 0..drtio_routing::DEST_COUNT {
|
||||||
let hop = routing_table.0[destination][0];
|
let hop = routing_table.0[destination][0];
|
||||||
let destination = destination as u8;
|
let destination = destination as u8;
|
||||||
|
@ -216,8 +225,10 @@ pub mod drtio {
|
||||||
destination: destination
|
destination: destination
|
||||||
});
|
});
|
||||||
match reply {
|
match reply {
|
||||||
Ok(drtioaux::Packet::DestinationDownReply) =>
|
Ok(drtioaux::Packet::DestinationDownReply) => {
|
||||||
destination_set_up(routing_table, up_destinations, destination, false),
|
destination_set_up(routing_table, up_destinations, destination, false);
|
||||||
|
remote_dma::destination_changed(io, aux_mutex, routing_table, ddma_mutex, destination, false);
|
||||||
|
}
|
||||||
Ok(drtioaux::Packet::DestinationOkReply) => (),
|
Ok(drtioaux::Packet::DestinationOkReply) => (),
|
||||||
Ok(drtioaux::Packet::DestinationSequenceErrorReply { channel }) => {
|
Ok(drtioaux::Packet::DestinationSequenceErrorReply { channel }) => {
|
||||||
error!("[DEST#{}] RTIO sequence error involving channel 0x{:04x}:{}", destination, channel, resolve_channel_name(channel as u32));
|
error!("[DEST#{}] RTIO sequence error involving channel 0x{:04x}:{}", destination, channel, resolve_channel_name(channel as u32));
|
||||||
|
@ -236,6 +247,7 @@ pub mod drtio {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
destination_set_up(routing_table, up_destinations, destination, false);
|
destination_set_up(routing_table, up_destinations, destination, false);
|
||||||
|
remote_dma::destination_changed(io, aux_mutex, routing_table, ddma_mutex, destination, false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if up_links[linkno as usize] {
|
if up_links[linkno as usize] {
|
||||||
|
@ -247,6 +259,7 @@ pub mod drtio {
|
||||||
Ok(drtioaux::Packet::DestinationOkReply) => {
|
Ok(drtioaux::Packet::DestinationOkReply) => {
|
||||||
destination_set_up(routing_table, up_destinations, destination, true);
|
destination_set_up(routing_table, up_destinations, destination, true);
|
||||||
init_buffer_space(destination as u8, linkno);
|
init_buffer_space(destination as u8, linkno);
|
||||||
|
remote_dma::destination_changed(io, aux_mutex, routing_table, ddma_mutex, destination, true);
|
||||||
},
|
},
|
||||||
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)
|
||||||
|
@ -259,7 +272,8 @@ pub mod drtio {
|
||||||
|
|
||||||
pub fn link_thread(io: Io, aux_mutex: &Mutex,
|
pub fn link_thread(io: Io, aux_mutex: &Mutex,
|
||||||
routing_table: &drtio_routing::RoutingTable,
|
routing_table: &drtio_routing::RoutingTable,
|
||||||
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>) {
|
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
||||||
|
ddma_mutex: &Mutex) {
|
||||||
let mut up_links = [false; csr::DRTIO.len()];
|
let mut up_links = [false; csr::DRTIO.len()];
|
||||||
loop {
|
loop {
|
||||||
for linkno in 0..csr::DRTIO.len() {
|
for linkno in 0..csr::DRTIO.len() {
|
||||||
|
@ -267,7 +281,7 @@ pub mod drtio {
|
||||||
if up_links[linkno as usize] {
|
if up_links[linkno as usize] {
|
||||||
/* link was previously up */
|
/* link was previously up */
|
||||||
if link_rx_up(linkno) {
|
if link_rx_up(linkno) {
|
||||||
process_unsolicited_aux(&io, aux_mutex, linkno);
|
process_unsolicited_aux(&io, aux_mutex, linkno, ddma_mutex);
|
||||||
process_local_errors(linkno);
|
process_local_errors(linkno);
|
||||||
} else {
|
} else {
|
||||||
info!("[LINK#{}] link is down", linkno);
|
info!("[LINK#{}] link is down", linkno);
|
||||||
|
@ -297,7 +311,7 @@ pub mod drtio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
destination_survey(&io, aux_mutex, routing_table, &up_links, up_destinations);
|
destination_survey(&io, aux_mutex, routing_table, &up_links, up_destinations, ddma_mutex);
|
||||||
io.sleep(200).unwrap();
|
io.sleep(200).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,6 +342,72 @@ pub mod drtio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ddma_upload_trace(io: &Io, aux_mutex: &Mutex,
|
||||||
|
routing_table: &drtio_routing::RoutingTable,
|
||||||
|
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(io, aux_mutex, linkno,
|
||||||
|
&drtioaux::Packet::DmaAddTraceRequest {
|
||||||
|
id: id, destination: destination, last: last, length: len as u16, trace: trace_slice});
|
||||||
|
match reply {
|
||||||
|
Ok(drtioaux::Packet::DmaAddTraceReply { succeeded: true }) => (),
|
||||||
|
Ok(drtioaux::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 fn ddma_send_erase(io: &Io, aux_mutex: &Mutex,
|
||||||
|
routing_table: &drtio_routing::RoutingTable,
|
||||||
|
id: u32, destination: u8) -> Result<(), &'static str> {
|
||||||
|
let linkno = routing_table.0[destination as usize][0] - 1;
|
||||||
|
let reply = aux_transact(io, aux_mutex, linkno,
|
||||||
|
&drtioaux::Packet::DmaRemoveTraceRequest { id: id, destination: destination });
|
||||||
|
match reply {
|
||||||
|
Ok(drtioaux::Packet::DmaRemoveTraceReply { succeeded: true }) => Ok(()),
|
||||||
|
Ok(drtioaux::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 fn ddma_send_playback(io: &Io, aux_mutex: &Mutex,
|
||||||
|
routing_table: &drtio_routing::RoutingTable,
|
||||||
|
ddma_mutex: &Mutex, id: u32, destination: u8, timestamp: u64) -> Result<(), &'static str> {
|
||||||
|
let linkno = routing_table.0[destination as usize][0] - 1;
|
||||||
|
let _lock = aux_mutex.lock(io).unwrap();
|
||||||
|
drtioaux::send(linkno, &drtioaux::Packet::DmaPlaybackRequest{
|
||||||
|
id: id, destination: destination, timestamp: timestamp }).unwrap();
|
||||||
|
loop {
|
||||||
|
let reply = recv_aux_timeout(io, linkno, 200);
|
||||||
|
match reply {
|
||||||
|
Ok(drtioaux::Packet::DmaPlaybackReply { succeeded: true }) => { return Ok(()) },
|
||||||
|
Ok(drtioaux::Packet::DmaPlaybackReply { succeeded: false }) => {
|
||||||
|
return Err("error on DMA playback request") },
|
||||||
|
// in case we received status from another destination
|
||||||
|
// but we want to get DmaPlaybackReply anyway, thus the loop
|
||||||
|
Ok(drtioaux::Packet::DmaPlaybackStatus { id, destination, error, channel, timestamp }) => {
|
||||||
|
remote_dma::playback_done(io, ddma_mutex, id, destination, error, channel, timestamp);
|
||||||
|
},
|
||||||
|
Ok(_) => { return Err("received unexpected aux packet while DMA playback") },
|
||||||
|
Err(_) => { return Err("aux error on DMA playback") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(has_drtio))]
|
#[cfg(not(has_drtio))]
|
||||||
|
@ -336,7 +416,8 @@ pub mod drtio {
|
||||||
|
|
||||||
pub fn startup(_io: &Io, _aux_mutex: &Mutex,
|
pub fn startup(_io: &Io, _aux_mutex: &Mutex,
|
||||||
_routing_table: &Urc<RefCell<drtio_routing::RoutingTable>>,
|
_routing_table: &Urc<RefCell<drtio_routing::RoutingTable>>,
|
||||||
_up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>) {}
|
_up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
||||||
|
_ddma_mutex: &Mutex) {}
|
||||||
pub fn reset(_io: &Io, _aux_mutex: &Mutex) {}
|
pub fn reset(_io: &Io, _aux_mutex: &Mutex) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,9 +491,10 @@ pub fn resolve_channel_name(channel: u32) -> String {
|
||||||
|
|
||||||
pub fn startup(io: &Io, aux_mutex: &Mutex,
|
pub fn startup(io: &Io, aux_mutex: &Mutex,
|
||||||
routing_table: &Urc<RefCell<drtio_routing::RoutingTable>>,
|
routing_table: &Urc<RefCell<drtio_routing::RoutingTable>>,
|
||||||
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>) {
|
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
||||||
|
ddma_mutex: &Mutex) {
|
||||||
unsafe { RTIO_DEVICE_MAP = read_device_map(); }
|
unsafe { RTIO_DEVICE_MAP = read_device_map(); }
|
||||||
drtio::startup(io, aux_mutex, routing_table, up_destinations);
|
drtio::startup(io, aux_mutex, routing_table, up_destinations, ddma_mutex);
|
||||||
unsafe {
|
unsafe {
|
||||||
csr::rtio_core::reset_phy_write(1);
|
csr::rtio_core::reset_phy_write(1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -301,6 +301,10 @@ impl Mutex {
|
||||||
self.0.set(true);
|
self.0.set(true);
|
||||||
Ok(MutexGuard(&*self.0))
|
Ok(MutexGuard(&*self.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn test_lock<'a>(&'a self) -> bool {
|
||||||
|
self.0.get()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MutexGuard<'a>(&'a Cell<bool>);
|
pub struct MutexGuard<'a>(&'a Cell<bool>);
|
||||||
|
|
|
@ -10,6 +10,8 @@ use urc::Urc;
|
||||||
use sched::{ThreadHandle, Io, Mutex, TcpListener, TcpStream, Error as SchedError};
|
use sched::{ThreadHandle, Io, Mutex, TcpListener, TcpStream, Error as SchedError};
|
||||||
use rtio_clocking;
|
use rtio_clocking;
|
||||||
use rtio_dma::Manager as DmaManager;
|
use rtio_dma::Manager as DmaManager;
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
use rtio_dma::remote_dma;
|
||||||
use rtio_mgt::{get_async_errors, resolve_channel_name};
|
use rtio_mgt::{get_async_errors, resolve_channel_name};
|
||||||
use cache::Cache;
|
use cache::Cache;
|
||||||
use kern_hwreq;
|
use kern_hwreq;
|
||||||
|
@ -329,7 +331,7 @@ fn process_host_message(io: &Io,
|
||||||
fn process_kern_message(io: &Io, aux_mutex: &Mutex,
|
fn process_kern_message(io: &Io, aux_mutex: &Mutex,
|
||||||
routing_table: &drtio_routing::RoutingTable,
|
routing_table: &drtio_routing::RoutingTable,
|
||||||
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
||||||
mut stream: Option<&mut TcpStream>,
|
ddma_mutex: &Mutex, mut stream: Option<&mut TcpStream>,
|
||||||
session: &mut Session) -> Result<bool, Error<SchedError>> {
|
session: &mut Session) -> Result<bool, Error<SchedError>> {
|
||||||
kern_recv_notrace(io, |request| {
|
kern_recv_notrace(io, |request| {
|
||||||
match (request, session.kernel_state) {
|
match (request, session.kernel_state) {
|
||||||
|
@ -368,19 +370,31 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
|
||||||
}
|
}
|
||||||
|
|
||||||
&kern::DmaRecordStart(name) => {
|
&kern::DmaRecordStart(name) => {
|
||||||
session.congress.dma_manager.record_start(name);
|
if let Some(_id) = session.congress.dma_manager.record_start(name) {
|
||||||
|
// replace the record
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
remote_dma::erase(io, aux_mutex, routing_table, ddma_mutex, _id);
|
||||||
|
}
|
||||||
kern_acknowledge()
|
kern_acknowledge()
|
||||||
}
|
}
|
||||||
&kern::DmaRecordAppend(data) => {
|
&kern::DmaRecordAppend(data) => {
|
||||||
session.congress.dma_manager.record_append(data);
|
session.congress.dma_manager.record_append(data);
|
||||||
kern_acknowledge()
|
kern_acknowledge()
|
||||||
}
|
}
|
||||||
&kern::DmaRecordStop { duration } => {
|
&kern::DmaRecordStop { duration, enable_ddma } => {
|
||||||
session.congress.dma_manager.record_stop(duration);
|
let _id = session.congress.dma_manager.record_stop(duration, enable_ddma, io, ddma_mutex);
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
{
|
||||||
|
remote_dma::upload_traces(io, aux_mutex, routing_table, ddma_mutex, _id);
|
||||||
|
}
|
||||||
cache::flush_l2_cache();
|
cache::flush_l2_cache();
|
||||||
kern_acknowledge()
|
kern_acknowledge()
|
||||||
}
|
}
|
||||||
&kern::DmaEraseRequest { name } => {
|
&kern::DmaEraseRequest { name } => {
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
if let Some(id) = session.congress.dma_manager.get_id(name) {
|
||||||
|
remote_dma::erase(io, aux_mutex, routing_table, ddma_mutex, *id);
|
||||||
|
}
|
||||||
session.congress.dma_manager.erase(name);
|
session.congress.dma_manager.erase(name);
|
||||||
kern_acknowledge()
|
kern_acknowledge()
|
||||||
}
|
}
|
||||||
|
@ -392,6 +406,27 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
&kern::DmaStartRemoteRequest { id: _id, timestamp: _timestamp } => {
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
remote_dma::playback(io, aux_mutex, routing_table, ddma_mutex, _id as u32, _timestamp as u64);
|
||||||
|
kern_acknowledge()
|
||||||
|
}
|
||||||
|
&kern::DmaAwaitRemoteRequest { id: _id } => {
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
let reply = match remote_dma::await_done(io, ddma_mutex, _id as u32, 10_000) {
|
||||||
|
Ok(remote_dma::RemoteState::PlaybackEnded { error, channel, timestamp }) =>
|
||||||
|
kern::DmaAwaitRemoteReply {
|
||||||
|
timeout: false,
|
||||||
|
error: error,
|
||||||
|
channel: channel,
|
||||||
|
timestamp: timestamp
|
||||||
|
},
|
||||||
|
_ => kern::DmaAwaitRemoteReply { timeout: true, error: 0, channel: 0, timestamp: 0},
|
||||||
|
};
|
||||||
|
#[cfg(not(has_drtio))]
|
||||||
|
let reply = kern::DmaAwaitRemoteReply { timeout: false, error: 0, channel: 0, timestamp: 0};
|
||||||
|
kern_send(io, &reply)
|
||||||
|
}
|
||||||
|
|
||||||
&kern::RpcSend { async, service, tag, data } => {
|
&kern::RpcSend { async, service, tag, data } => {
|
||||||
match stream {
|
match stream {
|
||||||
|
@ -511,7 +546,7 @@ fn process_kern_queued_rpc(stream: &mut TcpStream,
|
||||||
fn host_kernel_worker(io: &Io, aux_mutex: &Mutex,
|
fn host_kernel_worker(io: &Io, aux_mutex: &Mutex,
|
||||||
routing_table: &drtio_routing::RoutingTable,
|
routing_table: &drtio_routing::RoutingTable,
|
||||||
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
||||||
stream: &mut TcpStream,
|
ddma_mutex: &Mutex, stream: &mut TcpStream,
|
||||||
congress: &mut Congress) -> Result<(), Error<SchedError>> {
|
congress: &mut Congress) -> Result<(), Error<SchedError>> {
|
||||||
let mut session = Session::new(congress);
|
let mut session = Session::new(congress);
|
||||||
|
|
||||||
|
@ -529,6 +564,7 @@ fn host_kernel_worker(io: &Io, aux_mutex: &Mutex,
|
||||||
if mailbox::receive() != 0 {
|
if mailbox::receive() != 0 {
|
||||||
process_kern_message(io, aux_mutex,
|
process_kern_message(io, aux_mutex,
|
||||||
routing_table, up_destinations,
|
routing_table, up_destinations,
|
||||||
|
ddma_mutex,
|
||||||
Some(stream), &mut session)?;
|
Some(stream), &mut session)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,7 +582,7 @@ fn host_kernel_worker(io: &Io, aux_mutex: &Mutex,
|
||||||
fn flash_kernel_worker(io: &Io, aux_mutex: &Mutex,
|
fn flash_kernel_worker(io: &Io, aux_mutex: &Mutex,
|
||||||
routing_table: &drtio_routing::RoutingTable,
|
routing_table: &drtio_routing::RoutingTable,
|
||||||
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
||||||
congress: &mut Congress,
|
ddma_mutex: &Mutex, congress: &mut Congress,
|
||||||
config_key: &str) -> Result<(), Error<SchedError>> {
|
config_key: &str) -> Result<(), Error<SchedError>> {
|
||||||
let mut session = Session::new(congress);
|
let mut session = Session::new(congress);
|
||||||
|
|
||||||
|
@ -568,7 +604,7 @@ fn flash_kernel_worker(io: &Io, aux_mutex: &Mutex,
|
||||||
}
|
}
|
||||||
|
|
||||||
if mailbox::receive() != 0 {
|
if mailbox::receive() != 0 {
|
||||||
if process_kern_message(io, aux_mutex, routing_table, up_destinations, None, &mut session)? {
|
if process_kern_message(io, aux_mutex, routing_table, up_destinations, ddma_mutex, None, &mut session)? {
|
||||||
return Ok(())
|
return Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -598,7 +634,8 @@ fn respawn<F>(io: &Io, handle: &mut Option<ThreadHandle>, f: F)
|
||||||
|
|
||||||
pub fn thread(io: Io, aux_mutex: &Mutex,
|
pub fn thread(io: Io, aux_mutex: &Mutex,
|
||||||
routing_table: &Urc<RefCell<drtio_routing::RoutingTable>>,
|
routing_table: &Urc<RefCell<drtio_routing::RoutingTable>>,
|
||||||
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>) {
|
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
||||||
|
ddma_mutex: &Mutex) {
|
||||||
let listener = TcpListener::new(&io, 65535);
|
let listener = TcpListener::new(&io, 65535);
|
||||||
listener.listen(1381).expect("session: cannot listen");
|
listener.listen(1381).expect("session: cannot listen");
|
||||||
info!("accepting network sessions");
|
info!("accepting network sessions");
|
||||||
|
@ -609,7 +646,7 @@ pub fn thread(io: Io, aux_mutex: &Mutex,
|
||||||
{
|
{
|
||||||
let mut congress = congress.borrow_mut();
|
let mut congress = congress.borrow_mut();
|
||||||
info!("running startup kernel");
|
info!("running startup kernel");
|
||||||
match flash_kernel_worker(&io, &aux_mutex, &routing_table.borrow(), &up_destinations, &mut congress, "startup_kernel") {
|
match flash_kernel_worker(&io, &aux_mutex, &routing_table.borrow(), &up_destinations, ddma_mutex, &mut congress, "startup_kernel") {
|
||||||
Ok(()) =>
|
Ok(()) =>
|
||||||
info!("startup kernel finished"),
|
info!("startup kernel finished"),
|
||||||
Err(Error::KernelNotFound) =>
|
Err(Error::KernelNotFound) =>
|
||||||
|
@ -649,12 +686,13 @@ pub fn thread(io: Io, aux_mutex: &Mutex,
|
||||||
let routing_table = routing_table.clone();
|
let routing_table = routing_table.clone();
|
||||||
let up_destinations = up_destinations.clone();
|
let up_destinations = up_destinations.clone();
|
||||||
let congress = congress.clone();
|
let congress = congress.clone();
|
||||||
|
let ddma_mutex = ddma_mutex.clone();
|
||||||
let stream = stream.into_handle();
|
let stream = stream.into_handle();
|
||||||
respawn(&io, &mut kernel_thread, move |io| {
|
respawn(&io, &mut kernel_thread, move |io| {
|
||||||
let routing_table = routing_table.borrow();
|
let routing_table = routing_table.borrow();
|
||||||
let mut congress = congress.borrow_mut();
|
let mut congress = congress.borrow_mut();
|
||||||
let mut stream = TcpStream::from_handle(&io, stream);
|
let mut stream = TcpStream::from_handle(&io, stream);
|
||||||
match host_kernel_worker(&io, &aux_mutex, &routing_table, &up_destinations, &mut stream, &mut *congress) {
|
match host_kernel_worker(&io, &aux_mutex, &routing_table, &up_destinations, &ddma_mutex, &mut stream, &mut *congress) {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
Err(Error::Protocol(host::Error::Io(IoError::UnexpectedEnd))) =>
|
Err(Error::Protocol(host::Error::Io(IoError::UnexpectedEnd))) =>
|
||||||
info!("connection closed"),
|
info!("connection closed"),
|
||||||
|
@ -677,10 +715,11 @@ pub fn thread(io: Io, aux_mutex: &Mutex,
|
||||||
let routing_table = routing_table.clone();
|
let routing_table = routing_table.clone();
|
||||||
let up_destinations = up_destinations.clone();
|
let up_destinations = up_destinations.clone();
|
||||||
let congress = congress.clone();
|
let congress = congress.clone();
|
||||||
|
let ddma_mutex = ddma_mutex.clone();
|
||||||
respawn(&io, &mut kernel_thread, move |io| {
|
respawn(&io, &mut kernel_thread, move |io| {
|
||||||
let routing_table = routing_table.borrow();
|
let routing_table = routing_table.borrow();
|
||||||
let mut congress = congress.borrow_mut();
|
let mut congress = congress.borrow_mut();
|
||||||
match flash_kernel_worker(&io, &aux_mutex, &routing_table, &up_destinations, &mut *congress, "idle_kernel") {
|
match flash_kernel_worker(&io, &aux_mutex, &routing_table, &up_destinations, &ddma_mutex, &mut *congress, "idle_kernel") {
|
||||||
Ok(()) =>
|
Ok(()) =>
|
||||||
info!("idle kernel finished, standing by"),
|
info!("idle kernel finished, standing by"),
|
||||||
Err(Error::Protocol(host::Error::Io(
|
Err(Error::Protocol(host::Error::Io(
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use board_misoc::csr;
|
use board_misoc::{csr, cache::flush_l2_cache};
|
||||||
use alloc::{vec::Vec, collections::btree_map::BTreeMap};
|
use alloc::{vec::Vec, collections::btree_map::BTreeMap};
|
||||||
|
|
||||||
const ALIGNMENT: usize = 64;
|
const ALIGNMENT: usize = 64;
|
||||||
|
@ -52,7 +52,19 @@ impl Manager {
|
||||||
|
|
||||||
pub fn add(&mut self, id: u32, last: bool, trace: &[u8], trace_len: usize) -> Result<(), Error> {
|
pub fn add(&mut self, id: u32, last: bool, trace: &[u8], trace_len: usize) -> Result<(), Error> {
|
||||||
let entry = match self.entries.get_mut(&id) {
|
let entry = match self.entries.get_mut(&id) {
|
||||||
Some(entry) => entry,
|
Some(entry) => {
|
||||||
|
if entry.complete {
|
||||||
|
// replace entry
|
||||||
|
self.entries.remove(&id);
|
||||||
|
self.entries.insert(id, Entry {
|
||||||
|
trace: Vec::new(),
|
||||||
|
padding_len: 0,
|
||||||
|
complete: false });
|
||||||
|
self.entries.get_mut(&id).unwrap()
|
||||||
|
} else {
|
||||||
|
entry
|
||||||
|
}
|
||||||
|
},
|
||||||
None => {
|
None => {
|
||||||
self.entries.insert(id, Entry {
|
self.entries.insert(id, Entry {
|
||||||
trace: Vec::new(),
|
trace: Vec::new(),
|
||||||
|
@ -80,6 +92,7 @@ impl Manager {
|
||||||
}
|
}
|
||||||
entry.complete = true;
|
entry.complete = true;
|
||||||
entry.padding_len = padding;
|
entry.padding_len = padding;
|
||||||
|
flush_l2_cache();
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -301,19 +301,22 @@ fn process_aux_packet(_manager: &mut DmaManager, _repeaters: &mut [repeater::Rep
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(has_rtio_dma)]
|
#[cfg(has_rtio_dma)]
|
||||||
drtioaux::Packet::DmaAddTraceRequest { id, last, length, trace } => {
|
drtioaux::Packet::DmaAddTraceRequest { destination: _destination, id, last, length, trace } => {
|
||||||
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
let succeeded = _manager.add(id, last, &trace, length as usize).is_ok();
|
let succeeded = _manager.add(id, last, &trace, length as usize).is_ok();
|
||||||
drtioaux::send(0,
|
drtioaux::send(0,
|
||||||
&drtioaux::Packet::DmaAddTraceReply { succeeded: succeeded })
|
&drtioaux::Packet::DmaAddTraceReply { succeeded: succeeded })
|
||||||
}
|
}
|
||||||
#[cfg(has_rtio_dma)]
|
#[cfg(has_rtio_dma)]
|
||||||
drtioaux::Packet::DmaRemoveTraceRequest { id } => {
|
drtioaux::Packet::DmaRemoveTraceRequest { destination: _destination, id } => {
|
||||||
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
let succeeded = _manager.erase(id).is_ok();
|
let succeeded = _manager.erase(id).is_ok();
|
||||||
drtioaux::send(0,
|
drtioaux::send(0,
|
||||||
&drtioaux::Packet::DmaRemoveTraceReply { succeeded: succeeded })
|
&drtioaux::Packet::DmaRemoveTraceReply { succeeded: succeeded })
|
||||||
}
|
}
|
||||||
#[cfg(has_rtio_dma)]
|
#[cfg(has_rtio_dma)]
|
||||||
drtioaux::Packet::DmaPlaybackRequest { id, timestamp } => {
|
drtioaux::Packet::DmaPlaybackRequest { destination: _destination, id, timestamp } => {
|
||||||
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
let succeeded = _manager.playback(id, timestamp).is_ok();
|
let succeeded = _manager.playback(id, timestamp).is_ok();
|
||||||
drtioaux::send(0,
|
drtioaux::send(0,
|
||||||
&drtioaux::Packet::DmaPlaybackReply { succeeded: succeeded })
|
&drtioaux::Packet::DmaPlaybackReply { succeeded: succeeded })
|
||||||
|
@ -463,7 +466,8 @@ pub extern fn main() -> i32 {
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
ALLOC.add_range(&mut _fheap, &mut _eheap);
|
ALLOC.add_range(&mut _fheap, &mut _eheap);
|
||||||
pmp::init_stack_guard(&_sstack_guard as *const u8 as usize);
|
// stack guard disabled, see https://github.com/m-labs/artiq/issues/2067
|
||||||
|
// pmp::init_stack_guard(&_sstack_guard as *const u8 as usize);
|
||||||
}
|
}
|
||||||
|
|
||||||
clock::init();
|
clock::init();
|
||||||
|
@ -571,8 +575,9 @@ pub extern fn main() -> i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(status) = dma_manager.check_state() {
|
if let Some(status) = dma_manager.check_state() {
|
||||||
|
info!("playback done, error: {}, channel: {}, timestamp: {}", status.error, status.channel, status.timestamp);
|
||||||
if let Err(e) = drtioaux::send(0, &drtioaux::Packet::DmaPlaybackStatus {
|
if let Err(e) = drtioaux::send(0, &drtioaux::Packet::DmaPlaybackStatus {
|
||||||
id: status.id, error: status.error, channel: status.channel, timestamp: status.timestamp }) {
|
destination: rank, id: status.id, error: status.error, channel: status.channel, timestamp: status.timestamp }) {
|
||||||
error!("error sending DMA playback status: {}", e);
|
error!("error sending DMA playback status: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue