diff options
| author | Til Blechschmidt <[email protected]> | 2022-03-08 16:42:46 +0100 |
|---|---|---|
| committer | Til Blechschmidt <[email protected]> | 2022-03-08 17:49:15 +0100 |
| commit | 63030bf998b0787b421f30af4f75159e2cb5c99f (patch) | |
| tree | 3a585a07c805b16abc59bb0850a5ede4af6d09e8 | |
| parent | 7540b44050f42ba75ea855f904b959cc4cfdefe5 (diff) | |
Move EasyDMA documentation to module level
| -rw-r--r-- | embassy-nrf/src/lib.rs | 36 | ||||
| -rw-r--r-- | embassy-nrf/src/spim.rs | 41 | ||||
| -rw-r--r-- | embassy-nrf/src/twim.rs | 8 | ||||
| -rw-r--r-- | embassy-nrf/src/uarte.rs | 6 |
4 files changed, 53 insertions, 38 deletions
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index b448f6ab6..06e8235e3 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs | |||
| @@ -1,3 +1,39 @@ | |||
| 1 | //! ## EasyDMA considerations | ||
| 2 | //! | ||
| 3 | //! On nRF chips, peripherals can use the so called EasyDMA feature to offload the task of interacting | ||
| 4 | //! with peripherals. It takes care of sending/receiving data over a variety of bus protocols (TWI/I2C, UART, SPI). | ||
| 5 | //! However, EasyDMA requires the buffers used to transmit and receive data to reside in RAM. Unfortunately, Rust | ||
| 6 | //! slices will not always do so. The following example using the SPI peripheral shows a common situation where this might happen: | ||
| 7 | //! | ||
| 8 | //! ```no_run | ||
| 9 | //! // As we pass a slice to the function whose contents will not ever change, | ||
| 10 | //! // the compiler writes it into the flash and thus the pointer to it will | ||
| 11 | //! // reference static memory. Since EasyDMA requires slices to reside in RAM, | ||
| 12 | //! // this function call will fail. | ||
| 13 | //! let result = spim.write_from_ram(&[1, 2, 3]); | ||
| 14 | //! assert_eq!(result, Err(Error::DMABufferNotInDataMemory)); | ||
| 15 | //! | ||
| 16 | //! // The data is still static and located in flash. However, since we are assigning | ||
| 17 | //! // it to a variable, the compiler will load it into memory. Passing a reference to the | ||
| 18 | //! // variable will yield a pointer that references dynamic memory, thus making EasyDMA happy. | ||
| 19 | //! // This function call succeeds. | ||
| 20 | //! let data = [1, 2, 3]; | ||
| 21 | //! let result = spim.write_from_ram(&data); | ||
| 22 | //! assert!(result.is_ok()); | ||
| 23 | //! ``` | ||
| 24 | //! | ||
| 25 | //! Each peripheral struct which uses EasyDMA ([`Spim`](spim::Spim), [`Uarte`](uarte::Uarte), [`Twim`](twim::Twim)) has two variants of their mutating functions: | ||
| 26 | //! - 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. | ||
| 27 | //! - 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. | ||
| 28 | //! | ||
| 29 | //! Since copying incurs a overhead, you are given the option to choose from `_from_ram` variants which will | ||
| 30 | //! fail and notify you, or the more convenient versions without the suffix which are potentially a little bit | ||
| 31 | //! more inefficient. Be aware that this overhead is not only in terms of instruction count but also in terms of memory usage | ||
| 32 | //! as the methods without the suffix will be allocating a statically sized buffer (up to 512 bytes for the nRF52840). | ||
| 33 | //! | ||
| 34 | //! 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 | ||
| 35 | //! mutable slices always reside in RAM. | ||
| 36 | |||
| 1 | #![no_std] | 37 | #![no_std] |
| 2 | #![cfg_attr( | 38 | #![cfg_attr( |
| 3 | feature = "nightly", | 39 | feature = "nightly", |
diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index c9c9cb25a..5d88b2326 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs | |||
| @@ -31,38 +31,7 @@ pub enum Error { | |||
| 31 | 31 | ||
| 32 | /// Interface for the SPIM peripheral using EasyDMA to offload the transmission and reception workload. | 32 | /// Interface for the SPIM peripheral using EasyDMA to offload the transmission and reception workload. |
| 33 | /// | 33 | /// |
| 34 | /// ## Data locality requirements | 34 | /// For more details about EasyDMA, consult the module documentation. |
| 35 | /// | ||
| 36 | /// On nRF chips, EasyDMA requires the buffers to reside in RAM. However, Rust | ||
| 37 | /// slices will not always do so. Take the following example: | ||
| 38 | /// | ||
| 39 | /// ```no_run | ||
| 40 | /// // As we pass a slice to the function whose contents will not ever change, | ||
| 41 | /// // the compiler writes it into the flash and thus the pointer to it will | ||
| 42 | /// // reference static memory. Since EasyDMA requires slices to reside in RAM, | ||
| 43 | /// // this function call will fail. | ||
| 44 | /// let result = spim.write_from_ram(&[1, 2, 3]); | ||
| 45 | /// assert_eq!(result, Error::DMABufferNotInDataMemory); | ||
| 46 | /// | ||
| 47 | /// // The data is still static and located in flash. However, since we are assigning | ||
| 48 | /// // it to a variable, the compiler will load it into memory. Passing a reference to the | ||
| 49 | /// // variable will yield a pointer that references dynamic memory, thus making EasyDMA happy. | ||
| 50 | /// // This function call succeeds. | ||
| 51 | /// let data = [1, 2, 3]; | ||
| 52 | /// let result = spim.write_from_ram(&data); | ||
| 53 | /// assert!(result.is_ok()); | ||
| 54 | /// ``` | ||
| 55 | /// | ||
| 56 | /// Each function in this struct has a `_from_ram` variant and one without this suffix. | ||
| 57 | /// - 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. | ||
| 58 | /// - 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. | ||
| 59 | /// | ||
| 60 | /// Since copying incurs a overhead, you are given the option to choose from `_from_ram` variants which will | ||
| 61 | /// fail and notify you, or the more convenient versions without the suffix which are potentially a little bit | ||
| 62 | /// more inefficient. | ||
| 63 | /// | ||
| 64 | /// Note that the [`read`](Spim::read) and [`transfer_in_place`](Spim::transfer_in_place) methods do not have the corresponding `_from_ram` variants as | ||
| 65 | /// mutable slices always reside in RAM. | ||
| 66 | pub struct Spim<'d, T: Instance> { | 35 | pub struct Spim<'d, T: Instance> { |
| 67 | phantom: PhantomData<&'d mut T>, | 36 | phantom: PhantomData<&'d mut T>, |
| 68 | } | 37 | } |
| @@ -325,7 +294,7 @@ impl<'d, T: Instance> Spim<'d, T> { | |||
| 325 | self.blocking_inner(read, write) | 294 | self.blocking_inner(read, write) |
| 326 | } | 295 | } |
| 327 | 296 | ||
| 328 | /// Same as [`blocking_transfer`](Spim::blocking_transfer) but will fail instead of copying data into RAM. | 297 | /// Same as [`blocking_transfer`](Spim::blocking_transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. |
| 329 | pub fn blocking_transfer_from_ram( | 298 | pub fn blocking_transfer_from_ram( |
| 330 | &mut self, | 299 | &mut self, |
| 331 | read: &mut [u8], | 300 | read: &mut [u8], |
| @@ -346,7 +315,7 @@ impl<'d, T: Instance> Spim<'d, T> { | |||
| 346 | self.blocking_inner(&mut [], data) | 315 | self.blocking_inner(&mut [], data) |
| 347 | } | 316 | } |
| 348 | 317 | ||
| 349 | /// Same as [`blocking_write`](Spim::blocking_write) but will fail instead of copying data into RAM. | 318 | /// Same as [`blocking_write`](Spim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. |
| 350 | pub fn blocking_write_from_ram(&mut self, data: &[u8]) -> Result<(), Error> { | 319 | pub fn blocking_write_from_ram(&mut self, data: &[u8]) -> Result<(), Error> { |
| 351 | self.blocking_inner(&mut [], data) | 320 | self.blocking_inner(&mut [], data) |
| 352 | } | 321 | } |
| @@ -362,7 +331,7 @@ impl<'d, T: Instance> Spim<'d, T> { | |||
| 362 | self.async_inner(read, write).await | 331 | self.async_inner(read, write).await |
| 363 | } | 332 | } |
| 364 | 333 | ||
| 365 | /// Same as [`transfer`](Spim::transfer) but will fail instead of copying data into RAM. | 334 | /// Same as [`transfer`](Spim::transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. |
| 366 | pub async fn transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> { | 335 | pub async fn transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> { |
| 367 | self.async_inner_from_ram(read, write).await | 336 | self.async_inner_from_ram(read, write).await |
| 368 | } | 337 | } |
| @@ -378,7 +347,7 @@ impl<'d, T: Instance> Spim<'d, T> { | |||
| 378 | self.async_inner(&mut [], data).await | 347 | self.async_inner(&mut [], data).await |
| 379 | } | 348 | } |
| 380 | 349 | ||
| 381 | /// Same as [`write`](Spim::write) but will fail instead of copying data into RAM. | 350 | /// Same as [`write`](Spim::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. |
| 382 | pub async fn write_from_ram(&mut self, data: &[u8]) -> Result<(), Error> { | 351 | pub async fn write_from_ram(&mut self, data: &[u8]) -> Result<(), Error> { |
| 383 | self.async_inner_from_ram(&mut [], data).await | 352 | self.async_inner_from_ram(&mut [], data).await |
| 384 | } | 353 | } |
diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index c8ad2a0e3..40705477f 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs | |||
| @@ -64,7 +64,9 @@ pub enum Error { | |||
| 64 | Overrun, | 64 | Overrun, |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | /// Interface to a TWIM instance. | 67 | /// Interface to a TWIM instance using EasyDMA to offload the transmission and reception workload. |
| 68 | /// | ||
| 69 | /// For more details about EasyDMA, consult the module documentation. | ||
| 68 | pub struct Twim<'d, T: Instance> { | 70 | pub struct Twim<'d, T: Instance> { |
| 69 | phantom: PhantomData<&'d mut T>, | 71 | phantom: PhantomData<&'d mut T>, |
| 70 | } | 72 | } |
| @@ -432,6 +434,7 @@ impl<'d, T: Instance> Twim<'d, T> { | |||
| 432 | Ok(()) | 434 | Ok(()) |
| 433 | } | 435 | } |
| 434 | 436 | ||
| 437 | /// Same as [`blocking_write`](Twim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. | ||
| 435 | pub fn blocking_write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { | 438 | pub fn blocking_write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { |
| 436 | self.setup_write_from_ram(address, buffer, false)?; | 439 | self.setup_write_from_ram(address, buffer, false)?; |
| 437 | self.blocking_wait(); | 440 | self.blocking_wait(); |
| @@ -474,6 +477,7 @@ impl<'d, T: Instance> Twim<'d, T> { | |||
| 474 | Ok(()) | 477 | Ok(()) |
| 475 | } | 478 | } |
| 476 | 479 | ||
| 480 | /// 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. | ||
| 477 | pub fn blocking_write_read_from_ram( | 481 | pub fn blocking_write_read_from_ram( |
| 478 | &mut self, | 482 | &mut self, |
| 479 | address: u8, | 483 | address: u8, |
| @@ -507,6 +511,7 @@ impl<'d, T: Instance> Twim<'d, T> { | |||
| 507 | Ok(()) | 511 | Ok(()) |
| 508 | } | 512 | } |
| 509 | 513 | ||
| 514 | /// Same as [`write`](Twim::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. | ||
| 510 | pub async fn write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { | 515 | pub async fn write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { |
| 511 | self.setup_write_from_ram(address, buffer, true)?; | 516 | self.setup_write_from_ram(address, buffer, true)?; |
| 512 | self.async_wait().await; | 517 | self.async_wait().await; |
| @@ -531,6 +536,7 @@ impl<'d, T: Instance> Twim<'d, T> { | |||
| 531 | Ok(()) | 536 | Ok(()) |
| 532 | } | 537 | } |
| 533 | 538 | ||
| 539 | /// Same as [`write_read`](Twim::write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. | ||
| 534 | pub async fn write_read_from_ram( | 540 | pub async fn write_read_from_ram( |
| 535 | &mut self, | 541 | &mut self, |
| 536 | address: u8, | 542 | address: u8, |
diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 7d7b904b1..111c8341b 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs | |||
| @@ -60,7 +60,9 @@ pub enum Error { | |||
| 60 | // TODO: add other error variants. | 60 | // TODO: add other error variants. |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | /// Interface to the UARTE peripheral | 63 | /// Interface to the UARTE peripheral using EasyDMA to offload the transmission and reception workload. |
| 64 | /// | ||
| 65 | /// For more details about EasyDMA, consult the module documentation. | ||
| 64 | pub struct Uarte<'d, T: Instance> { | 66 | pub struct Uarte<'d, T: Instance> { |
| 65 | phantom: PhantomData<&'d mut T>, | 67 | phantom: PhantomData<&'d mut T>, |
| 66 | tx: UarteTx<'d, T>, | 68 | tx: UarteTx<'d, T>, |
| @@ -224,6 +226,7 @@ impl<'d, T: Instance> Uarte<'d, T> { | |||
| 224 | self.tx.write(buffer).await | 226 | self.tx.write(buffer).await |
| 225 | } | 227 | } |
| 226 | 228 | ||
| 229 | /// Same as [`write`](Uarte::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. | ||
| 227 | pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { | 230 | pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { |
| 228 | self.tx.write_from_ram(buffer).await | 231 | self.tx.write_from_ram(buffer).await |
| 229 | } | 232 | } |
| @@ -236,6 +239,7 @@ impl<'d, T: Instance> Uarte<'d, T> { | |||
| 236 | self.tx.blocking_write(buffer) | 239 | self.tx.blocking_write(buffer) |
| 237 | } | 240 | } |
| 238 | 241 | ||
| 242 | /// Same as [`blocking_write`](Uarte::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. | ||
| 239 | pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { | 243 | pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { |
| 240 | self.tx.blocking_write_from_ram(buffer) | 244 | self.tx.blocking_write_from_ram(buffer) |
| 241 | } | 245 | } |
