use alloc::{collections::vec_deque::VecDeque, vec::Vec}; use core::cmp::min; #[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, 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) -> 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. // they're passed as soon as possible downstream (within the subtree), or sent upstream, // which is notified about pending packets. // for rank 1 (connected to master) satellites, these packets are passed as an answer to DestinationStatusRequest; // for higher ranks, after getting a notification, it will transact with downstream to get the pending packets. // forward! macro is not deprecated, as routable packets are only these that can originate // from both master and satellite, e.g. DDMA and Subkernel. pub struct Router { upstream_queue: VecDeque, local_queue: VecDeque, #[cfg(has_drtio_routing)] downstream_queue: VecDeque<(usize, drtioaux::Packet)>, upstream_notified: bool, } impl Router { pub fn new() -> Router { Router { upstream_queue: VecDeque::new(), local_queue: VecDeque::new(), #[cfg(has_drtio_routing)] downstream_queue: VecDeque::new(), upstream_notified: false, } } // Called by local sources (DDMA, kernel) and by repeaters on receiving async data; // messages are always buffered for both upstream and downstream pub fn route( &mut self, packet: drtioaux::Packet, _routing_table: &drtio_routing::RoutingTable, _rank: u8, self_destination: u8, ) { let destination = packet.routable_destination(); #[cfg(has_drtio_routing)] { if let Some(destination) = destination { let hop = _routing_table.0[destination as usize][_rank as usize] as usize; if destination == self_destination { self.local_queue.push_back(packet); } else if hop > 0 && hop < csr::DRTIOREP.len() { let repno = (hop - 1) as usize; self.downstream_queue.push_back((repno, packet)); } else { self.upstream_queue.push_back(packet); } } else { error!("Received an unroutable packet: {:?}", packet); } } #[cfg(not(has_drtio_routing))] { if destination == Some(self_destination) { self.local_queue.push_back(packet); } else { self.upstream_queue.push_back(packet); } } } // Sends a packet to a required destination, routing if necessary pub fn send( &mut self, packet: drtioaux::Packet, _routing_table: &drtio_routing::RoutingTable, _rank: u8, _destination: u8, ) -> Result<(), drtioaux::Error> { #[cfg(has_drtio_routing)] { let destination = packet.routable_destination(); if let Some(destination) = destination { let hop = _routing_table.0[destination as usize][_rank as usize] as usize; if destination == 0 { // response is needed immediately if master required it drtioaux::send(0, &packet)?; } else if !(hop > 0 && hop < csr::DRTIOREP.len()) { // higher rank can wait self.upstream_queue.push_back(packet); } else { let repno = (hop - 1) as usize; // transaction will occur at closest possible opportunity self.downstream_queue.push_back((repno, packet)); } Ok(()) } else { // packet not supported in routing, fallback - sent directly drtioaux::send(0, &packet) } } #[cfg(not(has_drtio_routing))] { drtioaux::send(0, &packet) } } pub fn any_upstream_waiting(&mut self) -> bool { let empty = self.upstream_queue.is_empty(); if !empty && !self.upstream_notified { self.upstream_notified = true; // so upstream will not get spammed with notifications true } else { false } } pub fn get_upstream_packet(&mut self) -> Option { let packet = self.upstream_queue.pop_front(); if packet.is_none() { self.upstream_notified = false; } packet } #[cfg(has_drtio_routing)] pub fn get_downstream_packet(&mut self) -> Option<(usize, drtioaux::Packet)> { self.downstream_queue.pop_front() } pub fn get_local_packet(&mut self) -> Option { self.local_queue.pop_front() } }