From ca419aa3c23c1466811f8998a12b1d0f2c86f741 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 28 Dec 2017 07:06:45 +0000 Subject: [PATCH] firmware: split out libboard_artiq from libboard. --- artiq/firmware/Cargo.lock | 18 ++- artiq/firmware/libboard/Cargo.toml | 4 +- artiq/firmware/libboard/build.rs | 44 ++---- artiq/firmware/libboard/clock.rs | 49 +------ artiq/firmware/libboard/config.rs | 135 +++++++++++------- artiq/firmware/libboard/ident.rs | 13 ++ artiq/firmware/libboard/lib.rs | 52 ++----- artiq/firmware/libboard/{ => or1k}/cache.rs | 22 +-- artiq/firmware/libboard/{ => or1k}/irq.rs | 0 artiq/firmware/libboard/or1k/mod.rs | 3 + artiq/firmware/libboard/{ => or1k}/spr.rs | 0 artiq/firmware/libboard/{ => or1k}/vectors.S | 0 artiq/firmware/libboard/spiflash.rs | 4 +- artiq/firmware/libboard_artiq/Cargo.toml | 19 +++ .../{libboard => libboard_artiq}/ad9154.rs | 0 .../ad9154_reg.rs | 2 + .../{libboard => libboard_artiq}/boot.rs | 2 +- artiq/firmware/libboard_artiq/build.rs | 28 ++++ .../hmc7043_gen_writes.py | 0 .../hmc7043_guiexport_6gbps.py | 0 .../hmc830_7043.rs | 0 .../{libboard => libboard_artiq}/i2c.rs | 0 artiq/firmware/libboard_artiq/lib.rs | 25 ++++ .../{libboard => libboard_artiq}/pcr.rs | 2 +- .../{libboard => libboard_artiq}/serwb.rs | 2 +- .../{libboard => libboard_artiq}/si5324.rs | 0 .../{libboard => libboard_artiq}/spi.rs | 0 artiq/firmware/libbuild_misoc/Cargo.toml | 8 ++ artiq/firmware/libbuild_misoc/lib.rs | 15 ++ artiq/firmware/liblogger_artiq/lib.rs | 8 +- artiq/firmware/runtime/Cargo.toml | 3 +- artiq/firmware/runtime/kern_hwreq.rs | 28 ++-- artiq/firmware/runtime/lib.rs | 46 +++--- artiq/firmware/runtime/mgmt.rs | 6 +- artiq/firmware/runtime/rtio_mgt.rs | 5 +- artiq/firmware/runtime/session.rs | 9 +- artiq/firmware/runtime/watchdog.rs | 47 ++++++ 37 files changed, 352 insertions(+), 247 deletions(-) create mode 100644 artiq/firmware/libboard/ident.rs rename artiq/firmware/libboard/{ => or1k}/cache.rs (50%) rename artiq/firmware/libboard/{ => or1k}/irq.rs (100%) create mode 100644 artiq/firmware/libboard/or1k/mod.rs rename artiq/firmware/libboard/{ => or1k}/spr.rs (100%) rename artiq/firmware/libboard/{ => or1k}/vectors.S (100%) create mode 100644 artiq/firmware/libboard_artiq/Cargo.toml rename artiq/firmware/{libboard => libboard_artiq}/ad9154.rs (100%) rename artiq/firmware/{libboard => libboard_artiq}/ad9154_reg.rs (99%) rename artiq/firmware/{libboard => libboard_artiq}/boot.rs (98%) create mode 100644 artiq/firmware/libboard_artiq/build.rs rename artiq/firmware/{libboard => libboard_artiq}/hmc7043_gen_writes.py (100%) rename artiq/firmware/{libboard => libboard_artiq}/hmc7043_guiexport_6gbps.py (100%) rename artiq/firmware/{libboard => libboard_artiq}/hmc830_7043.rs (100%) rename artiq/firmware/{libboard => libboard_artiq}/i2c.rs (100%) create mode 100644 artiq/firmware/libboard_artiq/lib.rs rename artiq/firmware/{libboard => libboard_artiq}/pcr.rs (98%) rename artiq/firmware/{libboard => libboard_artiq}/serwb.rs (99%) rename artiq/firmware/{libboard => libboard_artiq}/si5324.rs (100%) rename artiq/firmware/{libboard => libboard_artiq}/spi.rs (100%) create mode 100644 artiq/firmware/libbuild_misoc/Cargo.toml create mode 100644 artiq/firmware/libbuild_misoc/lib.rs create mode 100644 artiq/firmware/runtime/watchdog.rs diff --git a/artiq/firmware/Cargo.lock b/artiq/firmware/Cargo.lock index 700bc76ee..3b6ed3581 100644 --- a/artiq/firmware/Cargo.lock +++ b/artiq/firmware/Cargo.lock @@ -26,11 +26,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "board" version = "0.0.0" dependencies = [ - "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "build_artiq 0.0.0", + "build_misoc 0.0.0", "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "board_artiq" +version = "0.0.0" +dependencies = [ + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "board 0.0.0", + "build_artiq 0.0.0", ] [[package]] @@ -40,6 +47,10 @@ dependencies = [ "walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "build_misoc" +version = "0.0.0" + [[package]] name = "byteorder" version = "1.2.1" @@ -169,6 +180,7 @@ dependencies = [ "amp 0.0.0", "backtrace_artiq 0.0.0", "board 0.0.0", + "board_artiq 0.0.0", "build_artiq 0.0.0", "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "compiler_builtins 0.1.0 (git+https://github.com/m-labs/compiler-builtins?rev=97916b1)", diff --git a/artiq/firmware/libboard/Cargo.toml b/artiq/firmware/libboard/Cargo.toml index f7dff2d1b..9604e9a6e 100644 --- a/artiq/firmware/libboard/Cargo.toml +++ b/artiq/firmware/libboard/Cargo.toml @@ -10,12 +10,10 @@ path = "lib.rs" [build-dependencies] cc = "1.0" -build_artiq = { path = "../libbuild_artiq" } +build_misoc = { path = "../libbuild_misoc" } [dependencies] -bitflags = "1.0" byteorder = { version = "1.0", default-features = false } -log = { version = "0.3", default-features = false } [features] uart_console = [] diff --git a/artiq/firmware/libboard/build.rs b/artiq/firmware/libboard/build.rs index 6090fad77..c0a5971eb 100644 --- a/artiq/firmware/libboard/build.rs +++ b/artiq/firmware/libboard/build.rs @@ -1,38 +1,18 @@ -extern crate build_artiq; +extern crate build_misoc; extern crate cc; use std::env; -use std::fs::File; -use std::io::Write; -use std::path::PathBuf; -use std::process::Command; - -fn build_vectors() { - println!("cargo:rerun-if-changed=vectors.S"); - cc::Build::new() - .file("vectors.S") - .compile("vectors"); -} - -fn gen_hmc7043_writes() { - println!("cargo:rerun-if-changed=hmc7043_gen_writes.py"); - println!("cargo:rerun-if-changed=hmc7043_guiexport_6gbps.py"); - - let hmc7043_writes = - Command::new("python3") - .arg("hmc7043_gen_writes.py") - .arg("hmc7043_guiexport_6gbps.py") - .output() - .ok() - .and_then(|o| String::from_utf8(o.stdout).ok()) - .unwrap(); - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - let mut f = File::create(out_dir.join("hmc7043_writes.rs")).unwrap(); - write!(f, "{}", hmc7043_writes).unwrap(); -} +use std::path::Path; fn main() { - build_artiq::misoc_cfg(); - build_vectors(); - gen_hmc7043_writes(); + build_misoc::cfg(); + + let triple = env::var("TARGET").unwrap(); + let arch = triple.split("-").next().unwrap(); + let vectors_path = Path::new(arch).join("vectors.S"); + + println!("cargo:rerun-if-changed={}", vectors_path.to_str().unwrap()); + cc::Build::new() + .file(vectors_path) + .compile("vectors"); } diff --git a/artiq/firmware/libboard/clock.rs b/artiq/firmware/libboard/clock.rs index 31fe7bafc..deddd8c39 100644 --- a/artiq/firmware/libboard/clock.rs +++ b/artiq/firmware/libboard/clock.rs @@ -1,6 +1,7 @@ +use core::i64; use csr; -const INIT: u64 = ::core::i64::MAX as u64; +const INIT: u64 = i64::MAX as u64; const FREQ: u64 = csr::CONFIG_CLOCK_FREQUENCY as u64; pub fn init() { @@ -35,49 +36,3 @@ pub fn spin_us(interval: u64) { } } } - -#[derive(Debug, Clone, Copy)] -struct Watchdog { - active: bool, - threshold: u64 -} - -pub const MAX_WATCHDOGS: usize = 16; - -#[derive(Debug)] -pub struct WatchdogSet { - watchdogs: [Watchdog; MAX_WATCHDOGS] -} - -impl WatchdogSet { - pub fn new() -> WatchdogSet { - WatchdogSet { - watchdogs: [Watchdog { active: false, threshold: 0 }; MAX_WATCHDOGS] - } - } - - pub fn set_ms(&mut self, interval: u64) -> Result { - for (index, watchdog) in self.watchdogs.iter_mut().enumerate() { - if !watchdog.active { - watchdog.active = true; - watchdog.threshold = get_ms() + interval; - return Ok(index) - } - } - - Err(()) - } - - pub fn clear(&mut self, index: usize) { - if index < MAX_WATCHDOGS { - self.watchdogs[index].active = false - } - } - - pub fn expired(&self) -> bool { - self.watchdogs.iter() - .filter(|wd| wd.active) - .min_by_key(|wd| wd.threshold) - .map_or(false, |wd| get_ms() > wd.threshold) - } -} diff --git a/artiq/firmware/libboard/config.rs b/artiq/firmware/libboard/config.rs index 5c673da5b..aec58979b 100644 --- a/artiq/firmware/libboard/config.rs +++ b/artiq/firmware/libboard/config.rs @@ -1,9 +1,41 @@ +use core::{str, fmt}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Error { + AlreadyLocked, + SpaceExhausted, + Truncated { offset: usize }, + InvalidSize { offset: usize, size: usize }, + MissingSeparator { offset: usize }, + Utf8Error(str::Utf8Error) +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + &Error::AlreadyLocked => + write!(f, "attempt at reentrant access"), + &Error::SpaceExhausted => + write!(f, "space exhausted"), + &Error::Truncated { offset }=> + write!(f, "truncated record at offset {}", offset), + &Error::InvalidSize { offset, size } => + write!(f, "invalid record size {} at offset {}", size, offset), + &Error::MissingSeparator { offset } => + write!(f, "missing separator at offset {}", offset), + &Error::Utf8Error(err) => + write!(f, "{}", err) + } + } +} + #[cfg(has_spiflash)] mod imp { use core::str; use byteorder::{ByteOrder, BigEndian}; use cache; use spiflash; + use super::Error; // One flash sector immediately after the bootloader. const ADDR: usize = ::mem::FLASH_BOOT_ADDRESS - spiflash::PAGE_SIZE; @@ -12,17 +44,18 @@ mod imp { mod lock { use core::slice; use core::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; + use super::Error; static LOCKED: AtomicUsize = ATOMIC_USIZE_INIT; pub struct Lock; impl Lock { - pub fn take() -> Result { + pub fn take() -> Result { if LOCKED.swap(1, Ordering::SeqCst) != 0 { - Err(()) // already locked + Err(Error::AlreadyLocked) } else { - Ok(Lock) // locked now + Ok(Lock) } } @@ -53,29 +86,27 @@ mod imp { } impl<'a> Iterator for Iter<'a> { - type Item = Result<(&'a [u8], &'a [u8]), ()>; + type Item = Result<(&'a [u8], &'a [u8]), Error>; fn next(&mut self) -> Option { let data = &self.data[self.offset..]; if data.len() < 4 { - error!("offset {}: truncated record", self.offset); - return Some(Err(())) + // error!("offset {}: truncated record", self.offset); + return Some(Err(Error::Truncated { offset: self.offset })) } let record_size = BigEndian::read_u32(data) as usize; if record_size == !0 /* all ones; erased flash */ { return None } else if record_size < 4 || record_size > data.len() { - error!("offset {}: invalid record size {}", self.offset, record_size); - return Some(Err(())) + return Some(Err(Error::InvalidSize { offset: self.offset, size: record_size })) } let record_body = &data[4..record_size]; match record_body.iter().position(|&x| x == 0) { None => { - error!("offset {}: missing separator", self.offset); - Some(Err(())) + return Some(Err(Error::MissingSeparator { offset: self.offset })) } Some(pos) => { self.offset += record_size; @@ -87,7 +118,7 @@ mod imp { } } - pub fn read) -> R, R>(key: &str, f: F) -> R { + pub fn read) -> R, R>(key: &str, f: F) -> R { f(Lock::take().and_then(|lock| { let mut iter = Iter::new(lock.data()); let mut value = &[][..]; @@ -102,52 +133,53 @@ mod imp { })) } - pub fn read_str) -> R, R>(key: &str, f: F) -> R { + pub fn read_str) -> R, R>(key: &str, f: F) -> R { read(key, |result| { - f(result.and_then(|value| str::from_utf8(value).map_err(|_| ()))) + f(result.and_then(|value| str::from_utf8(value).map_err(Error::Utf8Error))) }) } - unsafe fn append_at<'a>(mut data: &'a [u8], key: &[u8], value: &[u8]) -> Result<&'a [u8], ()> { + unsafe fn append_at(data: &[u8], mut offset: usize, + key: &[u8], value: &[u8]) -> Result { let record_size = 4 + key.len() + 1 + value.len(); - if data.len() < record_size { - return Err(()) + if offset + record_size > data.len() { + return Err(Error::SpaceExhausted) } let mut record_size_bytes = [0u8; 4]; BigEndian::write_u32(&mut record_size_bytes[..], record_size as u32); - spiflash::write(data.as_ptr() as usize, &record_size_bytes[..]); - data = &data[record_size_bytes.len()..]; + { + let mut write = |payload| { + spiflash::write(data.as_ptr().offset(offset as isize) as usize, payload); + offset += payload.len(); + }; - spiflash::write(data.as_ptr() as usize, key); - data = &data[key.len()..]; + write(&record_size_bytes[..]); + write(key); + write(&[0]); + write(value); + cache::flush_l2_cache(); + } - spiflash::write(data.as_ptr() as usize, &[0]); - data = &data[1..]; - - spiflash::write(data.as_ptr() as usize, value); - data = &data[value.len()..]; - - cache::flush_l2_cache(); - - Ok(data) + Ok(offset) } - fn compact() -> Result<(), ()> { + fn compact() -> Result<(), Error> { let lock = Lock::take()?; + let data = lock.data(); static mut OLD_DATA: [u8; SIZE] = [0; SIZE]; let old_data = unsafe { - OLD_DATA.copy_from_slice(lock.data()); + OLD_DATA.copy_from_slice(data); &OLD_DATA[..] }; - let mut data = lock.data(); unsafe { spiflash::erase_sector(data.as_ptr() as usize) }; // This is worst-case quadratic, but we're limited by a small SPI flash sector size, // so it does not really matter. + let mut offset = 0; let mut iter = Iter::new(old_data); while let Some(result) = iter.next() { let (key, mut value) = result?; @@ -159,47 +191,48 @@ mod imp { value = next_value } } - data = unsafe { append_at(data, key, value)? }; + offset = unsafe { append_at(data, offset, key, value)? }; } Ok(()) } - fn append(key: &str, value: &[u8]) -> Result<(), ()> { + fn append(key: &str, value: &[u8]) -> Result<(), Error> { let lock = Lock::take()?; + let data = lock.data(); - let free = { - let mut iter = Iter::new(lock.data()); + let free_offset = { + let mut iter = Iter::new(data); while let Some(result) = iter.next() { let _ = result?; } - &iter.data[iter.offset..] + iter.offset }; - unsafe { append_at(free, key.as_bytes(), value)? }; + unsafe { append_at(data, free_offset, key.as_bytes(), value)? }; Ok(()) } - pub fn write(key: &str, value: &[u8]) -> Result<(), ()> { + pub fn write(key: &str, value: &[u8]) -> Result<(), Error> { match append(key, value) { - Ok(()) => (), - Err(()) => { + Err(Error::SpaceExhausted) => { compact()?; - append(key, value)?; + append(key, value) } + res => res } - Ok(()) } - pub fn remove(key: &str) -> Result<(), ()> { + pub fn remove(key: &str) -> Result<(), Error> { write(key, &[]) } - pub fn erase() -> Result<(), ()> { + pub fn erase() -> Result<(), Error> { let lock = Lock::take()?; + let data = lock.data(); - unsafe { spiflash::erase_sector(lock.data().as_ptr() as usize) }; + unsafe { spiflash::erase_sector(data.as_ptr() as usize) }; cache::flush_l2_cache(); Ok(()) @@ -208,23 +241,23 @@ mod imp { #[cfg(not(has_spiflash))] mod imp { - pub fn read) -> R, R>(_key: &str, f: F) -> R { + pub fn read) -> R, R>(_key: &str, f: F) -> R { f(Err(())) } - pub fn read_str) -> R, R>(_key: &str, f: F) -> R { + pub fn read_str) -> R, R>(_key: &str, f: F) -> R { f(Err(())) } - pub fn write(_key: &str, _value: &[u8]) -> Result<(), ()> { + pub fn write(_key: &str, _value: &[u8]) -> Result<(), Error> { Err(()) } - pub fn remove(_key: &str) -> Result<(), ()> { + pub fn remove(_key: &str) -> Result<(), Error> { Err(()) } - pub fn erase() -> Result<(), ()> { + pub fn erase() -> Result<(), Error> { Err(()) } } diff --git a/artiq/firmware/libboard/ident.rs b/artiq/firmware/libboard/ident.rs new file mode 100644 index 000000000..26b5befdc --- /dev/null +++ b/artiq/firmware/libboard/ident.rs @@ -0,0 +1,13 @@ +use core::{ptr, cmp, str}; +use csr; + +pub fn read(buf: &mut [u8]) -> &str { + unsafe { + let len = ptr::read_volatile(csr::IDENTIFIER_MEM_BASE); + let len = cmp::min(len as usize, buf.len()); + for i in 0..len { + buf[i] = ptr::read_volatile(csr::IDENTIFIER_MEM_BASE.offset(1 + i as isize)) as u8 + } + str::from_utf8_unchecked(&buf[..len]) + } +} diff --git a/artiq/firmware/libboard/lib.rs b/artiq/firmware/libboard/lib.rs index 8711cff3c..9eb6bd31e 100644 --- a/artiq/firmware/libboard/lib.rs +++ b/artiq/firmware/libboard/lib.rs @@ -1,58 +1,22 @@ -#![feature(asm, lang_items)] #![no_std] +#![feature(asm)] -#[macro_use] -extern crate bitflags; extern crate byteorder; -#[macro_use] -extern crate log; -use core::{cmp, ptr, str}; +#[cfg(target_arch = "or1k")] +#[path = "or1k/mod.rs"] +mod arch; + +pub use arch::*; include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/mem.rs")); include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/csr.rs")); include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/sdram_phy.rs")); -pub mod spr; -pub mod irq; -pub mod cache; -pub mod pcr; +pub mod ident; pub mod clock; pub mod uart; -#[cfg(feature = "uart_console")] -pub mod uart_console; - #[cfg(has_spiflash)] pub mod spiflash; pub mod config; - -pub mod i2c; -pub mod spi; - -#[cfg(has_si5324)] -pub mod si5324; - -#[cfg(has_serwb_phy_amc)] -pub mod serwb; -#[cfg(has_hmc830_7043)] -pub mod hmc830_7043; -#[cfg(has_ad9154)] -#[allow(dead_code)] -mod ad9154_reg; -#[cfg(has_ad9154)] -pub mod ad9154; - -pub mod boot; - #[cfg(feature = "uart_console")] -pub use uart_console::Console; - -pub fn ident(buf: &mut [u8]) -> &str { - unsafe { - let len = ptr::read_volatile(csr::IDENTIFIER_MEM_BASE); - let len = cmp::min(len as usize, buf.len()); - for i in 0..len { - buf[i] = ptr::read_volatile(csr::IDENTIFIER_MEM_BASE.offset(1 + i as isize)) as u8 - } - str::from_utf8_unchecked(&buf[..len]) - } -} +pub mod uart_console; diff --git a/artiq/firmware/libboard/cache.rs b/artiq/firmware/libboard/or1k/cache.rs similarity index 50% rename from artiq/firmware/libboard/cache.rs rename to artiq/firmware/libboard/or1k/cache.rs index e90bb1061..8e79b11ec 100644 --- a/artiq/firmware/libboard/cache.rs +++ b/artiq/firmware/libboard/or1k/cache.rs @@ -1,19 +1,19 @@ use core::ptr; -use spr::{self, mfspr, mtspr}; +use super::spr::*; use csr; use mem; pub fn flush_cpu_icache() { unsafe { - let iccfgr = mfspr(spr::SPR_ICCFGR); - let ways = 1 << (iccfgr & spr::SPR_ICCFGR_NCW); - let set_size = 1 << ((iccfgr & spr::SPR_ICCFGR_NCS) >> 3); - let block_size = if iccfgr & spr::SPR_ICCFGR_CBS != 0 { 32 } else { 16 }; + let iccfgr = mfspr(SPR_ICCFGR); + let ways = 1 << (iccfgr & SPR_ICCFGR_NCW); + let set_size = 1 << ((iccfgr & SPR_ICCFGR_NCS) >> 3); + let block_size = if iccfgr & SPR_ICCFGR_CBS != 0 { 32 } else { 16 }; let size = set_size * ways * block_size; let mut i = 0; while i < size { - mtspr(spr::SPR_ICBIR, i); + mtspr(SPR_ICBIR, i); i += block_size; } } @@ -21,15 +21,15 @@ pub fn flush_cpu_icache() { pub fn flush_cpu_dcache() { unsafe { - let dccfgr = mfspr(spr::SPR_DCCFGR); - let ways = 1 << (dccfgr & spr::SPR_ICCFGR_NCW); - let set_size = 1 << ((dccfgr & spr::SPR_DCCFGR_NCS) >> 3); - let block_size = if dccfgr & spr::SPR_DCCFGR_CBS != 0 { 32 } else { 16 }; + let dccfgr = mfspr(SPR_DCCFGR); + let ways = 1 << (dccfgr & SPR_ICCFGR_NCW); + let set_size = 1 << ((dccfgr & SPR_DCCFGR_NCS) >> 3); + let block_size = if dccfgr & SPR_DCCFGR_CBS != 0 { 32 } else { 16 }; let size = set_size * ways * block_size; let mut i = 0; while i < size { - mtspr(spr::SPR_DCBIR, i); + mtspr(SPR_DCBIR, i); i += block_size; } } diff --git a/artiq/firmware/libboard/irq.rs b/artiq/firmware/libboard/or1k/irq.rs similarity index 100% rename from artiq/firmware/libboard/irq.rs rename to artiq/firmware/libboard/or1k/irq.rs diff --git a/artiq/firmware/libboard/or1k/mod.rs b/artiq/firmware/libboard/or1k/mod.rs new file mode 100644 index 000000000..01589ddc8 --- /dev/null +++ b/artiq/firmware/libboard/or1k/mod.rs @@ -0,0 +1,3 @@ +pub mod spr; +pub mod irq; +pub mod cache; diff --git a/artiq/firmware/libboard/spr.rs b/artiq/firmware/libboard/or1k/spr.rs similarity index 100% rename from artiq/firmware/libboard/spr.rs rename to artiq/firmware/libboard/or1k/spr.rs diff --git a/artiq/firmware/libboard/vectors.S b/artiq/firmware/libboard/or1k/vectors.S similarity index 100% rename from artiq/firmware/libboard/vectors.S rename to artiq/firmware/libboard/or1k/vectors.S diff --git a/artiq/firmware/libboard/spiflash.rs b/artiq/firmware/libboard/spiflash.rs index 643a2160c..2273185d5 100644 --- a/artiq/firmware/libboard/spiflash.rs +++ b/artiq/firmware/libboard/spiflash.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] - use core::cmp; use csr; @@ -8,7 +6,7 @@ pub const PAGE_SIZE: usize = csr::CONFIG_SPIFLASH_PAGE_SIZE as usize; const PAGE_MASK: usize = PAGE_SIZE - 1; const CMD_PP: u8 = 0x02; -const CMD_WRDI: u8 = 0x04; +// const CMD_WRDI: u8 = 0x04; const CMD_RDSR: u8 = 0x05; const CMD_WREN: u8 = 0x06; const CMD_SE: u8 = 0xd8; diff --git a/artiq/firmware/libboard_artiq/Cargo.toml b/artiq/firmware/libboard_artiq/Cargo.toml new file mode 100644 index 000000000..8ac55b6a2 --- /dev/null +++ b/artiq/firmware/libboard_artiq/Cargo.toml @@ -0,0 +1,19 @@ +[package] +authors = ["M-Labs"] +name = "board_artiq" +version = "0.0.0" +build = "build.rs" + +[lib] +name = "board_artiq" +path = "lib.rs" + +[build-dependencies] +build_artiq = { path = "../libbuild_artiq" } + +[dependencies] +bitflags = "1.0" +board = { path = "../libboard" } + +[features] +uart_console = [] diff --git a/artiq/firmware/libboard/ad9154.rs b/artiq/firmware/libboard_artiq/ad9154.rs similarity index 100% rename from artiq/firmware/libboard/ad9154.rs rename to artiq/firmware/libboard_artiq/ad9154.rs diff --git a/artiq/firmware/libboard/ad9154_reg.rs b/artiq/firmware/libboard_artiq/ad9154_reg.rs similarity index 99% rename from artiq/firmware/libboard/ad9154_reg.rs rename to artiq/firmware/libboard_artiq/ad9154_reg.rs index 3830f89c8..3af180496 100644 --- a/artiq/firmware/libboard/ad9154_reg.rs +++ b/artiq/firmware/libboard_artiq/ad9154_reg.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + pub const SPI_INTFCONFA : u16 = 0x000; pub const SOFTRESET : u8 = 1 << 0; pub const LSBFIRST : u8 = 1 << 1; diff --git a/artiq/firmware/libboard/boot.rs b/artiq/firmware/libboard_artiq/boot.rs similarity index 98% rename from artiq/firmware/libboard/boot.rs rename to artiq/firmware/libboard_artiq/boot.rs index 7e98257b0..803a094a6 100644 --- a/artiq/firmware/libboard/boot.rs +++ b/artiq/firmware/libboard_artiq/boot.rs @@ -1,4 +1,4 @@ -use irq; +use board::irq; pub unsafe fn reboot() -> ! { irq::set_ie(false); diff --git a/artiq/firmware/libboard_artiq/build.rs b/artiq/firmware/libboard_artiq/build.rs new file mode 100644 index 000000000..b787e5007 --- /dev/null +++ b/artiq/firmware/libboard_artiq/build.rs @@ -0,0 +1,28 @@ +extern crate build_artiq; + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; +use std::process::Command; + +fn gen_hmc7043_writes() { + println!("cargo:rerun-if-changed=hmc7043_gen_writes.py"); + println!("cargo:rerun-if-changed=hmc7043_guiexport_6gbps.py"); + + let hmc7043_writes = + Command::new("python3") + .arg("hmc7043_gen_writes.py") + .arg("hmc7043_guiexport_6gbps.py") + .output() + .ok() + .and_then(|o| String::from_utf8(o.stdout).ok()) + .unwrap(); + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let mut f = File::create(out_dir.join("hmc7043_writes.rs")).unwrap(); + write!(f, "{}", hmc7043_writes).unwrap(); +} + +fn main() { + gen_hmc7043_writes(); +} diff --git a/artiq/firmware/libboard/hmc7043_gen_writes.py b/artiq/firmware/libboard_artiq/hmc7043_gen_writes.py similarity index 100% rename from artiq/firmware/libboard/hmc7043_gen_writes.py rename to artiq/firmware/libboard_artiq/hmc7043_gen_writes.py diff --git a/artiq/firmware/libboard/hmc7043_guiexport_6gbps.py b/artiq/firmware/libboard_artiq/hmc7043_guiexport_6gbps.py similarity index 100% rename from artiq/firmware/libboard/hmc7043_guiexport_6gbps.py rename to artiq/firmware/libboard_artiq/hmc7043_guiexport_6gbps.py diff --git a/artiq/firmware/libboard/hmc830_7043.rs b/artiq/firmware/libboard_artiq/hmc830_7043.rs similarity index 100% rename from artiq/firmware/libboard/hmc830_7043.rs rename to artiq/firmware/libboard_artiq/hmc830_7043.rs diff --git a/artiq/firmware/libboard/i2c.rs b/artiq/firmware/libboard_artiq/i2c.rs similarity index 100% rename from artiq/firmware/libboard/i2c.rs rename to artiq/firmware/libboard_artiq/i2c.rs diff --git a/artiq/firmware/libboard_artiq/lib.rs b/artiq/firmware/libboard_artiq/lib.rs new file mode 100644 index 000000000..894866980 --- /dev/null +++ b/artiq/firmware/libboard_artiq/lib.rs @@ -0,0 +1,25 @@ +#![feature(asm, lang_items)] +#![no_std] + +#[macro_use] +extern crate bitflags; +extern crate board; + +pub mod pcr; + +pub mod i2c; +pub mod spi; + +#[cfg(has_si5324)] +pub mod si5324; + +#[cfg(has_serwb_phy_amc)] +pub mod serwb; +#[cfg(has_hmc830_7043)] +pub mod hmc830_7043; +#[cfg(has_ad9154)] +mod ad9154_reg; +#[cfg(has_ad9154)] +pub mod ad9154; + +pub mod boot; diff --git a/artiq/firmware/libboard/pcr.rs b/artiq/firmware/libboard_artiq/pcr.rs similarity index 98% rename from artiq/firmware/libboard/pcr.rs rename to artiq/firmware/libboard_artiq/pcr.rs index 59518e8bf..543ad4df6 100644 --- a/artiq/firmware/libboard/pcr.rs +++ b/artiq/firmware/libboard_artiq/pcr.rs @@ -1,4 +1,4 @@ -use spr::*; +use board::spr::*; bitflags! { pub struct Counters: u32 { diff --git a/artiq/firmware/libboard/serwb.rs b/artiq/firmware/libboard_artiq/serwb.rs similarity index 99% rename from artiq/firmware/libboard/serwb.rs rename to artiq/firmware/libboard_artiq/serwb.rs index 0d76ff5f7..687d5ea04 100644 --- a/artiq/firmware/libboard/serwb.rs +++ b/artiq/firmware/libboard_artiq/serwb.rs @@ -21,7 +21,7 @@ pub fn wait_init() { error!("incorrect RTM identifier: 0x{:08x}", rtm_identifier); // proceed anyway } - + unsafe { debug!("AMC serwb settings:"); debug!(" delay_min_found: {}", csr::serwb_phy_amc::control_delay_min_found_read()); diff --git a/artiq/firmware/libboard/si5324.rs b/artiq/firmware/libboard_artiq/si5324.rs similarity index 100% rename from artiq/firmware/libboard/si5324.rs rename to artiq/firmware/libboard_artiq/si5324.rs diff --git a/artiq/firmware/libboard/spi.rs b/artiq/firmware/libboard_artiq/spi.rs similarity index 100% rename from artiq/firmware/libboard/spi.rs rename to artiq/firmware/libboard_artiq/spi.rs diff --git a/artiq/firmware/libbuild_misoc/Cargo.toml b/artiq/firmware/libbuild_misoc/Cargo.toml new file mode 100644 index 000000000..400e5fbe1 --- /dev/null +++ b/artiq/firmware/libbuild_misoc/Cargo.toml @@ -0,0 +1,8 @@ +[package] +authors = ["M-Labs"] +name = "build_misoc" +version = "0.0.0" + +[lib] +name = "build_misoc" +path = "lib.rs" diff --git a/artiq/firmware/libbuild_misoc/lib.rs b/artiq/firmware/libbuild_misoc/lib.rs new file mode 100644 index 000000000..36f354fa1 --- /dev/null +++ b/artiq/firmware/libbuild_misoc/lib.rs @@ -0,0 +1,15 @@ +use std::env; +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::path::Path; + +pub fn cfg() { + let out_dir = env::var("BUILDINC_DIRECTORY").unwrap(); + let cfg_path = Path::new(&out_dir).join("generated").join("rust-cfg"); + println!("cargo:rerun-if-changed={}", cfg_path.to_str().unwrap()); + + let f = BufReader::new(File::open(&cfg_path).unwrap()); + for line in f.lines() { + println!("cargo:rustc-cfg={}", line.unwrap()); + } +} diff --git a/artiq/firmware/liblogger_artiq/lib.rs b/artiq/firmware/liblogger_artiq/lib.rs index 67b2c36f5..c51d45b7b 100644 --- a/artiq/firmware/liblogger_artiq/lib.rs +++ b/artiq/firmware/liblogger_artiq/lib.rs @@ -2,13 +2,14 @@ extern crate log; extern crate log_buffer; +#[macro_use] extern crate board; use core::cell::{Cell, RefCell}; use core::fmt::Write; use log::{Log, LogMetadata, LogRecord, LogLevelFilter, MaxLogLevelFilter}; use log_buffer::LogBuffer; -use board::{Console, clock}; +use board::clock; pub struct BufferLogger { buffer: RefCell>, @@ -110,9 +111,8 @@ impl Log for BufferLogger { record.level(), record.target(), record.args()).unwrap(); if record.level() <= self.uart_filter.get() { - writeln!(Console, - "[{:6}.{:06}s] {:>5}({}): {}", seconds, micros, - record.level(), record.target(), record.args()).unwrap(); + println!("[{:6}.{:06}s] {:>5}({}): {}", seconds, micros, + record.level(), record.target(), record.args()); } } } diff --git a/artiq/firmware/runtime/Cargo.toml b/artiq/firmware/runtime/Cargo.toml index 468ca295d..e6c20ba4d 100644 --- a/artiq/firmware/runtime/Cargo.toml +++ b/artiq/firmware/runtime/Cargo.toml @@ -16,11 +16,12 @@ build_artiq = { path = "../libbuild_artiq" } byteorder = { version = "1.0", default-features = false } cslice = { version = "0.3" } log = { version = "0.3", default-features = false } +board = { path = "../libboard", features = ["uart_console"] } alloc_list = { path = "../liballoc_list" } std_artiq = { path = "../libstd_artiq", features = ["alloc", "io_error_alloc"] } logger_artiq = { path = "../liblogger_artiq" } backtrace_artiq = { path = "../libbacktrace_artiq" } -board = { path = "../libboard", features = ["uart_console"] } +board_artiq = { path = "../libboard_artiq" } proto = { path = "../libproto", features = ["log"] } amp = { path = "../libamp" } drtioaux = { path = "../libdrtioaux" } diff --git a/artiq/firmware/runtime/kern_hwreq.rs b/artiq/firmware/runtime/kern_hwreq.rs index 245fdaeab..aae60c3c8 100644 --- a/artiq/firmware/runtime/kern_hwreq.rs +++ b/artiq/firmware/runtime/kern_hwreq.rs @@ -120,14 +120,14 @@ mod drtio_i2c { } mod i2c { - use board; + use board_artiq::i2c as local_i2c; use super::drtio_i2c; pub fn start(busno: u32) -> Result<(), ()> { let nodeno = (busno >> 16) as u8; let node_busno = busno as u8; if nodeno == 0 { - board::i2c::start(node_busno) + local_i2c::start(node_busno) } else { drtio_i2c::start(nodeno, node_busno) } @@ -137,7 +137,7 @@ mod i2c { let nodeno = (busno >> 16) as u8; let node_busno = busno as u8; if nodeno == 0 { - board::i2c::restart(node_busno) + local_i2c::restart(node_busno) } else { drtio_i2c::restart(nodeno, node_busno) } @@ -147,7 +147,7 @@ mod i2c { let nodeno = (busno >> 16) as u8; let node_busno = busno as u8; if nodeno == 0 { - board::i2c::stop(node_busno) + local_i2c::stop(node_busno) } else { drtio_i2c::stop(nodeno, node_busno) } @@ -157,7 +157,7 @@ mod i2c { let nodeno = (busno >> 16 )as u8; let node_busno = busno as u8; if nodeno == 0 { - board::i2c::write(node_busno, data) + local_i2c::write(node_busno, data) } else { drtio_i2c::write(nodeno, node_busno, data) } @@ -167,7 +167,7 @@ mod i2c { let nodeno = (busno >> 16) as u8; let node_busno = busno as u8; if nodeno == 0 { - board::i2c::read(node_busno, ack) + local_i2c::read(node_busno, ack) } else { drtio_i2c::read(nodeno, node_busno, ack) } @@ -254,11 +254,13 @@ mod drtio_spi { #[cfg(not(has_drtio))] mod drtio_spi { - pub fn set_config(_nodeno: u8, _busno: u8, _flags: u8, _write_div: u8, _read_div: u8) -> Result<(), ()> { + pub fn set_config(_nodeno: u8, _busno: u8, _flags: u8, + _write_div: u8, _read_div: u8) -> Result<(), ()> { Err(()) } - pub fn set_xfer(_nodeno: u8, _busno: u8, _chip_select: u16, _write_length: u8, _read_length: u8) -> Result<(), ()> { + pub fn set_xfer(_nodeno: u8, _busno: u8, _chip_select: u16, + _write_length: u8, _read_length: u8) -> Result<(), ()> { Err(()) } @@ -272,14 +274,14 @@ mod drtio_spi { } mod spi { - use board; + use board_artiq::spi as local_spi; use super::drtio_spi; pub fn set_config(busno: u32, flags: u8, write_div: u8, read_div: u8) -> Result<(), ()> { let nodeno = (busno >> 16) as u8; let node_busno = busno as u8; if nodeno == 0 { - board::spi::set_config(node_busno, flags, write_div, read_div) + local_spi::set_config(node_busno, flags, write_div, read_div) } else { drtio_spi::set_config(nodeno, node_busno, flags, write_div, read_div) } @@ -289,7 +291,7 @@ mod spi { let nodeno = (busno >> 16) as u8; let node_busno = busno as u8; if nodeno == 0 { - board::spi::set_xfer(node_busno, chip_select, write_length, read_length) + local_spi::set_xfer(node_busno, chip_select, write_length, read_length) } else { drtio_spi::set_xfer(nodeno, node_busno, chip_select, write_length, read_length) } @@ -299,7 +301,7 @@ mod spi { let nodeno = (busno >> 16) as u8; let node_busno = busno as u8; if nodeno == 0 { - board::spi::write(node_busno, data) + local_spi::write(node_busno, data) } else { drtio_spi::write(nodeno, node_busno, data) } @@ -309,7 +311,7 @@ mod spi { let nodeno = (busno >> 16) as u8; let node_busno = busno as u8; if nodeno == 0 { - board::spi::read(node_busno) + local_spi::read(node_busno) } else { drtio_spi::read(nodeno, node_busno) } diff --git a/artiq/firmware/runtime/lib.rs b/artiq/firmware/runtime/lib.rs index b167cd2a3..158abecb1 100644 --- a/artiq/firmware/runtime/lib.rs +++ b/artiq/firmware/runtime/lib.rs @@ -17,6 +17,7 @@ extern crate logger_artiq; extern crate backtrace_artiq; #[macro_use] extern crate board; +extern crate board_artiq; extern crate proto; extern crate amp; #[cfg(has_drtio)] @@ -41,6 +42,7 @@ mod rtio_dma; mod mgmt; mod kernel; mod kern_hwreq; +mod watchdog; mod session; #[cfg(any(has_rtio_moninj, has_drtio))] mod moninj; @@ -51,7 +53,7 @@ fn startup() { board::clock::init(); info!("ARTIQ runtime starting..."); info!("software version {}", include_str!(concat!(env!("OUT_DIR"), "/git-describe"))); - info!("gateware version {}", board::ident(&mut [0; 64])); + info!("gateware version {}", board::ident::read(&mut [0; 64])); #[cfg(has_serwb_phy_amc)] board::serwb::wait_init(); @@ -69,7 +71,7 @@ fn startup() { info!("continuing boot"); #[cfg(has_i2c)] - board::i2c::init(); + board_artiq::i2c::init(); #[cfg(si5324_free_running)] setup_si5324_free_running(); #[cfg(has_hmc830_7043)] @@ -106,27 +108,27 @@ fn setup_si5324_free_running() #[cfg(has_ethmac)] fn startup_ethernet() { let hardware_addr; - match config::read_str("mac", |r| r?.parse()) { - Err(()) => { - hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); - warn!("using default MAC address {}; consider changing it", hardware_addr); - } - Ok(addr) => { + match config::read_str("mac", |r| r.map(|s| s.parse())) { + Ok(Ok(addr)) => { hardware_addr = addr; info!("using MAC address {}", hardware_addr); } + _ => { + hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); + warn!("using default MAC address {}; consider changing it", hardware_addr); + } } let protocol_addr; - match config::read_str("ip", |r| r?.parse()) { - Err(()) => { - protocol_addr = IpAddress::v4(192, 168, 1, 50); - info!("using default IP address {}", protocol_addr); - } - Ok(addr) => { + match config::read_str("ip", |r| r.map(|s| s.parse())) { + Ok(Ok(addr)) => { protocol_addr = addr; info!("using IP address {}", protocol_addr); } + _ => { + protocol_addr = IpAddress::v4(192, 168, 1, 50); + info!("using default IP address {}", protocol_addr); + } } // fn _net_trace_writer(timestamp: u64, printer: smoltcp::wire::PrettyPrinter) @@ -159,26 +161,24 @@ fn startup_ethernet() { #[cfg(has_rtio_analyzer)] io.spawn(4096, analyzer::thread); - match config::read_str("log_level", |r| r?.parse()) { - Err(()) => (), - Ok(log_level_filter) => { + match config::read_str("log_level", |r| r.map(|s| s.parse())) { + Ok(Ok(log_level_filter)) => { info!("log level set to {} by `log_level` config key", log_level_filter); logger_artiq::BufferLogger::with(|logger| logger.set_max_log_level(log_level_filter)); } + _ => info!("log level set to INFO by default") } - match config::read_str("uart_log_level", |r| r?.parse()) { - Err(()) => { - info!("UART log level set to INFO by default"); - }, - Ok(uart_log_level_filter) => { + match config::read_str("uart_log_level", |r| r.map(|s| s.parse())) { + Ok(Ok(uart_log_level_filter)) => { info!("UART log level set to {} by `uart_log_level` config key", uart_log_level_filter); logger_artiq::BufferLogger::with(|logger| logger.set_uart_log_level(uart_log_level_filter)); } + _ => info!("UART log level set to INFO by default") } let mut net_stats = ethmac::EthernetStatistics::new(); @@ -247,7 +247,7 @@ pub extern fn panic_fmt(args: core::fmt::Arguments, file: &'static str, line: u3 if config::read_str("panic_reboot", |r| r == Ok("1")) { println!("rebooting..."); - unsafe { board::boot::reboot() } + unsafe { board_artiq::boot::reboot() } } else { println!("halting."); println!("use `artiq_coreconfig write -s panic_reboot 1` to reboot instead"); diff --git a/artiq/firmware/runtime/mgmt.rs b/artiq/firmware/runtime/mgmt.rs index 0cdb7eef6..23898f0f3 100644 --- a/artiq/firmware/runtime/mgmt.rs +++ b/artiq/firmware/runtime/mgmt.rs @@ -1,9 +1,9 @@ +use board_artiq::boot; use std::io::{self, Read, Write}; use log::LogLevelFilter; use logger_artiq::BufferLogger; use sched::Io; use sched::{TcpListener, TcpStream}; -use board; use proto::WriteExt; use mgmt_proto::*; @@ -86,14 +86,14 @@ fn worker(io: &Io, stream: &mut TcpStream) -> io::Result<()> { Reply::RebootImminent.write_to(stream)?; stream.close()?; stream.flush()?; - unsafe { board::boot::hotswap(&firmware) } + unsafe { boot::hotswap(&firmware) } }, Request::Reboot => { Reply::RebootImminent.write_to(stream)?; stream.close()?; warn!("rebooting"); - unsafe { board::boot::reboot() } + unsafe { boot::reboot() } } }; } diff --git a/artiq/firmware/runtime/rtio_mgt.rs b/artiq/firmware/runtime/rtio_mgt.rs index 35083315c..6917dc771 100644 --- a/artiq/firmware/runtime/rtio_mgt.rs +++ b/artiq/firmware/runtime/rtio_mgt.rs @@ -226,12 +226,13 @@ pub fn startup(io: &Io) { info!("using external startup RTIO clock"); RtioClock::External }, - Err(()) => { + Err(_) => { info!("using internal startup RTIO clock (by default)"); RtioClock::Internal }, Ok(_) => { - error!("unrecognized startup_clock configuration entry, using internal RTIO clock"); + error!("unrecognized startup_clock configuration entry, \ + using internal RTIO clock"); RtioClock::Internal } } diff --git a/artiq/firmware/runtime/session.rs b/artiq/firmware/runtime/session.rs index dd1fe3fde..1cb26509e 100644 --- a/artiq/firmware/runtime/session.rs +++ b/artiq/firmware/runtime/session.rs @@ -15,6 +15,7 @@ use rtio_mgt; use rtio_dma::Manager as DmaManager; use cache::Cache; use kern_hwreq; +use watchdog::WatchdogSet; use rpc_proto as rpc; use session_proto as host; @@ -66,7 +67,7 @@ enum KernelState { struct Session<'a> { congress: &'a mut Congress, kernel_state: KernelState, - watchdog_set: board::clock::WatchdogSet, + watchdog_set: WatchdogSet, log_buffer: String } @@ -75,7 +76,7 @@ impl<'a> Session<'a> { Session { congress: congress, kernel_state: KernelState::Absent, - watchdog_set: board::clock::WatchdogSet::new(), + watchdog_set: WatchdogSet::new(), log_buffer: String::new() } } @@ -226,7 +227,7 @@ fn process_host_message(io: &Io, match host_read(stream)? { host::Request::SystemInfo => { host_write(stream, host::Reply::SystemInfo { - ident: board::ident(&mut [0; 64]), + ident: board::ident::read(&mut [0; 64]), finished_cleanly: session.congress.finished_cleanly.get() })?; session.congress.finished_cleanly.set(true); @@ -238,7 +239,7 @@ fn process_host_message(io: &Io, config::read(key, |result| { match result { Ok(value) => host_write(stream, host::Reply::FlashRead(&value)), - Err(()) => host_write(stream, host::Reply::FlashError) + Err(_) => host_write(stream, host::Reply::FlashError) } }) } diff --git a/artiq/firmware/runtime/watchdog.rs b/artiq/firmware/runtime/watchdog.rs new file mode 100644 index 000000000..921472dae --- /dev/null +++ b/artiq/firmware/runtime/watchdog.rs @@ -0,0 +1,47 @@ +use board::clock; + +#[derive(Debug, Clone, Copy)] +struct Watchdog { + active: bool, + threshold: u64 +} + +pub const MAX_WATCHDOGS: usize = 16; + +#[derive(Debug)] +pub struct WatchdogSet { + watchdogs: [Watchdog; MAX_WATCHDOGS] +} + +impl WatchdogSet { + pub fn new() -> WatchdogSet { + WatchdogSet { + watchdogs: [Watchdog { active: false, threshold: 0 }; MAX_WATCHDOGS] + } + } + + pub fn set_ms(&mut self, interval: u64) -> Result { + for (index, watchdog) in self.watchdogs.iter_mut().enumerate() { + if !watchdog.active { + watchdog.active = true; + watchdog.threshold = clock::get_ms() + interval; + return Ok(index) + } + } + + Err(()) + } + + pub fn clear(&mut self, index: usize) { + if index < MAX_WATCHDOGS { + self.watchdogs[index].active = false + } + } + + pub fn expired(&self) -> bool { + self.watchdogs.iter() + .filter(|wd| wd.active) + .min_by_key(|wd| wd.threshold) + .map_or(false, |wd| clock::get_ms() > wd.threshold) + } +}