From c17954ccbaefbcf755ad819f76eb3c9f83ef325f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Thu, 31 Dec 2020 15:38:10 +0100 Subject: [PATCH 01/11] Fix warnings for tasks that take mutable parameters #[task] pub async fn run(mut param: Param) {} warning: variable does not need to be mutable --- embassy-macros/src/lib.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs index c2e2d9e2..c46f114a 100644 --- a/embassy-macros/src/lib.rs +++ b/embassy-macros/src/lib.rs @@ -55,9 +55,9 @@ pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { let mut arg_names: syn::punctuated::Punctuated = syn::punctuated::Punctuated::new(); - let args = &task_fn.sig.inputs; + let mut args = task_fn.sig.inputs.clone(); - for arg in args.iter() { + for arg in args.iter_mut() { match arg { syn::FnArg::Receiver(_) => { arg.span() @@ -66,8 +66,11 @@ pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { .emit(); fail = true; } - syn::FnArg::Typed(t) => match t.pat.as_ref() { - syn::Pat::Ident(i) => arg_names.push(i.ident.clone()), + syn::FnArg::Typed(t) => match t.pat.as_mut() { + syn::Pat::Ident(i) => { + arg_names.push(i.ident.clone()); + i.mutability = None; + } _ => { arg.span() .unwrap() From 4783222f675c1316d0e1a0fda92591f7aca63456 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 1 Jan 2021 22:30:11 +0100 Subject: [PATCH 02/11] Rename WakerStore -> WakerRegistration. --- embassy-nrf/src/buffered_uarte.rs | 14 ++++++------ embassy/src/util/mod.rs | 4 ++-- embassy/src/util/waker.rs | 38 +++++++++++++++++++++++++++++++ embassy/src/util/waker_store.rs | 23 ------------------- 4 files changed, 47 insertions(+), 32 deletions(-) create mode 100644 embassy/src/util/waker.rs delete mode 100644 embassy/src/util/waker_store.rs diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index db6a83fb..6227e332 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -26,7 +26,7 @@ use crate::pac::{uarte0, UARTE0}; pub use uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; use embassy::io::{AsyncBufRead, AsyncWrite, Result}; -use embassy::util::WakerStore; +use embassy::util::WakerRegistration; use crate::fmt::{assert, panic, todo, *}; @@ -147,11 +147,11 @@ pub struct UarteState { rx: RingBuf, rx_state: RxState, - rx_waker: WakerStore, + rx_waker: WakerRegistration, tx: RingBuf, tx_state: TxState, - tx_waker: WakerStore, + tx_waker: WakerRegistration, _pin: PhantomPinned, } @@ -233,11 +233,11 @@ impl BufferedUarte { rx: RingBuf::new(), rx_state: RxState::Idle, - rx_waker: WakerStore::new(), + rx_waker: WakerRegistration::new(), tx: RingBuf::new(), tx_state: TxState::Idle, - tx_waker: WakerStore::new(), + tx_waker: WakerRegistration::new(), _pin: PhantomPinned, }), @@ -327,7 +327,7 @@ impl UarteState { this.inner.tasks_stoprx.write(|w| unsafe { w.bits(1) }); } - this.rx_waker.store(cx.waker()); + this.rx_waker.register(cx.waker()); Poll::Pending } @@ -346,7 +346,7 @@ impl UarteState { let tx_buf = this.tx.push_buf(); if tx_buf.len() == 0 { trace!("poll_write: pending"); - this.tx_waker.store(cx.waker()); + this.tx_waker.register(cx.waker()); return Poll::Pending; } diff --git a/embassy/src/util/mod.rs b/embassy/src/util/mod.rs index 94745b83..5694d6bf 100644 --- a/embassy/src/util/mod.rs +++ b/embassy/src/util/mod.rs @@ -2,10 +2,10 @@ mod drop_bomb; mod forever; mod portal; mod signal; -mod waker_store; +mod waker; pub use drop_bomb::*; pub use forever::*; pub use portal::*; pub use signal::*; -pub use waker_store::*; +pub use waker::*; diff --git a/embassy/src/util/waker.rs b/embassy/src/util/waker.rs new file mode 100644 index 00000000..9735438b --- /dev/null +++ b/embassy/src/util/waker.rs @@ -0,0 +1,38 @@ +use core::task::Context; +use core::task::Waker; + +/// Utility struct to register and wake a waker. +#[derive(Debug)] +pub struct WakerRegistration { + waker: Option, +} + +impl WakerRegistration { + pub const fn new() -> Self { + Self { waker: None } + } + + /// Register a waker. Overwrites the previous waker, if any. + pub fn register(&mut self, w: &Waker) { + match self.waker { + // Optimization: If both the old and new Wakers wake the same task, we can simply + // keep the old waker, skipping the clone. (In most executor implementations, + // cloning a waker is somewhat expensive, comparable to cloning an Arc). + Some(ref w2) if (w2.will_wake(w)) => {} + // In all other cases + // - we have no waker registered + // - we have a waker registered but it's for a different task. + // then clone the new waker and store it + _ => self.waker = Some(w.clone()), + } + } + + /// Wake the registered waker, if any. + pub fn wake(&mut self) { + self.waker.take().map(|w| w.wake()); + } + + pub fn context(&self) -> Option> { + self.waker.as_ref().map(|w| Context::from_waker(w)) + } +} \ No newline at end of file diff --git a/embassy/src/util/waker_store.rs b/embassy/src/util/waker_store.rs deleted file mode 100644 index 0b2f09f4..00000000 --- a/embassy/src/util/waker_store.rs +++ /dev/null @@ -1,23 +0,0 @@ -use core::task::Waker; - -pub struct WakerStore { - waker: Option, -} - -impl WakerStore { - pub const fn new() -> Self { - Self { waker: None } - } - - pub fn store(&mut self, w: &Waker) { - match self.waker { - Some(ref w2) if (w2.will_wake(w)) => {} - Some(_) => panic!("Waker overflow"), - None => self.waker = Some(w.clone()), - } - } - - pub fn wake(&mut self) { - self.waker.take().map(|w| w.wake()); - } -} From 20d3dc87f95f844e747a7e93036ce9ddac369081 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 1 Jan 2021 22:41:59 +0100 Subject: [PATCH 03/11] Rename examples -> embassy-nrf-examples --- Cargo.toml | 2 +- {examples => embassy-nrf-examples}/.cargo/config | 0 {examples => embassy-nrf-examples}/Cargo.toml | 2 +- {examples => embassy-nrf-examples}/build.rs | 0 {examples => embassy-nrf-examples}/memory.x | 0 {examples => embassy-nrf-examples}/src/bin/buffered_uart.rs | 0 .../src/bin/executor_fairness_test.rs | 0 {examples => embassy-nrf-examples}/src/bin/gpiote.rs | 0 {examples => embassy-nrf-examples}/src/bin/gpiote_port.rs | 0 {examples => embassy-nrf-examples}/src/bin/multiprio.rs | 0 {examples => embassy-nrf-examples}/src/bin/qspi.rs | 0 {examples => embassy-nrf-examples}/src/bin/rtc_async.rs | 0 {examples => embassy-nrf-examples}/src/bin/rtc_raw.rs | 0 {examples => embassy-nrf-examples}/src/bin/uart.rs | 0 {examples => embassy-nrf-examples}/src/example_common.rs | 0 test-build.sh | 6 +++--- 16 files changed, 5 insertions(+), 5 deletions(-) rename {examples => embassy-nrf-examples}/.cargo/config (100%) rename {examples => embassy-nrf-examples}/Cargo.toml (96%) rename {examples => embassy-nrf-examples}/build.rs (100%) rename {examples => embassy-nrf-examples}/memory.x (100%) rename {examples => embassy-nrf-examples}/src/bin/buffered_uart.rs (100%) rename {examples => embassy-nrf-examples}/src/bin/executor_fairness_test.rs (100%) rename {examples => embassy-nrf-examples}/src/bin/gpiote.rs (100%) rename {examples => embassy-nrf-examples}/src/bin/gpiote_port.rs (100%) rename {examples => embassy-nrf-examples}/src/bin/multiprio.rs (100%) rename {examples => embassy-nrf-examples}/src/bin/qspi.rs (100%) rename {examples => embassy-nrf-examples}/src/bin/rtc_async.rs (100%) rename {examples => embassy-nrf-examples}/src/bin/rtc_raw.rs (100%) rename {examples => embassy-nrf-examples}/src/bin/uart.rs (100%) rename {examples => embassy-nrf-examples}/src/example_common.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 79280404..5ba9ba7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,8 +3,8 @@ members = [ "embassy", "embassy-nrf", + "embassy-nrf-examples", "embassy-macros", - "examples", ] exclude = [ diff --git a/examples/.cargo/config b/embassy-nrf-examples/.cargo/config similarity index 100% rename from examples/.cargo/config rename to embassy-nrf-examples/.cargo/config diff --git a/examples/Cargo.toml b/embassy-nrf-examples/Cargo.toml similarity index 96% rename from examples/Cargo.toml rename to embassy-nrf-examples/Cargo.toml index c845c1bf..0c812db1 100644 --- a/examples/Cargo.toml +++ b/embassy-nrf-examples/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["Dario Nieuwenhuis "] edition = "2018" -name = "embassy-examples" +name = "embassy-nrf-examples" version = "0.1.0" [features] diff --git a/examples/build.rs b/embassy-nrf-examples/build.rs similarity index 100% rename from examples/build.rs rename to embassy-nrf-examples/build.rs diff --git a/examples/memory.x b/embassy-nrf-examples/memory.x similarity index 100% rename from examples/memory.x rename to embassy-nrf-examples/memory.x diff --git a/examples/src/bin/buffered_uart.rs b/embassy-nrf-examples/src/bin/buffered_uart.rs similarity index 100% rename from examples/src/bin/buffered_uart.rs rename to embassy-nrf-examples/src/bin/buffered_uart.rs diff --git a/examples/src/bin/executor_fairness_test.rs b/embassy-nrf-examples/src/bin/executor_fairness_test.rs similarity index 100% rename from examples/src/bin/executor_fairness_test.rs rename to embassy-nrf-examples/src/bin/executor_fairness_test.rs diff --git a/examples/src/bin/gpiote.rs b/embassy-nrf-examples/src/bin/gpiote.rs similarity index 100% rename from examples/src/bin/gpiote.rs rename to embassy-nrf-examples/src/bin/gpiote.rs diff --git a/examples/src/bin/gpiote_port.rs b/embassy-nrf-examples/src/bin/gpiote_port.rs similarity index 100% rename from examples/src/bin/gpiote_port.rs rename to embassy-nrf-examples/src/bin/gpiote_port.rs diff --git a/examples/src/bin/multiprio.rs b/embassy-nrf-examples/src/bin/multiprio.rs similarity index 100% rename from examples/src/bin/multiprio.rs rename to embassy-nrf-examples/src/bin/multiprio.rs diff --git a/examples/src/bin/qspi.rs b/embassy-nrf-examples/src/bin/qspi.rs similarity index 100% rename from examples/src/bin/qspi.rs rename to embassy-nrf-examples/src/bin/qspi.rs diff --git a/examples/src/bin/rtc_async.rs b/embassy-nrf-examples/src/bin/rtc_async.rs similarity index 100% rename from examples/src/bin/rtc_async.rs rename to embassy-nrf-examples/src/bin/rtc_async.rs diff --git a/examples/src/bin/rtc_raw.rs b/embassy-nrf-examples/src/bin/rtc_raw.rs similarity index 100% rename from examples/src/bin/rtc_raw.rs rename to embassy-nrf-examples/src/bin/rtc_raw.rs diff --git a/examples/src/bin/uart.rs b/embassy-nrf-examples/src/bin/uart.rs similarity index 100% rename from examples/src/bin/uart.rs rename to embassy-nrf-examples/src/bin/uart.rs diff --git a/examples/src/example_common.rs b/embassy-nrf-examples/src/example_common.rs similarity index 100% rename from examples/src/example_common.rs rename to embassy-nrf-examples/src/example_common.rs diff --git a/test-build.sh b/test-build.sh index 52bf7bb7..f67cc5b2 100755 --- a/test-build.sh +++ b/test-build.sh @@ -2,9 +2,6 @@ set -euxo pipefail -# examples -(cd examples; cargo build --target thumbv7em-none-eabi --bins) - # embassy std (cd embassy; cargo build --features log,std) @@ -14,6 +11,9 @@ set -euxo pipefail (cd embassy; cargo build --target thumbv7em-none-eabi --features defmt) # embassy-nrf + +(cd embassy-nrf-examples; cargo build --target thumbv7em-none-eabi --bins) + (cd embassy-nrf; cargo build --target thumbv7em-none-eabi --features 52810) #(cd embassy-nrf; cargo build --target thumbv7em-none-eabi --features 52811) # nrf52811-hal doesn't exist yet (cd embassy-nrf; cargo build --target thumbv7em-none-eabi --features 52832) From 396d7650caa2dda1d4e04202b7dd7ea75ad1d8d6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 1 Jan 2021 23:04:18 +0100 Subject: [PATCH 04/11] Add some comments to uart example. --- embassy-nrf-examples/src/bin/uart.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/embassy-nrf-examples/src/bin/uart.rs b/embassy-nrf-examples/src/bin/uart.rs index 10793668..883dadde 100644 --- a/embassy-nrf-examples/src/bin/uart.rs +++ b/embassy-nrf-examples/src/bin/uart.rs @@ -27,17 +27,26 @@ async fn run(mut uart: uarte::Uarte) { uart.send(&buf).await; info!("wrote hello in uart!"); - info!("reading..."); loop { - let received = match select( - uart.receive(&mut buf), - Timer::after(Duration::from_millis(10)), - ) - .await - { + info!("reading..."); + + // `receive()` doesn't return until the buffer has been completely filled with + // incoming data, which in this case is 8 bytes. + // + // This example shows how to use `select` to run an uart receive concurrently with a + // 1 second timer, effectively adding a timeout to the receive operation. + let recv_fut = uart.receive(&mut buf); + let timer_fut = Timer::after(Duration::from_millis(1000)); + let received = match select(recv_fut, timer_fut).await { + // recv_fut completed first, so we've received `buf_len` bytes. Either::Left((buf, _)) => buf, - Either::Right((_, read)) => { - let (buf, n) = read.stop().await; + // timer_fut completed first. `select` gives us back the future that didn't complete, which + // is `recv_fut` in this case, so we can do further stuff with it. + // + // The recv_fut would stop the uart read automatically when dropped. However, we want to know how + // many bytes have been received, so we have to "gracefully stop" it with `.stop()`. + Either::Right((_, recv_fut)) => { + let (buf, n) = recv_fut.stop().await; &buf[..n] } }; From 3c7e7220b367605ad39362e649846463717fe2be Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 2 Jan 2021 19:14:54 +0100 Subject: [PATCH 05/11] Don't use embedded_dma in nrf uarte. --- embassy-nrf-examples/src/bin/uart.rs | 11 +++--- embassy-nrf/src/uarte.rs | 53 +++++++++++----------------- 2 files changed, 26 insertions(+), 38 deletions(-) diff --git a/embassy-nrf-examples/src/bin/uart.rs b/embassy-nrf-examples/src/bin/uart.rs index 883dadde..e8230c81 100644 --- a/embassy-nrf-examples/src/bin/uart.rs +++ b/embassy-nrf-examples/src/bin/uart.rs @@ -28,6 +28,7 @@ async fn run(mut uart: uarte::Uarte) { info!("wrote hello in uart!"); loop { + let buf_len = buf.len(); info!("reading..."); // `receive()` doesn't return until the buffer has been completely filled with @@ -37,19 +38,17 @@ async fn run(mut uart: uarte::Uarte) { // 1 second timer, effectively adding a timeout to the receive operation. let recv_fut = uart.receive(&mut buf); let timer_fut = Timer::after(Duration::from_millis(1000)); - let received = match select(recv_fut, timer_fut).await { + let received_len = match select(recv_fut, timer_fut).await { // recv_fut completed first, so we've received `buf_len` bytes. - Either::Left((buf, _)) => buf, + Either::Left(_) => buf_len, // timer_fut completed first. `select` gives us back the future that didn't complete, which // is `recv_fut` in this case, so we can do further stuff with it. // // The recv_fut would stop the uart read automatically when dropped. However, we want to know how // many bytes have been received, so we have to "gracefully stop" it with `.stop()`. - Either::Right((_, recv_fut)) => { - let (buf, n) = recv_fut.stop().await; - &buf[..n] - } + Either::Right((_, recv_fut)) => recv_fut.stop().await, }; + let received = &mut buf[..received_len]; if received.len() > 0 { info!("read done, got {:[u8]}", received); diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index f0337be8..3983c96b 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -10,7 +10,6 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; use embassy::util::Signal; -use embedded_dma::{ReadBuffer, WriteBuffer}; use crate::fmt::{assert, *}; #[cfg(any(feature = "52833", feature = "52840"))] @@ -145,10 +144,7 @@ where /// `tx_buffer` is marked as static as per `embedded-dma` requirements. /// It it safe to use a buffer with a non static lifetime if memory is not /// reused until the future has finished. - pub fn send<'a, B>(&'a mut self, tx_buffer: B) -> SendFuture<'a, T, B> - where - B: ReadBuffer, - { + pub fn send<'a>(&'a mut self, tx_buffer: &'a [u8]) -> SendFuture<'a, T> { // Panic if TX is running which can happen if the user has called // `mem::forget()` on a previous future after polling it once. assert!(!self.tx_started()); @@ -175,10 +171,7 @@ where /// `rx_buffer` is marked as static as per `embedded-dma` requirements. /// It it safe to use a buffer with a non static lifetime if memory is not /// reused until the future has finished. - pub fn receive<'a, B>(&'a mut self, rx_buffer: B) -> ReceiveFuture<'a, T, B> - where - B: WriteBuffer, - { + pub fn receive<'a>(&'a mut self, rx_buffer: &'a mut [u8]) -> ReceiveFuture<'a, T> { // Panic if RX is running which can happen if the user has called // `mem::forget()` on a previous future after polling it once. assert!(!self.rx_started()); @@ -187,7 +180,7 @@ where ReceiveFuture { uarte: self, - buf: Some(rx_buffer), + buf: rx_buffer, } } @@ -239,15 +232,15 @@ where } /// Future for the [`Uarte::send()`] method. -pub struct SendFuture<'a, T, B> +pub struct SendFuture<'a, T> where T: Instance, { uarte: &'a Uarte, - buf: B, + buf: &'a [u8], } -impl<'a, T, B> Drop for SendFuture<'a, T, B> +impl<'a, T> Drop for SendFuture<'a, T> where T: Instance, { @@ -266,10 +259,9 @@ where } } -impl<'a, T, B> Future for SendFuture<'a, T, B> +impl<'a, T> Future for SendFuture<'a, T> where T: Instance, - B: ReadBuffer, { type Output = (); @@ -281,7 +273,8 @@ where T::state().tx_done.reset(); - let (ptr, len) = unsafe { buf.read_buffer() }; + let ptr = buf.as_ptr(); + let len = buf.len(); assert!(len <= EASY_DMA_SIZE); // TODO: panic if buffer is not in SRAM @@ -301,15 +294,15 @@ where } /// Future for the [`Uarte::receive()`] method. -pub struct ReceiveFuture<'a, T, B> +pub struct ReceiveFuture<'a, T> where T: Instance, { uarte: &'a Uarte, - buf: Option, + buf: &'a mut [u8], } -impl<'a, T, B> Drop for ReceiveFuture<'a, T, B> +impl<'a, T> Drop for ReceiveFuture<'a, T> where T: Instance, { @@ -327,14 +320,13 @@ where } } -impl<'a, T, B> Future for ReceiveFuture<'a, T, B> +impl<'a, T> Future for ReceiveFuture<'a, T> where T: Instance, - B: WriteBuffer, { - type Output = B; + type Output = (); - fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let Self { uarte, buf } = unsafe { self.get_unchecked_mut() }; if !uarte.rx_started() { @@ -342,7 +334,8 @@ where T::state().rx_done.reset(); - let (ptr, len) = unsafe { buf.as_mut().unwrap().write_buffer() }; + let ptr = buf.as_ptr(); + let len = buf.len(); assert!(len <= EASY_DMA_SIZE); compiler_fence(Ordering::SeqCst); @@ -356,24 +349,20 @@ where uarte.tasks_startrx.write(|w| unsafe { w.bits(1) }); } - T::state() - .rx_done - .poll_wait(cx) - .map(|_| buf.take().unwrap()) + T::state().rx_done.poll_wait(cx).map(|_| ()) } } /// Future for the [`receive()`] method. -impl<'a, T, B> ReceiveFuture<'a, T, B> +impl<'a, T> ReceiveFuture<'a, T> where T: Instance, { /// Stops the ongoing reception and returns the number of bytes received. - pub async fn stop(mut self) -> (B, usize) { - let buf = self.buf.take().unwrap(); + pub async fn stop(self) -> usize { drop(self); let len = T::state().rx_done.wait().await; - (buf, len as _) + len as _ } } From 1d53985abed58354bed9959e94b306870af214a4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 2 Jan 2021 19:56:05 +0100 Subject: [PATCH 06/11] Remove now-stable slice_fill feature --- embassy/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy/src/lib.rs b/embassy/src/lib.rs index bc06ebd1..8f93535e 100644 --- a/embassy/src/lib.rs +++ b/embassy/src/lib.rs @@ -1,5 +1,4 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![feature(slice_fill)] #![feature(generic_associated_types)] #![feature(const_fn)] #![feature(const_fn_fn_ptr_basics)] From 0ab88ea27974af5987cbe6efcf9d1849900b2bd7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 2 Jan 2021 19:59:37 +0100 Subject: [PATCH 07/11] Add Uart trait, implement it for nrf. --- embassy-nrf-examples/src/bin/uart.rs | 5 +- embassy-nrf/src/uarte.rs | 101 ++++++++++++++------------- embassy/src/lib.rs | 1 + embassy/src/uart.rs | 15 ++++ 4 files changed, 73 insertions(+), 49 deletions(-) create mode 100644 embassy/src/uart.rs diff --git a/embassy-nrf-examples/src/bin/uart.rs b/embassy-nrf-examples/src/bin/uart.rs index e8230c81..cb38e8fc 100644 --- a/embassy-nrf-examples/src/bin/uart.rs +++ b/embassy-nrf-examples/src/bin/uart.rs @@ -10,6 +10,7 @@ use cortex_m_rt::entry; use defmt::panic; use embassy::executor::{task, Executor}; use embassy::time::{Duration, Timer}; +use embassy::uart::Uart; use embassy::util::Forever; use embassy_nrf::{interrupt, pac, rtc, uarte}; use futures::future::{select, Either}; @@ -24,7 +25,7 @@ async fn run(mut uart: uarte::Uarte) { let mut buf = [0; 8]; buf.copy_from_slice(b"Hello!\r\n"); - uart.send(&buf).await; + unwrap!(uart.send(&buf).await); info!("wrote hello in uart!"); loop { @@ -54,7 +55,7 @@ async fn run(mut uart: uarte::Uarte) { info!("read done, got {:[u8]}", received); // Echo back received data - uart.send(received).await; + unwrap!(uart.send(received).await); } } } diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 3983c96b..648298b8 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -139,51 +139,10 @@ where self.instance.enable.write(|w| w.enable().enabled()); } - /// Sends serial data. - /// - /// `tx_buffer` is marked as static as per `embedded-dma` requirements. - /// It it safe to use a buffer with a non static lifetime if memory is not - /// reused until the future has finished. - pub fn send<'a>(&'a mut self, tx_buffer: &'a [u8]) -> SendFuture<'a, T> { - // Panic if TX is running which can happen if the user has called - // `mem::forget()` on a previous future after polling it once. - assert!(!self.tx_started()); - - self.enable(); - - SendFuture { - uarte: self, - buf: tx_buffer, - } - } - fn tx_started(&self) -> bool { self.instance.events_txstarted.read().bits() != 0 } - /// Receives serial data. - /// - /// The future is pending until the buffer is completely filled. - /// A common pattern is to use [`stop()`](ReceiveFuture::stop) to cancel - /// unfinished transfers after a timeout to prevent lockup when no more data - /// is incoming. - /// - /// `rx_buffer` is marked as static as per `embedded-dma` requirements. - /// It it safe to use a buffer with a non static lifetime if memory is not - /// reused until the future has finished. - pub fn receive<'a>(&'a mut self, rx_buffer: &'a mut [u8]) -> ReceiveFuture<'a, T> { - // Panic if RX is running which can happen if the user has called - // `mem::forget()` on a previous future after polling it once. - assert!(!self.rx_started()); - - self.enable(); - - ReceiveFuture { - uarte: self, - buf: rx_buffer, - } - } - fn rx_started(&self) -> bool { self.instance.events_rxstarted.read().bits() != 0 } @@ -231,6 +190,52 @@ where } } +impl embassy::uart::Uart for Uarte { + type ReceiveFuture<'a> = ReceiveFuture<'a, T>; + type SendFuture<'a> = SendFuture<'a, T>; + + /// Sends serial data. + /// + /// `tx_buffer` is marked as static as per `embedded-dma` requirements. + /// It it safe to use a buffer with a non static lifetime if memory is not + /// reused until the future has finished. + fn send<'a>(&'a mut self, tx_buffer: &'a [u8]) -> SendFuture<'a, T> { + // Panic if TX is running which can happen if the user has called + // `mem::forget()` on a previous future after polling it once. + assert!(!self.tx_started()); + + self.enable(); + + SendFuture { + uarte: self, + buf: tx_buffer, + } + } + + /// Receives serial data. + /// + /// The future is pending until the buffer is completely filled. + /// A common pattern is to use [`stop()`](ReceiveFuture::stop) to cancel + /// unfinished transfers after a timeout to prevent lockup when no more data + /// is incoming. + /// + /// `rx_buffer` is marked as static as per `embedded-dma` requirements. + /// It it safe to use a buffer with a non static lifetime if memory is not + /// reused until the future has finished. + fn receive<'a>(&'a mut self, rx_buffer: &'a mut [u8]) -> ReceiveFuture<'a, T> { + // Panic if RX is running which can happen if the user has called + // `mem::forget()` on a previous future after polling it once. + assert!(!self.rx_started()); + + self.enable(); + + ReceiveFuture { + uarte: self, + buf: rx_buffer, + } + } +} + /// Future for the [`Uarte::send()`] method. pub struct SendFuture<'a, T> where @@ -263,9 +268,9 @@ impl<'a, T> Future for SendFuture<'a, T> where T: Instance, { - type Output = (); + type Output = Result<(), embassy::uart::Error>; - fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let Self { uarte, buf } = unsafe { self.get_unchecked_mut() }; if !uarte.tx_started() { @@ -289,7 +294,7 @@ where uarte.tasks_starttx.write(|w| unsafe { w.bits(1) }); } - T::state().tx_done.poll_wait(cx) + T::state().tx_done.poll_wait(cx).map(|()| Ok(())) } } @@ -324,7 +329,7 @@ impl<'a, T> Future for ReceiveFuture<'a, T> where T: Instance, { - type Output = (); + type Output = Result<(), embassy::uart::Error>; fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let Self { uarte, buf } = unsafe { self.get_unchecked_mut() }; @@ -349,7 +354,7 @@ where uarte.tasks_startrx.write(|w| unsafe { w.bits(1) }); } - T::state().rx_done.poll_wait(cx).map(|_| ()) + T::state().rx_done.poll_wait(cx).map(|_| Ok(())) } } @@ -370,7 +375,9 @@ mod private { pub trait Sealed {} } -pub trait Instance: Deref + Sized + private::Sealed { +pub trait Instance: + Deref + Sized + private::Sealed + 'static +{ type Interrupt: OwnedInterrupt; #[doc(hidden)] diff --git a/embassy/src/lib.rs b/embassy/src/lib.rs index 8f93535e..02d72a84 100644 --- a/embassy/src/lib.rs +++ b/embassy/src/lib.rs @@ -12,4 +12,5 @@ pub mod interrupt; pub mod io; pub mod rand; pub mod time; +pub mod uart; pub mod util; diff --git a/embassy/src/uart.rs b/embassy/src/uart.rs new file mode 100644 index 00000000..b40b9e9b --- /dev/null +++ b/embassy/src/uart.rs @@ -0,0 +1,15 @@ +use core::future::Future; + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + Other, +} + +pub trait Uart { + type ReceiveFuture<'a>: Future>; + type SendFuture<'a>: Future>; + fn receive<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReceiveFuture<'a>; + fn send<'a>(&'a mut self, buf: &'a [u8]) -> Self::SendFuture<'a>; +} From 8fa3294f35accd1559d5d704d8add8a29e529826 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 2 Jan 2021 20:13:35 +0100 Subject: [PATCH 08/11] Update readme --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6f7470cf..0f1cb1be 100644 --- a/README.md +++ b/README.md @@ -11,15 +11,17 @@ Embassy is a project to make async/await a first-class option for embedded devel - `embassy::time`: `Clock` and `Alarm` traits. Std-like `Duration` and `Instant`. - More traits for SPI, I2C, UART async HAL coming soon. -## Executor with timers +## Executor -The `embassy::executor` module provides an async/await executor based on [static-executor](https://github.com/Dirbaio/static-executor). +The `embassy::executor` module provides an async/await executor designed for embedded usage. - No `alloc`, no heap needed. Task futures are statically allocated. -- Integrated timer queue allows simple sleeping: `Timer::after(Duration::from_ticks(64000)).await;`. -- Suitable for low-power operation. Using interrupts or `WFE/SEV` ensures the CPU sleeps when there's no work to do. No busy-loop polling. +- No "fixed capacity" data structures, executor works with 1 or 1000 tasks without needing config/tuning. +- Integrated timer queue: sleeping is easy, just do `Timer::after(Duration::from_secs(1)).await;`. +- No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`. +- Efficient polling: a wake will only poll the woken task, not all of them. +- Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time. - Creating multiple executor instances is supported, to run tasks with multiple priority levels. This allows higher-priority tasks to preempt lower-priority tasks. -- Compatible with RTIC (example coming soon). ## Utils @@ -54,9 +56,7 @@ cargo run --bin rtc_async ## Minimum supported Rust version (MSRV) -`rustc 1.48.0-nightly (1fd5b9d51 2020-09-20)` - -Any recent nightly should work. Nightly is required for: +Only recent nighly supported. Nightly is required for: - `generic_associated_types`: for trait funcs returning futures. - `type_alias_impl_trait`: for trait funcs returning futures implemented with `async{}` blocks, and for `static-executor`. From 4ce51795f2f4a9d6803dc4101b04f2a191f8d39d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 2 Jan 2021 20:31:50 +0100 Subject: [PATCH 09/11] use hal::Pins for BufferedUarte --- embassy-nrf/src/buffered_uarte.rs | 36 ++++++++++--------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 6227e332..9863e3fa 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -12,25 +12,18 @@ use core::pin::Pin; use core::ptr; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; - -use embedded_hal::digital::v2::OutputPin; - -use crate::hal::gpio::{Floating, Input, Output, Pin as GpioPin, Port as GpioPort, PushPull}; -use crate::interrupt; -use crate::interrupt::{CriticalSection, OwnedInterrupt}; -#[cfg(any(feature = "52833", feature = "52840", feature = "9160"))] -use crate::pac::UARTE1; -use crate::pac::{uarte0, UARTE0}; - -// Re-export SVD variants to allow user to directly set values -pub use uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; - use embassy::io::{AsyncBufRead, AsyncWrite, Result}; use embassy::util::WakerRegistration; +use embedded_hal::digital::v2::OutputPin; use crate::fmt::{assert, panic, todo, *}; +use crate::hal::gpio::Port as GpioPort; +use crate::interrupt::{self, CriticalSection, OwnedInterrupt}; +use crate::pac::uarte0; -//use crate::trace; +// Re-export SVD variants to allow user to directly set values +pub use crate::hal::uarte::Pins; +pub use uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; const RINGBUF_SIZE: usize = 512; struct RingBuf { @@ -504,13 +497,6 @@ impl UarteState { } } -pub struct Pins { - pub rxd: GpioPin>, - pub txd: GpioPin>, - pub cts: Option>>, - pub rts: Option>>, -} - mod private { pub trait Sealed {} @@ -529,11 +515,11 @@ pub trait Instance: Deref + Sized + private::Sea fn set_state(_cs: &CriticalSection, state: *mut UarteState); } -static mut UARTE0_STATE: *mut UarteState = ptr::null_mut(); +static mut UARTE0_STATE: *mut UarteState = ptr::null_mut(); #[cfg(any(feature = "52833", feature = "52840", feature = "9160"))] -static mut UARTE1_STATE: *mut UarteState = ptr::null_mut(); +static mut UARTE1_STATE: *mut UarteState = ptr::null_mut(); -impl Instance for UARTE0 { +impl Instance for crate::pac::UARTE0 { type Interrupt = interrupt::UARTE0_UART0Interrupt; fn get_state(_cs: &CriticalSection) -> *mut UarteState { @@ -545,7 +531,7 @@ impl Instance for UARTE0 { } #[cfg(any(feature = "52833", feature = "52840", feature = "9160"))] -impl Instance for UARTE1 { +impl Instance for crate::pac::UARTE1 { type Interrupt = interrupt::UARTE1Interrupt; fn get_state(_cs: &CriticalSection) -> *mut UarteState { From ace4f40f8089f8cebbac023a4a1b960d976d2776 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 3 Jan 2021 01:40:40 +0100 Subject: [PATCH 10/11] Introduce "peripheral" abstraction to share state between main and interrupt. --- embassy-nrf-examples/.cargo/config | 6 +- embassy-nrf-examples/src/bin/buffered_uart.rs | 11 +- embassy-nrf/src/buffered_uarte.rs | 315 ++++++------------ embassy-nrf/src/lib.rs | 1 + embassy-nrf/src/util/mod.rs | 2 + embassy-nrf/src/util/peripheral.rs | 107 ++++++ embassy-nrf/src/util/ring_buffer.rs | 80 +++++ 7 files changed, 304 insertions(+), 218 deletions(-) create mode 100644 embassy-nrf/src/util/mod.rs create mode 100644 embassy-nrf/src/util/peripheral.rs create mode 100644 embassy-nrf/src/util/ring_buffer.rs diff --git a/embassy-nrf-examples/.cargo/config b/embassy-nrf-examples/.cargo/config index 3f319ae5..59128887 100644 --- a/embassy-nrf-examples/.cargo/config +++ b/embassy-nrf-examples/.cargo/config @@ -20,8 +20,4 @@ rustflags = [ ] [build] -# Pick ONE of these compilation targets -# target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ -# target = "thumbv7m-none-eabi" # Cortex-M3 -# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) -target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) +target = "thumbv7em-none-eabi" diff --git a/embassy-nrf-examples/src/bin/buffered_uart.rs b/embassy-nrf-examples/src/bin/buffered_uart.rs index 6e15fbcf..68a76f71 100644 --- a/embassy-nrf-examples/src/bin/buffered_uart.rs +++ b/embassy-nrf-examples/src/bin/buffered_uart.rs @@ -8,15 +8,17 @@ use example_common::*; use cortex_m_rt::entry; use defmt::panic; -use futures::pin_mut; use nrf52840_hal::gpio; use embassy::executor::{task, Executor}; -use embassy::io::{AsyncBufRead, AsyncBufReadExt, AsyncWrite, AsyncWriteExt}; +use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; use embassy::util::Forever; use embassy_nrf::buffered_uarte; use embassy_nrf::interrupt; +static mut TX_BUFFER: [u8; 4096] = [0; 4096]; +static mut RX_BUFFER: [u8; 4096] = [0; 4096]; + #[task] async fn run() { let p = unwrap!(embassy_nrf::pac::Peripherals::take()); @@ -34,14 +36,15 @@ async fn run() { }; let irq = interrupt::take!(UARTE0_UART0); - let u = buffered_uarte::BufferedUarte::new( + let mut u = buffered_uarte::BufferedUarte::new( p.UARTE0, irq, + unsafe { &mut RX_BUFFER }, + unsafe { &mut TX_BUFFER }, pins, buffered_uarte::Parity::EXCLUDED, buffered_uarte::Baudrate::BAUD115200, ); - pin_mut!(u); info!("uarte initialized!"); diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 9863e3fa..4749c3b4 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -4,107 +4,29 @@ //! //! - nrf52832: Section 35 //! - nrf52840: Section 6.34 -use core::cell::UnsafeCell; use core::cmp::min; -use core::marker::PhantomPinned; +use core::marker::PhantomData; +use core::mem; use core::ops::Deref; use core::pin::Pin; -use core::ptr; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; use embassy::io::{AsyncBufRead, AsyncWrite, Result}; use embassy::util::WakerRegistration; use embedded_hal::digital::v2::OutputPin; -use crate::fmt::{assert, panic, todo, *}; +use crate::fmt::{panic, todo, *}; use crate::hal::gpio::Port as GpioPort; -use crate::interrupt::{self, CriticalSection, OwnedInterrupt}; +use crate::interrupt::{self, OwnedInterrupt}; +use crate::pac; use crate::pac::uarte0; +use crate::util::peripheral; +use crate::util::ring_buffer::RingBuffer; // Re-export SVD variants to allow user to directly set values pub use crate::hal::uarte::Pins; pub use uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; -const RINGBUF_SIZE: usize = 512; -struct RingBuf { - buf: [u8; RINGBUF_SIZE], - start: usize, - end: usize, - empty: bool, -} - -impl RingBuf { - fn new() -> Self { - RingBuf { - buf: [0; RINGBUF_SIZE], - start: 0, - end: 0, - empty: true, - } - } - - fn push_buf(&mut self) -> &mut [u8] { - if self.start == self.end && !self.empty { - trace!(" ringbuf: push_buf empty"); - return &mut self.buf[..0]; - } - - let n = if self.start <= self.end { - RINGBUF_SIZE - self.end - } else { - self.start - self.end - }; - - trace!(" ringbuf: push_buf {:?}..{:?}", self.end, self.end + n); - &mut self.buf[self.end..self.end + n] - } - - fn push(&mut self, n: usize) { - trace!(" ringbuf: push {:?}", n); - if n == 0 { - return; - } - - self.end = Self::wrap(self.end + n); - self.empty = false; - } - - fn pop_buf(&mut self) -> &mut [u8] { - if self.empty { - trace!(" ringbuf: pop_buf empty"); - return &mut self.buf[..0]; - } - - let n = if self.end <= self.start { - RINGBUF_SIZE - self.start - } else { - self.end - self.start - }; - - trace!(" ringbuf: pop_buf {:?}..{:?}", self.start, self.start + n); - &mut self.buf[self.start..self.start + n] - } - - fn pop(&mut self, n: usize) { - trace!(" ringbuf: pop {:?}", n); - if n == 0 { - return; - } - - self.start = Self::wrap(self.start + n); - self.empty = self.start == self.end; - } - - fn wrap(n: usize) -> usize { - assert!(n <= RINGBUF_SIZE); - if n == RINGBUF_SIZE { - 0 - } else { - n - } - } -} - #[derive(Copy, Clone, Debug, PartialEq)] enum RxState { Idle, @@ -126,28 +48,12 @@ enum TxState { /// are disabled before using `Uarte`. See product specification: /// - nrf52832: Section 15.2 /// - nrf52840: Section 6.1.2 -pub struct BufferedUarte { - started: bool, - state: UnsafeCell>, +pub struct BufferedUarte<'a, T: Instance> { + reg: peripheral::Registration>, + wtf: PhantomData<&'a ()>, } -// public because it needs to be used in Instance::{get_state, set_state}, but -// should not be used outside the module -#[doc(hidden)] -pub struct UarteState { - inner: T, - irq: T::Interrupt, - - rx: RingBuf, - rx_state: RxState, - rx_waker: WakerRegistration, - - tx: RingBuf, - tx_state: TxState, - tx_waker: WakerRegistration, - - _pin: PhantomPinned, -} +impl<'a, T: Instance> Unpin for BufferedUarte<'a, T> {} #[cfg(any(feature = "52833", feature = "52840"))] fn port_bit(port: GpioPort) -> bool { @@ -157,10 +63,12 @@ fn port_bit(port: GpioPort) -> bool { } } -impl BufferedUarte { +impl<'a, T: Instance> BufferedUarte<'a, T> { pub fn new( uarte: T, irq: T::Interrupt, + rx_buffer: &'a mut [u8], + tx_buffer: &'a mut [u8], mut pins: Pins, parity: Parity, baudrate: Baudrate, @@ -218,87 +126,79 @@ impl BufferedUarte { // Configure frequency uarte.baudrate.write(|w| w.baudrate().variant(baudrate)); + irq.pend(); + BufferedUarte { - started: false, - state: UnsafeCell::new(UarteState { - inner: uarte, + reg: peripheral::Registration::new( irq, + State { + inner: uarte, - rx: RingBuf::new(), - rx_state: RxState::Idle, - rx_waker: WakerRegistration::new(), + rx: RingBuffer::new(rx_buffer), + rx_state: RxState::Idle, + rx_waker: WakerRegistration::new(), - tx: RingBuf::new(), - tx_state: TxState::Idle, - tx_waker: WakerRegistration::new(), - - _pin: PhantomPinned, - }), + tx: RingBuffer::new(tx_buffer), + tx_state: TxState::Idle, + tx_waker: WakerRegistration::new(), + }, + ), + wtf: PhantomData, } } - - fn with_state<'a, R>( - self: Pin<&'a mut Self>, - f: impl FnOnce(Pin<&'a mut UarteState>) -> R, - ) -> R { - let Self { state, started } = unsafe { self.get_unchecked_mut() }; - - interrupt::free(|cs| { - let ptr = state.get(); - - if !*started { - T::set_state(cs, ptr); - - *started = true; - - // safety: safe because critical section ensures only one *mut UartState - // exists at the same time. - unsafe { Pin::new_unchecked(&mut *ptr) }.start(); - } - - // safety: safe because critical section ensures only one *mut UartState - // exists at the same time. - f(unsafe { Pin::new_unchecked(&mut *ptr) }) - }) - } } -impl Drop for BufferedUarte { +impl<'a, T: Instance> Drop for BufferedUarte<'a, T> { fn drop(&mut self) { // stop DMA before dropping, because DMA is using the buffer in `self`. todo!() } } -impl AsyncBufRead for BufferedUarte { +impl<'a, T: Instance> AsyncBufRead for BufferedUarte<'a, T> { fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.with_state(|s| s.poll_fill_buf(cx)) + let this = unsafe { self.get_unchecked_mut() }; + this.reg.with(|state, _| { + let z: Poll> = state.poll_fill_buf(cx); + let z: Poll> = unsafe { mem::transmute(z) }; + z + }) } fn consume(self: Pin<&mut Self>, amt: usize) { - self.with_state(|s| s.consume(amt)) - } -} - -impl AsyncWrite for BufferedUarte { - fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { - self.with_state(|s| s.poll_write(cx, buf)) - } -} - -impl UarteState { - pub fn start(self: Pin<&mut Self>) { - self.irq.set_handler(|| unsafe { - interrupt::free(|cs| T::get_state(cs).as_mut().unwrap().on_interrupt()); - }); - - self.irq.pend(); - self.irq.enable(); - } - - fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = unsafe { self.get_unchecked_mut() }; + this.reg.with(|state, irq| state.consume(irq, amt)) + } +} +impl<'a, T: Instance> AsyncWrite for BufferedUarte<'a, T> { + fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { + let this = unsafe { self.get_unchecked_mut() }; + this.reg.with(|state, irq| state.poll_write(irq, cx, buf)) + } +} + +// ==================================== +// ==================================== +// ==================================== + +// public because it needs to be used in Instance trait, but +// should not be used outside the module +#[doc(hidden)] +pub struct State<'a, T: Instance> { + inner: T, + + rx: RingBuffer<'a>, + rx_state: RxState, + rx_waker: WakerRegistration, + + tx: RingBuffer<'a>, + tx_state: TxState, + tx_waker: WakerRegistration, +} + +impl<'a, T: Instance> State<'a, T> { + fn poll_fill_buf(&mut self, cx: &mut Context<'_>) -> Poll> { // Conservative compiler fence to prevent optimizations that do not // take in to account actions by DMA. The fence has been placed here, // before any DMA action has started @@ -306,7 +206,7 @@ impl UarteState { trace!("poll_read"); // We have data ready in buffer? Return it. - let buf = this.rx.pop_buf(); + let buf = self.rx.pop_buf(); if buf.len() != 0 { trace!(" got {:?} {:?}", buf.as_ptr() as u32, buf.len()); return Poll::Ready(Ok(buf)); @@ -314,38 +214,40 @@ impl UarteState { trace!(" empty"); - if this.rx_state == RxState::ReceivingReady { + if self.rx_state == RxState::ReceivingReady { trace!(" stopping"); - this.rx_state = RxState::Stopping; - this.inner.tasks_stoprx.write(|w| unsafe { w.bits(1) }); + self.rx_state = RxState::Stopping; + self.inner.tasks_stoprx.write(|w| unsafe { w.bits(1) }); } - this.rx_waker.register(cx.waker()); + self.rx_waker.register(cx.waker()); Poll::Pending } - fn consume(self: Pin<&mut Self>, amt: usize) { - let this = unsafe { self.get_unchecked_mut() }; + fn consume(&mut self, irq: &mut T::Interrupt, amt: usize) { trace!("consume {:?}", amt); - this.rx.pop(amt); - this.irq.pend(); + self.rx.pop(amt); + irq.pend(); } - fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { - let this = unsafe { self.get_unchecked_mut() }; - + fn poll_write( + &mut self, + irq: &mut T::Interrupt, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { trace!("poll_write: {:?}", buf.len()); - let tx_buf = this.tx.push_buf(); + let tx_buf = self.tx.push_buf(); if tx_buf.len() == 0 { trace!("poll_write: pending"); - this.tx_waker.register(cx.waker()); + self.tx_waker.register(cx.waker()); return Poll::Pending; } let n = min(tx_buf.len(), buf.len()); tx_buf[..n].copy_from_slice(&buf[..n]); - this.tx.push(n); + self.tx.push(n); trace!("poll_write: queued {:?}", n); @@ -354,10 +256,17 @@ impl UarteState { // before any DMA action has started compiler_fence(Ordering::SeqCst); - this.irq.pend(); + irq.pend(); Poll::Ready(Ok(n)) } +} + +impl<'a, T: Instance> peripheral::State for State<'a, T> { + type Interrupt = T::Interrupt; + fn store<'b>() -> &'b peripheral::Store { + unsafe { mem::transmute(T::storage()) } + } fn on_interrupt(&mut self) { trace!("irq: start"); @@ -505,39 +414,27 @@ mod private { impl Sealed for crate::pac::UARTE1 {} } -pub trait Instance: Deref + Sized + private::Sealed { +pub trait Instance: + Deref + Sized + private::Sealed + 'static +{ type Interrupt: OwnedInterrupt; - - #[doc(hidden)] - fn get_state(_cs: &CriticalSection) -> *mut UarteState; - - #[doc(hidden)] - fn set_state(_cs: &CriticalSection, state: *mut UarteState); + fn storage() -> &'static peripheral::Store>; } -static mut UARTE0_STATE: *mut UarteState = ptr::null_mut(); -#[cfg(any(feature = "52833", feature = "52840", feature = "9160"))] -static mut UARTE1_STATE: *mut UarteState = ptr::null_mut(); - -impl Instance for crate::pac::UARTE0 { +impl Instance for pac::UARTE0 { type Interrupt = interrupt::UARTE0_UART0Interrupt; - - fn get_state(_cs: &CriticalSection) -> *mut UarteState { - unsafe { UARTE0_STATE } // Safe because of CriticalSection - } - fn set_state(_cs: &CriticalSection, state: *mut UarteState) { - unsafe { UARTE0_STATE = state } // Safe because of CriticalSection + fn storage() -> &'static peripheral::Store> { + static STORAGE: peripheral::Store> = + peripheral::Store::uninit(); + &STORAGE } } -#[cfg(any(feature = "52833", feature = "52840", feature = "9160"))] -impl Instance for crate::pac::UARTE1 { +impl Instance for pac::UARTE1 { type Interrupt = interrupt::UARTE1Interrupt; - - fn get_state(_cs: &CriticalSection) -> *mut UarteState { - unsafe { UARTE1_STATE } // Safe because of CriticalSection - } - fn set_state(_cs: &CriticalSection, state: *mut UarteState) { - unsafe { UARTE1_STATE = state } // Safe because of CriticalSection + fn storage() -> &'static peripheral::Store> { + static STORAGE: peripheral::Store> = + peripheral::Store::uninit(); + &STORAGE } } diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index e97002a2..ac237176 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -50,6 +50,7 @@ pub use nrf52840_hal as hal; // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +pub(crate) mod util; pub mod buffered_uarte; pub mod gpiote; diff --git a/embassy-nrf/src/util/mod.rs b/embassy-nrf/src/util/mod.rs new file mode 100644 index 00000000..2fd5453d --- /dev/null +++ b/embassy-nrf/src/util/mod.rs @@ -0,0 +1,2 @@ +pub mod peripheral; +pub mod ring_buffer; diff --git a/embassy-nrf/src/util/peripheral.rs b/embassy-nrf/src/util/peripheral.rs new file mode 100644 index 00000000..85de3419 --- /dev/null +++ b/embassy-nrf/src/util/peripheral.rs @@ -0,0 +1,107 @@ +use core::mem; +use core::mem::MaybeUninit; +use core::ptr; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::{cell::UnsafeCell, marker::PhantomData}; + +use crate::interrupt::OwnedInterrupt; + +pub struct Store(MaybeUninit>); +impl Store { + pub const fn uninit() -> Self { + Self(MaybeUninit::uninit()) + } + + unsafe fn as_mut_ptr(&self) -> *mut T { + (*self.0.as_ptr()).get() + } + + unsafe fn as_mut(&self) -> &mut T { + &mut *self.as_mut_ptr() + } + + unsafe fn write(&self, val: T) { + ptr::write(self.as_mut_ptr(), val) + } + + unsafe fn drop_in_place(&self) { + ptr::drop_in_place(self.as_mut_ptr()) + } + + unsafe fn read(&self) -> T { + ptr::read(self.as_mut_ptr()) + } +} +unsafe impl Send for Store {} +unsafe impl Sync for Store {} + +pub trait State: Sized { + type Interrupt: OwnedInterrupt; + fn on_interrupt(&mut self); + #[doc(hidden)] + fn store<'a>() -> &'a Store; +} + +pub struct Registration { + irq: P::Interrupt, + not_send: PhantomData<*mut P>, +} + +impl Registration

