Merge pull request #14 from timokroeger/uarte-power-optimization

UARTE power optimization and improvements
This commit is contained in:
Dario Nieuwenhuis 2021-01-05 22:10:52 +01:00 committed by GitHub
commit 9bb4c97dc2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 91 additions and 42 deletions

View file

@ -17,8 +17,8 @@ use crate::hal::gpio::Port as GpioPort;
use crate::hal::pac; use crate::hal::pac;
use crate::hal::prelude::*; use crate::hal::prelude::*;
use crate::hal::target_constants::EASY_DMA_SIZE; use crate::hal::target_constants::EASY_DMA_SIZE;
use crate::interrupt;
use crate::interrupt::OwnedInterrupt; use crate::interrupt::OwnedInterrupt;
use crate::{interrupt, util};
pub use crate::hal::uarte::Pins; pub use crate::hal::uarte::Pins;
// Re-export SVD variants to allow user to directly set values. // Re-export SVD variants to allow user to directly set values.
@ -131,6 +131,8 @@ where
} }
pub fn free(self) -> (T, T::Interrupt, Pins) { pub fn free(self) -> (T, T::Interrupt, Pins) {
// Wait for the peripheral to be disabled from the ISR.
while self.instance.enable.read().enable().is_enabled() {}
(self.instance, self.irq, self.pins) (self.instance, self.irq, self.pins)
} }
@ -156,6 +158,13 @@ where
uarte.events_endtx.reset(); uarte.events_endtx.reset();
trace!("endtx"); trace!("endtx");
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
if uarte.events_txstarted.read().bits() != 0 {
// The ENDTX was signal triggered because DMA has finished.
uarte.events_txstarted.reset();
try_disable = true;
}
T::state().tx_done.signal(()); T::state().tx_done.signal(());
} }
@ -170,6 +179,13 @@ where
trace!("endrx"); trace!("endrx");
let len = uarte.rxd.amount.read().bits(); let len = uarte.rxd.amount.read().bits();
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
if uarte.events_rxstarted.read().bits() != 0 {
// The ENDRX was signal triggered because DMA buffer is full.
uarte.events_rxstarted.reset();
try_disable = true;
}
T::state().rx_done.signal(len); T::state().rx_done.signal(len);
} }
@ -204,7 +220,7 @@ impl<T: Instance> embassy::uart::Uart for Uarte<T> {
// `mem::forget()` on a previous future after polling it once. // `mem::forget()` on a previous future after polling it once.
assert!(!self.tx_started()); assert!(!self.tx_started());
self.enable(); T::state().tx_done.reset();
SendFuture { SendFuture {
uarte: self, uarte: self,
@ -227,7 +243,7 @@ impl<T: Instance> embassy::uart::Uart for Uarte<T> {
// `mem::forget()` on a previous future after polling it once. // `mem::forget()` on a previous future after polling it once.
assert!(!self.rx_started()); assert!(!self.rx_started());
self.enable(); T::state().rx_done.reset();
ReceiveFuture { ReceiveFuture {
uarte: self, uarte: self,
@ -241,7 +257,7 @@ pub struct SendFuture<'a, T>
where where
T: Instance, T: Instance,
{ {
uarte: &'a Uarte<T>, uarte: &'a mut Uarte<T>,
buf: &'a [u8], buf: &'a [u8],
} }
@ -259,7 +275,9 @@ where
.instance .instance
.tasks_stoptx .tasks_stoptx
.write(|w| unsafe { w.bits(1) }); .write(|w| unsafe { w.bits(1) });
T::state().tx_done.blocking_wait();
// TX is stopped almost instantly, spinning is fine.
while !T::state().tx_done.signaled() {}
} }
} }
} }
@ -273,28 +291,34 @@ where
fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let Self { uarte, buf } = unsafe { self.get_unchecked_mut() }; let Self { uarte, buf } = unsafe { self.get_unchecked_mut() };
if !uarte.tx_started() { if T::state().tx_done.poll_wait(cx).is_pending() {
let uarte = &uarte.instance;
T::state().tx_done.reset();
let ptr = buf.as_ptr(); let ptr = buf.as_ptr();
let len = buf.len(); let len = buf.len();
assert!(len <= EASY_DMA_SIZE); assert!(len <= EASY_DMA_SIZE);
// TODO: panic if buffer is not in SRAM // TODO: panic if buffer is not in SRAM
uarte.enable();
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
uarte.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
uarte uarte
.instance
.txd
.ptr
.write(|w| unsafe { w.ptr().bits(ptr as u32) });
uarte
.instance
.txd .txd
.maxcnt .maxcnt
.write(|w| unsafe { w.maxcnt().bits(len as _) }); .write(|w| unsafe { w.maxcnt().bits(len as _) });
trace!("starttx"); trace!("starttx");
uarte.tasks_starttx.write(|w| unsafe { w.bits(1) }); uarte.instance.tasks_starttx.write(|w| unsafe { w.bits(1) });
} while !uarte.tx_started() {} // Make sure transmission has started
T::state().tx_done.poll_wait(cx).map(|()| Ok(())) Poll::Pending
} else {
Poll::Ready(Ok(()))
}
} }
} }
@ -303,7 +327,7 @@ pub struct ReceiveFuture<'a, T>
where where
T: Instance, T: Instance,
{ {
uarte: &'a Uarte<T>, uarte: &'a mut Uarte<T>,
buf: &'a mut [u8], buf: &'a mut [u8],
} }
@ -313,14 +337,15 @@ where
{ {
fn drop(self: &mut Self) { fn drop(self: &mut Self) {
if self.uarte.rx_started() { if self.uarte.rx_started() {
trace!("stoprx"); trace!("stoprx (drop)");
self.uarte.instance.events_rxstarted.reset(); self.uarte.instance.events_rxstarted.reset();
self.uarte self.uarte
.instance .instance
.tasks_stoprx .tasks_stoprx
.write(|w| unsafe { w.bits(1) }); .write(|w| unsafe { w.bits(1) });
T::state().rx_done.blocking_wait();
util::low_power_wait_until(|| T::state().rx_done.signaled())
} }
} }
} }
@ -334,27 +359,35 @@ where
fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let Self { uarte, buf } = unsafe { self.get_unchecked_mut() }; let Self { uarte, buf } = unsafe { self.get_unchecked_mut() };
if !uarte.rx_started() { match T::state().rx_done.poll_wait(cx) {
let uarte = &uarte.instance; Poll::Pending if !uarte.rx_started() => {
let ptr = buf.as_ptr();
let len = buf.len();
assert!(len <= EASY_DMA_SIZE);
T::state().rx_done.reset(); uarte.enable();
let ptr = buf.as_ptr(); compiler_fence(Ordering::SeqCst);
let len = buf.len(); uarte
assert!(len <= EASY_DMA_SIZE); .instance
.rxd
.ptr
.write(|w| unsafe { w.ptr().bits(ptr as u32) });
uarte
.instance
.rxd
.maxcnt
.write(|w| unsafe { w.maxcnt().bits(len as _) });
compiler_fence(Ordering::SeqCst); trace!("startrx");
uarte.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); uarte.instance.tasks_startrx.write(|w| unsafe { w.bits(1) });
uarte while !uarte.rx_started() {} // Make sure reception has started
.rxd
.maxcnt
.write(|w| unsafe { w.maxcnt().bits(len as _) });
trace!("startrx"); Poll::Pending
uarte.tasks_startrx.write(|w| unsafe { w.bits(1) }); }
Poll::Pending => Poll::Pending,
Poll::Ready(_) => Poll::Ready(Ok(())),
} }
T::state().rx_done.poll_wait(cx).map(|_| Ok(()))
} }
} }
@ -365,8 +398,19 @@ where
{ {
/// Stops the ongoing reception and returns the number of bytes received. /// Stops the ongoing reception and returns the number of bytes received.
pub async fn stop(self) -> usize { pub async fn stop(self) -> usize {
drop(self); let len = if self.uarte.rx_started() {
let len = T::state().rx_done.wait().await; trace!("stoprx (stop)");
self.uarte.instance.events_rxstarted.reset();
self.uarte
.instance
.tasks_stoprx
.write(|w| unsafe { w.bits(1) });
T::state().rx_done.wait().await
} else {
// Transfer was stopped before it even started. No bytes were sent.
0
};
len as _ len as _
} }
} }

View file

@ -1,2 +1,12 @@
pub mod peripheral; pub mod peripheral;
pub mod ring_buffer; pub mod ring_buffer;
/// Low power blocking wait loop using WFE/SEV.
pub fn low_power_wait_until(mut condition: impl FnMut() -> bool) {
while !condition() {
// WFE might "eat" an event that would have otherwise woken the executor.
cortex_m::asm::wfe();
}
// Retrigger an event to be transparent to the executor.
cortex_m::asm::sev();
}

View file

@ -63,12 +63,7 @@ impl<T: Send> Signal<T> {
futures::future::poll_fn(move |cx| self.poll_wait(cx)) futures::future::poll_fn(move |cx| self.poll_wait(cx))
} }
/// Blocks until the signal has been received. pub fn signaled(&self) -> bool {
/// cortex_m::interrupt::free(|_| matches!(unsafe { &*self.state.get() }, State::Signaled(_)))
/// Returns immediately when [`poll_wait()`] has not been called before.
pub fn blocking_wait(&self) {
while cortex_m::interrupt::free(|_| {
matches!(unsafe { &*self.state.get() }, State::Waiting(_))
}) {}
} }
} }