diff --git a/rtic-macros/src/syntax/ast.rs b/rtic-macros/src/syntax/ast.rs index 06feb1fae6e0..bc892e3b6d78 100644 --- a/rtic-macros/src/syntax/ast.rs +++ b/rtic-macros/src/syntax/ast.rs @@ -70,6 +70,7 @@ pub struct AppArgs { pub dispatchers: Dispatchers, /// Backend-specific arguments + #[allow(dead_code)] pub backend: Option, } diff --git a/rtic-monotonics/CHANGELOG.md b/rtic-monotonics/CHANGELOG.md index 4a9af3586c74..3000f889b008 100644 --- a/rtic-monotonics/CHANGELOG.md +++ b/rtic-monotonics/CHANGELOG.md @@ -5,7 +5,15 @@ This project adheres to [Semantic Versioning](http://semver.org/). For each category, *Added*, *Changed*, *Fixed* add new entries at the top! -## Unreleased - v2.0.0 +## Unreleased + +## v2.0.1 - 2024-06-02 + +### Changed + +- Make monotonics created with their respective macros public + +## v2.0.0 - 2024-05-29 ### Changed diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml index 0b25ced65255..369de915b1cd 100644 --- a/rtic-monotonics/Cargo.toml +++ b/rtic-monotonics/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rtic-monotonics" -version = "2.0.0" +version = "2.0.1" edition = "2021" authors = [ diff --git a/rtic-monotonics/src/imxrt.rs b/rtic-monotonics/src/imxrt.rs index 6641d2fd4121..f413d5c4c260 100644 --- a/rtic-monotonics/src/imxrt.rs +++ b/rtic-monotonics/src/imxrt.rs @@ -63,7 +63,7 @@ macro_rules! __internal_create_imxrt_timer_interrupt { macro_rules! __internal_create_imxrt_timer_struct { ($name:ident, $mono_backend:ident, $timer:ident, $tick_rate_hz:expr) => { /// A `Monotonic` based on the GPT peripheral. - struct $name; + pub struct $name; impl $name { /// Starts the `Monotonic`. diff --git a/rtic-monotonics/src/nrf/rtc.rs b/rtic-monotonics/src/nrf/rtc.rs index caa86f18621c..f76e31934411 100644 --- a/rtic-monotonics/src/nrf/rtc.rs +++ b/rtic-monotonics/src/nrf/rtc.rs @@ -85,7 +85,7 @@ macro_rules! __internal_create_nrf_rtc_interrupt { macro_rules! __internal_create_nrf_rtc_struct { ($name:ident, $mono_backend:ident, $timer:ident) => { /// A `Monotonic` based on the nRF RTC peripheral. - struct $name; + pub struct $name; impl $name { /// Starts the `Monotonic`. diff --git a/rtic-monotonics/src/nrf/timer.rs b/rtic-monotonics/src/nrf/timer.rs index c8c5905983c4..6bf1968938bd 100644 --- a/rtic-monotonics/src/nrf/timer.rs +++ b/rtic-monotonics/src/nrf/timer.rs @@ -95,7 +95,7 @@ macro_rules! __internal_create_nrf_timer_interrupt { macro_rules! __internal_create_nrf_timer_struct { ($name:ident, $mono_backend:ident, $timer:ident, $tick_rate_hz:expr) => { /// A `Monotonic` based on the nRF Timer peripheral. - struct $name; + pub struct $name; impl $name { /// Starts the `Monotonic`. diff --git a/rtic-monotonics/src/rp2040.rs b/rtic-monotonics/src/rp2040.rs index 9c5a1ddfe4cd..47146b45fa69 100644 --- a/rtic-monotonics/src/rp2040.rs +++ b/rtic-monotonics/src/rp2040.rs @@ -130,7 +130,7 @@ impl TimerQueueBackend for TimerBackend { macro_rules! rp2040_timer_monotonic { ($name:ident) => { /// A `Monotonic` based on the RP2040 Timer peripheral. - struct $name; + pub struct $name; impl $name { /// Starts the `Monotonic`. diff --git a/rtic-monotonics/src/stm32.rs b/rtic-monotonics/src/stm32.rs index eb19b9943a7e..6b3e4c97f05e 100644 --- a/rtic-monotonics/src/stm32.rs +++ b/rtic-monotonics/src/stm32.rs @@ -83,7 +83,8 @@ macro_rules! __internal_create_stm32_timer_interrupt { #[macro_export] macro_rules! __internal_create_stm32_timer_struct { ($name:ident, $mono_backend:ident, $timer:ident, $tick_rate_hz:expr) => { - struct $name; + /// A `Monotonic` based on an STM32 timer peripheral. + pub struct $name; impl $name { /// Starts the `Monotonic`. diff --git a/rtic-monotonics/src/systick.rs b/rtic-monotonics/src/systick.rs index b0816f5389a4..288c7dc4c9f8 100644 --- a/rtic-monotonics/src/systick.rs +++ b/rtic-monotonics/src/systick.rs @@ -148,7 +148,7 @@ macro_rules! systick_monotonic { }; ($name:ident, $tick_rate_hz:expr) => { /// A `Monotonic` based on SysTick. - struct $name; + pub struct $name; impl $name { /// Starts the `Monotonic`. diff --git a/rtic-sync/CHANGELOG.md b/rtic-sync/CHANGELOG.md index 135b17bd22a1..6cd3a36ad957 100644 --- a/rtic-sync/CHANGELOG.md +++ b/rtic-sync/CHANGELOG.md @@ -14,6 +14,7 @@ For each category, _Added_, _Changed_, _Fixed_ add new entries at the top! ### Added - `defmt v0.3` derives added and forwarded to `embedded-hal(-x)` crates. +- signal structure ## v1.2.0 - 2024-01-10 diff --git a/rtic-sync/Cargo.toml b/rtic-sync/Cargo.toml index 6fcf9006d03e..6d9358f2d17f 100644 --- a/rtic-sync/Cargo.toml +++ b/rtic-sync/Cargo.toml @@ -29,9 +29,10 @@ embedded-hal-bus = { version = "0.1.0", features = ["async"] } defmt-03 = { package = "defmt", version = "0.3", optional = true } [dev-dependencies] +static_cell = "2.1.0" tokio = { version = "1", features = ["rt", "macros", "time"] } [features] default = [] testing = ["critical-section/std", "rtic-common/testing"] -defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async/defmt-03", "embedded-hal-bus/defmt-03"] \ No newline at end of file +defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async/defmt-03", "embedded-hal-bus/defmt-03"] diff --git a/rtic-sync/src/lib.rs b/rtic-sync/src/lib.rs index 90afff63b399..f8845888ed58 100644 --- a/rtic-sync/src/lib.rs +++ b/rtic-sync/src/lib.rs @@ -9,6 +9,7 @@ use defmt_03 as defmt; pub mod arbiter; pub mod channel; pub use portable_atomic; +pub mod signal; #[cfg(test)] #[macro_use] diff --git a/rtic-sync/src/signal.rs b/rtic-sync/src/signal.rs new file mode 100644 index 000000000000..2595f6fb7a2d --- /dev/null +++ b/rtic-sync/src/signal.rs @@ -0,0 +1,201 @@ +//! A "latest only" value store with unlimited writers and async waiting. + +use core::{cell::UnsafeCell, future::poll_fn, task::Poll}; +use rtic_common::waker_registration::CriticalSectionWakerRegistration; + +/// Basically an Option but for indicating +/// whether the store has been set or not +#[derive(Clone, Copy)] +enum Store { + Set(T), + Unset, +} + +/// A "latest only" value store with unlimited writers and async waiting. +pub struct Signal { + waker: CriticalSectionWakerRegistration, + store: UnsafeCell>, +} + +impl Default for Signal { + fn default() -> Self { + Self::new() + } +} + +unsafe impl Send for Signal {} +unsafe impl Sync for Signal {} + +impl Signal { + /// Create a new signal. + pub const fn new() -> Self { + Self { + waker: CriticalSectionWakerRegistration::new(), + store: UnsafeCell::new(Store::Unset), + } + } + + /// Split the signal into a writer and reader. + pub fn split(&self) -> (SignalWriter, SignalReader) { + (SignalWriter { parent: self }, SignalReader { parent: self }) + } +} + +/// Fascilitates the writing of values to a Signal. +#[derive(Clone)] +pub struct SignalWriter<'a, T: Copy> { + parent: &'a Signal, +} + +impl<'a, T: Copy> SignalWriter<'a, T> { + /// Write a raw Store value to the Signal. + fn write_inner(&mut self, value: Store) { + critical_section::with(|_| { + // SAFETY: in a cs: exclusive access + unsafe { self.parent.store.get().replace(value) }; + }); + + self.parent.waker.wake(); + } + + /// Write a value to the Signal. + pub fn write(&mut self, value: T) { + self.write_inner(Store::Set(value)); + } + + /// Clear the stored value in the Signal (if any). + pub fn clear(&mut self) { + self.write_inner(Store::Unset); + } +} + +/// Fascilitates the async reading of values from the Signal. +pub struct SignalReader<'a, T: Copy> { + parent: &'a Signal, +} + +impl<'a, T: Copy> SignalReader<'a, T> { + /// Immediately read and evict the latest value stored in the Signal. + fn take(&mut self) -> Store { + critical_section::with(|_| { + // SAFETY: in a cs: exclusive access + unsafe { self.parent.store.get().replace(Store::Unset) } + }) + } + + /// Returns a pending value if present, or None if no value is available. + /// + /// Upon read, the stored value is evicted. + pub fn try_read(&mut self) -> Option { + match self.take() { + Store::Unset => None, + Store::Set(value) => Some(value), + } + } + + /// Wait for a new value to be written and read it. + /// + /// If a value is already pending it will be returned immediately. + /// + /// Upon read, the stored value is evicted. + pub async fn wait(&mut self) -> T { + poll_fn(|ctx| { + self.parent.waker.register(ctx.waker()); + match self.take() { + Store::Unset => Poll::Pending, + Store::Set(value) => Poll::Ready(value), + } + }) + .await + } + + /// Wait for a new value to be written and read it. + /// + /// If a value is already pending, it will be evicted and a new + /// value must be written for the wait to resolve. + /// + /// Upon read, the stored value is evicted. + pub async fn wait_fresh(&mut self) -> T { + self.take(); + self.wait().await + } +} + +/// Convenience macro for creating a Signal. +#[macro_export] +macro_rules! make_signal { + ( $T:ty ) => {{ + static SIGNAL: Signal<$T> = Signal::new(); + + SIGNAL.split() + }}; +} + +#[cfg(test)] +mod tests { + use static_cell::StaticCell; + + use super::*; + + #[test] + fn empty() { + let (_writer, mut reader) = make_signal!(u32); + + assert!(reader.try_read().is_none()); + } + + #[test] + fn ping_pong() { + let (mut writer, mut reader) = make_signal!(u32); + + writer.write(0xde); + assert!(reader.try_read().is_some_and(|value| value == 0xde)); + } + + #[test] + fn latest() { + let (mut writer, mut reader) = make_signal!(u32); + + writer.write(0xde); + writer.write(0xad); + writer.write(0xbe); + writer.write(0xef); + assert!(reader.try_read().is_some_and(|value| value == 0xef)); + } + + #[test] + fn consumption() { + let (mut writer, mut reader) = make_signal!(u32); + + writer.write(0xaa); + assert!(reader.try_read().is_some_and(|value| value == 0xaa)); + assert!(reader.try_read().is_none()); + } + + #[tokio::test] + async fn pending() { + let (mut writer, mut reader) = make_signal!(u32); + + writer.write(0xaa); + + assert_eq!(reader.wait().await, 0xaa); + } + + #[tokio::test] + async fn waiting() { + static READER: StaticCell> = StaticCell::new(); + let (mut writer, reader) = make_signal!(u32); + + writer.write(0xaa); + + let reader = READER.init(reader); + let handle = tokio::spawn(reader.wait_fresh()); + + tokio::task::yield_now().await; // encourage tokio executor to poll reader future + assert!(!handle.is_finished()); // verify reader future did not resolve after poll + + writer.write(0xab); + + assert!(handle.await.is_ok_and(|value| value == 0xab)); + } +} diff --git a/rtic-time/CHANGELOG.md b/rtic-time/CHANGELOG.md index 197c4263288b..22772d16518e 100644 --- a/rtic-time/CHANGELOG.md +++ b/rtic-time/CHANGELOG.md @@ -5,12 +5,14 @@ This project adheres to [Semantic Versioning](http://semver.org/). For each category, *Added*, *Changed*, *Fixed* add new entries at the top! -## Unreleased - v2.0.0 +## Unreleased +## v2.0.0 - 2024-05-29 ### Added ### Changed + - Full rewrite of the `Monotonic` API. - Now split into multiple traits: - `Monotonic` - A user-facing trait that defines what the functionality of a monotonic is. diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 652fdedadc8c..db673771df3d 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -10,4 +10,4 @@ clap = { version = "4", features = ["derive"] } pretty_env_logger = "0.5.0" log = "0.4.17" rayon = { version = "1.6.1", optional = true } -diffy = "0.3.0" +diffy = "0.4.0" diff --git a/xtask/src/cargo_command.rs b/xtask/src/cargo_command.rs index 78e81b1d77b6..ab57fd70e4bb 100644 --- a/xtask/src/cargo_command.rs +++ b/xtask/src/cargo_command.rs @@ -44,6 +44,7 @@ pub enum CargoCommand<'a> { }, ExampleCheck { cargoarg: &'a Option<&'a str>, + #[allow(dead_code)] platform: Platforms, // to tell which platform. If None, it assumes lm3s6965 example: &'a str, target: Option>,