use crate::preempt::tcb::MAX_PTASKS;
use core::sync::atomic::{AtomicBool, Ordering};
static mut BUSY_CYCLES: [u64; MAX_PTASKS] = [0; MAX_PTASKS];
static mut LAST_DISPATCH: u64 = 0;
static mut BOOT_CYCLE: u64 = 0;
static mut WALLCLOCK_BOOT_US: u64 = 0;
static STARTED: AtomicBool = AtomicBool::new(false);
pub fn on_first_dispatch() {
crate::critical::enter(|| {
let now = crate::port::arch::cycle_count();
let now_us = crate::port::board::now_us();
unsafe {
BOOT_CYCLE = now;
LAST_DISPATCH = now;
WALLCLOCK_BOOT_US = now_us;
}
STARTED.store(true, Ordering::Release);
});
}
pub fn on_switch(outgoing: usize) {
if !STARTED.load(Ordering::Acquire) {
return;
}
crate::critical::enter(|| {
let now = crate::port::arch::cycle_count();
unsafe {
let elapsed = now.wrapping_sub(LAST_DISPATCH);
LAST_DISPATCH = now;
if outgoing < MAX_PTASKS {
BUSY_CYCLES[outgoing] = BUSY_CYCLES[outgoing].wrapping_add(elapsed);
}
}
});
}
pub fn busy_cycles(id: usize) -> u64 {
if id >= MAX_PTASKS {
return 0;
}
crate::critical::enter(|| unsafe { BUSY_CYCLES[id] })
}
pub fn busy_cycles_live(id: usize) -> u64 {
let completed = busy_cycles(id);
let in_progress = crate::critical::enter(|| {
let now = crate::port::arch::cycle_count();
unsafe { now.wrapping_sub(LAST_DISPATCH) }
});
completed.wrapping_add(in_progress)
}
pub fn cycles_since_boot() -> u64 {
if !STARTED.load(Ordering::Acquire) {
return 0;
}
crate::critical::enter(|| {
let now = crate::port::arch::cycle_count();
unsafe { now.wrapping_sub(BOOT_CYCLE) }
})
}
pub fn wallclock_us_since_boot() -> u64 {
if !STARTED.load(Ordering::Acquire) {
return 0;
}
crate::critical::enter(|| {
let now_us = crate::port::board::now_us();
unsafe { now_us.wrapping_sub(WALLCLOCK_BOOT_US) }
})
}
pub fn estimate_us_from_cycles(cycles: u64) -> u64 {
let total_cycles = cycles_since_boot();
if total_cycles == 0 {
return 0;
}
let total_us = wallclock_us_since_boot();
cycles.saturating_mul(total_us) / total_cycles
}
pub fn busy_percent(id: usize) -> u8 {
let total = cycles_since_boot();
if total == 0 {
return 0;
}
let busy = busy_cycles(id);
(busy.saturating_mul(100) / total).min(100) as u8
}
#[cfg(feature = "test-support")]
pub(crate) fn reset_for_test() {
crate::critical::enter(|| {
unsafe {
let base = core::ptr::addr_of_mut!(BUSY_CYCLES) as *mut u64;
for i in 0..MAX_PTASKS {
base.add(i).write(0);
}
LAST_DISPATCH = 0;
BOOT_CYCLE = 0;
WALLCLOCK_BOOT_US = 0;
}
});
STARTED.store(false, Ordering::Relaxed);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn busy_percent_zero_before_start() {
crate::kernel_test! {
assert_eq!(busy_percent(0), 0);
}
}
#[test]
fn accounts_switch_time() {
crate::kernel_test! {
on_first_dispatch();
for _ in 0..10 {
crate::port::arch::cycle_count();
}
on_switch(0);
assert!(busy_cycles(0) > 0);
assert_eq!(busy_cycles(1), 0);
}
}
}