diff options
| -rw-r--r-- | embassy-nrf/src/spim.rs | 112 | ||||
| -rw-r--r-- | embassy-nrf/src/util.rs | 13 |
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}; | |||
| 17 | use crate::gpio::sealed::Pin as _; | 17 | use crate::gpio::sealed::Pin as _; |
| 18 | use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; | 18 | use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; |
| 19 | use crate::interrupt::typelevel::Interrupt; | 19 | use crate::interrupt::typelevel::Interrupt; |
| 20 | use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; | 20 | use crate::util::{slice_in_ram_or, slice_ptr_len, slice_ptr_parts, slice_ptr_parts_mut}; |
| 21 | use crate::{interrupt, pac, Peripheral}; | 21 | use 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] |
| 27 | pub enum Error { | 27 | pub 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 { | |||
| 528 | impl embedded_hal_1::spi::Error for Error { | 554 | impl 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; | |||
| 4 | const SRAM_LOWER: usize = 0x2000_0000; | 4 | const SRAM_LOWER: usize = 0x2000_0000; |
| 5 | const SRAM_UPPER: usize = 0x3000_0000; | 5 | const SRAM_UPPER: usize = 0x3000_0000; |
| 6 | 6 | ||
| 7 | // #![feature(const_slice_ptr_len)] | ||
| 8 | // https://github.com/rust-lang/rust/issues/71146 | ||
| 9 | pub(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 |
| 8 | pub(crate) fn slice_ptr_parts<T>(slice: *const [T]) -> (*const T, usize) { | 21 | pub(crate) fn slice_ptr_parts<T>(slice: *const [T]) -> (*const T, usize) { |
| 9 | unsafe { mem::transmute(slice) } | 22 | unsafe { mem::transmute(slice) } |
