From 306aa47170fd59369b7a184924e287dc3706d64d Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 23 Jan 2023 20:05:47 +0100 Subject: Add rtic-timer (timerqueue + monotonic) and rtic-monotonics (systick-monotonic) --- rtic-monotonics/src/lib.rs | 11 +++++++++++ rtic-monotonics/src/systick_monotonic.rs | 1 + 2 files changed, 12 insertions(+) create mode 100644 rtic-monotonics/src/lib.rs create mode 100644 rtic-monotonics/src/systick_monotonic.rs (limited to 'rtic-monotonics/src') diff --git a/rtic-monotonics/src/lib.rs b/rtic-monotonics/src/lib.rs new file mode 100644 index 0000000..88398ca --- /dev/null +++ b/rtic-monotonics/src/lib.rs @@ -0,0 +1,11 @@ +//! Crate + +#![no_std] +#![no_main] +#![deny(missing_docs)] +#![allow(incomplete_features)] +#![feature(async_fn_in_trait)] + +pub use rtic_timer::{Monotonic, TimeoutError, TimerQueue}; + +pub mod systick_monotonic; diff --git a/rtic-monotonics/src/systick_monotonic.rs b/rtic-monotonics/src/systick_monotonic.rs new file mode 100644 index 0000000..491cf81 --- /dev/null +++ b/rtic-monotonics/src/systick_monotonic.rs @@ -0,0 +1 @@ +//! ... -- cgit v1.2.3 From 71b5f9438e1beb5fe12b90415d9d6307e79c0cdf Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 23 Jan 2023 20:57:56 +0100 Subject: Fixed systick monotonic --- rtic-monotonics/Cargo.toml | 4 +- rtic-monotonics/src/lib.rs | 2 +- rtic-monotonics/src/systick_monotonic.rs | 133 ++++++++++++ rtic-time/.gitignore | 6 + rtic-time/CHANGELOG.md | 0 rtic-time/Cargo.toml | 10 + rtic-time/rust-toolchain.toml | 4 + rtic-time/src/lib.rs | 336 +++++++++++++++++++++++++++++++ rtic-time/src/linked_list.rs | 173 ++++++++++++++++ rtic-time/src/monotonic.rs | 60 ++++++ rtic-timer/.gitignore | 6 - rtic-timer/CHANGELOG.md | 0 rtic-timer/Cargo.toml | 10 - rtic-timer/rust-toolchain.toml | 4 - rtic-timer/src/lib.rs | 336 ------------------------------- rtic-timer/src/linked_list.rs | 173 ---------------- rtic-timer/src/monotonic.rs | 60 ------ 17 files changed, 725 insertions(+), 592 deletions(-) create mode 100644 rtic-time/.gitignore create mode 100644 rtic-time/CHANGELOG.md create mode 100644 rtic-time/Cargo.toml create mode 100644 rtic-time/rust-toolchain.toml create mode 100644 rtic-time/src/lib.rs create mode 100644 rtic-time/src/linked_list.rs create mode 100644 rtic-time/src/monotonic.rs delete mode 100644 rtic-timer/.gitignore delete mode 100644 rtic-timer/CHANGELOG.md delete mode 100644 rtic-timer/Cargo.toml delete mode 100644 rtic-timer/rust-toolchain.toml delete mode 100644 rtic-timer/src/lib.rs delete mode 100644 rtic-timer/src/linked_list.rs delete mode 100644 rtic-timer/src/monotonic.rs (limited to 'rtic-monotonics/src') diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml index 24448fb..68daba4 100644 --- a/rtic-monotonics/Cargo.toml +++ b/rtic-monotonics/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rtic-timer" +name = "rtic-monotonics" version = "0.1.0" edition = "2021" @@ -9,4 +9,4 @@ edition = "2021" cortex-m = { version = "0.7.6" } embedded-hal-async = "0.2.0-alpha.0" fugit = { version = "0.3.6", features = ["defmt"] } -rtic-timer = { version = "1.0.0", path = "../rtic-timer" } +rtic-time = { version = "1.0.0", path = "../rtic-time" } diff --git a/rtic-monotonics/src/lib.rs b/rtic-monotonics/src/lib.rs index 88398ca..ce30c36 100644 --- a/rtic-monotonics/src/lib.rs +++ b/rtic-monotonics/src/lib.rs @@ -6,6 +6,6 @@ #![allow(incomplete_features)] #![feature(async_fn_in_trait)] -pub use rtic_timer::{Monotonic, TimeoutError, TimerQueue}; +pub use rtic_time::{Monotonic, TimeoutError, TimerQueue}; pub mod systick_monotonic; diff --git a/rtic-monotonics/src/systick_monotonic.rs b/rtic-monotonics/src/systick_monotonic.rs index 491cf81..7c713b6 100644 --- a/rtic-monotonics/src/systick_monotonic.rs +++ b/rtic-monotonics/src/systick_monotonic.rs @@ -1 +1,134 @@ //! ... + +use super::Monotonic; +pub use super::{TimeoutError, TimerQueue}; +use core::{ + ops::Deref, + sync::atomic::{AtomicU32, Ordering}, +}; +use cortex_m::peripheral::SYST; +use embedded_hal_async::delay::DelayUs; +use fugit::ExtU32; + +/// Systick implementing `rtic_monotonic::Monotonic` which runs at a +/// settable rate using the `TIMER_HZ` parameter. +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. + #[must_use] + pub fn start(mut systick: cortex_m::peripheral::SYST, sysclk: u32) -> Self { + // + 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 {} + } + + fn systick() -> SYST { + unsafe { core::mem::transmute::<(), SYST>(()) } + } +} + +static SYSTICK_CNT: AtomicU32 = AtomicU32::new(0); + +impl Monotonic for Systick { + type Instant = fugit::TimerInstantU32; + type Duration = fugit::TimerDurationU32; + + 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() {} +} + +/// Timer queue wrapper to implement traits on +pub struct SystickTimerQueue(TimerQueue>); + +impl SystickTimerQueue { + /// Create a new timer queue. + pub const fn new() -> Self { + Self(TimerQueue::new()) + } +} + +impl Deref for SystickTimerQueue { + type Target = TimerQueue>; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DelayUs for SystickTimerQueue { + type Error = core::convert::Infallible; + + async fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { + self.delay(us.micros()).await; + Ok(()) + } + + async fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { + self.delay(ms.millis()).await; + Ok(()) + } +} + +/// Register the Systick interrupt and crate a timer queue with a specific name and speed. +#[macro_export] +macro_rules! make_systick_timer_queue { + ($timer_queue_name:ident, $systick_speed:expr) => { + static $timer_queue_name: SystickTimerQueue<$systick_speed> = SystickTimerQueue::new(); + + #[no_mangle] + #[allow(non_snake_case)] + unsafe extern "C" fn SysTick() { + $timer_queue_name.on_monotonic_interrupt(); + } + }; +} diff --git a/rtic-time/.gitignore b/rtic-time/.gitignore new file mode 100644 index 0000000..c400256 --- /dev/null +++ b/rtic-time/.gitignore @@ -0,0 +1,6 @@ +**/*.rs.bk +.#* +.gdb_history +/target +Cargo.lock +*.hex diff --git a/rtic-time/CHANGELOG.md b/rtic-time/CHANGELOG.md new file mode 100644 index 0000000..e69de29 diff --git a/rtic-time/Cargo.toml b/rtic-time/Cargo.toml new file mode 100644 index 0000000..ea05939 --- /dev/null +++ b/rtic-time/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "rtic-time" +version = "1.0.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +critical-section = "1" +futures-util = { version = "0.3.25", default-features = false } diff --git a/rtic-time/rust-toolchain.toml b/rtic-time/rust-toolchain.toml new file mode 100644 index 0000000..e28b55d --- /dev/null +++ b/rtic-time/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-time/src/lib.rs b/rtic-time/src/lib.rs new file mode 100644 index 0000000..d7faa07 --- /dev/null +++ b/rtic-time/src/lib.rs @@ -0,0 +1,336 @@ +//! Crate + +#![no_std] +#![no_main] +#![deny(missing_docs)] +#![allow(incomplete_features)] +#![feature(async_fn_in_trait)] + +pub mod monotonic; + +use core::future::{poll_fn, Future}; +use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use core::task::{Poll, Waker}; +use futures_util::{ + future::{select, Either}, + pin_mut, +}; +pub use monotonic::Monotonic; + +mod linked_list; + +use linked_list::{Link, LinkedList}; + +/// Holds a waker and at which time instant this waker shall be awoken. +struct WaitingWaker { + waker: Waker, + release_at: Mono::Instant, +} + +impl Clone for WaitingWaker { + fn clone(&self) -> Self { + Self { + waker: self.waker.clone(), + release_at: self.release_at, + } + } +} + +impl PartialEq for WaitingWaker { + fn eq(&self, other: &Self) -> bool { + self.release_at == other.release_at + } +} + +impl PartialOrd for WaitingWaker { + fn partial_cmp(&self, other: &Self) -> Option { + self.release_at.partial_cmp(&other.release_at) + } +} + +/// A generic timer queue for async executors. +/// +/// # Blocking +/// +/// The internal priority queue uses global critical sections to manage access. This means that +/// `await`ing a delay will cause a lock of the entire system for O(n) time. In practice the lock +/// duration is ~10 clock cycles per element in the queue. +/// +/// # Safety +/// +/// This timer queue is based on an intrusive linked list, and by extension the links are strored +/// on the async stacks of callers. The links are deallocated on `drop` or when the wait is +/// complete. +/// +/// Do not call `mem::forget` on an awaited future, or there will be dragons! +pub struct TimerQueue { + queue: LinkedList>, + initialized: AtomicBool, +} + +/// This indicates that there was a timeout. +pub struct TimeoutError; + +impl TimerQueue { + /// Make a new queue. + pub const fn new() -> Self { + Self { + queue: LinkedList::new(), + initialized: AtomicBool::new(false), + } + } + + /// Forwards the `Monotonic::now()` method. + #[inline(always)] + pub fn now(&self) -> Mono::Instant { + Mono::now() + } + + /// Takes the initialized monotonic to initialize the TimerQueue. + pub fn initialize(&self, monotonic: Mono) { + self.initialized.store(true, Ordering::SeqCst); + + // Don't run drop on `Mono` + core::mem::forget(monotonic); + } + + /// Call this in the interrupt handler of the hardware timer supporting the `Monotonic` + /// + /// # Safety + /// + /// It's always safe to call, but it must only be called from the interrupt of the + /// monotonic timer for correct operation. + pub unsafe fn on_monotonic_interrupt(&self) { + Mono::clear_compare_flag(); + Mono::on_interrupt(); + + loop { + let mut release_at = None; + let head = self.queue.pop_if(|head| { + release_at = Some(head.release_at); + + Mono::now() >= head.release_at + }); + + match (head, release_at) { + (Some(link), _) => { + link.waker.wake(); + } + (None, Some(instant)) => { + Mono::enable_timer(); + Mono::set_compare(instant); + + if Mono::now() >= instant { + // The time for the next instant passed while handling it, + // continue dequeueing + continue; + } + + break; + } + (None, None) => { + // Queue is empty + Mono::disable_timer(); + + break; + } + } + } + } + + /// Timeout at a specific time. + pub async fn timeout_at( + &self, + instant: Mono::Instant, + future: F, + ) -> Result { + let delay = self.delay_until(instant); + + pin_mut!(future); + pin_mut!(delay); + + match select(future, delay).await { + Either::Left((r, _)) => Ok(r), + Either::Right(_) => Err(TimeoutError), + } + } + + /// Timeout after a specific duration. + #[inline] + pub async fn timeout_after( + &self, + duration: Mono::Duration, + future: F, + ) -> Result { + self.timeout_at(Mono::now() + duration, future).await + } + + /// Delay for some duration of time. + #[inline] + pub async fn delay(&self, duration: Mono::Duration) { + let now = Mono::now(); + + self.delay_until(now + duration).await; + } + + /// Delay to some specific time instant. + pub async fn delay_until(&self, instant: Mono::Instant) { + if !self.initialized.load(Ordering::Relaxed) { + panic!( + "The timer queue is not initialized with a monotonic, you need to run `initialize`" + ); + } + + let mut first_run = true; + let queue = &self.queue; + let mut link = Link::new(WaitingWaker { + waker: poll_fn(|cx| Poll::Ready(cx.waker().clone())).await, + release_at: instant, + }); + + let marker = &AtomicUsize::new(0); + + let dropper = OnDrop::new(|| { + queue.delete(marker.load(Ordering::Relaxed)); + }); + + poll_fn(|_| { + if Mono::now() >= instant { + return Poll::Ready(()); + } + + if first_run { + first_run = false; + let (was_empty, addr) = queue.insert(&mut link); + marker.store(addr, Ordering::Relaxed); + + if was_empty { + // Pend the monotonic handler if the queue was empty to setup the timer. + Mono::pend_interrupt(); + } + } + + Poll::Pending + }) + .await; + + // Make sure that our link is deleted from the list before we drop this stack + drop(dropper); + } +} + +struct OnDrop { + f: core::mem::MaybeUninit, +} + +impl OnDrop { + pub fn new(f: F) -> Self { + Self { + f: core::mem::MaybeUninit::new(f), + } + } + + #[allow(unused)] + pub fn defuse(self) { + core::mem::forget(self) + } +} + +impl Drop for OnDrop { + fn drop(&mut self) { + unsafe { self.f.as_ptr().read()() } + } +} + +// -------- Test program --------- +// +// +// use systick_monotonic::{Systick, TimerQueue}; +// +// // same panicking *behavior* as `panic-probe` but doesn't print a panic message +// // this prevents the panic message being printed *twice* when `defmt::panic` is invoked +// #[defmt::panic_handler] +// fn panic() -> ! { +// cortex_m::asm::udf() +// } +// +// /// Terminates the application and makes `probe-run` exit with exit-code = 0 +// pub fn exit() -> ! { +// loop { +// cortex_m::asm::bkpt(); +// } +// } +// +// defmt::timestamp!("{=u64:us}", { +// let time_us: fugit::MicrosDurationU32 = MONO.now().duration_since_epoch().convert(); +// +// time_us.ticks() as u64 +// }); +// +// make_systick_timer_queue!(MONO, Systick<1_000>); +// +// #[rtic::app( +// device = nrf52832_hal::pac, +// dispatchers = [SWI0_EGU0, SWI1_EGU1, SWI2_EGU2, SWI3_EGU3, SWI4_EGU4, SWI5_EGU5], +// )] +// mod app { +// use super::{Systick, MONO}; +// use fugit::ExtU32; +// +// #[shared] +// struct Shared {} +// +// #[local] +// struct Local {} +// +// #[init] +// fn init(cx: init::Context) -> (Shared, Local) { +// defmt::println!("init"); +// +// let systick = Systick::start(cx.core.SYST, 64_000_000); +// +// defmt::println!("initializing monotonic"); +// +// MONO.initialize(systick); +// +// async_task::spawn().ok(); +// async_task2::spawn().ok(); +// async_task3::spawn().ok(); +// +// (Shared {}, Local {}) +// } +// +// #[idle] +// fn idle(_: idle::Context) -> ! { +// defmt::println!("idle"); +// +// loop { +// core::hint::spin_loop(); +// } +// } +// +// #[task] +// async fn async_task(_: async_task::Context) { +// loop { +// defmt::println!("async task waiting for 1 second"); +// MONO.delay(1.secs()).await; +// } +// } +// +// #[task] +// async fn async_task2(_: async_task2::Context) { +// loop { +// defmt::println!(" async task 2 waiting for 0.5 second"); +// MONO.delay(500.millis()).await; +// } +// } +// +// #[task] +// async fn async_task3(_: async_task3::Context) { +// loop { +// defmt::println!(" async task 3 waiting for 0.2 second"); +// MONO.delay(200.millis()).await; +// } +// } +// } +// diff --git a/rtic-time/src/linked_list.rs b/rtic-time/src/linked_list.rs new file mode 100644 index 0000000..42ff8cb --- /dev/null +++ b/rtic-time/src/linked_list.rs @@ -0,0 +1,173 @@ +//! ... + +use core::marker::PhantomPinned; +use core::sync::atomic::{AtomicPtr, Ordering}; +use critical_section as cs; + +/// A sorted linked list for the timer queue. +pub struct LinkedList { + head: AtomicPtr>, +} + +impl LinkedList { + /// Create a new linked list. + pub const fn new() -> Self { + Self { + head: AtomicPtr::new(core::ptr::null_mut()), + } + } +} + +impl LinkedList { + /// Pop the first element in the queue if the closure returns true. + pub fn pop_if bool>(&self, f: F) -> Option { + cs::with(|_| { + // Make sure all previous writes are visible + core::sync::atomic::fence(Ordering::SeqCst); + + let head = self.head.load(Ordering::Relaxed); + + // SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link + if let Some(head) = unsafe { head.as_ref() } { + if f(&head.val) { + // Move head to the next element + self.head + .store(head.next.load(Ordering::Relaxed), Ordering::Relaxed); + + // We read the value at head + let head_val = head.val.clone(); + + return Some(head_val); + } + } + None + }) + } + + /// Delete a link at an address. + pub fn delete(&self, addr: usize) { + cs::with(|_| { + // Make sure all previous writes are visible + core::sync::atomic::fence(Ordering::SeqCst); + + let head = self.head.load(Ordering::Relaxed); + + // SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link + let head_ref = if let Some(head_ref) = unsafe { head.as_ref() } { + head_ref + } else { + // 1. List is empty, do nothing + return; + }; + + if head as *const _ as usize == addr { + // 2. Replace head with head.next + self.head + .store(head_ref.next.load(Ordering::Relaxed), Ordering::Relaxed); + + return; + } + + // 3. search list for correct node + let mut curr = head_ref; + let mut next = head_ref.next.load(Ordering::Relaxed); + + // SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link + while let Some(next_link) = unsafe { next.as_ref() } { + // Next is not null + + if next as *const _ as usize == addr { + curr.next + .store(next_link.next.load(Ordering::Relaxed), Ordering::Relaxed); + + return; + } + + // Continue searching + curr = next_link; + next = next_link.next.load(Ordering::Relaxed); + } + }) + } + + /// Insert a new link into the linked list. + /// The return is (was_empty, address), where the address of the link is for use with `delete`. + pub fn insert(&self, val: &mut Link) -> (bool, usize) { + cs::with(|_| { + let addr = val as *const _ as usize; + + // Make sure all previous writes are visible + core::sync::atomic::fence(Ordering::SeqCst); + + let head = self.head.load(Ordering::Relaxed); + + // 3 cases to handle + + // 1. List is empty, write to head + // SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link + let head_ref = if let Some(head_ref) = unsafe { head.as_ref() } { + head_ref + } else { + self.head.store(val, Ordering::Relaxed); + return (true, addr); + }; + + // 2. val needs to go in first + if val.val < head_ref.val { + // Set current head as next of `val` + val.next.store(head, Ordering::Relaxed); + + // `val` is now first in the queue + self.head.store(val, Ordering::Relaxed); + + return (false, addr); + } + + // 3. search list for correct place + let mut curr = head_ref; + let mut next = head_ref.next.load(Ordering::Relaxed); + + // SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link + while let Some(next_link) = unsafe { next.as_ref() } { + // Next is not null + + if val.val < next_link.val { + // Replace next with `val` + val.next.store(next, Ordering::Relaxed); + + // Insert `val` + curr.next.store(val, Ordering::Relaxed); + + return (false, addr); + } + + // Continue searching + curr = next_link; + next = next_link.next.load(Ordering::Relaxed); + } + + // No next, write link to last position in list + curr.next.store(val, Ordering::Relaxed); + + (false, addr) + }) + } +} + +/// A link in the linked list. +pub struct Link { + val: T, + next: AtomicPtr>, + _up: PhantomPinned, +} + +impl Link { + /// Create a new link. + pub const fn new(val: T) -> Self { + Self { + val, + next: AtomicPtr::new(core::ptr::null_mut()), + _up: PhantomPinned, + } + } +} diff --git a/rtic-time/src/monotonic.rs b/rtic-time/src/monotonic.rs new file mode 100644 index 0000000..9b3742f --- /dev/null +++ b/rtic-time/src/monotonic.rs @@ -0,0 +1,60 @@ +//! ... + +/// # A monotonic clock / counter definition. +/// +/// ## Correctness +/// +/// The trait enforces that proper time-math is implemented between `Instant` and `Duration`. This +/// is a requirement on the time library that the user chooses to use. +pub trait Monotonic { + /// The time at time zero. + const ZERO: Self::Instant; + + /// The type for instant, defining an instant in time. + /// + /// **Note:** In all APIs in RTIC that use instants from this monotonic, this type will be used. + type Instant: Ord + + Copy + + core::ops::Add + + core::ops::Sub + + core::ops::Sub; + + /// The type for duration, defining an duration of time. + /// + /// **Note:** In all APIs in RTIC that use duration from this monotonic, this type will be used. + type Duration; + + /// Get the current time. + fn now() -> Self::Instant; + + /// Set the compare value of the timer interrupt. + /// + /// **Note:** This method does not need to handle race conditions of the monotonic, the timer + /// queue in RTIC checks this. + fn set_compare(instant: Self::Instant); + + /// Clear the compare interrupt flag. + fn clear_compare_flag(); + + /// Pend the timer's interrupt. + fn pend_interrupt(); + + /// Optional. Runs on interrupt before any timer queue handling. + fn on_interrupt() {} + + /// Optional. This is used to save power, this is called when the timer queue is not empty. + /// + /// Enabling and disabling the monotonic needs to propagate to `now` so that an instant + /// based of `now()` is still valid. + /// + /// NOTE: This may be called more than once. + fn enable_timer() {} + + /// Optional. This is used to save power, this is called when the timer queue is empty. + /// + /// Enabling and disabling the monotonic needs to propagate to `now` so that an instant + /// based of `now()` is still valid. + /// + /// NOTE: This may be called more than once. + fn disable_timer() {} +} diff --git a/rtic-timer/.gitignore b/rtic-timer/.gitignore deleted file mode 100644 index c400256..0000000 --- a/rtic-timer/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -**/*.rs.bk -.#* -.gdb_history -/target -Cargo.lock -*.hex diff --git a/rtic-timer/CHANGELOG.md b/rtic-timer/CHANGELOG.md deleted file mode 100644 index e69de29..0000000 diff --git a/rtic-timer/Cargo.toml b/rtic-timer/Cargo.toml deleted file mode 100644 index b7b3a5f..0000000 --- a/rtic-timer/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "rtic-timer" -version = "1.0.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -critical-section = "1" -futures-util = { version = "0.3.25", default-features = false } diff --git a/rtic-timer/rust-toolchain.toml b/rtic-timer/rust-toolchain.toml deleted file mode 100644 index e28b55d..0000000 --- a/rtic-timer/rust-toolchain.toml +++ /dev/null @@ -1,4 +0,0 @@ -[toolchain] -channel = "nightly" -components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] -targets = [ "thumbv6m-none-eabi", "thumbv7m-none-eabi" ] diff --git a/rtic-timer/src/lib.rs b/rtic-timer/src/lib.rs deleted file mode 100644 index d7faa07..0000000 --- a/rtic-timer/src/lib.rs +++ /dev/null @@ -1,336 +0,0 @@ -//! Crate - -#![no_std] -#![no_main] -#![deny(missing_docs)] -#![allow(incomplete_features)] -#![feature(async_fn_in_trait)] - -pub mod monotonic; - -use core::future::{poll_fn, Future}; -use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use core::task::{Poll, Waker}; -use futures_util::{ - future::{select, Either}, - pin_mut, -}; -pub use monotonic::Monotonic; - -mod linked_list; - -use linked_list::{Link, LinkedList}; - -/// Holds a waker and at which time instant this waker shall be awoken. -struct WaitingWaker { - waker: Waker, - release_at: Mono::Instant, -} - -impl Clone for WaitingWaker { - fn clone(&self) -> Self { - Self { - waker: self.waker.clone(), - release_at: self.release_at, - } - } -} - -impl PartialEq for WaitingWaker { - fn eq(&self, other: &Self) -> bool { - self.release_at == other.release_at - } -} - -impl PartialOrd for WaitingWaker { - fn partial_cmp(&self, other: &Self) -> Option { - self.release_at.partial_cmp(&other.release_at) - } -} - -/// A generic timer queue for async executors. -/// -/// # Blocking -/// -/// The internal priority queue uses global critical sections to manage access. This means that -/// `await`ing a delay will cause a lock of the entire system for O(n) time. In practice the lock -/// duration is ~10 clock cycles per element in the queue. -/// -/// # Safety -/// -/// This timer queue is based on an intrusive linked list, and by extension the links are strored -/// on the async stacks of callers. The links are deallocated on `drop` or when the wait is -/// complete. -/// -/// Do not call `mem::forget` on an awaited future, or there will be dragons! -pub struct TimerQueue { - queue: LinkedList>, - initialized: AtomicBool, -} - -/// This indicates that there was a timeout. -pub struct TimeoutError; - -impl TimerQueue { - /// Make a new queue. - pub const fn new() -> Self { - Self { - queue: LinkedList::new(), - initialized: AtomicBool::new(false), - } - } - - /// Forwards the `Monotonic::now()` method. - #[inline(always)] - pub fn now(&self) -> Mono::Instant { - Mono::now() - } - - /// Takes the initialized monotonic to initialize the TimerQueue. - pub fn initialize(&self, monotonic: Mono) { - self.initialized.store(true, Ordering::SeqCst); - - // Don't run drop on `Mono` - core::mem::forget(monotonic); - } - - /// Call this in the interrupt handler of the hardware timer supporting the `Monotonic` - /// - /// # Safety - /// - /// It's always safe to call, but it must only be called from the interrupt of the - /// monotonic timer for correct operation. - pub unsafe fn on_monotonic_interrupt(&self) { - Mono::clear_compare_flag(); - Mono::on_interrupt(); - - loop { - let mut release_at = None; - let head = self.queue.pop_if(|head| { - release_at = Some(head.release_at); - - Mono::now() >= head.release_at - }); - - match (head, release_at) { - (Some(link), _) => { - link.waker.wake(); - } - (None, Some(instant)) => { - Mono::enable_timer(); - Mono::set_compare(instant); - - if Mono::now() >= instant { - // The time for the next instant passed while handling it, - // continue dequeueing - continue; - } - - break; - } - (None, None) => { - // Queue is empty - Mono::disable_timer(); - - break; - } - } - } - } - - /// Timeout at a specific time. - pub async fn timeout_at( - &self, - instant: Mono::Instant, - future: F, - ) -> Result { - let delay = self.delay_until(instant); - - pin_mut!(future); - pin_mut!(delay); - - match select(future, delay).await { - Either::Left((r, _)) => Ok(r), - Either::Right(_) => Err(TimeoutError), - } - } - - /// Timeout after a specific duration. - #[inline] - pub async fn timeout_after( - &self, - duration: Mono::Duration, - future: F, - ) -> Result { - self.timeout_at(Mono::now() + duration, future).await - } - - /// Delay for some duration of time. - #[inline] - pub async fn delay(&self, duration: Mono::Duration) { - let now = Mono::now(); - - self.delay_until(now + duration).await; - } - - /// Delay to some specific time instant. - pub async fn delay_until(&self, instant: Mono::Instant) { - if !self.initialized.load(Ordering::Relaxed) { - panic!( - "The timer queue is not initialized with a monotonic, you need to run `initialize`" - ); - } - - let mut first_run = true; - let queue = &self.queue; - let mut link = Link::new(WaitingWaker { - waker: poll_fn(|cx| Poll::Ready(cx.waker().clone())).await, - release_at: instant, - }); - - let marker = &AtomicUsize::new(0); - - let dropper = OnDrop::new(|| { - queue.delete(marker.load(Ordering::Relaxed)); - }); - - poll_fn(|_| { - if Mono::now() >= instant { - return Poll::Ready(()); - } - - if first_run { - first_run = false; - let (was_empty, addr) = queue.insert(&mut link); - marker.store(addr, Ordering::Relaxed); - - if was_empty { - // Pend the monotonic handler if the queue was empty to setup the timer. - Mono::pend_interrupt(); - } - } - - Poll::Pending - }) - .await; - - // Make sure that our link is deleted from the list before we drop this stack - drop(dropper); - } -} - -struct OnDrop { - f: core::mem::MaybeUninit, -} - -impl OnDrop { - pub fn new(f: F) -> Self { - Self { - f: core::mem::MaybeUninit::new(f), - } - } - - #[allow(unused)] - pub fn defuse(self) { - core::mem::forget(self) - } -} - -impl Drop for OnDrop { - fn drop(&mut self) { - unsafe { self.f.as_ptr().read()() } - } -} - -// -------- Test program --------- -// -// -// use systick_monotonic::{Systick, TimerQueue}; -// -// // same panicking *behavior* as `panic-probe` but doesn't print a panic message -// // this prevents the panic message being printed *twice* when `defmt::panic` is invoked -// #[defmt::panic_handler] -// fn panic() -> ! { -// cortex_m::asm::udf() -// } -// -// /// Terminates the application and makes `probe-run` exit with exit-code = 0 -// pub fn exit() -> ! { -// loop { -// cortex_m::asm::bkpt(); -// } -// } -// -// defmt::timestamp!("{=u64:us}", { -// let time_us: fugit::MicrosDurationU32 = MONO.now().duration_since_epoch().convert(); -// -// time_us.ticks() as u64 -// }); -// -// make_systick_timer_queue!(MONO, Systick<1_000>); -// -// #[rtic::app( -// device = nrf52832_hal::pac, -// dispatchers = [SWI0_EGU0, SWI1_EGU1, SWI2_EGU2, SWI3_EGU3, SWI4_EGU4, SWI5_EGU5], -// )] -// mod app { -// use super::{Systick, MONO}; -// use fugit::ExtU32; -// -// #[shared] -// struct Shared {} -// -// #[local] -// struct Local {} -// -// #[init] -// fn init(cx: init::Context) -> (Shared, Local) { -// defmt::println!("init"); -// -// let systick = Systick::start(cx.core.SYST, 64_000_000); -// -// defmt::println!("initializing monotonic"); -// -// MONO.initialize(systick); -// -// async_task::spawn().ok(); -// async_task2::spawn().ok(); -// async_task3::spawn().ok(); -// -// (Shared {}, Local {}) -// } -// -// #[idle] -// fn idle(_: idle::Context) -> ! { -// defmt::println!("idle"); -// -// loop { -// core::hint::spin_loop(); -// } -// } -// -// #[task] -// async fn async_task(_: async_task::Context) { -// loop { -// defmt::println!("async task waiting for 1 second"); -// MONO.delay(1.secs()).await; -// } -// } -// -// #[task] -// async fn async_task2(_: async_task2::Context) { -// loop { -// defmt::println!(" async task 2 waiting for 0.5 second"); -// MONO.delay(500.millis()).await; -// } -// } -// -// #[task] -// async fn async_task3(_: async_task3::Context) { -// loop { -// defmt::println!(" async task 3 waiting for 0.2 second"); -// MONO.delay(200.millis()).await; -// } -// } -// } -// diff --git a/rtic-timer/src/linked_list.rs b/rtic-timer/src/linked_list.rs deleted file mode 100644 index 42ff8cb..0000000 --- a/rtic-timer/src/linked_list.rs +++ /dev/null @@ -1,173 +0,0 @@ -//! ... - -use core::marker::PhantomPinned; -use core::sync::atomic::{AtomicPtr, Ordering}; -use critical_section as cs; - -/// A sorted linked list for the timer queue. -pub struct LinkedList { - head: AtomicPtr>, -} - -impl LinkedList { - /// Create a new linked list. - pub const fn new() -> Self { - Self { - head: AtomicPtr::new(core::ptr::null_mut()), - } - } -} - -impl LinkedList { - /// Pop the first element in the queue if the closure returns true. - pub fn pop_if bool>(&self, f: F) -> Option { - cs::with(|_| { - // Make sure all previous writes are visible - core::sync::atomic::fence(Ordering::SeqCst); - - let head = self.head.load(Ordering::Relaxed); - - // SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link - if let Some(head) = unsafe { head.as_ref() } { - if f(&head.val) { - // Move head to the next element - self.head - .store(head.next.load(Ordering::Relaxed), Ordering::Relaxed); - - // We read the value at head - let head_val = head.val.clone(); - - return Some(head_val); - } - } - None - }) - } - - /// Delete a link at an address. - pub fn delete(&self, addr: usize) { - cs::with(|_| { - // Make sure all previous writes are visible - core::sync::atomic::fence(Ordering::SeqCst); - - let head = self.head.load(Ordering::Relaxed); - - // SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link - let head_ref = if let Some(head_ref) = unsafe { head.as_ref() } { - head_ref - } else { - // 1. List is empty, do nothing - return; - }; - - if head as *const _ as usize == addr { - // 2. Replace head with head.next - self.head - .store(head_ref.next.load(Ordering::Relaxed), Ordering::Relaxed); - - return; - } - - // 3. search list for correct node - let mut curr = head_ref; - let mut next = head_ref.next.load(Ordering::Relaxed); - - // SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link - while let Some(next_link) = unsafe { next.as_ref() } { - // Next is not null - - if next as *const _ as usize == addr { - curr.next - .store(next_link.next.load(Ordering::Relaxed), Ordering::Relaxed); - - return; - } - - // Continue searching - curr = next_link; - next = next_link.next.load(Ordering::Relaxed); - } - }) - } - - /// Insert a new link into the linked list. - /// The return is (was_empty, address), where the address of the link is for use with `delete`. - pub fn insert(&self, val: &mut Link) -> (bool, usize) { - cs::with(|_| { - let addr = val as *const _ as usize; - - // Make sure all previous writes are visible - core::sync::atomic::fence(Ordering::SeqCst); - - let head = self.head.load(Ordering::Relaxed); - - // 3 cases to handle - - // 1. List is empty, write to head - // SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link - let head_ref = if let Some(head_ref) = unsafe { head.as_ref() } { - head_ref - } else { - self.head.store(val, Ordering::Relaxed); - return (true, addr); - }; - - // 2. val needs to go in first - if val.val < head_ref.val { - // Set current head as next of `val` - val.next.store(head, Ordering::Relaxed); - - // `val` is now first in the queue - self.head.store(val, Ordering::Relaxed); - - return (false, addr); - } - - // 3. search list for correct place - let mut curr = head_ref; - let mut next = head_ref.next.load(Ordering::Relaxed); - - // SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link - while let Some(next_link) = unsafe { next.as_ref() } { - // Next is not null - - if val.val < next_link.val { - // Replace next with `val` - val.next.store(next, Ordering::Relaxed); - - // Insert `val` - curr.next.store(val, Ordering::Relaxed); - - return (false, addr); - } - - // Continue searching - curr = next_link; - next = next_link.next.load(Ordering::Relaxed); - } - - // No next, write link to last position in list - curr.next.store(val, Ordering::Relaxed); - - (false, addr) - }) - } -} - -/// A link in the linked list. -pub struct Link { - val: T, - next: AtomicPtr>, - _up: PhantomPinned, -} - -impl Link { - /// Create a new link. - pub const fn new(val: T) -> Self { - Self { - val, - next: AtomicPtr::new(core::ptr::null_mut()), - _up: PhantomPinned, - } - } -} diff --git a/rtic-timer/src/monotonic.rs b/rtic-timer/src/monotonic.rs deleted file mode 100644 index 9b3742f..0000000 --- a/rtic-timer/src/monotonic.rs +++ /dev/null @@ -1,60 +0,0 @@ -//! ... - -/// # A monotonic clock / counter definition. -/// -/// ## Correctness -/// -/// The trait enforces that proper time-math is implemented between `Instant` and `Duration`. This -/// is a requirement on the time library that the user chooses to use. -pub trait Monotonic { - /// The time at time zero. - const ZERO: Self::Instant; - - /// The type for instant, defining an instant in time. - /// - /// **Note:** In all APIs in RTIC that use instants from this monotonic, this type will be used. - type Instant: Ord - + Copy - + core::ops::Add - + core::ops::Sub - + core::ops::Sub; - - /// The type for duration, defining an duration of time. - /// - /// **Note:** In all APIs in RTIC that use duration from this monotonic, this type will be used. - type Duration; - - /// Get the current time. - fn now() -> Self::Instant; - - /// Set the compare value of the timer interrupt. - /// - /// **Note:** This method does not need to handle race conditions of the monotonic, the timer - /// queue in RTIC checks this. - fn set_compare(instant: Self::Instant); - - /// Clear the compare interrupt flag. - fn clear_compare_flag(); - - /// Pend the timer's interrupt. - fn pend_interrupt(); - - /// Optional. Runs on interrupt before any timer queue handling. - fn on_interrupt() {} - - /// Optional. This is used to save power, this is called when the timer queue is not empty. - /// - /// Enabling and disabling the monotonic needs to propagate to `now` so that an instant - /// based of `now()` is still valid. - /// - /// NOTE: This may be called more than once. - fn enable_timer() {} - - /// Optional. This is used to save power, this is called when the timer queue is empty. - /// - /// Enabling and disabling the monotonic needs to propagate to `now` so that an instant - /// based of `now()` is still valid. - /// - /// NOTE: This may be called more than once. - fn disable_timer() {} -} -- cgit v1.2.3 From bdf577c30800aedb0ab6b27768e228c057e18fb5 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 24 Jan 2023 12:34:11 +0100 Subject: Systick runs at 1 kHz --- rtic-monotonics/src/systick_monotonic.rs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'rtic-monotonics/src') diff --git a/rtic-monotonics/src/systick_monotonic.rs b/rtic-monotonics/src/systick_monotonic.rs index 7c713b6..af17f93 100644 --- a/rtic-monotonics/src/systick_monotonic.rs +++ b/rtic-monotonics/src/systick_monotonic.rs @@ -10,11 +10,12 @@ use cortex_m::peripheral::SYST; use embedded_hal_async::delay::DelayUs; use fugit::ExtU32; -/// Systick implementing `rtic_monotonic::Monotonic` which runs at a -/// settable rate using the `TIMER_HZ` parameter. -pub struct Systick; +const TIMER_HZ: u32 = 1_000; -impl Systick { +/// Systick implementing `rtic_monotonic::Monotonic` which runs at 1 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 @@ -49,7 +50,7 @@ impl Systick { static SYSTICK_CNT: AtomicU32 = AtomicU32::new(0); -impl Monotonic for Systick { +impl Monotonic for Systick { type Instant = fugit::TimerInstantU32; type Duration = fugit::TimerDurationU32; @@ -87,17 +88,17 @@ impl Monotonic for Systick { } /// Timer queue wrapper to implement traits on -pub struct SystickTimerQueue(TimerQueue>); +pub struct SystickTimerQueue(TimerQueue); -impl SystickTimerQueue { +impl SystickTimerQueue { /// Create a new timer queue. pub const fn new() -> Self { Self(TimerQueue::new()) } } -impl Deref for SystickTimerQueue { - type Target = TimerQueue>; +impl Deref for SystickTimerQueue { + type Target = TimerQueue; #[inline(always)] fn deref(&self) -> &Self::Target { @@ -105,7 +106,7 @@ impl Deref for SystickTimerQueue { } } -impl DelayUs for SystickTimerQueue { +impl DelayUs for SystickTimerQueue { type Error = core::convert::Infallible; async fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { @@ -122,8 +123,8 @@ impl DelayUs for SystickTimerQueue { /// Register the Systick interrupt and crate a timer queue with a specific name and speed. #[macro_export] macro_rules! make_systick_timer_queue { - ($timer_queue_name:ident, $systick_speed:expr) => { - static $timer_queue_name: SystickTimerQueue<$systick_speed> = SystickTimerQueue::new(); + ($timer_queue_name:ident) => { + static $timer_queue_name: SystickTimerQueue = SystickTimerQueue::new(); #[no_mangle] #[allow(non_snake_case)] -- cgit v1.2.3 From 1baa4a4228cae4576e194174618bf35f5c206959 Mon Sep 17 00:00:00 2001 From: Henrik Tjäder Date: Fri, 27 Jan 2023 13:18:29 +0100 Subject: CI: Don't let warnings get away --- rtic-channel/src/lib.rs | 1 + rtic-monotonics/src/lib.rs | 1 + rtic-time/src/lib.rs | 1 + 3 files changed, 3 insertions(+) (limited to 'rtic-monotonics/src') diff --git a/rtic-channel/src/lib.rs b/rtic-channel/src/lib.rs index a7098ee..166015f 100644 --- a/rtic-channel/src/lib.rs +++ b/rtic-channel/src/lib.rs @@ -2,6 +2,7 @@ #![no_std] #![deny(missing_docs)] +//deny_warnings_placeholder_for_ci use core::{ cell::UnsafeCell, diff --git a/rtic-monotonics/src/lib.rs b/rtic-monotonics/src/lib.rs index ce30c36..1e23088 100644 --- a/rtic-monotonics/src/lib.rs +++ b/rtic-monotonics/src/lib.rs @@ -3,6 +3,7 @@ #![no_std] #![no_main] #![deny(missing_docs)] +//deny_warnings_placeholder_for_ci #![allow(incomplete_features)] #![feature(async_fn_in_trait)] diff --git a/rtic-time/src/lib.rs b/rtic-time/src/lib.rs index 78ece1d..eeecd86 100644 --- a/rtic-time/src/lib.rs +++ b/rtic-time/src/lib.rs @@ -2,6 +2,7 @@ #![no_std] #![deny(missing_docs)] +//deny_warnings_placeholder_for_ci #![allow(incomplete_features)] #![feature(async_fn_in_trait)] -- cgit v1.2.3 From 922f1ad0eb56d362e527ff28e456b6fa133e212b Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Fri, 27 Jan 2023 20:20:14 +0100 Subject: Added examples for async crates + fixed codegen for non-Copy arguments --- rtic-channel/.gitignore | 2 + rtic-channel/src/lib.rs | 36 ++++++++++++++++-- rtic-channel/src/wait_queue.rs | 22 ----------- rtic-monotonics/Cargo.toml | 1 + rtic-monotonics/src/systick_monotonic.rs | 8 ++-- rtic/Cargo.toml | 3 ++ rtic/ci/expected/async-channel.run | 6 +++ rtic/ci/expected/message_passing.run | 2 - rtic/examples/async-channel.rs | 61 ++++++++++++++++++++++++++++++ rtic/examples/async-delay.no_rs | 63 ------------------------------- rtic/examples/async-delay.rs | 65 ++++++++++++++++++++++++++++++++ rtic/examples/message_passing.no_rs | 37 ------------------ rtic/examples/message_passing.rs | 34 +++++++++++++++++ rtic/macros/src/codegen/module.rs | 19 ++++++---- rtic/src/export/executor.rs | 24 ++++++------ 15 files changed, 229 insertions(+), 154 deletions(-) create mode 100644 rtic-channel/.gitignore create mode 100644 rtic/ci/expected/async-channel.run create mode 100644 rtic/examples/async-channel.rs delete mode 100644 rtic/examples/async-delay.no_rs create mode 100644 rtic/examples/async-delay.rs delete mode 100644 rtic/examples/message_passing.no_rs create mode 100644 rtic/examples/message_passing.rs (limited to 'rtic-monotonics/src') diff --git a/rtic-channel/.gitignore b/rtic-channel/.gitignore new file mode 100644 index 0000000..1e7caa9 --- /dev/null +++ b/rtic-channel/.gitignore @@ -0,0 +1,2 @@ +Cargo.lock +target/ diff --git a/rtic-channel/src/lib.rs b/rtic-channel/src/lib.rs index 166015f..5ee2c71 100644 --- a/rtic-channel/src/lib.rs +++ b/rtic-channel/src/lib.rs @@ -67,7 +67,7 @@ impl Channel { /// Split the queue into a `Sender`/`Receiver` pair. pub fn split<'a>(&'a mut self) -> (Sender<'a, T, N>, Receiver<'a, T, N>) { // Fill free queue - for idx in 0..(N - 1) as u8 { + for idx in 0..N as u8 { debug_assert!(!self.freeq.get_mut().is_full()); // SAFETY: This safe as the loop goes from 0 to the capacity of the underlying queue. @@ -114,11 +114,26 @@ macro_rules! make_channel { /// Error state for when the receiver has been dropped. pub struct NoReceiver(pub T); +impl core::fmt::Debug for NoReceiver +where + T: core::fmt::Debug, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "NoReceiver({:?})", self.0) + } +} + /// A `Sender` can send to the channel and can be cloned. pub struct Sender<'a, T, const N: usize>(&'a Channel); unsafe impl<'a, T, const N: usize> Send for Sender<'a, T, N> {} +impl<'a, T, const N: usize> core::fmt::Debug for Sender<'a, T, N> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Sender") + } +} + impl<'a, T, const N: usize> Sender<'a, T, N> { #[inline(always)] fn send_footer(&mut self, idx: u8, val: T) { @@ -204,7 +219,7 @@ impl<'a, T, const N: usize> Sender<'a, T, N> { // Return the index Poll::Ready(Ok(idx)) } else { - return Poll::Pending; + Poll::Pending } }) .await; @@ -267,16 +282,29 @@ impl<'a, T, const N: usize> Clone for Sender<'a, T, N> { /// A receiver of the channel. There can only be one receiver at any time. pub struct Receiver<'a, T, const N: usize>(&'a Channel); +unsafe impl<'a, T, const N: usize> Send for Receiver<'a, T, N> {} + +impl<'a, T, const N: usize> core::fmt::Debug for Receiver<'a, T, N> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Receiver") + } +} + /// Error state for when all senders has been dropped. pub struct NoSender; +impl core::fmt::Debug for NoSender { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "NoSender") + } +} + impl<'a, T, const N: usize> Receiver<'a, T, N> { /// Receives a value if there is one in the channel, non-blocking. /// Note; this does not check if the channel is closed. pub fn try_recv(&mut self) -> Option { // Try to get a ready slot. - let ready_slot = - critical_section::with(|cs| self.0.access(cs).readyq.pop_front().map(|rs| rs)); + let ready_slot = critical_section::with(|cs| self.0.access(cs).readyq.pop_front()); if let Some(rs) = ready_slot { // Read the value from the slots, note; this memcpy is not under a critical section. diff --git a/rtic-channel/src/wait_queue.rs b/rtic-channel/src/wait_queue.rs index 90d762b..5b59983 100644 --- a/rtic-channel/src/wait_queue.rs +++ b/rtic-channel/src/wait_queue.rs @@ -1,6 +1,5 @@ //! ... -use core::cell::UnsafeCell; use core::marker::PhantomPinned; use core::ptr::null_mut; use core::sync::atomic::{AtomicPtr, Ordering}; @@ -9,27 +8,6 @@ use critical_section as cs; pub type WaitQueue = LinkedList; -struct MyLinkPtr(UnsafeCell<*mut Link>); - -impl MyLinkPtr { - #[inline(always)] - fn new(val: *mut Link) -> Self { - Self(UnsafeCell::new(val)) - } - - /// SAFETY: Only use this in a critical section, and don't forget them barriers. - #[inline(always)] - unsafe fn load_relaxed(&self) -> *mut Link { - unsafe { *self.0.get() } - } - - /// SAFETY: Only use this in a critical section, and don't forget them barriers. - #[inline(always)] - unsafe fn store_relaxed(&self, val: *mut Link) { - unsafe { self.0.get().write(val) } - } -} - /// A FIFO linked list for a wait queue. pub struct LinkedList { head: AtomicPtr>, // UnsafeCell<*mut Link> diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml index 68daba4..9d364c8 100644 --- a/rtic-monotonics/Cargo.toml +++ b/rtic-monotonics/Cargo.toml @@ -10,3 +10,4 @@ cortex-m = { version = "0.7.6" } embedded-hal-async = "0.2.0-alpha.0" fugit = { version = "0.3.6", features = ["defmt"] } rtic-time = { version = "1.0.0", path = "../rtic-time" } +atomic-polyfill = "1" diff --git a/rtic-monotonics/src/systick_monotonic.rs b/rtic-monotonics/src/systick_monotonic.rs index af17f93..fec97f2 100644 --- a/rtic-monotonics/src/systick_monotonic.rs +++ b/rtic-monotonics/src/systick_monotonic.rs @@ -2,13 +2,11 @@ use super::Monotonic; pub use super::{TimeoutError, TimerQueue}; -use core::{ - ops::Deref, - sync::atomic::{AtomicU32, Ordering}, -}; +use atomic_polyfill::{AtomicU32, Ordering}; +use core::ops::Deref; use cortex_m::peripheral::SYST; use embedded_hal_async::delay::DelayUs; -use fugit::ExtU32; +pub use fugit::ExtU32; const TIMER_HZ: u32 = 1_000; diff --git a/rtic/Cargo.toml b/rtic/Cargo.toml index 6eb691d..12a34da 100644 --- a/rtic/Cargo.toml +++ b/rtic/Cargo.toml @@ -38,6 +38,9 @@ version_check = "0.9" lm3s6965 = "0.1.3" cortex-m-semihosting = "0.5.0" systick-monotonic = "1.0.0" +rtic-time = { path = "../rtic-time" } +rtic-channel = { path = "../rtic-channel" } +rtic-monotonics = { path = "../rtic-monotonics" } [dev-dependencies.panic-semihosting] features = ["exit"] diff --git a/rtic/ci/expected/async-channel.run b/rtic/ci/expected/async-channel.run new file mode 100644 index 0000000..3e3c232 --- /dev/null +++ b/rtic/ci/expected/async-channel.run @@ -0,0 +1,6 @@ +Sender 1 sending: 1 +Sender 2 sending: 2 +Sender 3 sending: 3 +Receiver got: 1 +Receiver got: 2 +Receiver got: 5 diff --git a/rtic/ci/expected/message_passing.run b/rtic/ci/expected/message_passing.run index a1448d8..9085e13 100644 --- a/rtic/ci/expected/message_passing.run +++ b/rtic/ci/expected/message_passing.run @@ -1,3 +1 @@ foo 1, 1 -foo 1, 2 -foo 2, 3 diff --git a/rtic/examples/async-channel.rs b/rtic/examples/async-channel.rs new file mode 100644 index 0000000..3a2e491 --- /dev/null +++ b/rtic/examples/async-channel.rs @@ -0,0 +1,61 @@ +//! examples/async-channel.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0])] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use rtic_channel::*; + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local) { + let (s, r) = make_channel!(u32, 5); + + receiver::spawn(r).unwrap(); + sender1::spawn(s.clone()).unwrap(); + sender2::spawn(s.clone()).unwrap(); + sender3::spawn(s).unwrap(); + + (Shared {}, Local {}) + } + + #[task] + async fn receiver(_c: receiver::Context, mut receiver: Receiver<'static, u32, 5>) { + while let Ok(val) = receiver.recv().await { + hprintln!("Receiver got: {}", val); + if val == 5 { + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + } + } + } + + #[task] + async fn sender1(_c: sender1::Context, mut sender: Sender<'static, u32, 5>) { + hprintln!("Sender 1 sending: 1"); + sender.send(1).await.unwrap(); + } + + #[task] + async fn sender2(_c: sender2::Context, mut sender: Sender<'static, u32, 5>) { + hprintln!("Sender 2 sending: 2"); + sender.send(2).await.unwrap(); + } + + #[task] + async fn sender3(_c: sender3::Context, mut sender: Sender<'static, u32, 5>) { + hprintln!("Sender 3 sending: 3"); + sender.send(5).await.unwrap(); + } +} diff --git a/rtic/examples/async-delay.no_rs b/rtic/examples/async-delay.no_rs deleted file mode 100644 index fb478c3..0000000 --- a/rtic/examples/async-delay.no_rs +++ /dev/null @@ -1,63 +0,0 @@ -#![no_main] -#![no_std] -#![feature(type_alias_impl_trait)] - -use panic_semihosting as _; - -#[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)] -mod app { - use cortex_m_semihosting::{debug, hprintln}; - use systick_monotonic::*; - - #[shared] - struct Shared {} - - #[local] - struct Local {} - - #[monotonic(binds = SysTick, default = true)] - type MyMono = Systick<100>; - - #[init] - fn init(cx: init::Context) -> (Shared, Local) { - hprintln!("init").unwrap(); - - foo::spawn().ok(); - bar::spawn().ok(); - baz::spawn().ok(); - - (Shared {}, Local {}) - } - - #[idle] - fn idle(_: idle::Context) -> ! { - // debug::exit(debug::EXIT_SUCCESS); - loop { - // hprintln!("idle"); - cortex_m::asm::wfi(); // put the MCU in sleep mode until interrupt occurs - } - } - - #[task] - async fn foo(_cx: foo::Context) { - hprintln!("hello from foo").ok(); - monotonics::delay(100.millis()).await; - hprintln!("bye from foo").ok(); - } - - #[task] - async fn bar(_cx: bar::Context) { - hprintln!("hello from bar").ok(); - monotonics::delay(200.millis()).await; - hprintln!("bye from bar").ok(); - } - - #[task] - async fn baz(_cx: baz::Context) { - hprintln!("hello from baz").ok(); - monotonics::delay(300.millis()).await; - hprintln!("bye from baz").ok(); - - debug::exit(debug::EXIT_SUCCESS); - } -} diff --git a/rtic/examples/async-delay.rs b/rtic/examples/async-delay.rs new file mode 100644 index 0000000..0440f77 --- /dev/null +++ b/rtic/examples/async-delay.rs @@ -0,0 +1,65 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use rtic_monotonics::systick_monotonic::*; + + rtic_monotonics::make_systick_timer_queue!(TIMER); + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(cx: init::Context) -> (Shared, Local) { + hprintln!("init"); + + let systick = Systick::start(cx.core.SYST, 12_000_000); + TIMER.initialize(systick); + + foo::spawn().ok(); + bar::spawn().ok(); + baz::spawn().ok(); + + (Shared {}, Local {}) + } + + #[idle] + fn idle(_: idle::Context) -> ! { + // debug::exit(debug::EXIT_SUCCESS); + loop { + // hprintln!("idle"); + cortex_m::asm::wfi(); // put the MCU in sleep mode until interrupt occurs + } + } + + #[task] + async fn foo(_cx: foo::Context) { + hprintln!("hello from foo"); + TIMER.delay(100.millis()).await; + hprintln!("bye from foo"); + } + + #[task] + async fn bar(_cx: bar::Context) { + hprintln!("hello from bar"); + TIMER.delay(200.millis()).await; + hprintln!("bye from bar"); + } + + #[task] + async fn baz(_cx: baz::Context) { + hprintln!("hello from baz"); + TIMER.delay(300.millis()).await; + hprintln!("bye from baz"); + + debug::exit(debug::EXIT_SUCCESS); + } +} diff --git a/rtic/examples/message_passing.no_rs b/rtic/examples/message_passing.no_rs deleted file mode 100644 index ffa9537..0000000 --- a/rtic/examples/message_passing.no_rs +++ /dev/null @@ -1,37 +0,0 @@ -//! examples/message_passing.rs - -#![deny(unsafe_code)] -#![deny(warnings)] -#![no_main] -#![no_std] - -use panic_semihosting as _; - -#[rtic::app(device = lm3s6965, dispatchers = [SSI0])] -mod app { - use cortex_m_semihosting::{debug, hprintln}; - - #[shared] - struct Shared {} - - #[local] - struct Local {} - - #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - foo::spawn(1, 1).unwrap(); - foo::spawn(1, 2).unwrap(); - foo::spawn(2, 3).unwrap(); - assert!(foo::spawn(1, 4).is_err()); // The capacity of `foo` is reached - - (Shared {}, Local {}, init::Monotonics()) - } - - #[task(capacity = 3)] - fn foo(_c: foo::Context, x: i32, y: u32) { - hprintln!("foo {}, {}", x, y).unwrap(); - if x == 2 { - debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator - } - } -} diff --git a/rtic/examples/message_passing.rs b/rtic/examples/message_passing.rs new file mode 100644 index 0000000..01f1a65 --- /dev/null +++ b/rtic/examples/message_passing.rs @@ -0,0 +1,34 @@ +//! examples/message_passing.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0])] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local) { + foo::spawn(1, 1).unwrap(); + assert!(foo::spawn(1, 4).is_err()); // The capacity of `foo` is reached + + (Shared {}, Local {}) + } + + #[task] + async fn foo(_c: foo::Context, x: i32, y: u32) { + hprintln!("foo {}, {}", x, y); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + } +} diff --git a/rtic/macros/src/codegen/module.rs b/rtic/macros/src/codegen/module.rs index 4725b9a..8b3fca2 100644 --- a/rtic/macros/src/codegen/module.rs +++ b/rtic/macros/src/codegen/module.rs @@ -157,14 +157,17 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { #[allow(non_snake_case)] #[doc(hidden)] pub fn #internal_spawn_ident(#(#input_args,)*) -> Result<(), #input_ty> { - - if #exec_name.spawn(|| #name(unsafe { #name::Context::new() } #(,#input_untupled)*) ) { - - #pend_interrupt - - Ok(()) - } else { - Err(#input_tupled) + // SAFETY: If `try_allocate` suceeds one must call `spawn`, which we do. + unsafe { + if #exec_name.try_allocate() { + let f = #name(unsafe { #name::Context::new() } #(,#input_untupled)*); + #exec_name.spawn(f); + #pend_interrupt + + Ok(()) + } else { + Err(#input_tupled) + } } } diff --git a/rtic/src/export/executor.rs b/rtic/src/export/executor.rs index e64cc43..36e47d7 100644 --- a/rtic/src/export/executor.rs +++ b/rtic/src/export/executor.rs @@ -70,25 +70,23 @@ impl AsyncTaskExecutor { self.pending.store(true, Ordering::Release); } - /// Spawn a future + /// Allocate the executor. To use with `spawn`. #[inline(always)] - pub fn spawn(&self, future: impl Fn() -> F) -> bool { + pub unsafe fn try_allocate(&self) -> bool { // Try to reserve the executor for a future. - if self - .running + self.running .compare_exchange(false, true, Ordering::AcqRel, Ordering::Relaxed) .is_ok() - { - // This unsafe is protected by `running` being false and the atomic setting it to true. - unsafe { - self.task.get().write(MaybeUninit::new(future())); - } - self.set_pending(); + } - true - } else { - false + /// Spawn a future + #[inline(always)] + pub unsafe fn spawn(&self, future: F) { + // This unsafe is protected by `running` being false and the atomic setting it to true. + unsafe { + self.task.get().write(MaybeUninit::new(future)); } + self.set_pending(); } /// Poll the future in the executor. -- cgit v1.2.3 From 82f2f0834943cd7f69130f30904f4372d39f92e7 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Thu, 2 Feb 2023 20:14:41 +0100 Subject: Make xtask pass, clean up rtic Cargo.toml --- rtic-monotonics/Cargo.toml | 7 +++++++ rtic-monotonics/src/systick_monotonic.rs | 7 +++++++ rtic/Cargo.toml | 12 +++++------- rtic/ci/expected/async-timeout.run | 22 +++++++++++----------- rtic/src/lib.rs | 2 +- 5 files changed, 31 insertions(+), 19 deletions(-) (limited to 'rtic-monotonics/src') diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml index 5e6586e..f94a78b 100644 --- a/rtic-monotonics/Cargo.toml +++ b/rtic-monotonics/Cargo.toml @@ -22,3 +22,10 @@ embedded-hal-async = "0.2.0-alpha.0" fugit = { version = "0.3.6", features = ["defmt"] } rtic-time = { version = "1.0.0-alpha.0", path = "../rtic-time" } atomic-polyfill = "1" + +[features] +default = ["systick_1khz"] + +systick_100hz = [] +systick_1khz = [] +systick_10khz = [] diff --git a/rtic-monotonics/src/systick_monotonic.rs b/rtic-monotonics/src/systick_monotonic.rs index fec97f2..24deb54 100644 --- a/rtic-monotonics/src/systick_monotonic.rs +++ b/rtic-monotonics/src/systick_monotonic.rs @@ -8,8 +8,15 @@ use cortex_m::peripheral::SYST; use embedded_hal_async::delay::DelayUs; pub use fugit::ExtU32; +#[cfg(feature = "systick_100hz")] +const TIMER_HZ: u32 = 100; + +#[cfg(feature = "systick_1khz")] const TIMER_HZ: u32 = 1_000; +#[cfg(feature = "systick_10khz")] +const TIMER_HZ: u32 = 10_000; + /// Systick implementing `rtic_monotonic::Monotonic` which runs at 1 kHz. pub struct Systick; diff --git a/rtic/Cargo.toml b/rtic/Cargo.toml index 7bca4c1..d722e65 100644 --- a/rtic/Cargo.toml +++ b/rtic/Cargo.toml @@ -35,25 +35,23 @@ name = "rtic" [dependencies] cortex-m = "0.7.0" -rtic-macros = { path = "macros", version = "2.0.0-alpha.0" } -rtic-monotonic = "1.0.0" -rtic-core = "1.0.0" -heapless = "0.7.7" bare-metal = "1.0.0" #portable-atomic = { version = "0.3.19" } atomic-polyfill = "1" +rtic-macros = { path = "./macros", version = "2.0.0-alpha.0" } +rtic-core = "1" [build-dependencies] version_check = "0.9" [dev-dependencies] +heapless = "0.7.7" lm3s6965 = "0.1.3" cortex-m-semihosting = "0.5.0" -systick-monotonic = "1.0.0" rtic-time = { path = "../rtic-time" } rtic-channel = { path = "../rtic-channel" } -rtic-monotonics = { path = "../rtic-monotonics" } +rtic-monotonics = { path = "../rtic-monotonics", default-features = false } [dev-dependencies.futures] version = "0.3.26" @@ -94,7 +92,7 @@ overflow-checks = false lm3s6965 = { git = "https://github.com/japaric/lm3s6965" } [features] -test-critical-section = ["cortex-m/critical-section-single-core"] +test-critical-section = ["cortex-m/critical-section-single-core", "rtic-monotonics/systick_100hz"] # [[example]] # name = "pool" diff --git a/rtic/ci/expected/async-timeout.run b/rtic/ci/expected/async-timeout.run index ca929c7..dea8e5d 100644 --- a/rtic/ci/expected/async-timeout.run +++ b/rtic/ci/expected/async-timeout.run @@ -1,16 +1,16 @@ init -the hal takes a duration of Duration { ticks: 450 } +the hal takes a duration of Duration { ticks: 45 } timeout -the hal takes a duration of Duration { ticks: 450 } +the hal takes a duration of Duration { ticks: 45 } hal returned 5 -the hal takes a duration of Duration { ticks: 450 } +the hal takes a duration of Duration { ticks: 45 } hal returned 5 -now is Instant { ticks: 2102 }, timeout at Instant { ticks: 2602 } -the hal takes a duration of Duration { ticks: 350 } -hal returned 5 at time Instant { ticks: 2452 } -now is Instant { ticks: 3102 }, timeout at Instant { ticks: 3602 } -the hal takes a duration of Duration { ticks: 450 } -hal returned 5 at time Instant { ticks: 3552 } -now is Instant { ticks: 4102 }, timeout at Instant { ticks: 4602 } -the hal takes a duration of Duration { ticks: 550 } +now is Instant { ticks: 210 }, timeout at Instant { ticks: 260 } +the hal takes a duration of Duration { ticks: 35 } +hal returned 5 at time Instant { ticks: 245 } +now is Instant { ticks: 310 }, timeout at Instant { ticks: 360 } +the hal takes a duration of Duration { ticks: 45 } +hal returned 5 at time Instant { ticks: 355 } +now is Instant { ticks: 410 }, timeout at Instant { ticks: 460 } +the hal takes a duration of Duration { ticks: 55 } timeout diff --git a/rtic/src/lib.rs b/rtic/src/lib.rs index 860e743..a193e5c 100644 --- a/rtic/src/lib.rs +++ b/rtic/src/lib.rs @@ -36,7 +36,7 @@ use cortex_m::{interrupt::InterruptNumber, peripheral::NVIC}; pub use rtic_core::{prelude as mutex_prelude, Exclusive, Mutex}; pub use rtic_macros::app; -pub use rtic_monotonic::{self, Monotonic}; +// pub use rtic_monotonic::{self, Monotonic}; /// module `mutex::prelude` provides `Mutex` and multi-lock variants. Recommended over `mutex_prelude` pub mod mutex { -- cgit v1.2.3 From b6fdb9060bc2f19308d864c934078d8160a17ca3 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Thu, 2 Feb 2023 21:00:41 +0100 Subject: Simplify Systick Monotonic by integrating the TQ --- rtic-monotonics/src/systick_monotonic.rs | 80 +++++++++++++++++++------------- rtic/examples/async-delay.rs | 11 ++--- rtic/examples/async-timeout.rs | 30 ++++++------ 3 files changed, 68 insertions(+), 53 deletions(-) (limited to 'rtic-monotonics/src') diff --git a/rtic-monotonics/src/systick_monotonic.rs b/rtic-monotonics/src/systick_monotonic.rs index 24deb54..a5a0aef 100644 --- a/rtic-monotonics/src/systick_monotonic.rs +++ b/rtic-monotonics/src/systick_monotonic.rs @@ -3,7 +3,7 @@ use super::Monotonic; pub use super::{TimeoutError, TimerQueue}; use atomic_polyfill::{AtomicU32, Ordering}; -use core::ops::Deref; +use core::future::Future; use cortex_m::peripheral::SYST; use embedded_hal_async::delay::DelayUs; pub use fugit::ExtU32; @@ -30,8 +30,7 @@ impl Systick { /// `sysclk` and `TIMER_HZ`. /// /// Note: Give the return value to `TimerQueue::initialize()` to initialize the timer queue. - #[must_use] - pub fn start(mut systick: cortex_m::peripheral::SYST, sysclk: u32) -> Self { + 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; @@ -45,7 +44,7 @@ impl Systick { systick.enable_interrupt(); systick.enable_counter(); - Systick {} + SYSTICK_TIMER_QUEUE.initialize(Systick {}); } fn systick() -> SYST { @@ -54,6 +53,44 @@ impl Systick { } static SYSTICK_CNT: AtomicU32 = AtomicU32::new(0); +static SYSTICK_TIMER_QUEUE: TimerQueue = TimerQueue::new(); + +// Forward timerqueue interface +impl Systick { + /// Used to access the underlying timer queue + #[doc(hidden)] + pub fn __tq() -> &'static TimerQueue { + &SYSTICK_TIMER_QUEUE + } + + /// Timeout at a specific time. + pub async fn timeout_at( + instant: ::Instant, + future: F, + ) -> Result { + SYSTICK_TIMER_QUEUE.timeout_at(instant, future).await + } + + /// Timeout after a specific duration. + #[inline] + pub async fn timeout_after( + duration: ::Duration, + future: F, + ) -> Result { + SYSTICK_TIMER_QUEUE.timeout_after(duration, future).await + } + + /// Delay for some duration of time. + #[inline] + pub async fn delay(duration: ::Duration) { + SYSTICK_TIMER_QUEUE.delay(duration).await; + } + + /// Delay to some specific time instant. + pub async fn delay_until(instant: ::Instant) { + SYSTICK_TIMER_QUEUE.delay_until(instant).await; + } +} impl Monotonic for Systick { type Instant = fugit::TimerInstantU32; @@ -92,49 +129,28 @@ impl Monotonic for Systick { fn disable_timer() {} } -/// Timer queue wrapper to implement traits on -pub struct SystickTimerQueue(TimerQueue); - -impl SystickTimerQueue { - /// Create a new timer queue. - pub const fn new() -> Self { - Self(TimerQueue::new()) - } -} - -impl Deref for SystickTimerQueue { - type Target = TimerQueue; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DelayUs for SystickTimerQueue { +impl DelayUs for Systick { type Error = core::convert::Infallible; async fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { - self.delay(us.micros()).await; + SYSTICK_TIMER_QUEUE.delay(us.micros()).await; Ok(()) } async fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { - self.delay(ms.millis()).await; + SYSTICK_TIMER_QUEUE.delay(ms.millis()).await; Ok(()) } } -/// Register the Systick interrupt and crate a timer queue with a specific name and speed. +/// Register the Systick interrupt for the monotonic. #[macro_export] -macro_rules! make_systick_timer_queue { - ($timer_queue_name:ident) => { - static $timer_queue_name: SystickTimerQueue = SystickTimerQueue::new(); - +macro_rules! make_systick_handler { + () => { #[no_mangle] #[allow(non_snake_case)] unsafe extern "C" fn SysTick() { - $timer_queue_name.on_monotonic_interrupt(); + Systick::__tq().on_monotonic_interrupt(); } }; } diff --git a/rtic/examples/async-delay.rs b/rtic/examples/async-delay.rs index aa673dd..0988346 100644 --- a/rtic/examples/async-delay.rs +++ b/rtic/examples/async-delay.rs @@ -11,7 +11,7 @@ mod app { use cortex_m_semihosting::{debug, hprintln}; use rtic_monotonics::systick_monotonic::*; - rtic_monotonics::make_systick_timer_queue!(TIMER); + rtic_monotonics::make_systick_handler!(); #[shared] struct Shared {} @@ -23,8 +23,7 @@ mod app { fn init(cx: init::Context) -> (Shared, Local) { hprintln!("init"); - let systick = Systick::start(cx.core.SYST, 12_000_000); - TIMER.initialize(systick); + Systick::start(cx.core.SYST, 12_000_000); foo::spawn().ok(); bar::spawn().ok(); @@ -45,21 +44,21 @@ mod app { #[task] async fn foo(_cx: foo::Context) { hprintln!("hello from foo"); - TIMER.delay(100.millis()).await; + Systick::delay(100.millis()).await; hprintln!("bye from foo"); } #[task] async fn bar(_cx: bar::Context) { hprintln!("hello from bar"); - TIMER.delay(200.millis()).await; + Systick::delay(200.millis()).await; hprintln!("bye from bar"); } #[task] async fn baz(_cx: baz::Context) { hprintln!("hello from baz"); - TIMER.delay(300.millis()).await; + Systick::delay(300.millis()).await; hprintln!("bye from baz"); debug::exit(debug::EXIT_SUCCESS); diff --git a/rtic/examples/async-timeout.rs b/rtic/examples/async-timeout.rs index 30fee43..a850b10 100644 --- a/rtic/examples/async-timeout.rs +++ b/rtic/examples/async-timeout.rs @@ -12,8 +12,9 @@ use rtic_monotonics::systick_monotonic::*; mod app { use super::*; use futures::{future::FutureExt, select_biased}; + use rtic_monotonics::Monotonic; - rtic_monotonics::make_systick_timer_queue!(TIMER); + rtic_monotonics::make_systick_handler!(); #[shared] struct Shared {} @@ -25,8 +26,7 @@ mod app { fn init(cx: init::Context) -> (Shared, Local) { hprintln!("init"); - let systick = Systick::start(cx.core.SYST, 12_000_000); - TIMER.initialize(systick); + Systick::start(cx.core.SYST, 12_000_000); foo::spawn().ok(); @@ -37,37 +37,37 @@ mod app { async fn foo(_cx: foo::Context) { // Call hal with short relative timeout using `select_biased` select_biased! { - v = hal_get(&TIMER, 1).fuse() => hprintln!("hal returned {}", v), - _ = TIMER.delay(200.millis()).fuse() => hprintln!("timeout", ), // this will finish first + v = hal_get(1).fuse() => hprintln!("hal returned {}", v), + _ = Systick::delay(200.millis()).fuse() => hprintln!("timeout", ), // this will finish first } // Call hal with long relative timeout using `select_biased` select_biased! { - v = hal_get(&TIMER, 1).fuse() => hprintln!("hal returned {}", v), // hal finish first - _ = TIMER.delay(1000.millis()).fuse() => hprintln!("timeout", ), + v = hal_get(1).fuse() => hprintln!("hal returned {}", v), // hal finish first + _ = Systick::delay(1000.millis()).fuse() => hprintln!("timeout", ), } // Call hal with long relative timeout using monotonic `timeout_after` - match TIMER.timeout_after(1000.millis(), hal_get(&TIMER, 1)).await { + match Systick::timeout_after(1000.millis(), hal_get(1)).await { Ok(v) => hprintln!("hal returned {}", v), _ => hprintln!("timeout"), } // get the current time instance - let mut instant = TIMER.now(); + let mut instant = Systick::now(); // do this 3 times for n in 0..3 { // exact point in time without drift instant += 1000.millis(); - TIMER.delay_until(instant).await; + Systick::delay_until(instant).await; // exact point it time for timeout let timeout = instant + 500.millis(); - hprintln!("now is {:?}, timeout at {:?}", TIMER.now(), timeout); + hprintln!("now is {:?}, timeout at {:?}", Systick::now(), timeout); - match TIMER.timeout_at(timeout, hal_get(&TIMER, n)).await { - Ok(v) => hprintln!("hal returned {} at time {:?}", v, TIMER.now()), + match Systick::timeout_at(timeout, hal_get(n)).await { + Ok(v) => hprintln!("hal returned {} at time {:?}", v, Systick::now()), _ => hprintln!("timeout"), } } @@ -77,11 +77,11 @@ mod app { } // Emulate some hal -async fn hal_get(timer: &'static SystickTimerQueue, n: u32) -> u32 { +async fn hal_get(n: u32) -> u32 { // emulate some delay time dependent on n let d = 350.millis() + n * 100.millis(); hprintln!("the hal takes a duration of {:?}", d); - timer.delay(d).await; + Systick::delay(d).await; // emulate some return value 5 } -- cgit v1.2.3 From 858160a55d839bb4064006aa55ddb581259a3074 Mon Sep 17 00:00:00 2001 From: Henrik Tjäder Date: Sat, 4 Feb 2023 10:14:12 +0100 Subject: rtic-monotonics: Simplify features, default is 1kHz Make 100 Hz or 10 kHz opt in through features, which are meant for testing primarily. --- rtic-monotonics/CHANGELOG.md | 2 +- rtic-monotonics/Cargo.toml | 4 +--- rtic-monotonics/src/systick_monotonic.rs | 25 ++++++++++++++++--------- rtic/Cargo.toml | 2 +- 4 files changed, 19 insertions(+), 14 deletions(-) (limited to 'rtic-monotonics/src') diff --git a/rtic-monotonics/CHANGELOG.md b/rtic-monotonics/CHANGELOG.md index e990223..d3a9d84 100644 --- a/rtic-monotonics/CHANGELOG.md +++ b/rtic-monotonics/CHANGELOG.md @@ -13,4 +13,4 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Fixed -## [v0.1.0] - 2023-xx-xx +## [v1.0.0] - 2023-xx-xx diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml index f94a78b..4014fe7 100644 --- a/rtic-monotonics/Cargo.toml +++ b/rtic-monotonics/Cargo.toml @@ -22,10 +22,8 @@ embedded-hal-async = "0.2.0-alpha.0" fugit = { version = "0.3.6", features = ["defmt"] } rtic-time = { version = "1.0.0-alpha.0", path = "../rtic-time" } atomic-polyfill = "1" +cfg-if = "1.0.0" [features] -default = ["systick_1khz"] - systick_100hz = [] -systick_1khz = [] systick_10khz = [] diff --git a/rtic-monotonics/src/systick_monotonic.rs b/rtic-monotonics/src/systick_monotonic.rs index a5a0aef..885da92 100644 --- a/rtic-monotonics/src/systick_monotonic.rs +++ b/rtic-monotonics/src/systick_monotonic.rs @@ -8,16 +8,23 @@ use cortex_m::peripheral::SYST; use embedded_hal_async::delay::DelayUs; pub use fugit::ExtU32; -#[cfg(feature = "systick_100hz")] -const TIMER_HZ: u32 = 100; - -#[cfg(feature = "systick_1khz")] -const TIMER_HZ: u32 = 1_000; - -#[cfg(feature = "systick_10khz")] -const TIMER_HZ: u32 = 10_000; +// 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. +/// Systick implementing `rtic_monotonic::Monotonic` which runs at 1 kHz, 100Hz or 10 kHz. pub struct Systick; impl Systick { diff --git a/rtic/Cargo.toml b/rtic/Cargo.toml index d722e65..1aba9c7 100644 --- a/rtic/Cargo.toml +++ b/rtic/Cargo.toml @@ -51,7 +51,7 @@ lm3s6965 = "0.1.3" cortex-m-semihosting = "0.5.0" rtic-time = { path = "../rtic-time" } rtic-channel = { path = "../rtic-channel" } -rtic-monotonics = { path = "../rtic-monotonics", default-features = false } +rtic-monotonics = { path = "../rtic-monotonics" } [dev-dependencies.futures] version = "0.3.26" -- cgit v1.2.3 From 8963e0e27ae57f7a8c3a666fe9e5a993cdd09275 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Fri, 10 Feb 2023 07:52:50 +0100 Subject: RP2040 monotonic, rename systick monotonic to not have double name --- rtic-monotonics/Cargo.toml | 5 + rtic-monotonics/src/lib.rs | 5 +- rtic-monotonics/src/rp2040.rs | 143 +++++++++++++++++++++++++++ rtic-monotonics/src/systick.rs | 163 +++++++++++++++++++++++++++++++ rtic-monotonics/src/systick_monotonic.rs | 163 ------------------------------- 5 files changed, 315 insertions(+), 164 deletions(-) create mode 100644 rtic-monotonics/src/rp2040.rs create mode 100644 rtic-monotonics/src/systick.rs delete mode 100644 rtic-monotonics/src/systick_monotonic.rs (limited to 'rtic-monotonics/src') diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml index 4014fe7..2e75bdb 100644 --- a/rtic-monotonics/Cargo.toml +++ b/rtic-monotonics/Cargo.toml @@ -23,7 +23,12 @@ fugit = { version = "0.3.6", features = ["defmt"] } rtic-time = { version = "1.0.0-alpha.0", path = "../rtic-time" } atomic-polyfill = "1" cfg-if = "1.0.0" +rp2040-pac = { version = ">=0.2.0,<0.5", optional = true } [features] +default = [] + systick_100hz = [] systick_10khz = [] + +rp2040 = ["dep:rp2040-pac"] diff --git a/rtic-monotonics/src/lib.rs b/rtic-monotonics/src/lib.rs index 1e23088..fb5dc8a 100644 --- a/rtic-monotonics/src/lib.rs +++ b/rtic-monotonics/src/lib.rs @@ -9,4 +9,7 @@ pub use rtic_time::{Monotonic, TimeoutError, TimerQueue}; -pub mod systick_monotonic; +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..448c388 --- /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; +use embedded_hal_async::delay::DelayUs; +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 = TimerQueue::new(); + +// Forward timerqueue interface +impl Timer { + /// Used to access the underlying timer queue + #[doc(hidden)] + pub fn __tq() -> &'static TimerQueue { + &TIMER_QUEUE + } + + /// Timeout at a specific time. + pub async fn timeout_at( + instant: ::Instant, + future: F, + ) -> Result { + TIMER_QUEUE.timeout_at(instant, future).await + } + + /// Timeout after a specific duration. + #[inline] + pub async fn timeout_after( + duration: ::Duration, + future: F, + ) -> Result { + TIMER_QUEUE.timeout_after(duration, future).await + } + + /// Delay for some duration of time. + #[inline] + pub async fn delay(duration: ::Duration) { + TIMER_QUEUE.delay(duration).await; + } + + /// Delay to some specific time instant. + pub async fn delay_until(instant: ::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() { + cortex_m::peripheral::NVIC::pend(Interrupt::TIMER_IRQ_0); + } + + fn on_interrupt() {} + + fn enable_timer() {} + + fn disable_timer() {} +} + +impl 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..885da92 --- /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; +use embedded_hal_async::delay::DelayUs; +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 = TimerQueue::new(); + +// Forward timerqueue interface +impl Systick { + /// Used to access the underlying timer queue + #[doc(hidden)] + pub fn __tq() -> &'static TimerQueue { + &SYSTICK_TIMER_QUEUE + } + + /// Timeout at a specific time. + pub async fn timeout_at( + instant: ::Instant, + future: F, + ) -> Result { + SYSTICK_TIMER_QUEUE.timeout_at(instant, future).await + } + + /// Timeout after a specific duration. + #[inline] + pub async fn timeout_after( + duration: ::Duration, + future: F, + ) -> Result { + SYSTICK_TIMER_QUEUE.timeout_after(duration, future).await + } + + /// Delay for some duration of time. + #[inline] + pub async fn delay(duration: ::Duration) { + SYSTICK_TIMER_QUEUE.delay(duration).await; + } + + /// Delay to some specific time instant. + pub async fn delay_until(instant: ::Instant) { + SYSTICK_TIMER_QUEUE.delay_until(instant).await; + } +} + +impl Monotonic for Systick { + type Instant = fugit::TimerInstantU32; + type Duration = fugit::TimerDurationU32; + + 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() {} +} + +impl 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() { + Systick::__tq().on_monotonic_interrupt(); + } + }; +} diff --git a/rtic-monotonics/src/systick_monotonic.rs b/rtic-monotonics/src/systick_monotonic.rs deleted file mode 100644 index 885da92..0000000 --- a/rtic-monotonics/src/systick_monotonic.rs +++ /dev/null @@ -1,163 +0,0 @@ -//! ... - -use super::Monotonic; -pub use super::{TimeoutError, TimerQueue}; -use atomic_polyfill::{AtomicU32, Ordering}; -use core::future::Future; -use cortex_m::peripheral::SYST; -use embedded_hal_async::delay::DelayUs; -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 = TimerQueue::new(); - -// Forward timerqueue interface -impl Systick { - /// Used to access the underlying timer queue - #[doc(hidden)] - pub fn __tq() -> &'static TimerQueue { - &SYSTICK_TIMER_QUEUE - } - - /// Timeout at a specific time. - pub async fn timeout_at( - instant: ::Instant, - future: F, - ) -> Result { - SYSTICK_TIMER_QUEUE.timeout_at(instant, future).await - } - - /// Timeout after a specific duration. - #[inline] - pub async fn timeout_after( - duration: ::Duration, - future: F, - ) -> Result { - SYSTICK_TIMER_QUEUE.timeout_after(duration, future).await - } - - /// Delay for some duration of time. - #[inline] - pub async fn delay(duration: ::Duration) { - SYSTICK_TIMER_QUEUE.delay(duration).await; - } - - /// Delay to some specific time instant. - pub async fn delay_until(instant: ::Instant) { - SYSTICK_TIMER_QUEUE.delay_until(instant).await; - } -} - -impl Monotonic for Systick { - type Instant = fugit::TimerInstantU32; - type Duration = fugit::TimerDurationU32; - - 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() {} -} - -impl 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() { - Systick::__tq().on_monotonic_interrupt(); - } - }; -} -- cgit v1.2.3 From b566a893930f36cb4ee9a03743a04de34e8f0445 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 11 Feb 2023 07:55:08 +0100 Subject: rtic-monotonics: Feature gate monotonics correctly to support multiple MCUs --- rtic-monotonics/Cargo.toml | 10 +++++++--- rtic-monotonics/src/lib.rs | 1 + rtic-monotonics/src/rp2040.rs | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) (limited to 'rtic-monotonics/src') diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml index 2e75bdb..0c942a2 100644 --- a/rtic-monotonics/Cargo.toml +++ b/rtic-monotonics/Cargo.toml @@ -17,18 +17,22 @@ license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -cortex-m = { version = "0.7.6" } -embedded-hal-async = "0.2.0-alpha.0" -fugit = { version = "0.3.6", features = ["defmt"] } rtic-time = { version = "1.0.0-alpha.0", path = "../rtic-time" } +embedded-hal-async = "0.2.0-alpha.0" +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/src/lib.rs b/rtic-monotonics/src/lib.rs index fb5dc8a..4eb2261 100644 --- a/rtic-monotonics/src/lib.rs +++ b/rtic-monotonics/src/lib.rs @@ -9,6 +9,7 @@ pub use rtic_time::{Monotonic, TimeoutError, TimerQueue}; +#[cfg(feature = "cortex_m_sytick")] pub mod systick; #[cfg(feature = "rp2040")] diff --git a/rtic-monotonics/src/rp2040.rs b/rtic-monotonics/src/rp2040.rs index 448c388..064a50d 100644 --- a/rtic-monotonics/src/rp2040.rs +++ b/rtic-monotonics/src/rp2040.rs @@ -106,7 +106,7 @@ impl Monotonic for Timer { } fn pend_interrupt() { - cortex_m::peripheral::NVIC::pend(Interrupt::TIMER_IRQ_0); + rp2040_pac::NVIC::pend(Interrupt::TIMER_IRQ_0); } fn on_interrupt() {} -- cgit v1.2.3 From 60d5e9e1db24fd1e0456949c90464e5fd52d20fe Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Wed, 15 Feb 2023 23:20:29 +0100 Subject: Fix spelling on "cortex_m_systick" --- rtic-monotonics/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rtic-monotonics/src') diff --git a/rtic-monotonics/src/lib.rs b/rtic-monotonics/src/lib.rs index 4eb2261..7e513f8 100644 --- a/rtic-monotonics/src/lib.rs +++ b/rtic-monotonics/src/lib.rs @@ -9,7 +9,7 @@ pub use rtic_time::{Monotonic, TimeoutError, TimerQueue}; -#[cfg(feature = "cortex_m_sytick")] +#[cfg(feature = "cortex_m_systick")] pub mod systick; #[cfg(feature = "rp2040")] -- cgit v1.2.3 From 002d0b0d1685473f0f81cd17346d119fc671ad9c Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Wed, 15 Feb 2023 23:21:52 +0100 Subject: Make embedded-hal-async dependency optional for better compatibility with HALs Some hals implement traits for embedded-hal version `=1.0.0.alpha.`, which is explicitly incompatible with the version `=1.0.0.alpha.9` which embedded-hal-async depends on. Making the dependency optional allows downstream projects to include rtic-monotonic without requiring that all of their other libraries also implement that specific version of embedded-hal 1.0 --- rtic-monotonics/Cargo.toml | 2 +- rtic-monotonics/src/rp2040.rs | 4 ++-- rtic-monotonics/src/systick.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'rtic-monotonics/src') diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml index 0c942a2..d60b68c 100644 --- a/rtic-monotonics/Cargo.toml +++ b/rtic-monotonics/Cargo.toml @@ -18,7 +18,7 @@ license = "MIT OR Apache-2.0" [dependencies] rtic-time = { version = "1.0.0-alpha.0", path = "../rtic-time" } -embedded-hal-async = "0.2.0-alpha.0" +embedded-hal-async = { version = "0.2.0-alpha.0", optional = true } fugit = { version = "0.3.6" } atomic-polyfill = "1" cfg-if = "1.0.0" diff --git a/rtic-monotonics/src/rp2040.rs b/rtic-monotonics/src/rp2040.rs index 064a50d..e42c148 100644 --- a/rtic-monotonics/src/rp2040.rs +++ b/rtic-monotonics/src/rp2040.rs @@ -3,7 +3,6 @@ use super::Monotonic; pub use super::{TimeoutError, TimerQueue}; use core::future::Future; -use embedded_hal_async::delay::DelayUs; pub use fugit::ExtU64; use rp2040_pac::{timer, Interrupt, RESETS, TIMER}; @@ -116,7 +115,8 @@ impl Monotonic for Timer { fn disable_timer() {} } -impl DelayUs for 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> { diff --git a/rtic-monotonics/src/systick.rs b/rtic-monotonics/src/systick.rs index 885da92..2711022 100644 --- a/rtic-monotonics/src/systick.rs +++ b/rtic-monotonics/src/systick.rs @@ -5,7 +5,6 @@ pub use super::{TimeoutError, TimerQueue}; use atomic_polyfill::{AtomicU32, Ordering}; use core::future::Future; use cortex_m::peripheral::SYST; -use embedded_hal_async::delay::DelayUs; pub use fugit::ExtU32; // Features should be additive, here systick_100hz gets picked if both @@ -136,7 +135,8 @@ impl Monotonic for Systick { fn disable_timer() {} } -impl DelayUs for Systick { +#[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> { -- cgit v1.2.3 From b9e0f36aff96ec4e39cf4f728777cbc808df2c78 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 19 Feb 2023 14:30:49 +0100 Subject: Add feature flags --- rtic-macros/Cargo.toml | 11 ++++--- rtic-macros/src/codegen/bindings.rs | 19 ++++++++++-- rtic-macros/src/codegen/bindings/cortex.rs | 50 ++++++------------------------ rtic-macros/src/codegen/util.rs | 2 +- rtic-monotonics/src/systick.rs | 2 +- rtic/Cargo.toml | 16 +++++++--- rtic/build.rs | 19 ++++++------ rtic/examples/async-delay.rs | 2 +- rtic/examples/async-timeout.rs | 2 +- rtic/src/export.rs | 31 +++++++++++++++--- rtic/src/export/cortex_basepri.rs | 4 +-- rtic/src/export/cortex_source_mask.rs | 3 +- 12 files changed, 88 insertions(+), 73 deletions(-) (limited to 'rtic-monotonics/src') diff --git a/rtic-macros/Cargo.toml b/rtic-macros/Cargo.toml index 12cd0d9..bdc2c12 100644 --- a/rtic-macros/Cargo.toml +++ b/rtic-macros/Cargo.toml @@ -26,10 +26,13 @@ default = [] debugprint = [] # list of supported codegen backends -thumbv6 = [] -thumbv7 = [] -# riscv-clic = [] -# riscv-ch32 = [] +cortex_m_source_masking = [] +cortex_m_basepri = [] +# riscv_clic = [] +# riscv_ch32 = [] + +# backend API test +test_template = [] [dependencies] indexmap = "1.9.2" diff --git a/rtic-macros/src/codegen/bindings.rs b/rtic-macros/src/codegen/bindings.rs index 1efe0ce..a187820 100644 --- a/rtic-macros/src/codegen/bindings.rs +++ b/rtic-macros/src/codegen/bindings.rs @@ -1,5 +1,18 @@ -// TODO: Feature gate -mod cortex; +#[cfg(not(any( + feature = "cortex_m_source_masking", + feature = "cortex_m_basepri", + feaute = "test_template" +)))] +compile_error!("No backend selected"); + +#[cfg(any(feature = "cortex_m_source_masking", feature = "cortex_m_basepri"))] +pub use cortex::*; -// TODO: Feature gate +#[cfg(feature = "test_template")] pub use cortex::*; + +#[cfg(any(feature = "cortex_m_source_masking", feature = "cortex_m_basepri"))] +mod cortex; + +#[cfg(feature = "test_template")] +mod template; diff --git a/rtic-macros/src/codegen/bindings/cortex.rs b/rtic-macros/src/codegen/bindings/cortex.rs index 15976a1..f028cee 100644 --- a/rtic-macros/src/codegen/bindings/cortex.rs +++ b/rtic-macros/src/codegen/bindings/cortex.rs @@ -8,8 +8,9 @@ use quote::quote; use std::collections::HashSet; use syn::{parse, Attribute, Ident}; -// TODO: This should be feature gated -// pub use basepri::*; +#[cfg(feature = "cortex_m_basepri")] +pub use basepri::*; +#[cfg(feature = "cortex_m_source_masking")] pub use source_masking::*; /// Whether `name` is an exception with configurable priority @@ -29,7 +30,8 @@ fn is_exception(name: &Ident) -> bool { ) } -pub mod source_masking { +#[cfg(feature = "cortex_m_source_masking")] +mod source_masking { use super::*; use std::collections::HashMap; @@ -87,14 +89,6 @@ pub mod source_masking { )); } - // if uses_exceptions_with_resources { - // mod_app.push(quote!( - // #[doc(hidden)] - // #[allow(non_upper_case_globals)] - // const __rtic_internal_V6_ERROR: () = rtic::export::no_basepri_panic(); - // )); - // } - quote!( #(#cfgs)* impl<'a> rtic::Mutex for #path<'a> { @@ -121,38 +115,12 @@ pub mod source_masking { } pub fn extra_assertions(_: &App, _: &SyntaxAnalysis) -> Vec { - // let device = &app.args.device; - // let no_basepri_checks: Vec<_> = app - // .hardware_tasks - // .iter() - // .filter_map(|(_, task)| { - // if !is_exception(&task.args.binds) { - // let interrupt_name = &task.args.binds; - // Some(quote!( - // if (#device::Interrupt::#interrupt_name as usize) >= (#chunks_name * 32) { - // ::core::panic!("An interrupt out of range is used while in armv6 or armv8m.base"); - // } - // )) - // } else { - // None - // } - // }) - // .collect(); - - // let const_check = quote! { - // const _CONST_CHECK: () = { - // #(#no_basepri_checks)* - // }; - - // let _ = _CONST_CHECK; - // }; - - // vec![const_check] vec![] } } -pub mod basepri { +#[cfg(feature = "cortex_m_basepri")] +mod basepri { use super::*; /// Generates a `Mutex` implementation @@ -245,7 +213,7 @@ pub fn pre_init_enable_interrupts(app: &App, analysis: &CodegenAnalysis) -> Vec< stmts.push(quote!( core.NVIC.set_priority( #rt_err::#interrupt::#name, - rtic::export::logical2hw(#priority, #nvic_prio_bits), + rtic::export::cortex_logical2hw(#priority, #nvic_prio_bits), ); )); @@ -272,7 +240,7 @@ pub fn pre_init_enable_interrupts(app: &App, analysis: &CodegenAnalysis) -> Vec< stmts.push(quote!(core.SCB.set_priority( rtic::export::SystemHandler::#name, - rtic::export::logical2hw(#priority, #nvic_prio_bits), + rtic::export::cortex_logical2hw(#priority, #nvic_prio_bits), );)); } diff --git a/rtic-macros/src/codegen/util.rs b/rtic-macros/src/codegen/util.rs index 27c8a2a..2f44edb 100644 --- a/rtic-macros/src/codegen/util.rs +++ b/rtic-macros/src/codegen/util.rs @@ -2,7 +2,7 @@ use crate::syntax::{ast::App, Context}; use core::sync::atomic::{AtomicUsize, Ordering}; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::quote; -use syn::{Attribute, Ident, PatType}; +use syn::{Ident, PatType}; const RTIC_INTERNAL: &str = "__rtic_internal"; diff --git a/rtic-monotonics/src/systick.rs b/rtic-monotonics/src/systick.rs index 2711022..62d4e0c 100644 --- a/rtic-monotonics/src/systick.rs +++ b/rtic-monotonics/src/systick.rs @@ -157,7 +157,7 @@ macro_rules! make_systick_handler { #[no_mangle] #[allow(non_snake_case)] unsafe extern "C" fn SysTick() { - Systick::__tq().on_monotonic_interrupt(); + rtic_monotonics::systick::Systick::__tq().on_monotonic_interrupt(); } }; } diff --git a/rtic/Cargo.toml b/rtic/Cargo.toml index 24b7fd1..4448fd3 100644 --- a/rtic/Cargo.toml +++ b/rtic/Cargo.toml @@ -40,10 +40,7 @@ bare-metal = "1.0.0" atomic-polyfill = "1" rtic-macros = { path = "../rtic-macros", version = "2.0.0-alpha.0" } rtic-core = "1" - - -[build-dependencies] -version_check = "0.9" +critical-section = "1" [dev-dependencies] heapless = "0.7.7" @@ -66,6 +63,17 @@ version = "0.6.0" trybuild = "1" [features] +default = [] + +thumbv6 = ["rtic-macros/cortex_m_source_masking"] +thumbv7 = ["rtic-macros/cortex_m_basepri"] +thumbv8_base = ["rtic-macros/cortex_m_source_masking"] +thumbv8_main = ["rtic-macros/cortex_m_basepri"] +# riscv_clic = ["rtic-macros/riscv_clic"] +# riscv_ch32 = ["rtic-macros/riscv_ch32"] +# riscv_esp32c3 = ["rtic-macros/riscv_esp32c3"] + +# needed for testing test-critical-section = ["cortex-m/critical-section-single-core", "rtic-monotonics/systick_100hz"] # [[example]] diff --git a/rtic/build.rs b/rtic/build.rs index 35f8303..178a8e3 100644 --- a/rtic/build.rs +++ b/rtic/build.rs @@ -3,22 +3,21 @@ use std::env; fn main() { let target = env::var("TARGET").unwrap(); - if version_check::Channel::read().unwrap().is_nightly() { - println!("cargo:rustc-cfg=rustc_is_nightly"); - } - // These targets all have know support for the BASEPRI register. if target.starts_with("thumbv7m") | target.starts_with("thumbv7em") | target.starts_with("thumbv8m.main") { - println!("cargo:rustc-cfg=have_basepri"); + println!("cargo:rustc-cfg=cortex_m_basepri"); + } else if target.starts_with("thumbv6m") | target.starts_with("thumbv8m.base") { + println!("cargo:rustc-cfg=cortex_m_source_masking"); + } else if target.starts_with("riscv32i") { + panic!("No RISC-V support yet."); - // These targets are all known to _not_ have the BASEPRI register. - } else if target.starts_with("thumb") - && !(target.starts_with("thumbv6m") | target.starts_with("thumbv8m.base")) - { - panic!("Unknown target '{target}'. Need to update BASEPRI logic in build.rs."); + // TODO: Add feature here for risc-v targets + // println!("cargo:rustc-cfg=riscv"); + } else if target.starts_with("thumb") || target.starts_with("riscv32") { + panic!("Unknown target '{target}'. Need to update logic in build.rs."); } println!("cargo:rerun-if-changed=build.rs"); diff --git a/rtic/examples/async-delay.rs b/rtic/examples/async-delay.rs index ba6ec54..c163a3e 100644 --- a/rtic/examples/async-delay.rs +++ b/rtic/examples/async-delay.rs @@ -12,7 +12,7 @@ use panic_semihosting as _; #[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)] mod app { use cortex_m_semihosting::{debug, hprintln}; - use rtic_monotonics::systick_monotonic::*; + use rtic_monotonics::systick::*; rtic_monotonics::make_systick_handler!(); diff --git a/rtic/examples/async-timeout.rs b/rtic/examples/async-timeout.rs index cdccb3a..384c888 100644 --- a/rtic/examples/async-timeout.rs +++ b/rtic/examples/async-timeout.rs @@ -9,7 +9,7 @@ use cortex_m_semihosting::{debug, hprintln}; use panic_semihosting as _; -use rtic_monotonics::systick_monotonic::*; +use rtic_monotonics::systick::*; #[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)] mod app { diff --git a/rtic/src/export.rs b/rtic/src/export.rs index 91812fa..2ce8ce2 100644 --- a/rtic/src/export.rs +++ b/rtic/src/export.rs @@ -4,13 +4,36 @@ pub use atomic_polyfill as atomic; pub mod executor; -// #[cfg(have_basepri)] -pub mod cortex_basepri; +#[cfg(all( + cortex_m_basepri, + not(any(feature = "thumbv7", feature = "thumbv8_main")) +))] +compile_error!( + "Building for Cortex-M with basepri, but 'thumbv7' or 'thumbv8_main' backend not selected" +); -// #[cfg(not(have_basepri))] -pub mod cortex_source_mask; +#[cfg(all( + cortex_m_source_masking, + not(any(feature = "thumbv6", feature = "thumbv8_base")) +))] +compile_error!( + "Building for Cortex-M with source masking, but 'thumbv6' or 'thumbv8_base' backend not selected" +); + +#[cfg(cortex_m_basepri)] +pub use cortex_basepri::*; + +#[cfg(cortex_m_basepri)] +mod cortex_basepri; + +#[cfg(cortex_m_source_masking)] +pub use cortex_source_mask::*; + +#[cfg(cortex_m_source_masking)] +mod cortex_source_mask; /// Priority conversion, takes logical priorities 1..=N and converts it to NVIC priority. +#[cfg(any(cortex_m_basepri, cortex_m_source_masking))] #[inline] #[must_use] pub const fn cortex_logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 { diff --git a/rtic/src/export/cortex_basepri.rs b/rtic/src/export/cortex_basepri.rs index 04f8ae9..d3ceba3 100644 --- a/rtic/src/export/cortex_basepri.rs +++ b/rtic/src/export/cortex_basepri.rs @@ -59,14 +59,14 @@ where /// but can in theory be fixed. /// #[inline(always)] -pub unsafe fn lock( +pub unsafe fn lock( ptr: *mut T, ceiling: u8, nvic_prio_bits: u8, f: impl FnOnce(&mut T) -> R, ) -> R { if ceiling == (1 << nvic_prio_bits) { - let r = interrupt::free(|_| f(&mut *ptr)); + let r = critical_section::with(|_| f(&mut *ptr)); r } else { let current = basepri::read(); diff --git a/rtic/src/export/cortex_source_mask.rs b/rtic/src/export/cortex_source_mask.rs index db5c5f7..fe95b1b 100644 --- a/rtic/src/export/cortex_source_mask.rs +++ b/rtic/src/export/cortex_source_mask.rs @@ -126,7 +126,7 @@ pub unsafe fn lock( // execute closure under protection of raised system ceiling // safe to manipulate outside critical section - interrupt::free(|_| f(&mut *ptr)) + critical_section::with(|_| f(&mut *ptr)) } else { // safe to manipulate outside critical section let mask = compute_mask(0, ceiling, masks); @@ -164,6 +164,7 @@ pub const fn compute_mask( idx += 1; } + // old code (non const) // masks[from_prio as usize..to_prio as usize] // .iter() // .for_each(|m| res |= *m); -- cgit v1.2.3 From 44af1366056a06247b3ee0f153d5274cb4658c43 Mon Sep 17 00:00:00 2001 From: Henrik Tjäder Date: Sat, 4 Mar 2023 02:26:34 +0100 Subject: CFG: Align all crates to use hyphen --- rtic-monotonics/Cargo.toml | 6 +++--- rtic-monotonics/src/lib.rs | 2 +- rtic-monotonics/src/systick.rs | 8 ++++---- rtic/Cargo.toml | 4 ++-- rtic/build.rs | 6 +++--- rtic/src/export.rs | 14 +++++++------- 6 files changed, 20 insertions(+), 20 deletions(-) (limited to 'rtic-monotonics/src') diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml index d60b68c..c961c05 100644 --- a/rtic-monotonics/Cargo.toml +++ b/rtic-monotonics/Cargo.toml @@ -30,9 +30,9 @@ default = [] defmt = ["fugit/defmt"] # Systick on Cortex-M, default 1 kHz -cortex_m_systick = ["dep:cortex-m"] -systick_100hz = [] -systick_10khz = [] +cortex-m-systick = ["dep:cortex-m"] +systick-100hz = [] +systick-10khz = [] # Timer peripheral on the RP2040 rp2040 = ["dep:rp2040-pac"] diff --git a/rtic-monotonics/src/lib.rs b/rtic-monotonics/src/lib.rs index 7e513f8..a8068ce 100644 --- a/rtic-monotonics/src/lib.rs +++ b/rtic-monotonics/src/lib.rs @@ -9,7 +9,7 @@ pub use rtic_time::{Monotonic, TimeoutError, TimerQueue}; -#[cfg(feature = "cortex_m_systick")] +#[cfg(feature = "cortex-m-systick")] pub mod systick; #[cfg(feature = "rp2040")] diff --git a/rtic-monotonics/src/systick.rs b/rtic-monotonics/src/systick.rs index 62d4e0c..691d7c7 100644 --- a/rtic-monotonics/src/systick.rs +++ b/rtic-monotonics/src/systick.rs @@ -7,14 +7,14 @@ 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. +// 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")] + if #[cfg(feature = "systick-100hz")] { const TIMER_HZ: u32 = 100; - } else if #[cfg(feature = "systick_10khz")] + } else if #[cfg(feature = "systick-10khz")] { const TIMER_HZ: u32 = 10_000; } else { diff --git a/rtic/Cargo.toml b/rtic/Cargo.toml index 20aa6de..e73b970 100644 --- a/rtic/Cargo.toml +++ b/rtic/Cargo.toml @@ -48,7 +48,7 @@ lm3s6965 = "0.1.3" cortex-m-semihosting = "0.5.0" rtic-time = { path = "../rtic-time" } rtic-sync = { path = "../rtic-sync" } -rtic-monotonics = { path = "../rtic-monotonics", features = ["cortex_m_systick"] } +rtic-monotonics = { path = "../rtic-monotonics", features = ["cortex-m-systick"] } [dev-dependencies.futures] version = "0.3.26" @@ -74,7 +74,7 @@ thumbv8main-backend = ["rtic-macros/cortex-m-basepri"] # riscv-esp32c3-backend = ["rtic-macros/riscv-esp32c3"] # needed for testing -test-critical-section = ["cortex-m/critical-section-single-core", "rtic-monotonics/systick_100hz"] +test-critical-section = ["cortex-m/critical-section-single-core", "rtic-monotonics/systick-100hz"] # [[example]] # name = "pool" diff --git a/rtic/build.rs b/rtic/build.rs index 178a8e3..c88175d 100644 --- a/rtic/build.rs +++ b/rtic/build.rs @@ -8,14 +8,14 @@ fn main() { | target.starts_with("thumbv7em") | target.starts_with("thumbv8m.main") { - println!("cargo:rustc-cfg=cortex_m_basepri"); + println!("cargo:rustc-cfg=feature=\"cortex-m-basepri\""); } else if target.starts_with("thumbv6m") | target.starts_with("thumbv8m.base") { - println!("cargo:rustc-cfg=cortex_m_source_masking"); + println!("cargo:rustc-cfg=feature=\"cortex-m-source-masking\""); } else if target.starts_with("riscv32i") { panic!("No RISC-V support yet."); // TODO: Add feature here for risc-v targets - // println!("cargo:rustc-cfg=riscv"); + // println!("cargo:rustc-cfg=feature=\"riscv\""); } else if target.starts_with("thumb") || target.starts_with("riscv32") { panic!("Unknown target '{target}'. Need to update logic in build.rs."); } diff --git a/rtic/src/export.rs b/rtic/src/export.rs index 82132c2..6fc97ad 100644 --- a/rtic/src/export.rs +++ b/rtic/src/export.rs @@ -5,7 +5,7 @@ pub use atomic_polyfill as atomic; pub mod executor; #[cfg(all( - cortex_m_basepri, + feature = "cortex-m-basepri", not(any(feature = "thumbv7-backend", feature = "thumbv8main-backend")) ))] compile_error!( @@ -13,27 +13,27 @@ compile_error!( ); #[cfg(all( - cortex_m_source_masking, + feature = "cortex-m-source-masking", not(any(feature = "thumbv6-backend", feature = "thumbv8base-backend")) ))] compile_error!( "Building for Cortex-M with source masking, but 'thumbv6-backend' or 'thumbv8base-backend' backend not selected" ); -#[cfg(cortex_m_basepri)] +#[cfg(any(feature = "cortex-m-basepri"))] pub use cortex_basepri::*; -#[cfg(cortex_m_basepri)] +#[cfg(any(feature = "cortex-m-basepri"))] mod cortex_basepri; -#[cfg(cortex_m_source_masking)] +#[cfg(feature = "cortex-m-source-masking")] pub use cortex_source_mask::*; -#[cfg(cortex_m_source_masking)] +#[cfg(feature = "cortex-m-source-masking")] mod cortex_source_mask; /// Priority conversion, takes logical priorities 1..=N and converts it to NVIC priority. -#[cfg(any(cortex_m_basepri, cortex_m_source_masking))] +#[cfg(any(feature = "cortex-m-basepri", feature = "cortex-m-source-masking",))] #[inline] #[must_use] pub const fn cortex_logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 { -- cgit v1.2.3 From 98c5490d94950608d31cd5ad9dd260f2f853735c Mon Sep 17 00:00:00 2001 From: Henrik Tjäder Date: Sat, 4 Mar 2023 21:58:45 +0100 Subject: rtic-monotonics: Fix tests --- rtic-monotonics/src/lib.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'rtic-monotonics/src') diff --git a/rtic-monotonics/src/lib.rs b/rtic-monotonics/src/lib.rs index a8068ce..a4a1f42 100644 --- a/rtic-monotonics/src/lib.rs +++ b/rtic-monotonics/src/lib.rs @@ -1,7 +1,6 @@ //! Crate #![no_std] -#![no_main] #![deny(missing_docs)] //deny_warnings_placeholder_for_ci #![allow(incomplete_features)] -- cgit v1.2.3