aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUlf Lilleengen <[email protected]>2024-02-16 17:35:53 +0000
committerGitHub <[email protected]>2024-02-16 17:35:53 +0000
commite9907de39d640fee02df9eac1fbd7efa33e63da2 (patch)
treeb4b2a4fd44698252fe1505f0a651509546559eae
parentb462dfa99641e955df3b2c2b3637d57727b1d9c8 (diff)
parent50b8100fd30fd13ac706889b0951e6ec25c77821 (diff)
Merge pull request #2572 from plaes/nrf-spim-chunked-dma
NRF: Implement chunked DMA transfers for SPIM
-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) }