nrf/spim: share code between blocking+async.
This commit is contained in:
		| @@ -15,6 +15,7 @@ use crate::gpio; | |||||||
| use crate::gpio::sealed::Pin as _; | use crate::gpio::sealed::Pin as _; | ||||||
| use crate::gpio::{OptionalPin, Pin as GpioPin}; | use crate::gpio::{OptionalPin, Pin as GpioPin}; | ||||||
| use crate::interrupt::Interrupt; | use crate::interrupt::Interrupt; | ||||||
|  | use crate::util::{slice_ptr_parts, slice_ptr_parts_mut}; | ||||||
| use crate::{pac, util::slice_in_ram_or}; | use crate::{pac, util::slice_in_ram_or}; | ||||||
|  |  | ||||||
| pub use embedded_hal::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; | pub use embedded_hal::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; | ||||||
| @@ -157,6 +158,74 @@ impl<'d, T: Instance> Spim<'d, T> { | |||||||
|             r.intenclr.write(|w| w.end().clear()); |             r.intenclr.write(|w| w.end().clear()); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     fn start_transfer(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { | ||||||
|  |         slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?; | ||||||
|  |         // NOTE: RAM slice check for rx is not necessary, as a mutable | ||||||
|  |         // slice can only be built from data located in RAM. | ||||||
|  |  | ||||||
|  |         // Conservative compiler fence to prevent optimizations that do not | ||||||
|  |         // take in to account actions by DMA. The fence has been placed here, | ||||||
|  |         // before any DMA action has started. | ||||||
|  |         compiler_fence(Ordering::SeqCst); | ||||||
|  |  | ||||||
|  |         let r = T::regs(); | ||||||
|  |  | ||||||
|  |         // Set up the DMA write. | ||||||
|  |         let (ptr, len) = slice_ptr_parts(tx); | ||||||
|  |         r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); | ||||||
|  |         r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); | ||||||
|  |  | ||||||
|  |         // Set up the DMA read. | ||||||
|  |         let (ptr, len) = slice_ptr_parts_mut(rx); | ||||||
|  |         r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); | ||||||
|  |         r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); | ||||||
|  |  | ||||||
|  |         // Reset and enable the event | ||||||
|  |         r.events_end.reset(); | ||||||
|  |         r.intenset.write(|w| w.end().set()); | ||||||
|  |  | ||||||
|  |         // Start SPI transaction. | ||||||
|  |         r.tasks_start.write(|w| unsafe { w.bits(1) }); | ||||||
|  |  | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn blocking_transfer(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { | ||||||
|  |         self.start_transfer(rx, tx)?; | ||||||
|  |  | ||||||
|  |         // Wait for 'end' event. | ||||||
|  |         while T::regs().events_end.read().bits() == 0 {} | ||||||
|  |  | ||||||
|  |         // Conservative compiler fence to prevent optimizations that do not | ||||||
|  |         // take in to account actions by DMA. The fence has been placed here, | ||||||
|  |         // after all possible DMA actions have completed. | ||||||
|  |         compiler_fence(Ordering::SeqCst); | ||||||
|  |  | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async fn async_transfer(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { | ||||||
|  |         self.start_transfer(rx, tx)?; | ||||||
|  |  | ||||||
|  |         // Wait for 'end' event. | ||||||
|  |         poll_fn(|cx| { | ||||||
|  |             T::state().end_waker.register(cx.waker()); | ||||||
|  |             if T::regs().events_end.read().bits() != 0 { | ||||||
|  |                 return Poll::Ready(()); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             Poll::Pending | ||||||
|  |         }) | ||||||
|  |         .await; | ||||||
|  |  | ||||||
|  |         // Conservative compiler fence to prevent optimizations that do not | ||||||
|  |         // take in to account actions by DMA. The fence has been placed here, | ||||||
|  |         // after all possible DMA actions have completed. | ||||||
|  |         compiler_fence(Ordering::SeqCst); | ||||||
|  |  | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<'d, T: Instance> Drop for Spim<'d, T> { | impl<'d, T: Instance> Drop for Spim<'d, T> { | ||||||
| @@ -210,108 +279,14 @@ impl<'d, T: Instance> FullDuplex<u8> for Spim<'d, T> { | |||||||
|     = impl Future<Output = Result<(), Self::Error>> + 'a; |     = impl Future<Output = Result<(), Self::Error>> + 'a; | ||||||
|  |  | ||||||
|     fn read_write<'a>(&'a mut self, rx: &'a mut [u8], tx: &'a [u8]) -> Self::WriteReadFuture<'a> { |     fn read_write<'a>(&'a mut self, rx: &'a mut [u8], tx: &'a [u8]) -> Self::WriteReadFuture<'a> { | ||||||
|         async move { |         self.async_transfer(rx, tx) | ||||||
|             slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?; |  | ||||||
|             // NOTE: RAM slice check for rx is not necessary, as a mutable |  | ||||||
|             // slice can only be built from data located in RAM. |  | ||||||
|  |  | ||||||
|             // Conservative compiler fence to prevent optimizations that do not |  | ||||||
|             // take in to account actions by DMA. The fence has been placed here, |  | ||||||
|             // before any DMA action has started. |  | ||||||
|             compiler_fence(Ordering::SeqCst); |  | ||||||
|  |  | ||||||
|             let r = T::regs(); |  | ||||||
|             let s = T::state(); |  | ||||||
|  |  | ||||||
|             // Set up the DMA write. |  | ||||||
|             r.txd |  | ||||||
|                 .ptr |  | ||||||
|                 .write(|w| unsafe { w.ptr().bits(tx.as_ptr() as u32) }); |  | ||||||
|             r.txd |  | ||||||
|                 .maxcnt |  | ||||||
|                 .write(|w| unsafe { w.maxcnt().bits(tx.len() as _) }); |  | ||||||
|  |  | ||||||
|             // Set up the DMA read. |  | ||||||
|             r.rxd |  | ||||||
|                 .ptr |  | ||||||
|                 .write(|w| unsafe { w.ptr().bits(rx.as_mut_ptr() as u32) }); |  | ||||||
|             r.rxd |  | ||||||
|                 .maxcnt |  | ||||||
|                 .write(|w| unsafe { w.maxcnt().bits(rx.len() as _) }); |  | ||||||
|  |  | ||||||
|             // Reset and enable the event |  | ||||||
|             r.events_end.reset(); |  | ||||||
|             r.intenset.write(|w| w.end().set()); |  | ||||||
|  |  | ||||||
|             // Start SPI transaction. |  | ||||||
|             r.tasks_start.write(|w| unsafe { w.bits(1) }); |  | ||||||
|  |  | ||||||
|             // Conservative compiler fence to prevent optimizations that do not |  | ||||||
|             // take in to account actions by DMA. The fence has been placed here, |  | ||||||
|             // after all possible DMA actions have completed. |  | ||||||
|             compiler_fence(Ordering::SeqCst); |  | ||||||
|  |  | ||||||
|             // Wait for 'end' event. |  | ||||||
|             poll_fn(|cx| { |  | ||||||
|                 s.end_waker.register(cx.waker()); |  | ||||||
|                 if r.events_end.read().bits() != 0 { |  | ||||||
|                     return Poll::Ready(()); |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 Poll::Pending |  | ||||||
|             }) |  | ||||||
|             .await; |  | ||||||
|  |  | ||||||
|             Ok(()) |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| // Blocking functions are provided by implementing `embedded_hal` traits. |  | ||||||
| // |  | ||||||
| // Code could be shared between traits to reduce code size. |  | ||||||
| impl<'d, T: Instance> embedded_hal::blocking::spi::Transfer<u8> for Spim<'d, T> { | impl<'d, T: Instance> embedded_hal::blocking::spi::Transfer<u8> for Spim<'d, T> { | ||||||
|     type Error = Error; |     type Error = Error; | ||||||
|     fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { |     fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { | ||||||
|         slice_in_ram_or(words, Error::DMABufferNotInDataMemory)?; |         self.blocking_transfer(words, words)?; | ||||||
|  |  | ||||||
|         // Conservative compiler fence to prevent optimizations that do not |  | ||||||
|         // take in to account actions by DMA. The fence has been placed here, |  | ||||||
|         // before any DMA action has started. |  | ||||||
|         compiler_fence(Ordering::SeqCst); |  | ||||||
|  |  | ||||||
|         let r = T::regs(); |  | ||||||
|  |  | ||||||
|         // Set up the DMA write. |  | ||||||
|         r.txd |  | ||||||
|             .ptr |  | ||||||
|             .write(|w| unsafe { w.ptr().bits(words.as_ptr() as u32) }); |  | ||||||
|         r.txd |  | ||||||
|             .maxcnt |  | ||||||
|             .write(|w| unsafe { w.maxcnt().bits(words.len() as _) }); |  | ||||||
|  |  | ||||||
|         // Set up the DMA read. |  | ||||||
|         r.rxd |  | ||||||
|             .ptr |  | ||||||
|             .write(|w| unsafe { w.ptr().bits(words.as_mut_ptr() as u32) }); |  | ||||||
|         r.rxd |  | ||||||
|             .maxcnt |  | ||||||
|             .write(|w| unsafe { w.maxcnt().bits(words.len() as _) }); |  | ||||||
|  |  | ||||||
|         // Disable the end event since we are busy-polling. |  | ||||||
|         r.events_end.reset(); |  | ||||||
|  |  | ||||||
|         // Start SPI transaction. |  | ||||||
|         r.tasks_start.write(|w| unsafe { w.bits(1) }); |  | ||||||
|  |  | ||||||
|         // Wait for 'end' event. |  | ||||||
|         while r.events_end.read().bits() == 0 {} |  | ||||||
|  |  | ||||||
|         // Conservative compiler fence to prevent optimizations that do not |  | ||||||
|         // take in to account actions by DMA. The fence has been placed here, |  | ||||||
|         // after all possible DMA actions have completed. |  | ||||||
|         compiler_fence(Ordering::SeqCst); |  | ||||||
|  |  | ||||||
|         Ok(words) |         Ok(words) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -320,47 +295,7 @@ impl<'d, T: Instance> embedded_hal::blocking::spi::Write<u8> for Spim<'d, T> { | |||||||
|     type Error = Error; |     type Error = Error; | ||||||
|  |  | ||||||
|     fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { |     fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { | ||||||
|         slice_in_ram_or(words, Error::DMABufferNotInDataMemory)?; |         self.blocking_transfer(&mut [], words) | ||||||
|         let recv: &mut [u8] = &mut []; |  | ||||||
|  |  | ||||||
|         // Conservative compiler fence to prevent optimizations that do not |  | ||||||
|         // take in to account actions by DMA. The fence has been placed here, |  | ||||||
|         // before any DMA action has started. |  | ||||||
|         compiler_fence(Ordering::SeqCst); |  | ||||||
|  |  | ||||||
|         let r = T::regs(); |  | ||||||
|  |  | ||||||
|         // Set up the DMA write. |  | ||||||
|         r.txd |  | ||||||
|             .ptr |  | ||||||
|             .write(|w| unsafe { w.ptr().bits(words.as_ptr() as u32) }); |  | ||||||
|         r.txd |  | ||||||
|             .maxcnt |  | ||||||
|             .write(|w| unsafe { w.maxcnt().bits(words.len() as _) }); |  | ||||||
|  |  | ||||||
|         // Set up the DMA read. |  | ||||||
|         r.rxd |  | ||||||
|             .ptr |  | ||||||
|             .write(|w| unsafe { w.ptr().bits(recv.as_mut_ptr() as u32) }); |  | ||||||
|         r.rxd |  | ||||||
|             .maxcnt |  | ||||||
|             .write(|w| unsafe { w.maxcnt().bits(recv.len() as _) }); |  | ||||||
|  |  | ||||||
|         // Disable the end event since we are busy-polling. |  | ||||||
|         r.events_end.reset(); |  | ||||||
|  |  | ||||||
|         // Start SPI transaction. |  | ||||||
|         r.tasks_start.write(|w| unsafe { w.bits(1) }); |  | ||||||
|  |  | ||||||
|         // Wait for 'end' event. |  | ||||||
|         while r.events_end.read().bits() == 0 {} |  | ||||||
|  |  | ||||||
|         // Conservative compiler fence to prevent optimizations that do not |  | ||||||
|         // take in to account actions by DMA. The fence has been placed here, |  | ||||||
|         // after all possible DMA actions have completed. |  | ||||||
|         compiler_fence(Ordering::SeqCst); |  | ||||||
|  |  | ||||||
|         Ok(()) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,16 +1,28 @@ | |||||||
|  | use core::mem; | ||||||
|  |  | ||||||
| const SRAM_LOWER: usize = 0x2000_0000; | const SRAM_LOWER: usize = 0x2000_0000; | ||||||
| const SRAM_UPPER: usize = 0x3000_0000; | const SRAM_UPPER: usize = 0x3000_0000; | ||||||
|  |  | ||||||
|  | // TODO: replace transmutes with core::ptr::metadata once it's stable | ||||||
|  |  | ||||||
|  | pub(crate) fn slice_ptr_parts<T>(slice: *const [T]) -> (usize, usize) { | ||||||
|  |     unsafe { mem::transmute(slice) } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub(crate) fn slice_ptr_parts_mut<T>(slice: *mut [T]) -> (usize, usize) { | ||||||
|  |     unsafe { mem::transmute(slice) } | ||||||
|  | } | ||||||
|  |  | ||||||
| /// Does this slice reside entirely within RAM? | /// Does this slice reside entirely within RAM? | ||||||
| pub(crate) fn slice_in_ram<T>(slice: &[T]) -> bool { | pub(crate) fn slice_in_ram<T>(slice: *const [T]) -> bool { | ||||||
|     let ptr = slice.as_ptr() as usize; |     let (ptr, len) = slice_ptr_parts(slice); | ||||||
|     ptr >= SRAM_LOWER && (ptr + slice.len() * core::mem::size_of::<T>()) < SRAM_UPPER |     ptr >= SRAM_LOWER && (ptr + len * core::mem::size_of::<T>()) < SRAM_UPPER | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Return an error if slice is not in RAM. | /// Return an error if slice is not in RAM. | ||||||
| #[cfg(not(feature = "nrf51"))] | #[cfg(not(feature = "nrf51"))] | ||||||
| pub(crate) fn slice_in_ram_or<T, E>(slice: &[T], err: E) -> Result<(), E> { | pub(crate) fn slice_in_ram_or<T, E>(slice: *const [T], err: E) -> Result<(), E> { | ||||||
|     if slice.is_empty() || slice_in_ram(slice) { |     if slice_in_ram(slice) { | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } else { |     } else { | ||||||
|         Err(err) |         Err(err) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user