From 2b7b7a917d0fc5917cb968c52bd19d710675f540 Mon Sep 17 00:00:00 2001 From: Nils Fitinghoff Date: Fri, 22 Sep 2023 16:12:57 +0300 Subject: [PATCH 1/2] spim: Fix SPIM with polling executors Co-authored-by: Priit Laes --- embassy-nrf/src/spim.rs | 67 +++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 99a195a6e..a0d2c8eb1 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -69,29 +69,13 @@ impl interrupt::typelevel::Handler for InterruptHandl let s = T::state(); #[cfg(feature = "nrf52832")] - // NRF32 Anomaly 109 workaround... NRF52832 - if r.intenset.read().started().is_enabled() && r.events_started.read().bits() != 0 { - // Handle the first "fake" transmission - r.events_started.reset(); - r.events_end.reset(); - - // Update DMA registers with correct rx/tx buffer sizes - r.rxd - .maxcnt - .write(|w| unsafe { w.maxcnt().bits(s.rx.load(Ordering::Relaxed)) }); - r.txd - .maxcnt - .write(|w| unsafe { w.maxcnt().bits(s.tx.load(Ordering::Relaxed)) }); - - // Disable interrupt for STARTED event... + if r.events_started.read().bits() != 0 { + s.waker.wake(); r.intenclr.write(|w| w.started().clear()); - // ... and start actual, hopefully glitch-free transmission - r.tasks_start.write(|w| unsafe { w.bits(1) }); - return; } if r.events_end.read().bits() != 0 { - s.end_waker.wake(); + s.waker.wake(); r.intenclr.write(|w| w.end().clear()); } } @@ -254,6 +238,9 @@ impl<'d, T: Instance> Spim<'d, T> { fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { self.prepare(rx, tx)?; + #[cfg(feature = "nrf52832")] + while let Poll::Pending = self.nrf52832_dma_workaround_status() {} + // Wait for 'end' event. while T::regs().events_end.read().bits() == 0 {} @@ -278,9 +265,19 @@ impl<'d, T: Instance> Spim<'d, T> { async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { self.prepare(rx, tx)?; + #[cfg(feature = "nrf52832")] + poll_fn(|cx| { + let s = T::state(); + + s.waker.register(cx.waker()); + + self.nrf52832_dma_workaround_status() + }) + .await; + // Wait for 'end' event. poll_fn(|cx| { - T::state().end_waker.register(cx.waker()); + T::state().waker.register(cx.waker()); if T::regs().events_end.read().bits() != 0 { return Poll::Ready(()); } @@ -371,6 +368,32 @@ impl<'d, T: Instance> Spim<'d, T> { pub async fn write_from_ram(&mut self, data: &[u8]) -> Result<(), Error> { self.async_inner_from_ram(&mut [], data).await } + + #[cfg(feature = "nrf52832")] + fn nrf52832_dma_workaround_status(&mut self) -> Poll<()> { + let r = T::regs(); + if r.events_started.read().bits() != 0 { + let s = T::state(); + + // Handle the first "fake" transmission + r.events_started.reset(); + r.events_end.reset(); + + // Update DMA registers with correct rx/tx buffer sizes + r.rxd + .maxcnt + .write(|w| unsafe { w.maxcnt().bits(s.rx.load(Ordering::Relaxed)) }); + r.txd + .maxcnt + .write(|w| unsafe { w.maxcnt().bits(s.tx.load(Ordering::Relaxed)) }); + + r.intenset.write(|w| w.end().set()); + // ... and start actual, hopefully glitch-free transmission + r.tasks_start.write(|w| unsafe { w.bits(1) }); + return Poll::Ready(()); + } + Poll::Pending + } } impl<'d, T: Instance> Drop for Spim<'d, T> { @@ -403,7 +426,7 @@ pub(crate) mod sealed { use super::*; pub struct State { - pub end_waker: AtomicWaker, + pub waker: AtomicWaker, #[cfg(feature = "nrf52832")] pub rx: AtomicU8, #[cfg(feature = "nrf52832")] @@ -413,7 +436,7 @@ pub(crate) mod sealed { impl State { pub const fn new() -> Self { Self { - end_waker: AtomicWaker::new(), + waker: AtomicWaker::new(), #[cfg(feature = "nrf52832")] rx: AtomicU8::new(0), #[cfg(feature = "nrf52832")] From 6f83acc010786d89400426c3395f871ac7199d32 Mon Sep 17 00:00:00 2001 From: Priit Laes Date: Fri, 22 Sep 2023 17:30:09 +0300 Subject: [PATCH 2/2] Add separate work-around specific flag for DMA errata on NRF52832 --- embassy-nrf/Cargo.toml | 5 ++++- embassy-nrf/src/spim.rs | 21 ++++++++++----------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index eee927211..3c706b473 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -51,7 +51,7 @@ nrf52805 = ["nrf52805-pac", "_nrf52"] nrf52810 = ["nrf52810-pac", "_nrf52"] nrf52811 = ["nrf52811-pac", "_nrf52"] nrf52820 = ["nrf52820-pac", "_nrf52"] -nrf52832 = ["nrf52832-pac", "_nrf52"] +nrf52832 = ["nrf52832-pac", "_nrf52", "_nrf52832_anomaly_109"] nrf52833 = ["nrf52833-pac", "_nrf52", "_gpio-p1"] nrf52840 = ["nrf52840-pac", "_nrf52", "_gpio-p1"] nrf5340-app-s = ["_nrf5340-app", "_s"] @@ -90,6 +90,9 @@ _ppi = [] _dppi = [] _gpio-p1 = [] +# Errata workarounds +_nrf52832_anomaly_109 = [] + [dependencies] embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true } embassy-sync = { version = "0.3.0", path = "../embassy-sync" } diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index a0d2c8eb1..4828af43e 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -68,7 +68,7 @@ impl interrupt::typelevel::Handler for InterruptHandl let r = T::regs(); let s = T::state(); - #[cfg(feature = "nrf52832")] + #[cfg(feature = "_nrf52832_anomaly_109")] if r.events_started.read().bits() != 0 { s.waker.wake(); r.intenclr.write(|w| w.started().clear()); @@ -206,8 +206,7 @@ impl<'d, T: Instance> Spim<'d, T> { r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx_len as _) }); - // ANOMALY 109 workaround - #[cfg(feature = "nrf52832")] + #[cfg(feature = "_nrf52832_anomaly_109")] { let s = T::state(); @@ -238,7 +237,7 @@ impl<'d, T: Instance> Spim<'d, T> { fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { self.prepare(rx, tx)?; - #[cfg(feature = "nrf52832")] + #[cfg(feature = "_nrf52832_anomaly_109")] while let Poll::Pending = self.nrf52832_dma_workaround_status() {} // Wait for 'end' event. @@ -265,7 +264,7 @@ impl<'d, T: Instance> Spim<'d, T> { async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { self.prepare(rx, tx)?; - #[cfg(feature = "nrf52832")] + #[cfg(feature = "_nrf52832_anomaly_109")] poll_fn(|cx| { let s = T::state(); @@ -369,7 +368,7 @@ impl<'d, T: Instance> Spim<'d, T> { self.async_inner_from_ram(&mut [], data).await } - #[cfg(feature = "nrf52832")] + #[cfg(feature = "_nrf52832_anomaly_109")] fn nrf52832_dma_workaround_status(&mut self) -> Poll<()> { let r = T::regs(); if r.events_started.read().bits() != 0 { @@ -418,7 +417,7 @@ impl<'d, T: Instance> Drop for Spim<'d, T> { } pub(crate) mod sealed { - #[cfg(feature = "nrf52832")] + #[cfg(feature = "_nrf52832_anomaly_109")] use core::sync::atomic::AtomicU8; use embassy_sync::waitqueue::AtomicWaker; @@ -427,9 +426,9 @@ pub(crate) mod sealed { pub struct State { pub waker: AtomicWaker, - #[cfg(feature = "nrf52832")] + #[cfg(feature = "_nrf52832_anomaly_109")] pub rx: AtomicU8, - #[cfg(feature = "nrf52832")] + #[cfg(feature = "_nrf52832_anomaly_109")] pub tx: AtomicU8, } @@ -437,9 +436,9 @@ pub(crate) mod sealed { pub const fn new() -> Self { Self { waker: AtomicWaker::new(), - #[cfg(feature = "nrf52832")] + #[cfg(feature = "_nrf52832_anomaly_109")] rx: AtomicU8::new(0), - #[cfg(feature = "nrf52832")] + #[cfg(feature = "_nrf52832_anomaly_109")] tx: AtomicU8::new(0), } }