use core::time::Duration; use alloc::format; use log::info; use crate::{ process::ExecutionContext, scheduler::scheduler, set_csr, vga::{Color, FONT_WIDTH, Vga, WIDTH} }; pub const IRQ_M_TIMER: u8 = 1 << 7; pub const IRQ_S_TIMER: u8 = 1 << 5; const CLINT_TIMER_CMP: *mut u64 = 0x02004000 as *mut u64; const CLINT_TIMER: *const u64 = 0x0200bff8 as *const u64; const TIMER_FREQUENCY: u64 = 10000000; // 10MHz const INTERRUPT_FREQUENCY: u64 = 20; // 20Hz static mut START_TIME: Instant = Instant(0); pub fn setup_timer_interrupt() { unsafe { START_TIME = Instant::now() }; set_csr!(sie, IRQ_S_TIMER); setup_next_timer_interrupt(); } pub fn setup_next_timer_interrupt() { unsafe { core::ptr::write_volatile( CLINT_TIMER_CMP, Instant::now().0 + TIMER_FREQUENCY / INTERRUPT_FREQUENCY, ); } } pub fn timer_interrupt(interrupt_state: ExecutionContext) -> usize { let current_time = elapsed_time_since_startup(); let seconds = current_time.as_secs(); let minutes = seconds / 60 % 60; let hours = seconds / 3600 % 60; let seconds = seconds % 60; let formated_time = format!("{:02}:{:02}:{:02}", hours, minutes, seconds); unsafe { Vga::draw_string( (WIDTH - formated_time.len() * FONT_WIDTH) as u16, 0, formated_time, Color::WHITE, Color::BLACK, ) }; scheduler(interrupt_state) } pub fn elapsed_time_since_startup() -> Duration { unsafe { START_TIME.elapsed() } } #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Instant(u64); impl Instant { pub fn now() -> Self { Instant(unsafe { core::ptr::read_volatile(CLINT_TIMER) }) } pub fn elapsed(&self) -> Duration { let now = Self::now(); Duration::from_nanos((now.0 - self.0) * (1_000_000_000 / TIMER_FREQUENCY)) } }