aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmil Fresk <emil.fresk@gmail.com>2023-01-27 20:20:14 +0100
committerHenrik Tjäder <henrik@tjaders.com>2023-03-01 00:33:34 +0100
commit922f1ad0eb56d362e527ff28e456b6fa133e212b (patch)
tree81a66f352a6f10e28c702d4669f9bded7b42e5d1
parentd23de6282365bbe83099e3d10877bdd435559016 (diff)
Added examples for async crates + fixed codegen for non-Copy arguments
-rw-r--r--rtic-channel/.gitignore2
-rw-r--r--rtic-channel/src/lib.rs36
-rw-r--r--rtic-channel/src/wait_queue.rs22
-rw-r--r--rtic-monotonics/Cargo.toml1
-rw-r--r--rtic-monotonics/src/systick_monotonic.rs8
-rw-r--r--rtic/Cargo.toml3
-rw-r--r--rtic/ci/expected/async-channel.run6
-rw-r--r--rtic/ci/expected/message_passing.run2
-rw-r--r--rtic/examples/async-channel.rs61
-rw-r--r--rtic/examples/async-delay.rs (renamed from rtic/examples/async-delay.no_rs)30
-rw-r--r--rtic/examples/message_passing.rs (renamed from rtic/examples/message_passing.no_rs)17
-rw-r--r--rtic/macros/src/codegen/module.rs19
-rw-r--r--rtic/src/export/executor.rs24
13 files changed, 153 insertions, 78 deletions
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<T, const N: usize> Channel<T, N> {
/// 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<T>(pub T);
+impl<T> core::fmt::Debug for NoReceiver<T>
+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<T, N>);
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<T, N>);
+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<T> {
// 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<Waker>;
-struct MyLinkPtr<T>(UnsafeCell<*mut Link<T>>);
-
-impl<T> MyLinkPtr<T> {
- #[inline(always)]
- fn new(val: *mut Link<T>) -> 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<T> {
- 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<T>) {
- unsafe { self.0.get().write(val) }
- }
-}
-
/// A FIFO linked list for a wait queue.
pub struct LinkedList<T> {
head: AtomicPtr<Link<T>>, // UnsafeCell<*mut Link<T>>
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.rs
index fb478c3..0440f77 100644
--- a/rtic/examples/async-delay.no_rs
+++ b/rtic/examples/async-delay.rs
@@ -7,7 +7,9 @@ use panic_semihosting as _;
#[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)]
mod app {
use cortex_m_semihosting::{debug, hprintln};
- use systick_monotonic::*;
+ use rtic_monotonics::systick_monotonic::*;
+
+ rtic_monotonics::make_systick_timer_queue!(TIMER);
#[shared]
struct Shared {}
@@ -15,12 +17,12 @@ mod app {
#[local]
struct Local {}
- #[monotonic(binds = SysTick, default = true)]
- type MyMono = Systick<100>;
-
#[init]
fn init(cx: init::Context) -> (Shared, Local) {
- hprintln!("init").unwrap();
+ hprintln!("init");
+
+ let systick = Systick::start(cx.core.SYST, 12_000_000);
+ TIMER.initialize(systick);
foo::spawn().ok();
bar::spawn().ok();
@@ -40,23 +42,23 @@ mod app {
#[task]
async fn foo(_cx: foo::Context) {
- hprintln!("hello from foo").ok();
- monotonics::delay(100.millis()).await;
- hprintln!("bye from foo").ok();
+ hprintln!("hello from foo");
+ TIMER.delay(100.millis()).await;
+ hprintln!("bye from foo");
}
#[task]
async fn bar(_cx: bar::Context) {
- hprintln!("hello from bar").ok();
- monotonics::delay(200.millis()).await;
- hprintln!("bye from bar").ok();
+ hprintln!("hello from bar");
+ TIMER.delay(200.millis()).await;
+ hprintln!("bye from bar");
}
#[task]
async fn baz(_cx: baz::Context) {
- hprintln!("hello from baz").ok();
- monotonics::delay(300.millis()).await;
- hprintln!("bye from baz").ok();
+ 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.rs
index ffa9537..01f1a65 100644
--- a/rtic/examples/message_passing.no_rs
+++ b/rtic/examples/message_passing.rs
@@ -4,6 +4,7 @@
#![deny(warnings)]
#![no_main]
#![no_std]
+#![feature(type_alias_impl_trait)]
use panic_semihosting as _;
@@ -18,20 +19,16 @@ mod app {
struct Local {}
#[init]
- fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
+ fn init(_: init::Context) -> (Shared, Local) {
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())
+ (Shared {}, Local {})
}
- #[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
- }
+ #[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<F: Future> AsyncTaskExecutor<F> {
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.