2024-01-10 12:13:00 +08:00
|
|
|
use alloc::{collections::vec_deque::VecDeque, vec::Vec};
|
|
|
|
use core::cmp::min;
|
2023-12-12 16:51:54 +08:00
|
|
|
|
2024-01-10 12:13:00 +08:00
|
|
|
use libboard_artiq::{drtio_routing, drtioaux,
|
|
|
|
drtioaux_proto::{PayloadStatus, MASTER_PAYLOAD_MAX_SIZE, SAT_PAYLOAD_MAX_SIZE},
|
|
|
|
pl::csr};
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2023-12-12 16:51:54 +08:00
|
|
|
|
|
|
|
// 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<drtioaux::Packet>,
|
|
|
|
local_queue: VecDeque<drtioaux::Packet>,
|
|
|
|
downstream_queue: VecDeque<(usize, drtioaux::Packet)>,
|
|
|
|
upstream_notified: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Router {
|
|
|
|
pub fn new() -> Router {
|
|
|
|
Router {
|
|
|
|
upstream_queue: VecDeque::new(),
|
|
|
|
local_queue: VecDeque::new(),
|
|
|
|
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,
|
|
|
|
) {
|
|
|
|
#[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 == _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))]
|
|
|
|
{
|
|
|
|
self.upstream_queue.push_back(packet);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sends a packet to a required destination, routing if it's 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<drtioaux::Packet> {
|
|
|
|
let packet = self.upstream_queue.pop_front();
|
|
|
|
if packet.is_none() {
|
|
|
|
self.upstream_notified = false;
|
|
|
|
}
|
|
|
|
packet
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_downstream_packet(&mut self) -> Option<(usize, drtioaux::Packet)> {
|
|
|
|
self.downstream_queue.pop_front()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_local_packet(&mut self) -> Option<drtioaux::Packet> {
|
|
|
|
self.local_queue.pop_front()
|
|
|
|
}
|
|
|
|
}
|