use std::collections::HashSet;
use luwen_core::Arch;
use crate::{
chip::{wait_for_init, Chip, InitError, InitStatus},
error::{BtWrapper, PlatformError},
ChipImpl, EthAddr,
};
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
enum InterfaceIdOrCoord {
Id(u32),
Coord(EthAddr),
}
pub enum UninitChip {
Partially {
status: InitStatus,
underlying: Chip,
},
Initialized(Chip),
}
fn clone_chip(chip: &Chip) -> Chip {
if let Some(wh) = chip.as_wh() {
Chip::from(Box::new(wh.clone()) as Box<dyn ChipImpl>)
} else if let Some(gs) = chip.as_gs() {
Chip::from(Box::new(gs.clone()) as Box<dyn ChipImpl>)
} else {
unimplemented!(
"Don't have a clone handler for chip with arch {:?}.",
chip.get_arch()
)
}
}
impl Clone for UninitChip {
fn clone(&self) -> Self {
match self {
Self::Partially { status, underlying } => Self::Partially {
status: status.clone(),
underlying: clone_chip(underlying),
},
Self::Initialized(chip) => Self::Initialized(clone_chip(chip)),
}
}
}
impl UninitChip {
pub fn new(status: InitStatus, chip: &Chip) -> Self {
let chip = clone_chip(chip);
if status.init_complete() && !status.has_error() {
UninitChip::Initialized(chip)
} else {
UninitChip::Partially {
status,
underlying: chip,
}
}
}
pub fn status(&self) -> Option<&InitStatus> {
match self {
UninitChip::Partially { status, .. } => Some(status),
UninitChip::Initialized(_) => None,
}
}
pub fn init<E>(
self,
init_callback: &mut impl FnMut(crate::chip::ChipDetectState) -> Result<(), E>,
) -> Result<Chip, InitError<E>> {
match self {
UninitChip::Partially { mut underlying, .. } => {
wait_for_init(&mut underlying, init_callback, false, false)?;
Ok(underlying)
}
UninitChip::Initialized(chip) => Ok(chip),
}
}
pub fn upgrade(self) -> Chip {
match self {
UninitChip::Partially { underlying, .. } => underlying,
UninitChip::Initialized(chip) => chip,
}
}
pub fn try_upgrade(&self) -> Option<&Chip> {
match self {
UninitChip::Partially { status, underlying } => {
if status.init_complete() && !status.has_error() {
Some(underlying)
} else {
None
}
}
UninitChip::Initialized(chip) => Some(chip),
}
}
pub fn is_initialized(&self) -> bool {
match self {
UninitChip::Partially { status, .. } => status.init_complete(),
UninitChip::Initialized(_) => true,
}
}
pub fn is_healthy(&self) -> Option<bool> {
match self {
UninitChip::Partially { status, .. } => {
if status.init_complete() {
Some(status.has_error())
} else {
None
}
}
UninitChip::Initialized(_) => Some(true),
}
}
pub fn arc_alive(&self) -> bool {
match self {
UninitChip::Partially { status, .. } => {
!status.arc_status.is_waiting() && !status.arc_status.has_error()
}
UninitChip::Initialized(_) => true,
}
}
pub fn dram_safe(&self) -> bool {
match self {
UninitChip::Partially { status, .. } => {
!status.dram_status.is_waiting() && !status.dram_status.has_error()
}
UninitChip::Initialized(_) => true,
}
}
pub fn eth_safe(&self) -> bool {
match self {
UninitChip::Partially { status, .. } => {
!status.eth_status.is_waiting() && !status.eth_status.has_error()
}
UninitChip::Initialized(_) => true,
}
}
pub fn cpu_safe(&self) -> bool {
match self {
UninitChip::Partially { status, .. } => {
!status.cpu_status.is_waiting() && !status.cpu_status.has_error()
}
UninitChip::Initialized(_) => true,
}
}
}
pub struct ChipDetectOptions {
pub continue_on_failure: bool,
pub local_only: bool,
pub chip_filter: Vec<Arch>,
pub noc_safe: bool,
}
impl Default for ChipDetectOptions {
fn default() -> Self {
Self {
continue_on_failure: true,
local_only: false,
chip_filter: Vec::new(),
noc_safe: false,
}
}
}
impl ChipDetectOptions {
pub fn new() -> Self {
Self::default()
}
pub fn continue_on_failure(mut self, continue_on_failure: bool) -> Self {
self.continue_on_failure = continue_on_failure;
self
}
pub fn local_only(mut self, local_only: bool) -> Self {
self.local_only = local_only;
self
}
pub fn noc_safe(mut self, noc_safe: bool) -> Self {
self.noc_safe = noc_safe;
self
}
}
pub fn detect_chips<E>(
mut root_chips: Vec<Chip>,
init_callback: &mut impl FnMut(crate::chip::ChipDetectState) -> Result<(), E>,
options: ChipDetectOptions,
) -> Result<Vec<UninitChip>, InitError<E>> {
let ChipDetectOptions {
continue_on_failure,
local_only,
chip_filter,
noc_safe,
} = options;
let mut remotes_to_investigate = Vec::new();
let mut seen_chips = HashSet::new();
let mut output = Vec::new();
for (root_index, root_chip) in root_chips.iter_mut().enumerate() {
if !chip_filter.is_empty() && !chip_filter.contains(&root_chip.get_arch()) {
return Err(PlatformError::WrongChipArchs {
actual: root_chip.get_arch(),
expected: chip_filter.clone(),
backtrace: BtWrapper::capture(),
})?;
}
let status = wait_for_init(root_chip, init_callback, continue_on_failure, noc_safe)?;
let chip = UninitChip::new(status, root_chip);
let remote_ready = chip.eth_safe();
let arc_ready = chip.arc_alive();
output.push(chip);
let ident = if let Some(wh) = root_chip.as_wh() {
if arc_ready {
if let Ok(telem) = root_chip.get_telemetry() {
if !local_only && remote_ready {
remotes_to_investigate.push(root_index);
}
(
Some(telem.board_id),
Some(InterfaceIdOrCoord::Coord(wh.get_local_chip_coord()?)),
)
} else {
continue;
}
} else {
continue;
}
} else {
(
None,
root_chip
.get_device_info()?
.map(|v| InterfaceIdOrCoord::Id(v.interface_id)),
)
};
if !seen_chips.insert(ident) {
continue;
}
}
for root_chip in remotes_to_investigate.into_iter().map(|v| &root_chips[v]) {
let mut to_check = root_chip.get_neighbouring_chips()?;
let mut seen_coords = HashSet::new();
while let Some(nchip) = to_check.pop() {
if !seen_coords.insert(nchip.eth_addr) {
continue;
}
if !chip_filter.is_empty() && !chip_filter.contains(&root_chip.get_arch()) {
continue;
}
if let Some(wh) = root_chip.as_wh() {
let mut wh = wh.open_remote(nchip.eth_addr)?;
let status = wait_for_init(&mut wh, init_callback, continue_on_failure, noc_safe)?;
let local_coord = wh.get_local_chip_coord()?;
if local_coord != nchip.eth_addr {
return Err(PlatformError::Generic(
format!("When detecting chips in mesh found a mismatch between the expected chip coordinate {} and the actual {}", nchip.eth_addr, local_coord),
crate::error::BtWrapper::capture(),
))?;
}
if !status.arc_status.has_error() {
let telem = wh.get_telemetry()?;
let ident = (
Some(telem.board_id),
Some(InterfaceIdOrCoord::Coord(local_coord)),
);
if !seen_chips.insert(ident) {
init_callback(crate::chip::ChipDetectState {
chip: root_chip,
call: crate::chip::CallReason::NotNew,
})
.map_err(InitError::CallbackError)?;
continue;
}
for nchip in wh.get_neighbouring_chips()? {
to_check.push(nchip);
}
}
let chip = Chip::from(Box::new(wh) as Box<dyn ChipImpl>);
output.push(UninitChip::new(status, &chip));
} else {
unimplemented!("Don't have a handler for non-WH chips with ethernet support yet.")
}
}
}
Ok(output)
}
pub fn detect_initialized_chips<E>(
root_chips: Vec<Chip>,
init_callback: &mut impl FnMut(crate::chip::ChipDetectState) -> Result<(), E>,
options: ChipDetectOptions,
) -> Result<Vec<Chip>, InitError<E>> {
let chips = detect_chips(root_chips, init_callback, options)?;
let mut output = Vec::with_capacity(chips.len());
for chip in chips {
if chip.is_initialized() {
output.push(chip.upgrade());
} else {
output.push(chip.init(&mut |_| Ok(()))?);
}
}
Ok(output)
}
pub fn detect_chips_silent(
root_chips: Vec<Chip>,
options: ChipDetectOptions,
) -> Result<Vec<Chip>, PlatformError> {
detect_initialized_chips::<std::convert::Infallible>(root_chips, &mut |_| Ok(()), options)
.map_err(|v| match v {
InitError::PlatformError(err) => err,
InitError::CallbackError(_) => unreachable!(),
})
}