aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPriit Laes <[email protected]>2024-02-15 12:34:51 +0200
committerPriit Laes <[email protected]>2024-02-15 12:34:51 +0200
commit50b8100fd30fd13ac706889b0951e6ec25c77821 (patch)
treee7082f8232554a21c689e7e73887211267024c51
parent5220453d85b1e0f279e94dc1627b7d2434132920 (diff)
nrf: Implement chunked DMA transfers for SPIM peripheral
On some chips (notably nrf52832), the maximum DMA transfer is 255 bytes, which has caused subtle issues while interfacing with various devices over SPI bus.
-rw-r--r--embassy-nrf/src/spim.rs112
-rw-r--r--embassy-nrf/src/util.rs13
2 files changed, 81 insertions, 44 deletions
diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs
index 8937159df..2d70732a8 100644
--- a/embassy-nrf/src/spim.rs
+++ b/embassy-nrf/src/spim.rs
@@ -17,7 +17,7 @@ use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
17use crate::gpio::sealed::Pin as _; 17use crate::gpio::sealed::Pin as _;
18use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; 18use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits};
19use crate::interrupt::typelevel::Interrupt; 19use crate::interrupt::typelevel::Interrupt;
20use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; 20use crate::util::{slice_in_ram_or, slice_ptr_len, slice_ptr_parts, slice_ptr_parts_mut};
21use crate::{interrupt, pac, Peripheral}; 21use crate::{interrupt, pac, Peripheral};
22 22
23/// SPIM error 23/// SPIM error
@@ -25,10 +25,6 @@ use crate::{interrupt, pac, Peripheral};
25#[cfg_attr(feature = "defmt", derive(defmt::Format))] 25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26#[non_exhaustive] 26#[non_exhaustive]
27pub enum Error { 27pub enum Error {
28 /// Supplied TX buffer overflows EasyDMA transmit buffer
29 TxBufferTooLong,
30 /// Supplied RX buffer overflows EasyDMA receive buffer
31 RxBufferTooLong,
32 /// EasyDMA can only read from data memory, read only buffers in flash will fail. 28 /// EasyDMA can only read from data memory, read only buffers in flash will fail.
33 BufferNotInRAM, 29 BufferNotInRAM,
34} 30}
@@ -74,9 +70,13 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
74 let s = T::state(); 70 let s = T::state();
75 71
76 #[cfg(feature = "_nrf52832_anomaly_109")] 72 #[cfg(feature = "_nrf52832_anomaly_109")]
77 if r.events_started.read().bits() != 0 { 73 {
78 s.waker.wake(); 74 // Ideally we should call this only during the first chunk transfer,
79 r.intenclr.write(|w| w.started().clear()); 75 // but so far calling this every time doesn't seem to be causing any issues.
76 if r.events_started.read().bits() != 0 {
77 s.waker.wake();
78 r.intenclr.write(|w| w.started().clear());
79 }
80 } 80 }
81 81
82 if r.events_end.read().bits() != 0 { 82 if r.events_end.read().bits() != 0 {
@@ -209,35 +209,39 @@ impl<'d, T: Instance> Spim<'d, T> {
209 spim 209 spim
210 } 210 }
211 211
212 fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { 212 fn prepare_dma_transfer(&mut self, rx: *mut [u8], tx: *const [u8], offset: usize, length: usize) {
213 slice_in_ram_or(tx, Error::BufferNotInRAM)?;
214 // NOTE: RAM slice check for rx is not necessary, as a mutable
215 // slice can only be built from data located in RAM.
216
217 compiler_fence(Ordering::SeqCst); 213 compiler_fence(Ordering::SeqCst);
218 214
219 let r = T::regs(); 215 let r = T::regs();
220 216
221 // Set up the DMA write. 217 fn xfer_params(ptr: u32, total: usize, offset: usize, length: usize) -> (u32, usize) {
222 let (ptr, tx_len) = slice_ptr_parts(tx); 218 if total > offset {
223 if tx_len > EASY_DMA_SIZE { 219 (ptr.wrapping_add(offset as _), core::cmp::min(total - offset, length))
224 return Err(Error::TxBufferTooLong); 220 } else {
221 (ptr, 0)
222 }
225 } 223 }
226 224
227 r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
228 r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(tx_len as _) });
229
230 // Set up the DMA read. 225 // Set up the DMA read.
231 let (ptr, rx_len) = slice_ptr_parts_mut(rx); 226 let (ptr, len) = slice_ptr_parts_mut(rx);
232 if rx_len > EASY_DMA_SIZE { 227 let (rx_ptr, rx_len) = xfer_params(ptr as _, len as _, offset, length);
233 return Err(Error::RxBufferTooLong); 228 r.rxd.ptr.write(|w| unsafe { w.ptr().bits(rx_ptr) });
234 }
235
236 r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
237 r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx_len as _) }); 229 r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx_len as _) });
238 230
231 // Set up the DMA write.
232 let (ptr, len) = slice_ptr_parts(tx);
233 let (tx_ptr, tx_len) = xfer_params(ptr as _, len as _, offset, length);
234 r.txd.ptr.write(|w| unsafe { w.ptr().bits(tx_ptr) });
235 r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(tx_len as _) });
236
237 /*
238 trace!("XFER: offset: {}, length: {}", offset, length);
239 trace!("RX(len: {}, ptr: {=u32:02x})", rx_len, rx_ptr as u32);
240 trace!("TX(len: {}, ptr: {=u32:02x})", tx_len, tx_ptr as u32);
241 */
242
239 #[cfg(feature = "_nrf52832_anomaly_109")] 243 #[cfg(feature = "_nrf52832_anomaly_109")]
240 { 244 if offset == 0 {
241 let s = T::state(); 245 let s = T::state();
242 246
243 r.events_started.reset(); 247 r.events_started.reset();
@@ -260,21 +264,32 @@ impl<'d, T: Instance> Spim<'d, T> {
260 264
261 // Start SPI transaction. 265 // Start SPI transaction.
262 r.tasks_start.write(|w| unsafe { w.bits(1) }); 266 r.tasks_start.write(|w| unsafe { w.bits(1) });
263
264 Ok(())
265 } 267 }
266 268
267 fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { 269 fn blocking_inner_from_ram_chunk(&mut self, rx: *mut [u8], tx: *const [u8], offset: usize, length: usize) {
268 self.prepare(rx, tx)?; 270 self.prepare_dma_transfer(rx, tx, offset, length);
269 271
270 #[cfg(feature = "_nrf52832_anomaly_109")] 272 #[cfg(feature = "_nrf52832_anomaly_109")]
271 while let Poll::Pending = self.nrf52832_dma_workaround_status() {} 273 if offset == 0 {
274 while self.nrf52832_dma_workaround_status().is_pending() {}
275 }
272 276
273 // Wait for 'end' event. 277 // Wait for 'end' event.
274 while T::regs().events_end.read().bits() == 0 {} 278 while T::regs().events_end.read().bits() == 0 {}
275 279
276 compiler_fence(Ordering::SeqCst); 280 compiler_fence(Ordering::SeqCst);
281 }
277 282
283 fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
284 slice_in_ram_or(tx, Error::BufferNotInRAM)?;
285 // NOTE: RAM slice check for rx is not necessary, as a mutable
286 // slice can only be built from data located in RAM.
287
288 let xfer_len = core::cmp::max(slice_ptr_len(rx), slice_ptr_len(tx));
289 for offset in (0..xfer_len).step_by(EASY_DMA_SIZE) {
290 let length = core::cmp::min(xfer_len - offset, EASY_DMA_SIZE);
291 self.blocking_inner_from_ram_chunk(rx, tx, offset, length);
292 }
278 Ok(()) 293 Ok(())
279 } 294 }
280 295
@@ -287,22 +302,23 @@ impl<'d, T: Instance> Spim<'d, T> {
287 tx_ram_buf.copy_from_slice(tx); 302 tx_ram_buf.copy_from_slice(tx);
288 self.blocking_inner_from_ram(rx, tx_ram_buf) 303 self.blocking_inner_from_ram(rx, tx_ram_buf)
289 } 304 }
290 Err(error) => Err(error),
291 } 305 }
292 } 306 }
293 307
294 async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { 308 async fn async_inner_from_ram_chunk(&mut self, rx: *mut [u8], tx: *const [u8], offset: usize, length: usize) {
295 self.prepare(rx, tx)?; 309 self.prepare_dma_transfer(rx, tx, offset, length);
296 310
297 #[cfg(feature = "_nrf52832_anomaly_109")] 311 #[cfg(feature = "_nrf52832_anomaly_109")]
298 poll_fn(|cx| { 312 if offset == 0 {
299 let s = T::state(); 313 poll_fn(|cx| {
314 let s = T::state();
300 315
301 s.waker.register(cx.waker()); 316 s.waker.register(cx.waker());
302 317
303 self.nrf52832_dma_workaround_status() 318 self.nrf52832_dma_workaround_status()
304 }) 319 })
305 .await; 320 .await;
321 }
306 322
307 // Wait for 'end' event. 323 // Wait for 'end' event.
308 poll_fn(|cx| { 324 poll_fn(|cx| {
@@ -316,7 +332,18 @@ impl<'d, T: Instance> Spim<'d, T> {
316 .await; 332 .await;
317 333
318 compiler_fence(Ordering::SeqCst); 334 compiler_fence(Ordering::SeqCst);
335 }
336
337 async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
338 slice_in_ram_or(tx, Error::BufferNotInRAM)?;
339 // NOTE: RAM slice check for rx is not necessary, as a mutable
340 // slice can only be built from data located in RAM.
319 341
342 let xfer_len = core::cmp::max(slice_ptr_len(rx), slice_ptr_len(tx));
343 for offset in (0..xfer_len).step_by(EASY_DMA_SIZE) {
344 let length = core::cmp::min(xfer_len - offset, EASY_DMA_SIZE);
345 self.async_inner_from_ram_chunk(rx, tx, offset, length).await;
346 }
320 Ok(()) 347 Ok(())
321 } 348 }
322 349
@@ -329,7 +356,6 @@ impl<'d, T: Instance> Spim<'d, T> {
329 tx_ram_buf.copy_from_slice(tx); 356 tx_ram_buf.copy_from_slice(tx);
330 self.async_inner_from_ram(rx, tx_ram_buf).await 357 self.async_inner_from_ram(rx, tx_ram_buf).await
331 } 358 }
332 Err(error) => Err(error),
333 } 359 }
334 } 360 }
335 361
@@ -528,8 +554,6 @@ mod eh02 {
528impl embedded_hal_1::spi::Error for Error { 554impl embedded_hal_1::spi::Error for Error {
529 fn kind(&self) -> embedded_hal_1::spi::ErrorKind { 555 fn kind(&self) -> embedded_hal_1::spi::ErrorKind {
530 match *self { 556 match *self {
531 Self::TxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other,
532 Self::RxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other,
533 Self::BufferNotInRAM => embedded_hal_1::spi::ErrorKind::Other, 557 Self::BufferNotInRAM => embedded_hal_1::spi::ErrorKind::Other,
534 } 558 }
535 } 559 }
diff --git a/embassy-nrf/src/util.rs b/embassy-nrf/src/util.rs
index b408c517b..6cdb97f08 100644
--- a/embassy-nrf/src/util.rs
+++ b/embassy-nrf/src/util.rs
@@ -4,6 +4,19 @@ use core::mem;
4const SRAM_LOWER: usize = 0x2000_0000; 4const SRAM_LOWER: usize = 0x2000_0000;
5const SRAM_UPPER: usize = 0x3000_0000; 5const SRAM_UPPER: usize = 0x3000_0000;
6 6
7// #![feature(const_slice_ptr_len)]
8// https://github.com/rust-lang/rust/issues/71146
9pub(crate) fn slice_ptr_len<T>(ptr: *const [T]) -> usize {
10 use core::ptr::NonNull;
11 let ptr = ptr.cast_mut();
12 if let Some(ptr) = NonNull::new(ptr) {
13 ptr.len()
14 } else {
15 // We know ptr is null, so we know ptr.wrapping_byte_add(1) is not null.
16 NonNull::new(ptr.wrapping_byte_add(1)).unwrap().len()
17 }
18}
19
7// TODO: replace transmutes with core::ptr::metadata once it's stable 20// TODO: replace transmutes with core::ptr::metadata once it's stable
8pub(crate) fn slice_ptr_parts<T>(slice: *const [T]) -> (*const T, usize) { 21pub(crate) fn slice_ptr_parts<T>(slice: *const [T]) -> (*const T, usize) {
9 unsafe { mem::transmute(slice) } 22 unsafe { mem::transmute(slice) }