Added nRF monotonics

This commit is contained in:
Emil Fresk 2023-04-01 20:48:23 +02:00
parent 064cf19265
commit a2f153249f
11 changed files with 587 additions and 3 deletions

View file

@ -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

View file

@ -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"]

View file

@ -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.
///

View file

@ -0,0 +1,4 @@
//! Monotonic implementations for the nRF series of MCUs.
pub mod rtc;
pub mod timer;

View file

@ -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<Self>) {
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<F: Future>(
instant: <Self as Monotonic>::Instant,
future: F,
) -> Result<F::Output, TimeoutError> {
$tq.timeout_at(instant, future).await
}
/// Timeout after a specific duration.
#[inline]
pub async fn timeout_after<F: Future>(
duration: <Self as Monotonic>::Duration,
future: F,
) -> Result<F::Output, TimeoutError> {
$tq.timeout_after(duration, future).await
}
/// Delay for some duration of time.
#[inline]
pub async fn delay(duration: <Self as Monotonic>::Duration) {
$tq.delay(duration).await;
}
/// Delay to some specific time instant.
#[inline]
pub async fn delay_until(instant: <Self as Monotonic>::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() + <Self as Monotonic>::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);

View file

@ -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<Self>) {
// 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<F: Future>(
instant: <Self as Monotonic>::Instant,
future: F,
) -> Result<F::Output, TimeoutError> {
$tq.timeout_at(instant, future).await
}
/// Timeout after a specific duration.
#[inline]
pub async fn timeout_after<F: Future>(
duration: <Self as Monotonic>::Duration,
future: F,
) -> Result<F::Output, TimeoutError> {
$tq.timeout_after(duration, future).await
}
/// Delay for some duration of time.
#[inline]
pub async fn delay(duration: <Self as Monotonic>::Duration) {
$tq.delay(duration).await;
}
/// Delay to some specific time instant.
#[inline]
pub async fn delay_until(instant: <Self as Monotonic>::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);

View file

@ -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<F: Future>(
instant: <Self as Monotonic>::Instant,
future: F,
@ -64,6 +86,7 @@ impl Timer {
}
/// Delay to some specific time instant.
#[inline]
pub async fn delay_until(instant: <Self as Monotonic>::Instant) {
TIMER_QUEUE.delay_until(instant).await;
}

View file

@ -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};

View file

@ -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

View file

@ -131,7 +131,7 @@ impl<Mono: Monotonic> TimerQueue<Mono> {
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<Mono: Monotonic> TimerQueue<Mono> {
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;

View file

@ -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 {
<Self as Monotonic>::now() >= release_at
}
/// Clear the compare interrupt flag.
fn clear_compare_flag();