diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2023-03-04 21:10:24 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-04 21:10:24 +0000 |
| commit | 7c7d6558f6d9c50fbb4d2487c98c9a5be15f2f7b (patch) | |
| tree | 80a47f0dc40059014e9448c4c2eb34c54dff45fe /rtic-monotonics | |
| parent | 1c5db277e4161470136dbd2a11e914ff1d383581 (diff) | |
| parent | 98c5490d94950608d31cd5ad9dd260f2f853735c (diff) | |
Merge #694
694: RTIC 2 r=AfoHT a=korken89
Co-authored-by: Emil Fresk <emil.fresk@gmail.com>
Co-authored-by: Per Lindgren <per.lindgren@ltu.se>
Diffstat (limited to 'rtic-monotonics')
| -rw-r--r-- | rtic-monotonics/.gitignore | 6 | ||||
| -rw-r--r-- | rtic-monotonics/CHANGELOG.md | 16 | ||||
| -rw-r--r-- | rtic-monotonics/Cargo.toml | 38 | ||||
| -rw-r--r-- | rtic-monotonics/rust-toolchain.toml | 4 | ||||
| -rw-r--r-- | rtic-monotonics/src/lib.rs | 15 | ||||
| -rw-r--r-- | rtic-monotonics/src/rp2040.rs | 143 | ||||
| -rw-r--r-- | rtic-monotonics/src/systick.rs | 163 |
7 files changed, 385 insertions, 0 deletions
diff --git a/rtic-monotonics/.gitignore b/rtic-monotonics/.gitignore new file mode 100644 index 0000000..c400256 --- /dev/null +++ b/rtic-monotonics/.gitignore @@ -0,0 +1,6 @@ +**/*.rs.bk +.#* +.gdb_history +/target +Cargo.lock +*.hex diff --git a/rtic-monotonics/CHANGELOG.md b/rtic-monotonics/CHANGELOG.md new file mode 100644 index 0000000..d3a9d84 --- /dev/null +++ b/rtic-monotonics/CHANGELOG.md @@ -0,0 +1,16 @@ +# Change Log + +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/). + +For each category, *Added*, *Changed*, *Fixed* add new entries at the top! + +## [Unreleased] + +### Added + +### Changed + +### Fixed + +## [v1.0.0] - 2023-xx-xx diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml new file mode 100644 index 0000000..c961c05 --- /dev/null +++ b/rtic-monotonics/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "rtic-monotonics" +version = "1.0.0-alpha.0" + +edition = "2021" +authors = [ + "The Real-Time Interrupt-driven Concurrency developers", + "Emil Fresk <emil.fresk@gmail.com>", + "Henrik Tjäder <henrik@tjaders.com>", + "Jorge Aparicio <jorge@japaric.io>", + "Per Lindgren <per.lindgren@ltu.se>", +] +categories = ["concurrency", "embedded", "no-std", "asynchronous"] +description = "rtic-monotonics lib TODO" +license = "MIT OR Apache-2.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rtic-time = { version = "1.0.0-alpha.0", path = "../rtic-time" } +embedded-hal-async = { version = "0.2.0-alpha.0", optional = true } +fugit = { version = "0.3.6" } +atomic-polyfill = "1" +cfg-if = "1.0.0" +cortex-m = { version = "0.7.6", optional = true } +rp2040-pac = { version = ">=0.2.0,<0.5", optional = true } + +[features] +default = [] +defmt = ["fugit/defmt"] + +# Systick on Cortex-M, default 1 kHz +cortex-m-systick = ["dep:cortex-m"] +systick-100hz = [] +systick-10khz = [] + +# Timer peripheral on the RP2040 +rp2040 = ["dep:rp2040-pac"] diff --git a/rtic-monotonics/rust-toolchain.toml b/rtic-monotonics/rust-toolchain.toml new file mode 100644 index 0000000..e28b55d --- /dev/null +++ b/rtic-monotonics/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "nightly" +components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] +targets = [ "thumbv6m-none-eabi", "thumbv7m-none-eabi" ] diff --git a/rtic-monotonics/src/lib.rs b/rtic-monotonics/src/lib.rs new file mode 100644 index 0000000..a4a1f42 --- /dev/null +++ b/rtic-monotonics/src/lib.rs @@ -0,0 +1,15 @@ +//! Crate + +#![no_std] +#![deny(missing_docs)] +//deny_warnings_placeholder_for_ci +#![allow(incomplete_features)] +#![feature(async_fn_in_trait)] + +pub use rtic_time::{Monotonic, TimeoutError, TimerQueue}; + +#[cfg(feature = "cortex-m-systick")] +pub mod systick; + +#[cfg(feature = "rp2040")] +pub mod rp2040; diff --git a/rtic-monotonics/src/rp2040.rs b/rtic-monotonics/src/rp2040.rs new file mode 100644 index 0000000..e42c148 --- /dev/null +++ b/rtic-monotonics/src/rp2040.rs @@ -0,0 +1,143 @@ +//! A monotonic implementation for RP2040's Timer peripheral. + +use super::Monotonic; +pub use super::{TimeoutError, TimerQueue}; +use core::future::Future; +pub use fugit::ExtU64; +use rp2040_pac::{timer, Interrupt, RESETS, TIMER}; + +/// Timer implementing `rtic_monotonic::Monotonic` which runs at 1 MHz. +pub struct Timer; + +impl Timer { + /// Start a `Monotonic` based on RP2040's Timer. + pub fn start(timer: TIMER, resets: &mut RESETS) { + resets.reset.modify(|_, w| w.timer().clear_bit()); + while resets.reset_done.read().timer().bit_is_clear() {} + timer.inte.modify(|_, w| w.alarm_0().set_bit()); + + TIMER_QUEUE.initialize(Self {}); + } + + fn timer() -> &'static timer::RegisterBlock { + unsafe { &*TIMER::ptr() } + } +} + +static TIMER_QUEUE: TimerQueue<Timer> = TimerQueue::new(); + +// Forward timerqueue interface +impl Timer { + /// Used to access the underlying timer queue + #[doc(hidden)] + pub fn __tq() -> &'static TimerQueue<Timer> { + &TIMER_QUEUE + } + + /// Timeout at a specific time. + pub async fn timeout_at<F: Future>( + instant: <Self as Monotonic>::Instant, + future: F, + ) -> Result<F::Output, TimeoutError> { + TIMER_QUEUE.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> { + TIMER_QUEUE.timeout_after(duration, future).await + } + + /// Delay for some duration of time. + #[inline] + pub async fn delay(duration: <Self as Monotonic>::Duration) { + TIMER_QUEUE.delay(duration).await; + } + + /// Delay to some specific time instant. + pub async fn delay_until(instant: <Self as Monotonic>::Instant) { + TIMER_QUEUE.delay_until(instant).await; + } +} + +impl Monotonic for Timer { + type Instant = fugit::TimerInstantU64<1_000_000>; + type Duration = fugit::TimerDurationU64<1_000_000>; + + const ZERO: Self::Instant = Self::Instant::from_ticks(0); + + fn now() -> Self::Instant { + let timer = Self::timer(); + + let mut hi0 = timer.timerawh.read().bits(); + loop { + let low = timer.timerawl.read().bits(); + let hi1 = timer.timerawh.read().bits(); + if hi0 == hi1 { + break Self::Instant::from_ticks((u64::from(hi0) << 32) | u64::from(low)); + } + hi0 = hi1; + } + } + + fn set_compare(instant: Self::Instant) { + let now = Self::now(); + + let max = u32::MAX as u64; + + // Since the timer may or may not overflow based on the requested compare val, we check + // how many ticks are left. + let val = match instant.checked_duration_since(now) { + Some(x) if x.ticks() <= max => instant.duration_since_epoch().ticks() & max, // Will not overflow + _ => 0, // Will overflow or in the past, set the same value as after overflow to not get extra interrupts + }; + + Self::timer() + .alarm0 + .write(|w| unsafe { w.bits(val as u32) }); + } + + fn clear_compare_flag() { + Self::timer().intr.modify(|_, w| w.alarm_0().set_bit()); + } + + fn pend_interrupt() { + rp2040_pac::NVIC::pend(Interrupt::TIMER_IRQ_0); + } + + fn on_interrupt() {} + + fn enable_timer() {} + + fn disable_timer() {} +} + +#[cfg(feature = "embedded-hal-async")] +impl embedded_hal_async::delay::DelayUs for Timer { + type Error = core::convert::Infallible; + + async fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { + TIMER_QUEUE.delay((us as u64).micros()).await; + Ok(()) + } + + async fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { + TIMER_QUEUE.delay((ms as u64).millis()).await; + Ok(()) + } +} + +/// Register the Timer interrupt for the monotonic. +#[macro_export] +macro_rules! make_rp2040_monotonic_handler { + () => { + #[no_mangle] + #[allow(non_snake_case)] + unsafe extern "C" fn TIMER_IRQ_0() { + rtic_monotonics::rp2040::Timer::__tq().on_monotonic_interrupt(); + } + }; +} diff --git a/rtic-monotonics/src/systick.rs b/rtic-monotonics/src/systick.rs new file mode 100644 index 0000000..691d7c7 --- /dev/null +++ b/rtic-monotonics/src/systick.rs @@ -0,0 +1,163 @@ +//! ... + +use super::Monotonic; +pub use super::{TimeoutError, TimerQueue}; +use atomic_polyfill::{AtomicU32, Ordering}; +use core::future::Future; +use cortex_m::peripheral::SYST; +pub use fugit::ExtU32; + +// Features should be additive, here systick-100hz gets picked if both +// `systick-100hz` and `systick-10khz` are enabled. + +cfg_if::cfg_if! { + if #[cfg(feature = "systick-100hz")] + { + const TIMER_HZ: u32 = 100; + } else if #[cfg(feature = "systick-10khz")] + { + const TIMER_HZ: u32 = 10_000; + } else { + // Default case is 1 kHz + const TIMER_HZ: u32 = 1_000; + } +} + +/// Systick implementing `rtic_monotonic::Monotonic` which runs at 1 kHz, 100Hz or 10 kHz. +pub struct Systick; + +impl Systick { + /// Start a `Monotonic` based on SysTick. + /// + /// The `sysclk` parameter is the speed at which SysTick runs at. This value should come from + /// the clock generation function of the used HAL. + /// + /// Notice that the actual rate of the timer is a best approximation based on the given + /// `sysclk` and `TIMER_HZ`. + /// + /// Note: Give the return value to `TimerQueue::initialize()` to initialize the timer queue. + pub fn start(mut systick: cortex_m::peripheral::SYST, sysclk: u32) { + // + TIMER_HZ / 2 provides round to nearest instead of round to 0. + // - 1 as the counter range is inclusive [0, reload] + let reload = (sysclk + TIMER_HZ / 2) / TIMER_HZ - 1; + + assert!(reload <= 0x00ff_ffff); + assert!(reload > 0); + + systick.disable_counter(); + systick.set_clock_source(cortex_m::peripheral::syst::SystClkSource::Core); + systick.set_reload(reload); + systick.enable_interrupt(); + systick.enable_counter(); + + SYSTICK_TIMER_QUEUE.initialize(Systick {}); + } + + fn systick() -> SYST { + unsafe { core::mem::transmute::<(), SYST>(()) } + } +} + +static SYSTICK_CNT: AtomicU32 = AtomicU32::new(0); +static SYSTICK_TIMER_QUEUE: TimerQueue<Systick> = TimerQueue::new(); + +// Forward timerqueue interface +impl Systick { + /// Used to access the underlying timer queue + #[doc(hidden)] + pub fn __tq() -> &'static TimerQueue<Systick> { + &SYSTICK_TIMER_QUEUE + } + + /// Timeout at a specific time. + pub async fn timeout_at<F: Future>( + instant: <Self as Monotonic>::Instant, + future: F, + ) -> Result<F::Output, TimeoutError> { + SYSTICK_TIMER_QUEUE.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> { + SYSTICK_TIMER_QUEUE.timeout_after(duration, future).await + } + + /// Delay for some duration of time. + #[inline] + pub async fn delay(duration: <Self as Monotonic>::Duration) { + SYSTICK_TIMER_QUEUE.delay(duration).await; + } + + /// Delay to some specific time instant. + pub async fn delay_until(instant: <Self as Monotonic>::Instant) { + SYSTICK_TIMER_QUEUE.delay_until(instant).await; + } +} + +impl Monotonic for Systick { + type Instant = fugit::TimerInstantU32<TIMER_HZ>; + type Duration = fugit::TimerDurationU32<TIMER_HZ>; + + const ZERO: Self::Instant = Self::Instant::from_ticks(0); + + fn now() -> Self::Instant { + if Self::systick().has_wrapped() { + SYSTICK_CNT.fetch_add(1, Ordering::AcqRel); + } + + Self::Instant::from_ticks(SYSTICK_CNT.load(Ordering::Relaxed)) + } + + fn set_compare(_: Self::Instant) { + // No need to do something here, we get interrupts anyway. + } + + fn clear_compare_flag() { + // NOOP with SysTick interrupt + } + + fn pend_interrupt() { + cortex_m::peripheral::SCB::set_pendst(); + } + + fn on_interrupt() { + if Self::systick().has_wrapped() { + SYSTICK_CNT.fetch_add(1, Ordering::AcqRel); + } + } + + fn enable_timer() {} + + fn disable_timer() {} +} + +#[cfg(feature = "embedded-hal-async")] +impl embedded_hal_async::delay::DelayUs for Systick { + type Error = core::convert::Infallible; + + async fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { + SYSTICK_TIMER_QUEUE.delay(us.micros()).await; + Ok(()) + } + + async fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { + SYSTICK_TIMER_QUEUE.delay(ms.millis()).await; + Ok(()) + } +} + +/// Register the Systick interrupt for the monotonic. +#[macro_export] +macro_rules! make_systick_handler { + () => { + #[no_mangle] + #[allow(non_snake_case)] + unsafe extern "C" fn SysTick() { + rtic_monotonics::systick::Systick::__tq().on_monotonic_interrupt(); + } + }; +} |
