diff --git a/src/bsp/aarch64/mod.rs b/src/bsp/aarch64/mod.rs new file mode 100644 index 0000000..1f225ed --- /dev/null +++ b/src/bsp/aarch64/mod.rs @@ -0,0 +1,3 @@ +//! Modules specific to the aarch64 architecture. + +pub mod time; diff --git a/src/bsp/aarch64/time.rs b/src/bsp/aarch64/time.rs new file mode 100644 index 0000000..679c742 --- /dev/null +++ b/src/bsp/aarch64/time.rs @@ -0,0 +1,88 @@ +//! The implementation of the time management trait for aarch64. + +use core::arch::asm; +use core::time::Duration; + +use crate::traits::time::TimeManager; +use crate::println; + +const NS_PER_S: u64 = 1_000_000_000; +const CNTP_CTL_EL0_ENABLE_MASK: u64 = 0b001; +const CNTP_CTL_EL0_IMASK_MASK: u64 = 0b010; +const CNTP_CTL_EL0_ISTATUS_MASK: u64 = 0b100; + +/// AARCH64 Timer. +struct Timer; + +/// The global timer. +static TIME_MANAGER: Timer = Timer; + +/// Return a reference to the global time manager. +pub fn time_manager() -> &'static impl TimeManager { + &TIME_MANAGER +} + +impl TimeManager for Timer { + fn frequency(&self) -> u64 { + let freq: u64; + unsafe { + asm!("mrs {0:x}, CNTFRQ_EL0", out(reg) freq, options(nomem, nostack)); + } + freq + } + + fn uptime(&self) -> Duration { + let ticks: u64; + unsafe { + // Protect against out of order execution + // https://developer.arm.com/documentation/dui0802/b/CIHGHHIE + // In effect this flush the processor pipeline + asm!("ISB", options(nostack)); + asm!("mrs {0:x}, CNTPCT_EL0", out(reg) ticks, options(nomem, nostack)); + } + Duration::from_nanos((ticks*NS_PER_S)/self.frequency()) + } + + fn sleep(&self, duration: Duration) { + if duration.as_nanos() == 0 { + return; + } + + let tmp = match self.frequency().checked_mul(duration.as_nanos() as u64) { + None => { + println!("Cannot sleep this long, skipping"); + return; + } + Some(tmp) => tmp, + }; + let ticks = tmp / NS_PER_S; + + if ticks > u32::max_value().into() { + println!("Cannot wait more than 2^32-1 ticks on aarch64, skipping"); + return; + } + if ticks == 0 { + println!("Duration is smaller than a clock tick, skipping."); + return; + } + + // Enable the timer and mask (disable) the interrupt + let mut ctl: u64 = CNTP_CTL_EL0_ENABLE_MASK + CNTP_CTL_EL0_IMASK_MASK; + unsafe { + // set the register values for the timer + asm!("msr CNTP_TVAL_EL0, {0:x}", in(reg) ticks, options(nomem, nostack)); + asm!("msr CNTP_CTL_EL0, {0:x}", in(reg) ctl, options(nomem, nostack)); + } + // Loop waiting for the timer to run out + while (ctl & CNTP_CTL_EL0_ISTATUS_MASK) == 0 { + unsafe { + asm!("mrs {0:x}, CNTP_CTL_EL0", out(reg) ctl, options(nomem, nostack)); + } + } + // Disable the counter + ctl &= !CNTP_CTL_EL0_ENABLE_MASK; + unsafe { + asm!("msr CNTP_CTL_EL0, {0:x}", in(reg) ctl, options(nomem, nostack)); + } + } +} diff --git a/src/bsp/mod.rs b/src/bsp/mod.rs index 89e0ffe..0c1a74c 100644 --- a/src/bsp/mod.rs +++ b/src/bsp/mod.rs @@ -6,3 +6,6 @@ pub mod rpi3; #[cfg(feature = "target_rpi4")] pub mod rpi4; + +#[cfg(target_arch = "aarch64")] +pub mod aarch64; diff --git a/src/main.rs b/src/main.rs index 7683705..1853252 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,11 +26,17 @@ use core::arch::global_asm; #[cfg(not(test))] use core::arch::asm; +use core::time::Duration; + // TODO: handle this with features #[cfg(not(test))] use crate::bsp::rpi3::uart::console; #[cfg(not(test))] use crate::bsp::rpi3::gpio; +#[cfg(not(test))] +use crate::bsp::aarch64::time::time_manager; + +use crate::traits::time::TimeManager; // TODO: move this to BSP /// Pause the core with a infinit loop @@ -93,20 +99,18 @@ pub unsafe fn _start_rust() -> ! { Err(err) => println!("Failled to set pin: {err}"), } loop { + let uptime = time_manager().uptime(); match gpio::set_pin_output_state(21, gpio::PinOutputState::High) { - Ok(()) => (), + Ok(()) => println!("[{:>6}.{:06}] OUTPUT 21 UP", uptime.as_secs(), uptime.subsec_micros()), Err(_err) => println!("Failled to set pin 21 to High"), } - for _ in 0..5000000 { - asm!("nop"); - } + time_manager().sleep(Duration::from_secs(1)); + let uptime = time_manager().uptime(); match gpio::set_pin_output_state(21, gpio::PinOutputState::Low) { - Ok(()) => (), + Ok(()) => println!("[{:>6}.{:06}] OUTPUT 21 DOWN", uptime.as_secs(), uptime.subsec_micros()), Err(_err) => println!("Failled to set pin 21 to Low"), } - for _ in 0..5000000 { - asm!("nop"); - } + time_manager().sleep(Duration::from_secs(1)); } /* println!("Hello there"); diff --git a/src/traits/mod.rs b/src/traits/mod.rs index 00561f6..0b45ab8 100644 --- a/src/traits/mod.rs +++ b/src/traits/mod.rs @@ -10,3 +10,4 @@ pub mod console; pub mod synchronization; +pub mod time; diff --git a/src/traits/time.rs b/src/traits/time.rs new file mode 100644 index 0000000..983a902 --- /dev/null +++ b/src/traits/time.rs @@ -0,0 +1,16 @@ +//! Traits for time management primitives. + +use core::time::Duration; + +pub trait TimeManager { + + /// Return the clock frequency, meaning the resolution of the + /// time mesurement. + fn frequency(&self) -> u64; + + /// The uptime since last power-on. + fn uptime(&self) -> Duration; + + /// Sleep for the duration. + fn sleep(&self, duration: Duration); +}