1
0
Fork 0

subkernel: add support for (d)dma

This commit is contained in:
mwojcik 2024-01-10 12:13:00 +08:00 committed by sb10q
parent 8f4e30dd9c
commit 30e6bf4a3a
8 changed files with 697 additions and 91 deletions

View File

@ -207,7 +207,9 @@ pub enum Packet {
trace: [u8; MASTER_PAYLOAD_MAX_SIZE], trace: [u8; MASTER_PAYLOAD_MAX_SIZE],
}, },
DmaAddTraceReply { DmaAddTraceReply {
source: u8,
destination: u8, destination: u8,
id: u32,
succeeded: bool, succeeded: bool,
}, },
DmaRemoveTraceRequest { DmaRemoveTraceRequest {
@ -456,7 +458,9 @@ impl Packet {
} }
} }
0xb1 => Packet::DmaAddTraceReply { 0xb1 => Packet::DmaAddTraceReply {
source: reader.read_u8()?,
destination: reader.read_u8()?, destination: reader.read_u8()?,
id: reader.read_u32()?,
succeeded: reader.read_bool()?, succeeded: reader.read_bool()?,
}, },
0xb2 => Packet::DmaRemoveTraceRequest { 0xb2 => Packet::DmaRemoveTraceRequest {
@ -788,9 +792,16 @@ impl Packet {
writer.write_u16(length)?; writer.write_u16(length)?;
writer.write_all(&trace[0..length as usize])?; writer.write_all(&trace[0..length as usize])?;
} }
Packet::DmaAddTraceReply { destination, succeeded } => { Packet::DmaAddTraceReply {
source,
destination,
id,
succeeded,
} => {
writer.write_u8(0xb1)?; writer.write_u8(0xb1)?;
writer.write_u8(source)?;
writer.write_u8(destination)?; writer.write_u8(destination)?;
writer.write_u32(id)?;
writer.write_bool(succeeded)?; writer.write_bool(succeeded)?;
} }
Packet::DmaRemoveTraceRequest { Packet::DmaRemoveTraceRequest {

View File

@ -142,9 +142,9 @@ pub mod remote_dma {
} }
} }
pub async fn playback_done(&mut self, destination: u8, error: u8, channel: u32, timestamp: u64) { pub async fn playback_done(&mut self, source: u8, error: u8, channel: u32, timestamp: u64) {
let mut traces_locked = self.traces.async_lock().await; let mut traces_locked = self.traces.async_lock().await;
let mut trace = traces_locked.get_mut(&destination).unwrap(); let mut trace = traces_locked.get_mut(&source).unwrap();
trace.state = RemoteState::PlaybackEnded { trace.state = RemoteState::PlaybackEnded {
error: error, error: error,
channel: channel, channel: channel,

View File

@ -560,10 +560,12 @@ pub mod drtio {
Packet::DmaAddTraceReply { Packet::DmaAddTraceReply {
destination: 0, destination: 0,
succeeded: true, succeeded: true,
..
} => Ok(()), } => Ok(()),
Packet::DmaAddTraceReply { Packet::DmaAddTraceReply {
destination: 0, destination: 0,
succeeded: false, succeeded: false,
..
} => Err("error adding trace on satellite"), } => Err("error adding trace on satellite"),
_ => Err("adding DMA trace failed, unexpected aux packet"), _ => Err("adding DMA trace failed, unexpected aux packet"),
}, },

View File

@ -1,7 +1,13 @@
use alloc::{collections::btree_map::BTreeMap, vec::Vec}; use alloc::{collections::btree_map::BTreeMap, string::String, vec::Vec};
use core::mem;
use libboard_artiq::{drtioaux_proto::PayloadStatus, pl::csr}; use ksupport::kernel::DmaRecorder;
use libboard_artiq::{drtio_routing::RoutingTable,
drtioaux_proto::{Packet, PayloadStatus, MASTER_PAYLOAD_MAX_SIZE},
pl::csr};
use libcortex_a9::cache::dcci_slice; use libcortex_a9::cache::dcci_slice;
use routing::{Router, Sliceable};
use subkernel::Manager as KernelManager;
const ALIGNMENT: usize = 64; const ALIGNMENT: usize = 64;
@ -19,10 +25,13 @@ pub struct RtioStatus {
pub timestamp: u64, pub timestamp: u64,
} }
#[derive(Debug)]
pub enum Error { pub enum Error {
IdNotFound, IdNotFound,
PlaybackInProgress, PlaybackInProgress,
EntryNotComplete, EntryNotComplete,
MasterDmaFound,
UploadFail,
} }
#[derive(Debug)] #[derive(Debug)]
@ -30,6 +39,217 @@ struct Entry {
trace: Vec<u8>, trace: Vec<u8>,
padding_len: usize, padding_len: usize,
complete: bool, complete: bool,
duration: i64, // relevant for local DMA
}
impl Entry {
pub fn from_vec(data: Vec<u8>, duration: i64) -> Entry {
let mut entry = Entry {
trace: data,
padding_len: 0,
complete: true,
duration: duration,
};
entry.realign();
entry
}
pub fn id(&self) -> u32 {
self.trace[self.padding_len..].as_ptr() as u32
}
pub fn realign(&mut self) {
self.trace.push(0);
let data_len = self.trace.len();
self.trace.reserve(ALIGNMENT - 1);
let padding = ALIGNMENT - self.trace.as_ptr() as usize % ALIGNMENT;
let padding = if padding == ALIGNMENT { 0 } else { padding };
for _ in 0..padding {
// Vec guarantees that this will not reallocate
self.trace.push(0)
}
for i in 1..data_len + 1 {
self.trace[data_len + padding - i] = self.trace[data_len - i]
}
self.complete = true;
self.padding_len = padding;
dcci_slice(&self.trace);
}
}
#[derive(Debug)]
enum RemoteTraceState {
Unsent,
Sending(usize),
Ready,
Running(usize),
}
#[derive(Debug)]
struct RemoteTraces {
remote_traces: BTreeMap<u8, Sliceable>,
state: RemoteTraceState,
}
impl RemoteTraces {
pub fn new(traces: BTreeMap<u8, Sliceable>) -> RemoteTraces {
RemoteTraces {
remote_traces: traces,
state: RemoteTraceState::Unsent,
}
}
// on subkernel request
pub fn upload_traces(
&mut self,
id: u32,
router: &mut Router,
rank: u8,
self_destination: u8,
routing_table: &RoutingTable,
) -> usize {
let len = self.remote_traces.len();
if len > 0 {
self.state = RemoteTraceState::Sending(self.remote_traces.len());
for (dest, trace) in self.remote_traces.iter_mut() {
// queue up the first packet for all destinations, rest will be sent after first ACK
let mut data_slice: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
let meta = trace.get_slice_master(&mut data_slice);
router.route(
Packet::DmaAddTraceRequest {
source: self_destination,
destination: *dest,
id: id,
status: meta.status,
length: meta.len,
trace: data_slice,
},
routing_table,
rank,
self_destination,
);
}
}
len
}
// on incoming Packet::DmaAddTraceReply
pub fn ack_upload(
&mut self,
kernel_manager: &mut KernelManager,
source: u8,
id: u32,
succeeded: bool,
router: &mut Router,
rank: u8,
self_destination: u8,
routing_table: &RoutingTable,
) {
if let RemoteTraceState::Sending(count) = self.state {
if let Some(trace) = self.remote_traces.get_mut(&source) {
if trace.at_end() {
if count - 1 == 0 {
self.state = RemoteTraceState::Ready;
if let Some((id, timestamp)) = kernel_manager.ddma_remote_uploaded(succeeded) {
self.playback(id, timestamp, router, rank, self_destination, routing_table);
}
} else {
self.state = RemoteTraceState::Sending(count - 1);
}
} else {
// send next slice
let mut data_slice: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
let meta = trace.get_slice_master(&mut data_slice);
router.route(
Packet::DmaAddTraceRequest {
source: self_destination,
destination: meta.destination,
id: id,
status: meta.status,
length: meta.len,
trace: data_slice,
},
routing_table,
rank,
self_destination,
);
}
}
}
}
// on subkernel request
pub fn playback(
&mut self,
id: u32,
timestamp: u64,
router: &mut Router,
rank: u8,
self_destination: u8,
routing_table: &RoutingTable,
) {
// route all the playback requests
// remote traces (local trace runs on core1 unlike mainline firmware)
self.state = RemoteTraceState::Running(self.remote_traces.len());
for (dest, _) in self.remote_traces.iter() {
router.route(
Packet::DmaPlaybackRequest {
source: self_destination,
destination: *dest,
id: id,
timestamp: timestamp,
},
routing_table,
rank,
self_destination,
);
// response will be ignored (succeeded = false handled by the main thread)
}
}
// on incoming Packet::DmaPlaybackDone
pub fn remote_finished(&mut self, kernel_manager: &mut KernelManager, error: u8, channel: u32, timestamp: u64) {
if let RemoteTraceState::Running(count) = self.state {
if error != 0 || count - 1 == 0 {
// notify the kernel about a DDMA error or finish
kernel_manager.ddma_finished(error, channel, timestamp);
self.state = RemoteTraceState::Ready;
// further messages will be ignored (if there was an error)
} else {
// no error and not the last one awaited
self.state = RemoteTraceState::Running(count - 1);
}
}
}
pub fn erase(
&mut self,
id: u32,
router: &mut Router,
rank: u8,
self_destination: u8,
routing_table: &RoutingTable,
) {
for (dest, _) in self.remote_traces.iter() {
router.route(
Packet::DmaRemoveTraceRequest {
source: self_destination,
destination: *dest,
id: id,
},
routing_table,
rank,
self_destination,
);
// response will be ignored as this object will stop existing too
}
}
pub fn has_remote_traces(&self) -> bool {
self.remote_traces.len() > 0
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -38,6 +258,9 @@ pub struct Manager {
state: ManagerState, state: ManagerState,
current_id: u32, current_id: u32,
current_source: u8, current_source: u8,
remote_entries: BTreeMap<u32, RemoteTraces>,
name_map: BTreeMap<String, u32>,
} }
impl Manager { impl Manager {
@ -50,6 +273,8 @@ impl Manager {
current_id: 0, current_id: 0,
current_source: 0, current_source: 0,
state: ManagerState::Idle, state: ManagerState::Idle,
remote_entries: BTreeMap::new(),
name_map: BTreeMap::new(),
} }
} }
@ -72,6 +297,7 @@ impl Manager {
trace: Vec::new(), trace: Vec::new(),
padding_len: 0, padding_len: 0,
complete: false, complete: false,
duration: 0,
}, },
); );
self.entries.get_mut(&(source, id)).unwrap() self.entries.get_mut(&(source, id)).unwrap()
@ -86,6 +312,7 @@ impl Manager {
trace: Vec::new(), trace: Vec::new(),
padding_len: 0, padding_len: 0,
complete: false, complete: false,
duration: 0,
}, },
); );
self.entries.get_mut(&(source, id)).unwrap() self.entries.get_mut(&(source, id)).unwrap()
@ -94,27 +321,12 @@ impl Manager {
entry.trace.extend(&trace[0..trace_len]); entry.trace.extend(&trace[0..trace_len]);
if status.is_last() { if status.is_last() {
entry.trace.push(0); entry.realign();
let data_len = entry.trace.len();
// Realign.
entry.trace.reserve(ALIGNMENT - 1);
let padding = ALIGNMENT - entry.trace.as_ptr() as usize % ALIGNMENT;
let padding = if padding == ALIGNMENT { 0 } else { padding };
for _ in 0..padding {
// Vec guarantees that this will not reallocate
entry.trace.push(0)
}
for i in 1..data_len + 1 {
entry.trace[data_len + padding - i] = entry.trace[data_len - i]
}
entry.complete = true;
entry.padding_len = padding;
dcci_slice(&entry.trace);
} }
Ok(()) Ok(())
} }
// api for DRTIO
pub fn erase(&mut self, source: u8, id: u32) -> Result<(), Error> { pub fn erase(&mut self, source: u8, id: u32) -> Result<(), Error> {
match self.entries.remove(&(source, id)) { match self.entries.remove(&(source, id)) {
Some(_) => Ok(()), Some(_) => Ok(()),
@ -122,6 +334,168 @@ impl Manager {
} }
} }
// API for subkernel
pub fn erase_name(
&mut self,
name: &str,
router: &mut Router,
rank: u8,
self_destination: u8,
routing_table: &RoutingTable,
) {
if let Some(id) = self.name_map.get(name) {
if let Some(traces) = self.remote_entries.get_mut(&id) {
traces.erase(*id, router, rank, self_destination, routing_table);
self.remote_entries.remove(&id);
}
self.entries.remove(&(self_destination, *id));
self.name_map.remove(name);
}
}
pub fn remote_finished(
&mut self,
kernel_manager: &mut KernelManager,
id: u32,
error: u8,
channel: u32,
timestamp: u64,
) {
if let Some(entry) = self.remote_entries.get_mut(&id) {
entry.remote_finished(kernel_manager, error, channel, timestamp);
}
}
pub fn ack_upload(
&mut self,
kernel_manager: &mut KernelManager,
source: u8,
id: u32,
succeeded: bool,
router: &mut Router,
rank: u8,
self_destination: u8,
routing_table: &RoutingTable,
) {
if let Some(entry) = self.remote_entries.get_mut(&id) {
entry.ack_upload(
kernel_manager,
source,
id,
succeeded,
router,
rank,
self_destination,
routing_table,
);
}
}
// API for subkernel
pub fn upload_traces(
&mut self,
id: u32,
router: &mut Router,
rank: u8,
self_destination: u8,
routing_table: &RoutingTable,
) -> Result<usize, Error> {
let remote_traces = self.remote_entries.get_mut(&id);
let mut len = 0;
if let Some(traces) = remote_traces {
len = traces.upload_traces(id, router, rank, self_destination, routing_table);
}
Ok(len)
}
// API for subkernel
pub fn playback_remote(
&mut self,
id: u32,
timestamp: u64,
router: &mut Router,
rank: u8,
self_destination: u8,
routing_table: &RoutingTable,
) -> Result<(), Error> {
if let Some(traces) = self.remote_entries.get_mut(&id) {
traces.playback(id, timestamp, router, rank, self_destination, routing_table);
Ok(())
} else {
Err(Error::IdNotFound)
}
}
// API for subkernel
pub fn cleanup(&mut self, router: &mut Router, rank: u8, self_destination: u8, routing_table: &RoutingTable) {
// after subkernel ends, remove all self-generated traces
for (_, id) in self.name_map.iter_mut() {
if let Some(traces) = self.remote_entries.get_mut(&id) {
traces.erase(*id, router, rank, self_destination, routing_table);
self.remote_entries.remove(&id);
}
self.entries.remove(&(self_destination, *id));
}
self.name_map.clear();
}
// API for subkernel
pub fn retrieve(&self, self_destination: u8, name: &String) -> Option<(i32, i64, bool)> {
let id = self.name_map.get(name)?;
let duration = self.entries.get(&(self_destination, *id))?.duration;
let uses_ddma = self.has_remote_traces(*id);
Some((*id as i32, duration, uses_ddma))
}
pub fn has_remote_traces(&self, id: u32) -> bool {
match self.remote_entries.get(&id) {
Some(traces) => traces.has_remote_traces(),
_ => false,
}
}
pub fn put_record(&mut self, mut recorder: DmaRecorder, self_destination: u8) -> Result<u32, Error> {
let mut remote_traces: BTreeMap<u8, Sliceable> = BTreeMap::new();
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 {
return Err(Error::MasterDmaFound);
} else if destination == self_destination {
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,
Sliceable::new(destination, recorder.buffer[ptr..ptr + len].to_vec()),
);
}
}
// and jump to the next event
ptr += len;
}
let local_entry = Entry::from_vec(local_trace, recorder.duration);
let id = local_entry.id();
self.entries.insert((self_destination, id), local_entry);
self.remote_entries.insert(id, RemoteTraces::new(remote_traces));
let mut name = String::new();
mem::swap(&mut recorder.name, &mut name);
self.name_map.insert(name, id);
Ok(id)
}
pub fn playback(&mut self, source: u8, id: u32, timestamp: u64) -> Result<(), Error> { pub fn playback(&mut self, source: u8, id: u32, timestamp: u64) -> Result<(), Error> {
if self.state != ManagerState::Idle { if self.state != ManagerState::Idle {
return Err(Error::PlaybackInProgress); return Err(Error::PlaybackInProgress);

View File

@ -483,7 +483,9 @@ fn process_aux_packet(
let succeeded = dma_manager.add(source, id, status, &trace, length as usize).is_ok(); let succeeded = dma_manager.add(source, id, status, &trace, length as usize).is_ok();
router.send( router.send(
drtioaux::Packet::DmaAddTraceReply { drtioaux::Packet::DmaAddTraceReply {
source: *self_destination,
destination: source, destination: source,
id: id,
succeeded: succeeded, succeeded: succeeded,
}, },
_routing_table, _routing_table,
@ -491,6 +493,25 @@ fn process_aux_packet(
*self_destination, *self_destination,
) )
} }
drtioaux::Packet::DmaAddTraceReply {
source,
destination: _destination,
id,
succeeded,
} => {
forward!(_routing_table, _destination, *rank, _repeaters, &packet, timer);
dma_manager.ack_upload(
kernel_manager,
source,
id,
succeeded,
router,
*rank,
*self_destination,
_routing_table,
);
Ok(())
}
drtioaux::Packet::DmaRemoveTraceRequest { drtioaux::Packet::DmaRemoveTraceRequest {
source, source,
destination: _destination, destination: _destination,
@ -508,6 +529,13 @@ fn process_aux_packet(
*self_destination, *self_destination,
) )
} }
drtioaux::Packet::DmaRemoveTraceReply {
destination: _destination,
succeeded: _,
} => {
forward!(_routing_table, _destination, *rank, _repeaters, &packet, timer);
Ok(())
}
drtioaux::Packet::DmaPlaybackRequest { drtioaux::Packet::DmaPlaybackRequest {
source, source,
destination: _destination, destination: _destination,
@ -530,6 +558,28 @@ fn process_aux_packet(
*self_destination, *self_destination,
) )
} }
drtioaux::Packet::DmaPlaybackReply {
destination: _destination,
succeeded,
} => {
forward!(_routing_table, _destination, *rank, _repeaters, &packet, timer);
if !succeeded {
kernel_manager.ddma_nack();
}
Ok(())
}
drtioaux::Packet::DmaPlaybackStatus {
source: _,
destination: _destination,
id,
error,
channel,
timestamp,
} => {
forward!(_routing_table, _destination, *rank, _repeaters, &packet, timer);
dma_manager.remote_finished(kernel_manager, id, error, channel, timestamp);
Ok(())
}
drtioaux::Packet::SubkernelAddDataRequest { drtioaux::Packet::SubkernelAddDataRequest {
destination, destination,
@ -649,8 +699,8 @@ fn process_aux_packet(
Ok(()) Ok(())
} }
_ => { p => {
warn!("received unexpected aux packet"); warn!("received unexpected aux packet: {:?}", p);
Ok(()) Ok(())
} }
} }
@ -949,8 +999,16 @@ pub extern "C" fn main_core0() -> i32 {
); );
} }
kernel_manager.process_kern_requests(&mut router, &routing_table, rank, destination, &timer); kernel_manager.process_kern_requests(
&mut router,
&routing_table,
rank,
destination,
&mut dma_manager,
&timer,
);
#[cfg(has_drtio_routing)]
if let Some((repno, packet)) = router.get_downstream_packet() { if let Some((repno, packet)) = router.get_downstream_packet() {
if let Err(e) = repeaters[repno].aux_send(&packet) { if let Err(e) = repeaters[repno].aux_send(&packet) {
warn!("[REP#{}] Error when sending packet to satellite ({:?})", repno, e) warn!("[REP#{}] Error when sending packet to satellite ({:?})", repno, e)

View File

@ -347,7 +347,15 @@ impl Repeater {
Repeater::default() Repeater::default()
} }
pub fn service(&self, _routing_table: &drtio_routing::RoutingTable, _rank: u8, _timer: &mut GlobalTimer) {} pub fn service(
&self,
_routing_table: &drtio_routing::RoutingTable,
_rank: u8,
_destination: u8,
_router: &mut Router,
_timer: &mut GlobalTimer,
) {
}
pub fn sync_tsc(&self, _timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> { pub fn sync_tsc(&self, _timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> {
Ok(()) Ok(())

View File

@ -1,6 +1,65 @@
use alloc::collections::vec_deque::VecDeque; use alloc::{collections::vec_deque::VecDeque, vec::Vec};
use core::cmp::min;
use libboard_artiq::{drtio_routing, drtioaux, pl::csr}; #[cfg(has_drtio_routing)]
use libboard_artiq::pl::csr;
use libboard_artiq::{drtio_routing, drtioaux,
drtioaux_proto::{PayloadStatus, MASTER_PAYLOAD_MAX_SIZE, SAT_PAYLOAD_MAX_SIZE}};
pub struct SliceMeta {
pub destination: u8,
pub len: u16,
pub status: PayloadStatus,
}
/* represents data that has to be sent to Master */
#[derive(Debug)]
pub struct Sliceable {
it: usize,
data: Vec<u8>,
destination: u8,
}
macro_rules! get_slice_fn {
($name:tt, $size:expr) => {
pub fn $name(&mut self, data_slice: &mut [u8; $size]) -> SliceMeta {
let first = self.it == 0;
let len = min($size, self.data.len() - self.it);
let last = self.it + len == self.data.len();
let status = PayloadStatus::from_status(first, last);
data_slice[..len].clone_from_slice(&self.data[self.it..self.it + len]);
self.it += len;
SliceMeta {
destination: self.destination,
len: len as u16,
status: status,
}
}
};
}
impl Sliceable {
pub fn new(destination: u8, data: Vec<u8>) -> Sliceable {
Sliceable {
it: 0,
data: data,
destination: destination,
}
}
pub fn at_end(&self) -> bool {
self.it == self.data.len()
}
pub fn extend(&mut self, data: &[u8]) {
self.data.extend(data);
}
get_slice_fn!(get_slice_sat, SAT_PAYLOAD_MAX_SIZE);
get_slice_fn!(get_slice_master, MASTER_PAYLOAD_MAX_SIZE);
}
// Packets from downstream (further satellites) are received and routed appropriately. // Packets from downstream (further satellites) are received and routed appropriately.
// they're passed as soon as possible downstream (within the subtree), or sent upstream, // they're passed as soon as possible downstream (within the subtree), or sent upstream,
@ -14,6 +73,7 @@ use libboard_artiq::{drtio_routing, drtioaux, pl::csr};
pub struct Router { pub struct Router {
upstream_queue: VecDeque<drtioaux::Packet>, upstream_queue: VecDeque<drtioaux::Packet>,
local_queue: VecDeque<drtioaux::Packet>, local_queue: VecDeque<drtioaux::Packet>,
#[cfg(has_drtio_routing)]
downstream_queue: VecDeque<(usize, drtioaux::Packet)>, downstream_queue: VecDeque<(usize, drtioaux::Packet)>,
upstream_notified: bool, upstream_notified: bool,
} }
@ -23,26 +83,27 @@ impl Router {
Router { Router {
upstream_queue: VecDeque::new(), upstream_queue: VecDeque::new(),
local_queue: VecDeque::new(), local_queue: VecDeque::new(),
#[cfg(has_drtio_routing)]
downstream_queue: VecDeque::new(), downstream_queue: VecDeque::new(),
upstream_notified: false, upstream_notified: false,
} }
} }
// called by local sources (DDMA, kernel) and by repeaters on receiving async data // Called by local sources (DDMA, kernel) and by repeaters on receiving async data;
// messages are always buffered for both upstream and downstream // messages are always buffered for both upstream and downstream
pub fn route( pub fn route(
&mut self, &mut self,
packet: drtioaux::Packet, packet: drtioaux::Packet,
_routing_table: &drtio_routing::RoutingTable, _routing_table: &drtio_routing::RoutingTable,
_rank: u8, _rank: u8,
_self_destination: u8, self_destination: u8,
) { ) {
let destination = packet.routable_destination();
#[cfg(has_drtio_routing)] #[cfg(has_drtio_routing)]
{ {
let destination = packet.routable_destination();
if let Some(destination) = destination { if let Some(destination) = destination {
let hop = _routing_table.0[destination as usize][_rank as usize] as usize; let hop = _routing_table.0[destination as usize][_rank as usize] as usize;
if destination == _self_destination { if destination == self_destination {
self.local_queue.push_back(packet); self.local_queue.push_back(packet);
} else if hop > 0 && hop < csr::DRTIOREP.len() { } else if hop > 0 && hop < csr::DRTIOREP.len() {
let repno = (hop - 1) as usize; let repno = (hop - 1) as usize;
@ -56,11 +117,15 @@ impl Router {
} }
#[cfg(not(has_drtio_routing))] #[cfg(not(has_drtio_routing))]
{ {
if destination == Some(self_destination) {
self.local_queue.push_back(packet);
} else {
self.upstream_queue.push_back(packet); self.upstream_queue.push_back(packet);
} }
} }
}
// Sends a packet to a required destination, routing if it's necessary // Sends a packet to a required destination, routing if necessary
pub fn send( pub fn send(
&mut self, &mut self,
packet: drtioaux::Packet, packet: drtioaux::Packet,
@ -114,6 +179,7 @@ impl Router {
packet packet
} }
#[cfg(has_drtio_routing)]
pub fn get_downstream_packet(&mut self) -> Option<(usize, drtioaux::Packet)> { pub fn get_downstream_packet(&mut self) -> Option<(usize, drtioaux::Packet)> {
self.downstream_queue.pop_front() self.downstream_queue.pop_front()
} }

View File

@ -2,10 +2,11 @@ use alloc::{collections::{BTreeMap, VecDeque},
format, format,
string::{String, ToString}, string::{String, ToString},
vec::Vec}; vec::Vec};
use core::{cmp::min, option::NoneError, slice, str}; use core::{option::NoneError, slice, str};
use core_io::{Error as IoError, Write}; use core_io::{Error as IoError, Write};
use cslice::AsCSlice; use cslice::AsCSlice;
use dma::{Error as DmaError, Manager as DmaManager};
use io::{Cursor, ProtoWrite}; use io::{Cursor, ProtoWrite};
use ksupport::{eh_artiq, kernel, rpc}; use ksupport::{eh_artiq, kernel, rpc};
use libboard_artiq::{drtio_routing::RoutingTable, use libboard_artiq::{drtio_routing::RoutingTable,
@ -15,7 +16,7 @@ use libboard_artiq::{drtio_routing::RoutingTable,
use libboard_zynq::{time::Milliseconds, timer::GlobalTimer}; use libboard_zynq::{time::Milliseconds, timer::GlobalTimer};
use libcortex_a9::sync_channel::Receiver; use libcortex_a9::sync_channel::Receiver;
use log::warn; use log::warn;
use routing::Router; use routing::{Router, SliceMeta, Sliceable};
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
enum KernelState { enum KernelState {
@ -25,7 +26,23 @@ enum KernelState {
MsgAwait(Milliseconds, Vec<u8>), MsgAwait(Milliseconds, Vec<u8>),
MsgSending, MsgSending,
SubkernelAwaitLoad, SubkernelAwaitLoad,
SubkernelAwaitFinish { max_time: Milliseconds, id: u32 }, SubkernelAwaitFinish {
max_time: Milliseconds,
id: u32,
},
DmaUploading,
DmaPendingPlayback {
id: u32,
timestamp: u64,
},
DmaPendingAwait {
id: u32,
timestamp: u64,
max_time: Milliseconds,
},
DmaAwait {
max_time: Milliseconds,
},
} }
#[derive(Debug)] #[derive(Debug)]
@ -38,6 +55,7 @@ pub enum Error {
SubkernelIoError, SubkernelIoError,
DrtioError, DrtioError,
KernelException(Sliceable), KernelException(Sliceable),
DmaError(DmaError),
} }
impl From<NoneError> for Error { impl From<NoneError> for Error {
@ -52,6 +70,12 @@ impl From<IoError> for Error {
} }
} }
impl From<DmaError> for Error {
fn from(value: DmaError) -> Error {
Error::DmaError(value)
}
}
impl From<()> for Error { impl From<()> for Error {
fn from(_: ()) -> Error { fn from(_: ()) -> Error {
Error::NoMessage Error::NoMessage
@ -68,14 +92,6 @@ macro_rules! unexpected {
($($arg:tt)*) => (return Err(Error::Unexpected(format!($($arg)*)))); ($($arg:tt)*) => (return Err(Error::Unexpected(format!($($arg)*))));
} }
/* represents data that has to be sent to Master */
#[derive(Debug)]
pub struct Sliceable {
it: usize,
data: Vec<u8>,
destination: u8,
}
/* represents interkernel messages */ /* represents interkernel messages */
struct Message { struct Message {
count: u8, count: u8,
@ -123,11 +139,7 @@ impl Session {
fn running(&self) -> bool { fn running(&self) -> bool {
match self.kernel_state { match self.kernel_state {
KernelState::Absent | KernelState::Loaded => false, KernelState::Absent | KernelState::Loaded => false,
KernelState::Running _ => true,
| KernelState::MsgAwait { .. }
| KernelState::MsgSending
| KernelState::SubkernelAwaitLoad
| KernelState::SubkernelAwaitFinish { .. } => true,
} }
} }
} }
@ -153,45 +165,6 @@ pub struct SubkernelFinished {
pub source: u8, pub source: u8,
} }
pub struct SliceMeta {
pub destination: u8,
pub len: u16,
pub status: PayloadStatus,
}
macro_rules! get_slice_fn {
($name:tt, $size:expr) => {
pub fn $name(&mut self, data_slice: &mut [u8; $size]) -> SliceMeta {
let first = self.it == 0;
let len = min($size, self.data.len() - self.it);
let last = self.it + len == self.data.len();
let status = PayloadStatus::from_status(first, last);
data_slice[..len].clone_from_slice(&self.data[self.it..self.it + len]);
self.it += len;
SliceMeta {
destination: self.destination,
len: len as u16,
status: status,
}
}
};
}
impl Sliceable {
pub fn new(destination: u8, data: Vec<u8>) -> Sliceable {
Sliceable {
it: 0,
data: data,
destination: destination,
}
}
get_slice_fn!(get_slice_sat, SAT_PAYLOAD_MAX_SIZE);
get_slice_fn!(get_slice_master, MASTER_PAYLOAD_MAX_SIZE);
}
impl MessageManager { impl MessageManager {
pub fn new() -> MessageManager { pub fn new() -> MessageManager {
MessageManager { MessageManager {
@ -355,7 +328,6 @@ impl<'a> Manager<'_> {
} }
pub fn run(&mut self, source: u8, id: u32) -> Result<(), Error> { pub fn run(&mut self, source: u8, id: u32) -> Result<(), Error> {
info!("starting subkernel #{}", id);
if self.session.kernel_state != KernelState::Loaded || self.session.id != id { if self.session.kernel_state != KernelState::Loaded || self.session.id != id {
self.load(id)?; self.load(id)?;
} }
@ -466,12 +438,66 @@ impl<'a> Manager<'_> {
self.kernel_stop(); self.kernel_stop();
} }
pub fn ddma_finished(&mut self, error: u8, channel: u32, timestamp: u64) {
if let KernelState::DmaAwait { .. } = self.session.kernel_state {
self.control.tx.send(kernel::Message::DmaAwaitRemoteReply {
timeout: false,
error: error,
channel: channel,
timestamp: timestamp,
});
self.session.kernel_state = KernelState::Running;
}
}
pub fn ddma_nack(&mut self) {
// for simplicity treat it as a timeout...
if let KernelState::DmaAwait { .. } = self.session.kernel_state {
self.control.tx.send(kernel::Message::DmaAwaitRemoteReply {
timeout: true,
error: 0,
channel: 0,
timestamp: 0,
});
self.session.kernel_state = KernelState::Running;
}
}
pub fn ddma_remote_uploaded(&mut self, succeeded: bool) -> Option<(u32, u64)> {
// returns a tuple of id, timestamp in case a playback needs to be started immediately
if !succeeded {
self.kernel_stop();
self.runtime_exception(Error::DmaError(DmaError::UploadFail));
}
let res = match self.session.kernel_state {
KernelState::DmaPendingPlayback { id, timestamp } => {
self.session.kernel_state = KernelState::Running;
Some((id, timestamp))
}
KernelState::DmaPendingAwait {
id,
timestamp,
max_time,
} => {
self.session.kernel_state = KernelState::DmaAwait { max_time: max_time };
Some((id, timestamp))
}
KernelState::DmaUploading => {
self.session.kernel_state = KernelState::Running;
None
}
_ => None,
};
res
}
pub fn process_kern_requests( pub fn process_kern_requests(
&mut self, &mut self,
router: &mut Router, router: &mut Router,
routing_table: &RoutingTable, routing_table: &RoutingTable,
rank: u8, rank: u8,
destination: u8, destination: u8,
dma_manager: &mut DmaManager,
timer: &GlobalTimer, timer: &GlobalTimer,
) { ) {
if let Some(subkernel_finished) = self.last_finished.take() { if let Some(subkernel_finished) = self.last_finished.take() {
@ -520,7 +546,7 @@ impl<'a> Manager<'_> {
} }
} }
match self.process_kern_message(router, routing_table, rank, destination, timer) { match self.process_kern_message(router, routing_table, rank, destination, dma_manager, timer) {
Ok(true) => { Ok(true) => {
self.last_finished = Some(SubkernelFinished { self.last_finished = Some(SubkernelFinished {
id: self.session.id, id: self.session.id,
@ -583,12 +609,14 @@ impl<'a> Manager<'_> {
routing_table: &RoutingTable, routing_table: &RoutingTable,
rank: u8, rank: u8,
self_destination: u8, self_destination: u8,
dma_manager: &mut DmaManager,
timer: &GlobalTimer, timer: &GlobalTimer,
) -> Result<bool, Error> { ) -> Result<bool, Error> {
let reply = self.control.rx.try_recv()?; let reply = self.control.rx.try_recv()?;
match reply { match reply {
kernel::Message::KernelFinished(_async_errors) => { kernel::Message::KernelFinished(_async_errors) => {
self.kernel_stop(); self.kernel_stop();
dma_manager.cleanup(router, rank, self_destination, routing_table);
return Ok(true); return Ok(true);
} }
kernel::Message::KernelException(exceptions, stack_pointers, backtrace, async_errors) => { kernel::Message::KernelException(exceptions, stack_pointers, backtrace, async_errors) => {
@ -615,6 +643,53 @@ impl<'a> Manager<'_> {
let value = self.cache.get(&key).unwrap_or(&DEFAULT).clone(); let value = self.cache.get(&key).unwrap_or(&DEFAULT).clone();
self.control.tx.send(kernel::Message::CacheGetReply(value)); self.control.tx.send(kernel::Message::CacheGetReply(value));
} }
kernel::Message::DmaPutRequest(recorder) => {
// ddma is always used on satellites
if let Ok(id) = dma_manager.put_record(recorder, self_destination) {
dma_manager.upload_traces(id, router, rank, self_destination, routing_table)?;
self.session.kernel_state = KernelState::DmaUploading;
} else {
unexpected!("DMAError: found an unsupported call to RTIO devices on master")
}
}
kernel::Message::DmaEraseRequest(name) => {
dma_manager.erase_name(&name, router, rank, self_destination, routing_table);
}
kernel::Message::DmaGetRequest(name) => {
let dma_meta = dma_manager.retrieve(self_destination, &name);
self.control.tx.send(kernel::Message::DmaGetReply(dma_meta));
}
kernel::Message::DmaStartRemoteRequest { id, timestamp } => {
if self.session.kernel_state != KernelState::DmaUploading {
dma_manager.playback_remote(
id as u32,
timestamp as u64,
router,
rank,
self_destination,
routing_table,
)?;
} else {
self.session.kernel_state = KernelState::DmaPendingPlayback {
id: id as u32,
timestamp: timestamp as u64,
};
}
}
kernel::Message::DmaAwaitRemoteRequest(_id) => {
let max_time = timer.get_time() + Milliseconds(10000);
self.session.kernel_state = match self.session.kernel_state {
// if we are still waiting for the traces to be uploaded, extend the state by timeout
KernelState::DmaPendingPlayback { id, timestamp } => KernelState::DmaPendingAwait {
id: id,
timestamp: timestamp,
max_time: max_time,
},
_ => KernelState::DmaAwait { max_time: max_time },
};
}
kernel::Message::SubkernelMsgSend { kernel::Message::SubkernelMsgSend {
id: _id, id: _id,
destination: msg_dest, destination: msg_dest,
@ -728,6 +803,18 @@ impl<'a> Manager<'_> {
} }
Ok(()) Ok(())
} }
KernelState::DmaAwait { max_time } | KernelState::DmaPendingAwait { max_time, .. } => {
if timer.get_time() > *max_time {
self.control.tx.send(kernel::Message::DmaAwaitRemoteReply {
timeout: true,
error: 0,
channel: 0,
timestamp: 0,
});
self.session.kernel_state = KernelState::Running;
}
Ok(())
}
_ => Ok(()), _ => Ok(()),
} }
} }