diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index b448f6ab..06e8235e 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -1,3 +1,39 @@ +//! ## EasyDMA considerations +//! +//! On nRF chips, peripherals can use the so called EasyDMA feature to offload the task of interacting +//! with peripherals. It takes care of sending/receiving data over a variety of bus protocols (TWI/I2C, UART, SPI). +//! However, EasyDMA requires the buffers used to transmit and receive data to reside in RAM. Unfortunately, Rust +//! slices will not always do so. The following example using the SPI peripheral shows a common situation where this might happen: +//! +//! ```no_run +//! // As we pass a slice to the function whose contents will not ever change, +//! // the compiler writes it into the flash and thus the pointer to it will +//! // reference static memory. Since EasyDMA requires slices to reside in RAM, +//! // this function call will fail. +//! let result = spim.write_from_ram(&[1, 2, 3]); +//! assert_eq!(result, Err(Error::DMABufferNotInDataMemory)); +//! +//! // The data is still static and located in flash. However, since we are assigning +//! // it to a variable, the compiler will load it into memory. Passing a reference to the +//! // variable will yield a pointer that references dynamic memory, thus making EasyDMA happy. +//! // This function call succeeds. +//! let data = [1, 2, 3]; +//! let result = spim.write_from_ram(&data); +//! assert!(result.is_ok()); +//! ``` +//! +//! Each peripheral struct which uses EasyDMA ([`Spim`](spim::Spim), [`Uarte`](uarte::Uarte), [`Twim`](twim::Twim)) has two variants of their mutating functions: +//! - Functions with the suffix (e.g. [`write_from_ram`](Spim::write_from_ram), [`transfer_from_ram`](Spim::transfer_from_ram)) will return an error if the passed slice does not reside in RAM. +//! - Functions without the suffix (e.g. [`write`](Spim::write), [`transfer`](Spim::transfer)) will check whether the data is in RAM and copy it into memory prior to transmission. +//! +//! Since copying incurs a overhead, you are given the option to choose from `_from_ram` variants which will +//! fail and notify you, or the more convenient versions without the suffix which are potentially a little bit +//! more inefficient. Be aware that this overhead is not only in terms of instruction count but also in terms of memory usage +//! as the methods without the suffix will be allocating a statically sized buffer (up to 512 bytes for the nRF52840). +//! +//! Note that the methods that read data like [`read`](spim::Spim::read) and [`transfer_in_place`](spim::Spim::transfer_in_place) do not have the corresponding `_from_ram` variants as +//! mutable slices always reside in RAM. + #![no_std] #![cfg_attr( feature = "nightly", diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index c9c9cb25..5d88b232 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -31,38 +31,7 @@ pub enum Error { /// Interface for the SPIM peripheral using EasyDMA to offload the transmission and reception workload. /// -/// ## Data locality requirements -/// -/// On nRF chips, EasyDMA requires the buffers to reside in RAM. However, Rust -/// slices will not always do so. Take the following example: -/// -/// ```no_run -/// // As we pass a slice to the function whose contents will not ever change, -/// // the compiler writes it into the flash and thus the pointer to it will -/// // reference static memory. Since EasyDMA requires slices to reside in RAM, -/// // this function call will fail. -/// let result = spim.write_from_ram(&[1, 2, 3]); -/// assert_eq!(result, Error::DMABufferNotInDataMemory); -/// -/// // The data is still static and located in flash. However, since we are assigning -/// // it to a variable, the compiler will load it into memory. Passing a reference to the -/// // variable will yield a pointer that references dynamic memory, thus making EasyDMA happy. -/// // This function call succeeds. -/// let data = [1, 2, 3]; -/// let result = spim.write_from_ram(&data); -/// assert!(result.is_ok()); -/// ``` -/// -/// Each function in this struct has a `_from_ram` variant and one without this suffix. -/// - Functions with the suffix (e.g. [`write_from_ram`](Spim::write_from_ram), [`transfer_from_ram`](Spim::transfer_from_ram)) will return an error if the passed slice does not reside in RAM. -/// - Functions without the suffix (e.g. [`write`](Spim::write), [`transfer`](Spim::transfer)) will check whether the data is in RAM and copy it into memory prior to transmission. -/// -/// Since copying incurs a overhead, you are given the option to choose from `_from_ram` variants which will -/// fail and notify you, or the more convenient versions without the suffix which are potentially a little bit -/// more inefficient. -/// -/// Note that the [`read`](Spim::read) and [`transfer_in_place`](Spim::transfer_in_place) methods do not have the corresponding `_from_ram` variants as -/// mutable slices always reside in RAM. +/// For more details about EasyDMA, consult the module documentation. pub struct Spim<'d, T: Instance> { phantom: PhantomData<&'d mut T>, } @@ -325,7 +294,7 @@ impl<'d, T: Instance> Spim<'d, T> { self.blocking_inner(read, write) } - /// Same as [`blocking_transfer`](Spim::blocking_transfer) but will fail instead of copying data into RAM. + /// Same as [`blocking_transfer`](Spim::blocking_transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. pub fn blocking_transfer_from_ram( &mut self, read: &mut [u8], @@ -346,7 +315,7 @@ impl<'d, T: Instance> Spim<'d, T> { self.blocking_inner(&mut [], data) } - /// Same as [`blocking_write`](Spim::blocking_write) but will fail instead of copying data into RAM. + /// Same as [`blocking_write`](Spim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. pub fn blocking_write_from_ram(&mut self, data: &[u8]) -> Result<(), Error> { self.blocking_inner(&mut [], data) } @@ -362,7 +331,7 @@ impl<'d, T: Instance> Spim<'d, T> { self.async_inner(read, write).await } - /// Same as [`transfer`](Spim::transfer) but will fail instead of copying data into RAM. + /// Same as [`transfer`](Spim::transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. pub async fn transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> { self.async_inner_from_ram(read, write).await } @@ -378,7 +347,7 @@ impl<'d, T: Instance> Spim<'d, T> { self.async_inner(&mut [], data).await } - /// Same as [`write`](Spim::write) but will fail instead of copying data into RAM. + /// Same as [`write`](Spim::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. pub async fn write_from_ram(&mut self, data: &[u8]) -> Result<(), Error> { self.async_inner_from_ram(&mut [], data).await } diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index c8ad2a0e..40705477 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -64,7 +64,9 @@ pub enum Error { Overrun, } -/// Interface to a TWIM instance. +/// Interface to a TWIM instance using EasyDMA to offload the transmission and reception workload. +/// +/// For more details about EasyDMA, consult the module documentation. pub struct Twim<'d, T: Instance> { phantom: PhantomData<&'d mut T>, } @@ -432,6 +434,7 @@ impl<'d, T: Instance> Twim<'d, T> { Ok(()) } + /// Same as [`blocking_write`](Twim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. pub fn blocking_write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { self.setup_write_from_ram(address, buffer, false)?; self.blocking_wait(); @@ -474,6 +477,7 @@ impl<'d, T: Instance> Twim<'d, T> { Ok(()) } + /// Same as [`blocking_write_read`](Twim::blocking_write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. pub fn blocking_write_read_from_ram( &mut self, address: u8, @@ -507,6 +511,7 @@ impl<'d, T: Instance> Twim<'d, T> { Ok(()) } + /// Same as [`write`](Twim::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. pub async fn write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { self.setup_write_from_ram(address, buffer, true)?; self.async_wait().await; @@ -531,6 +536,7 @@ impl<'d, T: Instance> Twim<'d, T> { Ok(()) } + /// Same as [`write_read`](Twim::write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. pub async fn write_read_from_ram( &mut self, address: u8, diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 7d7b904b..111c8341 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -60,7 +60,9 @@ pub enum Error { // TODO: add other error variants. } -/// Interface to the UARTE peripheral +/// Interface to the UARTE peripheral using EasyDMA to offload the transmission and reception workload. +/// +/// For more details about EasyDMA, consult the module documentation. pub struct Uarte<'d, T: Instance> { phantom: PhantomData<&'d mut T>, tx: UarteTx<'d, T>, @@ -224,6 +226,7 @@ impl<'d, T: Instance> Uarte<'d, T> { self.tx.write(buffer).await } + /// Same as [`write`](Uarte::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { self.tx.write_from_ram(buffer).await } @@ -236,6 +239,7 @@ impl<'d, T: Instance> Uarte<'d, T> { self.tx.blocking_write(buffer) } + /// Same as [`blocking_write`](Uarte::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { self.tx.blocking_write_from_ram(buffer) }