{ + pub fn new(irq: P::Interrupt, state: P) -> Self { + // safety: + // - No other PeripheralRegistration can already exist because we have the owned interrupt + // - therefore, storage is uninitialized + // - therefore it's safe to overwrite it without dropping the previous contents + unsafe { P::store().write(state) } + + irq.set_handler(|| { + // safety: + // - If a PeripheralRegistration instance exists, P::storage() is initialized. + // - It's OK to get a &mut to it since the irq is disabled. + unsafe { P::store().as_mut() }.on_interrupt(); + }); + + compiler_fence(Ordering::SeqCst); + irq.enable(); + + Self { + irq, + not_send: PhantomData, + } + } + + pub fn with(&mut self, f: impl FnOnce(&mut P, &mut P::Interrupt) -> R) -> R { + self.irq.disable(); + compiler_fence(Ordering::SeqCst); + + // safety: + // - If a PeripheralRegistration instance exists, P::storage() is initialized. + // - It's OK to get a &mut to it since the irq is disabled. + let r = f(unsafe { P::store().as_mut() }, &mut self.irq); + + compiler_fence(Ordering::SeqCst); + self.irq.enable(); + + r + } + + pub fn free(self) -> (P::Interrupt, P) { + let irq = unsafe { ptr::read(&self.irq) }; + irq.disable(); + irq.set_handler(|| ()); + mem::forget(self); + let storage = P::store(); + (irq, unsafe { storage.read() }) + } +} + +impl Drop for Registration

{ + fn drop(&mut self) { + self.irq.disable(); + self.irq.set_handler(|| ()); + + let storage = P::store(); + unsafe { storage.drop_in_place() }; + } +} diff --git a/embassy-nrf/src/util/ring_buffer.rs b/embassy-nrf/src/util/ring_buffer.rs new file mode 100644 index 00000000..f395785a --- /dev/null +++ b/embassy-nrf/src/util/ring_buffer.rs @@ -0,0 +1,80 @@ +use crate::fmt::{assert, panic, todo, *}; + +pub struct RingBuffer<'a> { + buf: &'a mut [u8], + start: usize, + end: usize, + empty: bool, +} + +impl<'a> RingBuffer<'a> { + pub fn new(buf: &'a mut [u8]) -> Self { + Self { + buf, + start: 0, + end: 0, + empty: true, + } + } + + pub fn push_buf(&mut self) -> &mut [u8] { + if self.start == self.end && !self.empty { + trace!(" ringbuf: push_buf empty"); + return &mut self.buf[..0]; + } + + let n = if self.start <= self.end { + self.buf.len() - self.end + } else { + self.start - self.end + }; + + trace!(" ringbuf: push_buf {:?}..{:?}", self.end, self.end + n); + &mut self.buf[self.end..self.end + n] + } + + pub fn push(&mut self, n: usize) { + trace!(" ringbuf: push {:?}", n); + if n == 0 { + return; + } + + self.end = self.wrap(self.end + n); + self.empty = false; + } + + pub fn pop_buf(&mut self) -> &mut [u8] { + if self.empty { + trace!(" ringbuf: pop_buf empty"); + return &mut self.buf[..0]; + } + + let n = if self.end <= self.start { + self.buf.len() - self.start + } else { + self.end - self.start + }; + + trace!(" ringbuf: pop_buf {:?}..{:?}", self.start, self.start + n); + &mut self.buf[self.start..self.start + n] + } + + pub fn pop(&mut self, n: usize) { + trace!(" ringbuf: pop {:?}", n); + if n == 0 { + return; + } + + self.start = self.wrap(self.start + n); + self.empty = self.start == self.end; + } + + fn wrap(&self, n: usize) -> usize { + assert!(n <= self.buf.len()); + if n == self.buf.len() { + 0 + } else { + n + } + } +} From 39ca8b8dedb3db14f9770f09814ccf92481b6136 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 3 Jan 2021 22:30:47 +0100 Subject: [PATCH 11/11] Fix build on non-nrf52840 --- embassy-nrf/src/buffered_uarte.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 4749c3b4..c67b6f16 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -430,6 +430,7 @@ impl Instance for pac::UARTE0 { } } +#[cfg(any(feature = "52833", feature = "52840", feature = "9160"))] impl Instance for pac::UARTE1 { type Interrupt = interrupt::UARTE1Interrupt; fn storage() -> &'static peripheral::Store> {