From a2f153249f926876e7169016f3dc8e861a9ef065 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 1 Apr 2023 20:48:23 +0200 Subject: [PATCH 1/2] Added nRF monotonics --- rtic-monotonics/CHANGELOG.md | 1 + rtic-monotonics/Cargo.toml | 23 +++ rtic-monotonics/src/lib.rs | 12 ++ rtic-monotonics/src/nrf.rs | 4 + rtic-monotonics/src/nrf/rtc.rs | 232 +++++++++++++++++++++++++++ rtic-monotonics/src/nrf/timer.rs | 259 +++++++++++++++++++++++++++++++ rtic-monotonics/src/rp2040.rs | 23 +++ rtic-monotonics/src/systick.rs | 23 ++- rtic-time/CHANGELOG.md | 2 + rtic-time/src/lib.rs | 4 +- rtic-time/src/monotonic.rs | 7 + 11 files changed, 587 insertions(+), 3 deletions(-) create mode 100644 rtic-monotonics/src/nrf.rs create mode 100644 rtic-monotonics/src/nrf/rtc.rs create mode 100644 rtic-monotonics/src/nrf/timer.rs diff --git a/rtic-monotonics/CHANGELOG.md b/rtic-monotonics/CHANGELOG.md index 816bc24783..2a08694d19 100644 --- a/rtic-monotonics/CHANGELOG.md +++ b/rtic-monotonics/CHANGELOG.md @@ -9,6 +9,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Added +- nRF52xxx, nRF9160, nRF5340 Timer and RTC monotonics - Interrupt tokens for `Systick` and `rp2040` to make sure an interrupt handler exists ### Changed diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml index c961c05997..503c22db86 100644 --- a/rtic-monotonics/Cargo.toml +++ b/rtic-monotonics/Cargo.toml @@ -23,8 +23,21 @@ fugit = { version = "0.3.6" } atomic-polyfill = "1" cfg-if = "1.0.0" cortex-m = { version = "0.7.6", optional = true } +critical-section = { version = "1", optional = true } + +# RP2040 rp2040-pac = { version = ">=0.2.0,<0.5", optional = true } +# nRF52 +nrf52810-pac = { version = "0.12.2", optional = true } +nrf52811-pac = { version = "0.12.2", optional = true } +nrf52832-pac = { version = "0.12.2", optional = true } +nrf52833-pac = { version = "0.12.2", optional = true } +nrf52840-pac = { version = "0.12.2", optional = true } +nrf5340-app-pac = { version = "0.12.2", optional = true } +nrf5340-net-pac = { version = "0.12.2", optional = true } +nrf9160-pac = { version = "0.12.2", optional = true } + [features] default = [] defmt = ["fugit/defmt"] @@ -36,3 +49,13 @@ systick-10khz = [] # Timer peripheral on the RP2040 rp2040 = ["dep:rp2040-pac"] + +# nRF Timers and RTC +nrf52810 = ["dep:nrf52810-pac", "dep:critical-section"] +nrf52811 = ["dep:nrf52811-pac", "dep:critical-section"] +nrf52832 = ["dep:nrf52832-pac", "dep:critical-section"] +nrf52833 = ["dep:nrf52833-pac", "dep:critical-section"] +nrf52840 = ["dep:nrf52840-pac", "dep:critical-section"] +nrf5340-app = ["dep:nrf5340-app-pac", "dep:critical-section"] +nrf5340-net = ["dep:nrf5340-net-pac", "dep:critical-section"] +nrf9160 = ["dep:nrf9160-pac", "dep:critical-section"] diff --git a/rtic-monotonics/src/lib.rs b/rtic-monotonics/src/lib.rs index 6143fd0e5e..fab0b2dbd1 100644 --- a/rtic-monotonics/src/lib.rs +++ b/rtic-monotonics/src/lib.rs @@ -14,6 +14,18 @@ pub mod systick; #[cfg(feature = "rp2040")] pub mod rp2040; +#[cfg(any( + feature = "nrf52810", + feature = "nrf52811", + feature = "nrf52832", + feature = "nrf52833", + feature = "nrf52840", + feature = "nrf5340-app", + feature = "nrf5340-net", + feature = "nrf9160", +))] +pub mod nrf; + /// This marker is implemented on an interrupt token to enforce that the right tokens /// are given to the correct monotonic implementation. /// diff --git a/rtic-monotonics/src/nrf.rs b/rtic-monotonics/src/nrf.rs new file mode 100644 index 0000000000..0f6b97370d --- /dev/null +++ b/rtic-monotonics/src/nrf.rs @@ -0,0 +1,4 @@ +//! Monotonic implementations for the nRF series of MCUs. + +pub mod rtc; +pub mod timer; diff --git a/rtic-monotonics/src/nrf/rtc.rs b/rtic-monotonics/src/nrf/rtc.rs new file mode 100644 index 0000000000..724c477c87 --- /dev/null +++ b/rtic-monotonics/src/nrf/rtc.rs @@ -0,0 +1,232 @@ +//! RTIC Monotonic impl for the nRF RTCs. +//! +//! # Example +//! +//! ``` +//! use rtic_monotonics::nrf::rtc::*; +//! +//! # async fn usage() { +//! # let rtc = unsafe { core::mem::transmute(()) }; +//! // Generate the required token +//! let token = rtic_monotonics::create_nrf_rtc0_monotonic_token!(); +//! +//! // Start the monotonic +//! Rtc0::start(rtc, token); +//! +//! loop { +//! // Use the monotonic +//! Rtc0::delay(100.millis()).await; +//! } +//! # } +//! ``` + +#[cfg(feature = "nrf52810")] +use nrf52810_pac::{self as pac, Interrupt, RTC0, RTC1}; +#[cfg(feature = "nrf52811")] +use nrf52811_pac::{self as pac, Interrupt, RTC0, RTC1}; +#[cfg(feature = "nrf52832")] +use nrf52832_pac::{self as pac, Interrupt, RTC0, RTC1, RTC2}; +#[cfg(feature = "nrf52833")] +use nrf52833_pac::{self as pac, Interrupt, RTC0, RTC1, RTC2}; +#[cfg(feature = "nrf52840")] +use nrf52840_pac::{self as pac, Interrupt, RTC0, RTC1, RTC2}; +#[cfg(feature = "nrf5340-app")] +use nrf5340_app_pac::{self as pac, Interrupt, RTC0_NS as RTC0, RTC1_NS as RTC1}; +#[cfg(feature = "nrf5340-net")] +use nrf5340_net_pac::{self as pac, Interrupt, RTC0_NS as RTC0, RTC1_NS as RTC1}; +#[cfg(feature = "nrf9160")] +use nrf9160_pac::{self as pac, Interrupt, RTC0_NS as RTC0, RTC1_NS as RTC1}; + +use super::super::Monotonic; +pub use super::super::{TimeoutError, TimerQueue}; +use atomic_polyfill::{AtomicU32, Ordering}; +use core::future::Future; +pub use fugit::{self, ExtU64}; + +#[doc(hidden)] +#[macro_export] +macro_rules! __internal_create_nrf_rtc_interrupt { + ($mono_timer:ident, $rtc:ident, $rtc_token:ident) => {{ + #[no_mangle] + #[allow(non_snake_case)] + unsafe extern "C" fn $rtc() { + $crate::nrf::rtc::$mono_timer::__tq().on_monotonic_interrupt(); + } + + pub struct $rtc_token; + + unsafe impl $crate::InterruptToken<$crate::nrf::rtc::$mono_timer> for $rtc_token {} + + $rtc_token + }}; +} + +/// Register the Rtc0 interrupt for the monotonic. +#[macro_export] +macro_rules! create_nrf_rtc0_monotonic_token { + () => {{ + $crate::__internal_create_nrf_rtc_interrupt!(Rtc0, RTC0, Rtc0Token) + }}; +} + +/// Register the Rtc1 interrupt for the monotonic. +#[macro_export] +macro_rules! create_nrf_rtc1_monotonic_token { + () => {{ + $crate::__internal_create_nrf_rtc_interrupt!(Rtc1, RTC1, Rtc1Token) + }}; +} + +/// Register the Rtc2 interrupt for the monotonic. +#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] +#[macro_export] +macro_rules! create_nrf_rtc2_monotonic_token { + () => {{ + $crate::__internal_create_nrf_rtc_interrupt!(Rtc2, RTC2, Rtc2Token) + }}; +} + +macro_rules! make_rtc { + ($mono_name:ident, $rtc:ident, $overflow:ident, $tq:ident) => { + /// Monotonic timer queue implementation. + pub struct $mono_name; + + static $overflow: AtomicU32 = AtomicU32::new(0); + static $tq: TimerQueue<$mono_name> = TimerQueue::new(); + + impl $mono_name { + /// Start the timer monotonic. + pub fn start(rtc: $rtc, _interrupt_token: impl crate::InterruptToken) { + unsafe { rtc.prescaler.write(|w| w.bits(0)) }; + rtc.intenset.write(|w| w.compare0().set().ovrflw().set()); + rtc.evtenset.write(|w| w.compare0().set().ovrflw().set()); + + rtc.tasks_clear.write(|w| unsafe { w.bits(1) }); + rtc.tasks_start.write(|w| unsafe { w.bits(1) }); + + $tq.initialize(Self {}); + + unsafe { pac::NVIC::unmask(Interrupt::$rtc) }; + } + + /// Used to access the underlying timer queue + #[doc(hidden)] + pub fn __tq() -> &'static TimerQueue<$mono_name> { + &$tq + } + + /// Timeout at a specific time. + #[inline] + pub async fn timeout_at( + instant: ::Instant, + future: F, + ) -> Result { + $tq.timeout_at(instant, future).await + } + + /// Timeout after a specific duration. + #[inline] + pub async fn timeout_after( + duration: ::Duration, + future: F, + ) -> Result { + $tq.timeout_after(duration, future).await + } + + /// Delay for some duration of time. + #[inline] + pub async fn delay(duration: ::Duration) { + $tq.delay(duration).await; + } + + /// Delay to some specific time instant. + #[inline] + pub async fn delay_until(instant: ::Instant) { + $tq.delay_until(instant).await; + } + + #[inline(always)] + fn is_overflow() -> bool { + let rtc = unsafe { &*$rtc::PTR }; + rtc.events_ovrflw.read().bits() == 1 + } + } + + #[cfg(feature = "embedded-hal-async")] + impl embedded_hal_async::delay::DelayUs for $mono_name { + type Error = core::convert::Infallible; + + #[inline] + async fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { + $tq.delay((us as u64).micros()).await; + Ok(()) + } + + #[inline] + async fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { + $tq.delay((ms as u64).millis()).await; + Ok(()) + } + } + + impl Monotonic for $mono_name { + const ZERO: Self::Instant = Self::Instant::from_ticks(0); + + type Instant = fugit::TimerInstantU64<32_768>; + type Duration = fugit::TimerDurationU64<32_768>; + + fn now() -> Self::Instant { + // In a critical section to not get a race between overflow updates and reading it + // and the flag here. + critical_section::with(|_| { + let rtc = unsafe { &*$rtc::PTR }; + let cnt = rtc.counter.read().bits() as u64; + let ovf = if Self::is_overflow() { + $overflow.load(Ordering::Relaxed) + 1 + } else { + $overflow.load(Ordering::Relaxed) + } as u64; + + Self::Instant::from_ticks((ovf << 24) | cnt) + }) + } + + fn on_interrupt() { + let rtc = unsafe { &*$rtc::PTR }; + if Self::is_overflow() { + $overflow.fetch_add(1, Ordering::SeqCst); + rtc.events_ovrflw.write(|w| unsafe { w.bits(0) }); + } + } + + // NOTE: To fix errata for RTC, if the release time is within 4 ticks + // we release as the RTC will not generate a compare interrupt... + fn should_dequeue_check(release_at: Self::Instant) -> bool { + Self::now() + ::Duration::from_ticks(4) >= release_at + } + + fn enable_timer() {} + + fn disable_timer() {} + + fn set_compare(instant: Self::Instant) { + let rtc = unsafe { &*$rtc::PTR }; + unsafe { rtc.cc[0].write(|w| w.bits(instant.ticks() as u32 & 0xffffff)) }; + } + + fn clear_compare_flag() { + let rtc = unsafe { &*$rtc::PTR }; + unsafe { rtc.events_compare[0].write(|w| w.bits(0)) }; + } + + fn pend_interrupt() { + pac::NVIC::pend(Interrupt::$rtc); + } + } + }; +} + +make_rtc!(Rtc0, RTC0, RTC0_OVERFLOWS, RTC0_TQ); +make_rtc!(Rtc1, RTC1, RTC1_OVERFLOWS, RTC1_TQ); +#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] +make_rtc!(Rtc2, RTC2, RTC2_OVERFLOWS, RTC2_TQ); diff --git a/rtic-monotonics/src/nrf/timer.rs b/rtic-monotonics/src/nrf/timer.rs new file mode 100644 index 0000000000..ad490cc38e --- /dev/null +++ b/rtic-monotonics/src/nrf/timer.rs @@ -0,0 +1,259 @@ +//! Monotonic impl for the 32-bit timers of the nRF series. +//! +//! # Example +//! +//! ``` +//! use rtic_monotonics::nrf::timer::*; +//! +//! # async fn usage() { +//! # let timer = unsafe { core::mem::transmute(()) }; +//! // Generate the required token +//! let token = rtic_monotonics::create_nrf_timer0_monotonic_token!(); +//! +//! // Start the monotonic +//! Timer0::start(timer, token); +//! +//! loop { +//! // Use the monotonic +//! Timer0::delay(100.millis()).await; +//! } +//! # } +//! ``` + +use super::super::Monotonic; +pub use super::super::{TimeoutError, TimerQueue}; +use atomic_polyfill::{AtomicU32, Ordering}; +use core::future::Future; +pub use fugit::{self, ExtU64}; + +#[cfg(feature = "nrf52810")] +use nrf52810_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2}; +#[cfg(feature = "nrf52811")] +use nrf52811_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2}; +#[cfg(feature = "nrf52832")] +use nrf52832_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; +#[cfg(feature = "nrf52833")] +use nrf52833_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; +#[cfg(feature = "nrf52840")] +use nrf52840_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; +#[cfg(feature = "nrf5340-app")] +use nrf5340_app_pac::{ + self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, +}; +#[cfg(feature = "nrf5340-net")] +use nrf5340_net_pac::{ + self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, +}; +#[cfg(feature = "nrf9160")] +use nrf9160_pac::{ + self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, +}; + +#[doc(hidden)] +#[macro_export] +macro_rules! __internal_create_nrf_timer_interrupt { + ($mono_timer:ident, $timer:ident, $timer_token:ident) => {{ + #[no_mangle] + #[allow(non_snake_case)] + unsafe extern "C" fn $timer() { + $crate::nrf::timer::$mono_timer::__tq().on_monotonic_interrupt(); + } + + pub struct $timer_token; + + unsafe impl $crate::InterruptToken<$crate::nrf::timer::$mono_timer> for $timer_token {} + + $timer_token + }}; +} + +/// Register the Timer0 interrupt for the monotonic. +#[macro_export] +macro_rules! create_nrf_timer0_monotonic_token { + () => {{ + $crate::__internal_create_nrf_timer_interrupt!(Timer0, TIMER0, Timer0Token) + }}; +} + +/// Register the Timer1 interrupt for the monotonic. +#[macro_export] +macro_rules! create_nrf_timer1_monotonic_token { + () => {{ + $crate::__internal_create_nrf_timer_interrupt!(Timer1, TIMER1, Timer1Token) + }}; +} + +/// Register the Timer2 interrupt for the monotonic. +#[macro_export] +macro_rules! create_nrf_timer2_monotonic_token { + () => {{ + $crate::__internal_create_nrf_timer_interrupt!(Timer2, TIMER2, Timer2Token) + }}; +} + +/// Register the Timer3 interrupt for the monotonic. +#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] +#[macro_export] +macro_rules! create_nrf_timer3_monotonic_token { + () => {{ + $crate::__internal_create_nrf_timer_interrupt!(Timer3, TIMER3, Timer3Token) + }}; +} + +/// Register the Timer4 interrupt for the monotonic. +#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] +#[macro_export] +macro_rules! create_nrf_timer4_monotonic_token { + () => {{ + $crate::__internal_create_nrf_timer_interrupt!(Timer4, TIMER4, Timer4Token) + }}; +} + +macro_rules! make_timer { + ($mono_name:ident, $timer:ident, $overflow:ident, $tq:ident) => { + /// Monotonic timer queue implementation. + pub struct $mono_name; + + static $overflow: AtomicU32 = AtomicU32::new(0); + static $tq: TimerQueue<$mono_name> = TimerQueue::new(); + + impl $mono_name { + /// Start the timer monotonic. + pub fn start(timer: $timer, _interrupt_token: impl crate::InterruptToken) { + // 1 MHz + timer.prescaler.write(|w| unsafe { w.prescaler().bits(4) }); + timer.bitmode.write(|w| w.bitmode()._32bit()); + timer + .intenset + .modify(|_, w| w.compare0().set().compare1().set()); + timer.cc[1].write(|w| unsafe { w.cc().bits(0) }); // Overflow + timer.tasks_clear.write(|w| unsafe { w.bits(1) }); + timer.tasks_start.write(|w| unsafe { w.bits(1) }); + + $tq.initialize(Self {}); + + timer.events_compare[0].write(|w| w); + timer.events_compare[1].write(|w| w); + + unsafe { pac::NVIC::unmask(Interrupt::$timer) }; + } + + /// Used to access the underlying timer queue + #[doc(hidden)] + pub fn __tq() -> &'static TimerQueue<$mono_name> { + &$tq + } + + /// Timeout at a specific time. + #[inline] + pub async fn timeout_at( + instant: ::Instant, + future: F, + ) -> Result { + $tq.timeout_at(instant, future).await + } + + /// Timeout after a specific duration. + #[inline] + pub async fn timeout_after( + duration: ::Duration, + future: F, + ) -> Result { + $tq.timeout_after(duration, future).await + } + + /// Delay for some duration of time. + #[inline] + pub async fn delay(duration: ::Duration) { + $tq.delay(duration).await; + } + + /// Delay to some specific time instant. + #[inline] + pub async fn delay_until(instant: ::Instant) { + $tq.delay_until(instant).await; + } + } + + #[cfg(feature = "embedded-hal-async")] + impl embedded_hal_async::delay::DelayUs for $mono_name { + type Error = core::convert::Infallible; + + #[inline] + async fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { + $tq.delay((us as u64).micros()).await; + Ok(()) + } + + #[inline] + async fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { + $tq.delay((ms as u64).millis()).await; + Ok(()) + } + } + + impl Monotonic for $mono_name { + const ZERO: Self::Instant = Self::Instant::from_ticks(0); + + type Instant = fugit::TimerInstantU64<1_000_000>; + type Duration = fugit::TimerDurationU64<1_000_000>; + + fn now() -> Self::Instant { + // In a critical section to not get a race between overflow updates and reading it + // and the flag here. + critical_section::with(|_| { + let timer = unsafe { &*$timer::PTR }; + timer.tasks_capture[2].write(|w| unsafe { w.bits(1) }); + + let unhandled_overflow = if timer.events_compare[1].read().bits() & 1 != 0 { + // The overflow has not been handled yet, so add an extra to the read overflow. + 1 + } else { + 0 + }; + + Self::Instant::from_ticks( + (unhandled_overflow + $overflow.load(Ordering::Relaxed) as u64) << 32 + | timer.cc[2].read().bits() as u64, + ) + }) + } + + fn on_interrupt() { + let timer = unsafe { &*$timer::PTR }; + + // If there is a compare match on channel 1, it is an overflow + if timer.events_compare[1].read().bits() & 1 != 0 { + timer.events_compare[1].write(|w| w); + $overflow.fetch_add(1, Ordering::SeqCst); + } + } + + fn enable_timer() {} + + fn disable_timer() {} + + fn set_compare(instant: Self::Instant) { + let timer = unsafe { &*$timer::PTR }; + timer.cc[0].write(|w| unsafe { w.cc().bits(instant.ticks() as u32) }); + } + + fn clear_compare_flag() { + let timer = unsafe { &*$timer::PTR }; + timer.events_compare[0].write(|w| w); + } + + fn pend_interrupt() { + pac::NVIC::pend(Interrupt::$timer); + } + } + }; +} + +make_timer!(Timer0, TIMER0, TIMER0_OVERFLOWS, TIMER0_TQ); +make_timer!(Timer1, TIMER1, TIMER1_OVERFLOWS, TIMER1_TQ); +make_timer!(Timer2, TIMER2, TIMER2_OVERFLOWS, TIMER2_TQ); +#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] +make_timer!(Timer3, TIMER3, TIMER3_OVERFLOWS, TIMER3_TQ); +#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] +make_timer!(Timer4, TIMER4, TIMER4_OVERFLOWS, TIMER4_TQ); diff --git a/rtic-monotonics/src/rp2040.rs b/rtic-monotonics/src/rp2040.rs index 9d2f4f352c..5e880cd898 100644 --- a/rtic-monotonics/src/rp2040.rs +++ b/rtic-monotonics/src/rp2040.rs @@ -1,4 +1,25 @@ //! A monotonic implementation for RP2040's Timer peripheral. +//! +//! # Example +//! +//! ``` +//! use rtic_monotonics::rp2040::*; +//! +//! # async fn usage() { +//! # let timer = unsafe { core::mem::transmute(()) }; +//! # let mut resets = unsafe { core::mem::transmute(()) }; +//! // Generate the required token +//! let token = rtic_monotonics::create_rp2040_monotonic_token!(); +//! +//! // Start the monotonic +//! Timer::start(timer, &mut resets, token); +//! +//! loop { +//! // Use the monotonic +//! Timer::delay(100.millis()).await; +//! } +//! # } +//! ``` use super::Monotonic; pub use super::{TimeoutError, TimerQueue}; @@ -41,6 +62,7 @@ impl Timer { } /// Timeout at a specific time. + #[inline] pub async fn timeout_at( instant: ::Instant, future: F, @@ -64,6 +86,7 @@ impl Timer { } /// Delay to some specific time instant. + #[inline] pub async fn delay_until(instant: ::Instant) { TIMER_QUEUE.delay_until(instant).await; } diff --git a/rtic-monotonics/src/systick.rs b/rtic-monotonics/src/systick.rs index f4345d434d..f1d49daf33 100644 --- a/rtic-monotonics/src/systick.rs +++ b/rtic-monotonics/src/systick.rs @@ -1,4 +1,25 @@ -//! ... +//! A monotonics based on Cortex-M SysTick. Note that this implementation is inefficient as it +//! ticks, and generates interrupts, at a constant rate. +//! +//! # Example +//! +//! ``` +//! use rtic_monotonics::systick::*; +//! +//! # async fn usage() { +//! # let systick = unsafe { core::mem::transmute(()) }; +//! // Generate the required token +//! let systick_token = rtic_monotonics::create_systick_token!(); +//! +//! // Start the monotonic +//! Systick::start(systick, 12_000_000, systick_token); +//! +//! loop { +//! // Use the monotonic +//! Systick::delay(100.millis()).await; +//! } +//! # } +//! ``` use super::Monotonic; pub use super::{TimeoutError, TimerQueue}; diff --git a/rtic-time/CHANGELOG.md b/rtic-time/CHANGELOG.md index d3a9d846ee..3a2fb9104a 100644 --- a/rtic-time/CHANGELOG.md +++ b/rtic-time/CHANGELOG.md @@ -9,6 +9,8 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Added +- `should_dequeue` to the `Monotonic` trait to handle bugged timers + ### Changed ### Fixed diff --git a/rtic-time/src/lib.rs b/rtic-time/src/lib.rs index 78b57a4f16..9bf148558a 100644 --- a/rtic-time/src/lib.rs +++ b/rtic-time/src/lib.rs @@ -131,7 +131,7 @@ impl TimerQueue { let head = self.queue.pop_if(|head| { release_at = Some(head.release_at); - let should_pop = Mono::now() >= head.release_at; + let should_pop = Mono::should_dequeue_check(head.release_at); head.was_poped.store(should_pop, Ordering::Relaxed); should_pop @@ -145,7 +145,7 @@ impl TimerQueue { Mono::enable_timer(); Mono::set_compare(instant); - if Mono::now() >= instant { + if Mono::should_dequeue_check(instant) { // The time for the next instant passed while handling it, // continue dequeueing continue; diff --git a/rtic-time/src/monotonic.rs b/rtic-time/src/monotonic.rs index 9b3742fa87..513cc07bb8 100644 --- a/rtic-time/src/monotonic.rs +++ b/rtic-time/src/monotonic.rs @@ -33,6 +33,13 @@ pub trait Monotonic { /// queue in RTIC checks this. fn set_compare(instant: Self::Instant); + /// Override for the dequeue check, override with timers that have bugs. + /// + /// E.g. nRF52 RTCs needs to be dequeued if the time is within 4 ticks. + fn should_dequeue_check(release_at: Self::Instant) -> bool { + ::now() >= release_at + } + /// Clear the compare interrupt flag. fn clear_compare_flag(); From aeec8bd41bdf3d57098902407ec320f59365641a Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 2 Apr 2023 20:32:10 +0200 Subject: [PATCH 2/2] Add setting of priority to interrupts --- rtic-monotonics/Cargo.toml | 18 ++++++++-------- rtic-monotonics/src/lib.rs | 36 ++++++++++++++++++++++++++++++++ rtic-monotonics/src/nrf/rtc.rs | 33 ++++++++++++++++++----------- rtic-monotonics/src/nrf/timer.rs | 33 ++++++++++++++++++----------- rtic-monotonics/src/rp2040.rs | 32 ++++++++++++++++------------ rtic-monotonics/src/systick.rs | 25 ++++++++++++---------- 6 files changed, 120 insertions(+), 57 deletions(-) diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml index 503c22db86..728e171dc4 100644 --- a/rtic-monotonics/Cargo.toml +++ b/rtic-monotonics/Cargo.toml @@ -48,14 +48,14 @@ systick-100hz = [] systick-10khz = [] # Timer peripheral on the RP2040 -rp2040 = ["dep:rp2040-pac"] +rp2040 = ["dep:cortex-m", "dep:rp2040-pac"] # nRF Timers and RTC -nrf52810 = ["dep:nrf52810-pac", "dep:critical-section"] -nrf52811 = ["dep:nrf52811-pac", "dep:critical-section"] -nrf52832 = ["dep:nrf52832-pac", "dep:critical-section"] -nrf52833 = ["dep:nrf52833-pac", "dep:critical-section"] -nrf52840 = ["dep:nrf52840-pac", "dep:critical-section"] -nrf5340-app = ["dep:nrf5340-app-pac", "dep:critical-section"] -nrf5340-net = ["dep:nrf5340-net-pac", "dep:critical-section"] -nrf9160 = ["dep:nrf9160-pac", "dep:critical-section"] +nrf52810 = ["dep:cortex-m", "dep:nrf52810-pac", "dep:critical-section"] +nrf52811 = ["dep:cortex-m", "dep:nrf52811-pac", "dep:critical-section"] +nrf52832 = ["dep:cortex-m", "dep:nrf52832-pac", "dep:critical-section"] +nrf52833 = ["dep:cortex-m", "dep:nrf52833-pac", "dep:critical-section"] +nrf52840 = ["dep:cortex-m", "dep:nrf52840-pac", "dep:critical-section"] +nrf5340-app = ["dep:cortex-m", "dep:nrf5340-app-pac", "dep:critical-section"] +nrf5340-net = ["dep:cortex-m", "dep:nrf5340-net-pac", "dep:critical-section"] +nrf9160 = ["dep:cortex-m", "dep:nrf9160-pac", "dep:critical-section"] diff --git a/rtic-monotonics/src/lib.rs b/rtic-monotonics/src/lib.rs index fab0b2dbd1..04ce4e2451 100644 --- a/rtic-monotonics/src/lib.rs +++ b/rtic-monotonics/src/lib.rs @@ -26,6 +26,42 @@ pub mod rp2040; ))] pub mod nrf; +#[allow(dead_code)] +pub(crate) const fn cortex_logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 { + ((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits) +} + +#[cfg(any( + feature = "rp2040", + feature = "nrf52810", + feature = "nrf52811", + feature = "nrf52832", + feature = "nrf52833", + feature = "nrf52840", + feature = "nrf5340-app", + feature = "nrf5340-net", + feature = "nrf9160", +))] +pub(crate) unsafe fn set_monotonic_prio( + prio_bits: u8, + interrupt: impl cortex_m::interrupt::InterruptNumber, +) { + extern "C" { + static RTIC_ASYNC_MAX_LOGICAL_PRIO: u8; + } + + let max_prio = RTIC_ASYNC_MAX_LOGICAL_PRIO.max(1).min(1 << prio_bits); + + let hw_prio = crate::cortex_logical2hw(max_prio, prio_bits); + + // We take ownership of the entire IRQ and all settings to it, we only change settings + // for the IRQ we control. + // This will also compile-error in case the NVIC changes in size. + let mut nvic: cortex_m::peripheral::NVIC = core::mem::transmute(()); + + nvic.set_priority(interrupt, hw_prio); +} + /// This marker is implemented on an interrupt token to enforce that the right tokens /// are given to the correct monotonic implementation. /// diff --git a/rtic-monotonics/src/nrf/rtc.rs b/rtic-monotonics/src/nrf/rtc.rs index 724c477c87..03610c015b 100644 --- a/rtic-monotonics/src/nrf/rtc.rs +++ b/rtic-monotonics/src/nrf/rtc.rs @@ -5,19 +5,22 @@ //! ``` //! use rtic_monotonics::nrf::rtc::*; //! -//! # async fn usage() { -//! # let rtc = unsafe { core::mem::transmute(()) }; -//! // Generate the required token -//! let token = rtic_monotonics::create_nrf_rtc0_monotonic_token!(); +//! fn init() { +//! # // This is normally provided by the selected PAC +//! # let rtc = unsafe { core::mem::transmute(()) }; +//! // Generate the required token +//! let token = rtic_monotonics::create_nrf_rtc0_monotonic_token!(); //! -//! // Start the monotonic -//! Rtc0::start(rtc, token); -//! -//! loop { -//! // Use the monotonic -//! Rtc0::delay(100.millis()).await; +//! // Start the monotonic +//! Rtc0::start(rtc, token); +//! } +//! +//! async fn usage() { +//! loop { +//! // Use the monotonic +//! Rtc0::delay(100.millis()).await; +//! } //! } -//! # } //! ``` #[cfg(feature = "nrf52810")] @@ -106,7 +109,13 @@ macro_rules! make_rtc { $tq.initialize(Self {}); - unsafe { pac::NVIC::unmask(Interrupt::$rtc) }; + // SAFETY: We take full ownership of the peripheral and interrupt vector, + // plus we are not using any external shared resources so we won't impact + // basepri/source masking based critical sections. + unsafe { + crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, Interrupt::$rtc); + pac::NVIC::unmask(Interrupt::$rtc); + } } /// Used to access the underlying timer queue diff --git a/rtic-monotonics/src/nrf/timer.rs b/rtic-monotonics/src/nrf/timer.rs index ad490cc38e..db5316f237 100644 --- a/rtic-monotonics/src/nrf/timer.rs +++ b/rtic-monotonics/src/nrf/timer.rs @@ -5,19 +5,22 @@ //! ``` //! use rtic_monotonics::nrf::timer::*; //! -//! # async fn usage() { -//! # let timer = unsafe { core::mem::transmute(()) }; -//! // Generate the required token -//! let token = rtic_monotonics::create_nrf_timer0_monotonic_token!(); +//! fn init() { +//! # // This is normally provided by the selected PAC +//! # let timer = unsafe { core::mem::transmute(()) }; +//! // Generate the required token +//! let token = rtic_monotonics::create_nrf_timer0_monotonic_token!(); //! -//! // Start the monotonic -//! Timer0::start(timer, token); -//! -//! loop { -//! // Use the monotonic -//! Timer0::delay(100.millis()).await; +//! // Start the monotonic +//! Timer0::start(timer, token); +//! } +//! +//! async fn usage() { +//! loop { +//! // Use the monotonic +//! Timer0::delay(100.millis()).await; +//! } //! } -//! # } //! ``` use super::super::Monotonic; @@ -135,7 +138,13 @@ macro_rules! make_timer { timer.events_compare[0].write(|w| w); timer.events_compare[1].write(|w| w); - unsafe { pac::NVIC::unmask(Interrupt::$timer) }; + // SAFETY: We take full ownership of the peripheral and interrupt vector, + // plus we are not using any external shared resources so we won't impact + // basepri/source masking based critical sections. + unsafe { + crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, Interrupt::$timer); + pac::NVIC::unmask(Interrupt::$timer); + } } /// Used to access the underlying timer queue diff --git a/rtic-monotonics/src/rp2040.rs b/rtic-monotonics/src/rp2040.rs index 5e880cd898..c656afca74 100644 --- a/rtic-monotonics/src/rp2040.rs +++ b/rtic-monotonics/src/rp2040.rs @@ -5,20 +5,23 @@ //! ``` //! use rtic_monotonics::rp2040::*; //! -//! # async fn usage() { -//! # let timer = unsafe { core::mem::transmute(()) }; -//! # let mut resets = unsafe { core::mem::transmute(()) }; -//! // Generate the required token -//! let token = rtic_monotonics::create_rp2040_monotonic_token!(); +//! fn init() { +//! # // This is normally provided by the selected PAC +//! # let timer = unsafe { core::mem::transmute(()) }; +//! # let mut resets = unsafe { core::mem::transmute(()) }; +//! // Generate the required token +//! let token = rtic_monotonics::create_rp2040_monotonic_token!(); //! -//! // Start the monotonic -//! Timer::start(timer, &mut resets, token); -//! -//! loop { -//! // Use the monotonic -//! Timer::delay(100.millis()).await; +//! // Start the monotonic +//! Timer::start(timer, &mut resets, token); +//! } +//! +//! async fn usage() { +//! loop { +//! // Use the monotonic +//! Timer::delay(100.millis()).await; +//! } //! } -//! # } //! ``` use super::Monotonic; @@ -43,7 +46,10 @@ impl Timer { TIMER_QUEUE.initialize(Self {}); - unsafe { NVIC::unmask(Interrupt::TIMER_IRQ_0) }; + unsafe { + crate::set_monotonic_prio(rp2040_pac::NVIC_PRIO_BITS, Interrupt::TIMER_IRQ_0); + NVIC::unmask(Interrupt::TIMER_IRQ_0); + } } fn timer() -> &'static timer::RegisterBlock { diff --git a/rtic-monotonics/src/systick.rs b/rtic-monotonics/src/systick.rs index f1d49daf33..6a28a11774 100644 --- a/rtic-monotonics/src/systick.rs +++ b/rtic-monotonics/src/systick.rs @@ -6,19 +6,22 @@ //! ``` //! use rtic_monotonics::systick::*; //! -//! # async fn usage() { -//! # let systick = unsafe { core::mem::transmute(()) }; -//! // Generate the required token -//! let systick_token = rtic_monotonics::create_systick_token!(); +//! fn init() { +//! # // This is normally provided by the selected PAC +//! # let systick = unsafe { core::mem::transmute(()) }; +//! // Generate the required token +//! let systick_token = rtic_monotonics::create_systick_token!(); //! -//! // Start the monotonic -//! Systick::start(systick, 12_000_000, systick_token); -//! -//! loop { -//! // Use the monotonic -//! Systick::delay(100.millis()).await; +//! // Start the monotonic +//! Systick::start(systick, 12_000_000, systick_token); +//! } +//! +//! async fn usage() { +//! loop { +//! // Use the monotonic +//! Systick::delay(100.millis()).await; +//! } //! } -//! # } //! ``` use super::Monotonic;