diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2022-03-09 01:47:52 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-03-09 01:47:52 +0000 |
| commit | 13247897b06e4abce8b76b7fa9a545b859cf80b6 (patch) | |
| tree | 545f2c4988c0836f9fe1dc3eb0e3846712ff7546 | |
| parent | 3047098c554075d97a5def7acf0d248ea4df315b (diff) | |
| parent | 63030bf998b0787b421f30af4f75159e2cb5c99f (diff) | |
Merge #640
640: Skip EasyDMA slice location check for empty slices and copy data if necessary r=Dirbaio a=TilBlechschmidt
As discussed, this PR makes the following changes:
- Ignore pointer location of zero-length slices (fixes #631)
- Change default functions so they copy the tx buffer if it does not reside in RAM
- Introduce new variants for `write`, `transfer`, and their blocking versions which fails instead of copying
- Add documentation about the motivation behind all these variants
<img width="984" alt="image" src="https://user-images.githubusercontent.com/5037967/155415788-c2cd1055-9289-4004-959d-be3b1934a439.png">
Remaining TODOs:
- [x] Change copying behaviour for other peripherals
- [x] TWI
- [x] UART
- [x] Add module-level documentation regarding EasyDMA and `_from_ram` method variants
`@Dirbaio` it probably makes sense for you to review it now before I "copy" over the changes to the other two peripherals.
Co-authored-by: Til Blechschmidt <[email protected]>
| -rw-r--r-- | embassy-nrf/src/lib.rs | 36 | ||||
| -rw-r--r-- | embassy-nrf/src/spim.rs | 75 | ||||
| -rw-r--r-- | embassy-nrf/src/twim.rs | 130 | ||||
| -rw-r--r-- | embassy-nrf/src/uarte.rs | 42 | ||||
| -rw-r--r-- | embassy-nrf/src/util.rs | 5 |
5 files changed, 238 insertions, 50 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 6dbf67e3b..5d88b2326 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs | |||
| @@ -8,6 +8,7 @@ use embassy::util::Unborrow; | |||
| 8 | use embassy_hal_common::unborrow; | 8 | use embassy_hal_common::unborrow; |
| 9 | use futures::future::poll_fn; | 9 | use futures::future::poll_fn; |
| 10 | 10 | ||
| 11 | use crate::chip::FORCE_COPY_BUFFER_SIZE; | ||
| 11 | use crate::gpio::sealed::Pin as _; | 12 | use crate::gpio::sealed::Pin as _; |
| 12 | use crate::gpio::{self, AnyPin}; | 13 | use crate::gpio::{self, AnyPin}; |
| 13 | use crate::gpio::{Pin as GpioPin, PselBits}; | 14 | use crate::gpio::{Pin as GpioPin, PselBits}; |
| @@ -28,6 +29,9 @@ pub enum Error { | |||
| 28 | DMABufferNotInDataMemory, | 29 | DMABufferNotInDataMemory, |
| 29 | } | 30 | } |
| 30 | 31 | ||
| 32 | /// Interface for the SPIM peripheral using EasyDMA to offload the transmission and reception workload. | ||
| 33 | /// | ||
| 34 | /// For more details about EasyDMA, consult the module documentation. | ||
| 31 | pub struct Spim<'d, T: Instance> { | 35 | pub struct Spim<'d, T: Instance> { |
| 32 | phantom: PhantomData<&'d mut T>, | 36 | phantom: PhantomData<&'d mut T>, |
| 33 | } | 37 | } |
| @@ -223,7 +227,7 @@ impl<'d, T: Instance> Spim<'d, T> { | |||
| 223 | Ok(()) | 227 | Ok(()) |
| 224 | } | 228 | } |
| 225 | 229 | ||
| 226 | fn blocking_inner(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { | 230 | fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { |
| 227 | self.prepare(rx, tx)?; | 231 | self.prepare(rx, tx)?; |
| 228 | 232 | ||
| 229 | // Wait for 'end' event. | 233 | // Wait for 'end' event. |
| @@ -234,7 +238,20 @@ impl<'d, T: Instance> Spim<'d, T> { | |||
| 234 | Ok(()) | 238 | Ok(()) |
| 235 | } | 239 | } |
| 236 | 240 | ||
| 237 | async fn async_inner(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { | 241 | fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> { |
| 242 | match self.blocking_inner_from_ram(rx, tx) { | ||
| 243 | Ok(_) => Ok(()), | ||
| 244 | Err(Error::DMABufferNotInDataMemory) => { | ||
| 245 | trace!("Copying SPIM tx buffer into RAM for DMA"); | ||
| 246 | let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()]; | ||
| 247 | tx_ram_buf.copy_from_slice(tx); | ||
| 248 | self.blocking_inner_from_ram(rx, tx_ram_buf) | ||
| 249 | } | ||
| 250 | Err(error) => Err(error), | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { | ||
| 238 | self.prepare(rx, tx)?; | 255 | self.prepare(rx, tx)?; |
| 239 | 256 | ||
| 240 | // Wait for 'end' event. | 257 | // Wait for 'end' event. |
| @@ -253,37 +270,87 @@ impl<'d, T: Instance> Spim<'d, T> { | |||
| 253 | Ok(()) | 270 | Ok(()) |
| 254 | } | 271 | } |
| 255 | 272 | ||
| 273 | async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> { | ||
| 274 | match self.async_inner_from_ram(rx, tx).await { | ||
| 275 | Ok(_) => Ok(()), | ||
| 276 | Err(Error::DMABufferNotInDataMemory) => { | ||
| 277 | trace!("Copying SPIM tx buffer into RAM for DMA"); | ||
| 278 | let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()]; | ||
| 279 | tx_ram_buf.copy_from_slice(tx); | ||
| 280 | self.async_inner_from_ram(rx, tx_ram_buf).await | ||
| 281 | } | ||
| 282 | Err(error) => Err(error), | ||
| 283 | } | ||
| 284 | } | ||
| 285 | |||
| 286 | /// Reads data from the SPI bus without sending anything. Blocks until the buffer has been filled. | ||
| 256 | pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<(), Error> { | 287 | pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<(), Error> { |
| 257 | self.blocking_inner(data, &[]) | 288 | self.blocking_inner(data, &[]) |
| 258 | } | 289 | } |
| 259 | 290 | ||
| 291 | /// Simultaneously sends and receives data. Blocks until the transmission is completed. | ||
| 292 | /// If necessary, the write buffer will be copied into RAM (see struct description for detail). | ||
| 260 | pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> { | 293 | pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> { |
| 261 | self.blocking_inner(read, write) | 294 | self.blocking_inner(read, write) |
| 262 | } | 295 | } |
| 263 | 296 | ||
| 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. | ||
| 298 | pub fn blocking_transfer_from_ram( | ||
| 299 | &mut self, | ||
| 300 | read: &mut [u8], | ||
| 301 | write: &[u8], | ||
| 302 | ) -> Result<(), Error> { | ||
| 303 | self.blocking_inner(read, write) | ||
| 304 | } | ||
| 305 | |||
| 306 | /// Simultaneously sends and receives data. | ||
| 307 | /// Places the received data into the same buffer and blocks until the transmission is completed. | ||
| 264 | pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> { | 308 | pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> { |
| 265 | self.blocking_inner(data, data) | 309 | self.blocking_inner_from_ram(data, data) |
| 266 | } | 310 | } |
| 267 | 311 | ||
| 312 | /// Sends data, discarding any received data. Blocks until the transmission is completed. | ||
| 313 | /// If necessary, the write buffer will be copied into RAM (see struct description for detail). | ||
| 268 | pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> { | 314 | pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> { |
| 269 | self.blocking_inner(&mut [], data) | 315 | self.blocking_inner(&mut [], data) |
| 270 | } | 316 | } |
| 271 | 317 | ||
| 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. | ||
| 319 | pub fn blocking_write_from_ram(&mut self, data: &[u8]) -> Result<(), Error> { | ||
| 320 | self.blocking_inner(&mut [], data) | ||
| 321 | } | ||
| 322 | |||
| 323 | /// Reads data from the SPI bus without sending anything. | ||
| 272 | pub async fn read(&mut self, data: &mut [u8]) -> Result<(), Error> { | 324 | pub async fn read(&mut self, data: &mut [u8]) -> Result<(), Error> { |
| 273 | self.async_inner(data, &[]).await | 325 | self.async_inner(data, &[]).await |
| 274 | } | 326 | } |
| 275 | 327 | ||
| 328 | /// Simultaneously sends and receives data. | ||
| 329 | /// If necessary, the write buffer will be copied into RAM (see struct description for detail). | ||
| 276 | pub async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> { | 330 | pub async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> { |
| 277 | self.async_inner(read, write).await | 331 | self.async_inner(read, write).await |
| 278 | } | 332 | } |
| 279 | 333 | ||
| 334 | /// Same as [`transfer`](Spim::transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. | ||
| 335 | pub async fn transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> { | ||
| 336 | self.async_inner_from_ram(read, write).await | ||
| 337 | } | ||
| 338 | |||
| 339 | /// Simultaneously sends and receives data. Places the received data into the same buffer. | ||
| 280 | pub async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> { | 340 | pub async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> { |
| 281 | self.async_inner(data, data).await | 341 | self.async_inner_from_ram(data, data).await |
| 282 | } | 342 | } |
| 283 | 343 | ||
| 344 | /// Sends data, discarding any received data. | ||
| 345 | /// If necessary, the write buffer will be copied into RAM (see struct description for detail). | ||
| 284 | pub async fn write(&mut self, data: &[u8]) -> Result<(), Error> { | 346 | pub async fn write(&mut self, data: &[u8]) -> Result<(), Error> { |
| 285 | self.async_inner(&mut [], data).await | 347 | self.async_inner(&mut [], data).await |
| 286 | } | 348 | } |
| 349 | |||
| 350 | /// Same as [`write`](Spim::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. | ||
| 351 | pub async fn write_from_ram(&mut self, data: &[u8]) -> Result<(), Error> { | ||
| 352 | self.async_inner_from_ram(&mut [], data).await | ||
| 353 | } | ||
| 287 | } | 354 | } |
| 288 | 355 | ||
| 289 | impl<'d, T: Instance> Drop for Spim<'d, T> { | 356 | impl<'d, T: Instance> Drop for Spim<'d, T> { |
diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index ed2844f79..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 | } |
| @@ -287,7 +289,12 @@ impl<'d, T: Instance> Twim<'d, T> { | |||
| 287 | }) | 289 | }) |
| 288 | } | 290 | } |
| 289 | 291 | ||
| 290 | fn setup_write(&mut self, address: u8, buffer: &[u8], inten: bool) -> Result<(), Error> { | 292 | fn setup_write_from_ram( |
| 293 | &mut self, | ||
| 294 | address: u8, | ||
| 295 | buffer: &[u8], | ||
| 296 | inten: bool, | ||
| 297 | ) -> Result<(), Error> { | ||
| 291 | let r = T::regs(); | 298 | let r = T::regs(); |
| 292 | 299 | ||
| 293 | compiler_fence(SeqCst); | 300 | compiler_fence(SeqCst); |
| @@ -342,7 +349,7 @@ impl<'d, T: Instance> Twim<'d, T> { | |||
| 342 | Ok(()) | 349 | Ok(()) |
| 343 | } | 350 | } |
| 344 | 351 | ||
| 345 | fn setup_write_read( | 352 | fn setup_write_read_from_ram( |
| 346 | &mut self, | 353 | &mut self, |
| 347 | address: u8, | 354 | address: u8, |
| 348 | wr_buffer: &[u8], | 355 | wr_buffer: &[u8], |
| @@ -382,6 +389,38 @@ impl<'d, T: Instance> Twim<'d, T> { | |||
| 382 | Ok(()) | 389 | Ok(()) |
| 383 | } | 390 | } |
| 384 | 391 | ||
| 392 | fn setup_write_read( | ||
| 393 | &mut self, | ||
| 394 | address: u8, | ||
| 395 | wr_buffer: &[u8], | ||
| 396 | rd_buffer: &mut [u8], | ||
| 397 | inten: bool, | ||
| 398 | ) -> Result<(), Error> { | ||
| 399 | match self.setup_write_read_from_ram(address, wr_buffer, rd_buffer, inten) { | ||
| 400 | Ok(_) => Ok(()), | ||
| 401 | Err(Error::DMABufferNotInDataMemory) => { | ||
| 402 | trace!("Copying TWIM tx buffer into RAM for DMA"); | ||
| 403 | let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; | ||
| 404 | tx_ram_buf.copy_from_slice(wr_buffer); | ||
| 405 | self.setup_write_read_from_ram(address, &tx_ram_buf, rd_buffer, inten) | ||
| 406 | } | ||
| 407 | Err(error) => Err(error), | ||
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | fn setup_write(&mut self, address: u8, wr_buffer: &[u8], inten: bool) -> Result<(), Error> { | ||
| 412 | match self.setup_write_from_ram(address, wr_buffer, inten) { | ||
| 413 | Ok(_) => Ok(()), | ||
| 414 | Err(Error::DMABufferNotInDataMemory) => { | ||
| 415 | trace!("Copying TWIM tx buffer into RAM for DMA"); | ||
| 416 | let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; | ||
| 417 | tx_ram_buf.copy_from_slice(wr_buffer); | ||
| 418 | self.setup_write_from_ram(address, &tx_ram_buf, inten) | ||
| 419 | } | ||
| 420 | Err(error) => Err(error), | ||
| 421 | } | ||
| 422 | } | ||
| 423 | |||
| 385 | /// Write to an I2C slave. | 424 | /// Write to an I2C slave. |
| 386 | /// | 425 | /// |
| 387 | /// The buffer must have a length of at most 255 bytes on the nRF52832 | 426 | /// The buffer must have a length of at most 255 bytes on the nRF52832 |
| @@ -395,6 +434,16 @@ impl<'d, T: Instance> Twim<'d, T> { | |||
| 395 | Ok(()) | 434 | Ok(()) |
| 396 | } | 435 | } |
| 397 | 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. | ||
| 438 | pub fn blocking_write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { | ||
| 439 | self.setup_write_from_ram(address, buffer, false)?; | ||
| 440 | self.blocking_wait(); | ||
| 441 | compiler_fence(SeqCst); | ||
| 442 | self.check_errorsrc()?; | ||
| 443 | self.check_tx(buffer.len())?; | ||
| 444 | Ok(()) | ||
| 445 | } | ||
| 446 | |||
| 398 | /// Read from an I2C slave. | 447 | /// Read from an I2C slave. |
| 399 | /// | 448 | /// |
| 400 | /// The buffer must have a length of at most 255 bytes on the nRF52832 | 449 | /// The buffer must have a length of at most 255 bytes on the nRF52832 |
| @@ -428,45 +477,20 @@ impl<'d, T: Instance> Twim<'d, T> { | |||
| 428 | Ok(()) | 477 | Ok(()) |
| 429 | } | 478 | } |
| 430 | 479 | ||
| 431 | /// Copy data into RAM and write to an I2C slave. | 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. |
| 432 | /// | 481 | pub fn blocking_write_read_from_ram( |
| 433 | /// The write buffer must have a length of at most 255 bytes on the nRF52832 | ||
| 434 | /// and at most 1024 bytes on the nRF52840. | ||
| 435 | pub fn blocking_copy_write(&mut self, address: u8, wr_buffer: &[u8]) -> Result<(), Error> { | ||
| 436 | if wr_buffer.len() > FORCE_COPY_BUFFER_SIZE { | ||
| 437 | return Err(Error::TxBufferTooLong); | ||
| 438 | } | ||
| 439 | |||
| 440 | // Copy to RAM | ||
| 441 | let wr_ram_buffer = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; | ||
| 442 | wr_ram_buffer.copy_from_slice(wr_buffer); | ||
| 443 | |||
| 444 | self.blocking_write(address, wr_ram_buffer) | ||
| 445 | } | ||
| 446 | |||
| 447 | /// Copy data into RAM and write to an I2C slave, then read data from the slave without | ||
| 448 | /// triggering a stop condition between the two. | ||
| 449 | /// | ||
| 450 | /// The write buffer must have a length of at most 255 bytes on the nRF52832 | ||
| 451 | /// and at most 1024 bytes on the nRF52840. | ||
| 452 | /// | ||
| 453 | /// The read buffer must have a length of at most 255 bytes on the nRF52832 | ||
| 454 | /// and at most 65535 bytes on the nRF52840. | ||
| 455 | pub fn blocking_copy_write_read( | ||
| 456 | &mut self, | 482 | &mut self, |
| 457 | address: u8, | 483 | address: u8, |
| 458 | wr_buffer: &[u8], | 484 | wr_buffer: &[u8], |
| 459 | rd_buffer: &mut [u8], | 485 | rd_buffer: &mut [u8], |
| 460 | ) -> Result<(), Error> { | 486 | ) -> Result<(), Error> { |
| 461 | if wr_buffer.len() > FORCE_COPY_BUFFER_SIZE { | 487 | self.setup_write_read_from_ram(address, wr_buffer, rd_buffer, false)?; |
| 462 | return Err(Error::TxBufferTooLong); | 488 | self.blocking_wait(); |
| 463 | } | 489 | compiler_fence(SeqCst); |
| 464 | 490 | self.check_errorsrc()?; | |
| 465 | // Copy to RAM | 491 | self.check_tx(wr_buffer.len())?; |
| 466 | let wr_ram_buffer = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; | 492 | self.check_rx(rd_buffer.len())?; |
| 467 | wr_ram_buffer.copy_from_slice(wr_buffer); | 493 | Ok(()) |
| 468 | |||
| 469 | self.blocking_write_read(address, wr_ram_buffer, rd_buffer) | ||
| 470 | } | 494 | } |
| 471 | 495 | ||
| 472 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { | 496 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { |
| @@ -487,6 +511,16 @@ impl<'d, T: Instance> Twim<'d, T> { | |||
| 487 | Ok(()) | 511 | Ok(()) |
| 488 | } | 512 | } |
| 489 | 513 | ||
| 514 | /// Same as [`write`](Twim::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. | ||
| 515 | pub async fn write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { | ||
| 516 | self.setup_write_from_ram(address, buffer, true)?; | ||
| 517 | self.async_wait().await; | ||
| 518 | compiler_fence(SeqCst); | ||
| 519 | self.check_errorsrc()?; | ||
| 520 | self.check_tx(buffer.len())?; | ||
| 521 | Ok(()) | ||
| 522 | } | ||
| 523 | |||
| 490 | pub async fn write_read( | 524 | pub async fn write_read( |
| 491 | &mut self, | 525 | &mut self, |
| 492 | address: u8, | 526 | address: u8, |
| @@ -501,6 +535,22 @@ impl<'d, T: Instance> Twim<'d, T> { | |||
| 501 | self.check_rx(rd_buffer.len())?; | 535 | self.check_rx(rd_buffer.len())?; |
| 502 | Ok(()) | 536 | Ok(()) |
| 503 | } | 537 | } |
| 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. | ||
| 540 | pub async fn write_read_from_ram( | ||
| 541 | &mut self, | ||
| 542 | address: u8, | ||
| 543 | wr_buffer: &[u8], | ||
| 544 | rd_buffer: &mut [u8], | ||
| 545 | ) -> Result<(), Error> { | ||
| 546 | self.setup_write_read_from_ram(address, wr_buffer, rd_buffer, true)?; | ||
| 547 | self.async_wait().await; | ||
| 548 | compiler_fence(SeqCst); | ||
| 549 | self.check_errorsrc()?; | ||
| 550 | self.check_tx(wr_buffer.len())?; | ||
| 551 | self.check_rx(rd_buffer.len())?; | ||
| 552 | Ok(()) | ||
| 553 | } | ||
| 504 | } | 554 | } |
| 505 | 555 | ||
| 506 | impl<'a, T: Instance> Drop for Twim<'a, T> { | 556 | impl<'a, T: Instance> Drop for Twim<'a, T> { |
| @@ -601,11 +651,7 @@ mod eh02 { | |||
| 601 | bytes: &'w [u8], | 651 | bytes: &'w [u8], |
| 602 | buffer: &'w mut [u8], | 652 | buffer: &'w mut [u8], |
| 603 | ) -> Result<(), Error> { | 653 | ) -> Result<(), Error> { |
| 604 | if slice_in_ram(bytes) { | 654 | self.blocking_write_read(addr, bytes, buffer) |
| 605 | self.blocking_write_read(addr, bytes, buffer) | ||
| 606 | } else { | ||
| 607 | self.blocking_copy_write_read(addr, bytes, buffer) | ||
| 608 | } | ||
| 609 | } | 655 | } |
| 610 | } | 656 | } |
| 611 | } | 657 | } |
diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 119a17981..111c8341b 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs | |||
| @@ -22,7 +22,7 @@ use embassy_hal_common::drop::OnDrop; | |||
| 22 | use embassy_hal_common::unborrow; | 22 | use embassy_hal_common::unborrow; |
| 23 | use futures::future::poll_fn; | 23 | use futures::future::poll_fn; |
| 24 | 24 | ||
| 25 | use crate::chip::EASY_DMA_SIZE; | 25 | use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; |
| 26 | use crate::gpio::sealed::Pin as _; | 26 | use crate::gpio::sealed::Pin as _; |
| 27 | use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; | 27 | use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; |
| 28 | use crate::interrupt::Interrupt; | 28 | use crate::interrupt::Interrupt; |
| @@ -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,11 @@ 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. | ||
| 230 | pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { | ||
| 231 | self.tx.write_from_ram(buffer).await | ||
| 232 | } | ||
| 233 | |||
| 227 | pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { | 234 | pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { |
| 228 | self.rx.blocking_read(buffer) | 235 | self.rx.blocking_read(buffer) |
| 229 | } | 236 | } |
| @@ -231,10 +238,28 @@ impl<'d, T: Instance> Uarte<'d, T> { | |||
| 231 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { | 238 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { |
| 232 | self.tx.blocking_write(buffer) | 239 | self.tx.blocking_write(buffer) |
| 233 | } | 240 | } |
| 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. | ||
| 243 | pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { | ||
| 244 | self.tx.blocking_write_from_ram(buffer) | ||
| 245 | } | ||
| 234 | } | 246 | } |
| 235 | 247 | ||
| 236 | impl<'d, T: Instance> UarteTx<'d, T> { | 248 | impl<'d, T: Instance> UarteTx<'d, T> { |
| 237 | pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { | 249 | pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { |
| 250 | match self.write_from_ram(buffer).await { | ||
| 251 | Ok(_) => Ok(()), | ||
| 252 | Err(Error::DMABufferNotInDataMemory) => { | ||
| 253 | trace!("Copying UARTE tx buffer into RAM for DMA"); | ||
| 254 | let ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..buffer.len()]; | ||
| 255 | ram_buf.copy_from_slice(buffer); | ||
| 256 | self.write_from_ram(&ram_buf).await | ||
| 257 | } | ||
| 258 | Err(error) => Err(error), | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { | ||
| 238 | slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; | 263 | slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; |
| 239 | if buffer.len() == 0 { | 264 | if buffer.len() == 0 { |
| 240 | return Err(Error::BufferZeroLength); | 265 | return Err(Error::BufferZeroLength); |
| @@ -289,6 +314,19 @@ impl<'d, T: Instance> UarteTx<'d, T> { | |||
| 289 | } | 314 | } |
| 290 | 315 | ||
| 291 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { | 316 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { |
| 317 | match self.blocking_write_from_ram(buffer) { | ||
| 318 | Ok(_) => Ok(()), | ||
| 319 | Err(Error::DMABufferNotInDataMemory) => { | ||
| 320 | trace!("Copying UARTE tx buffer into RAM for DMA"); | ||
| 321 | let ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..buffer.len()]; | ||
| 322 | ram_buf.copy_from_slice(buffer); | ||
| 323 | self.blocking_write_from_ram(&ram_buf) | ||
| 324 | } | ||
| 325 | Err(error) => Err(error), | ||
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 329 | pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { | ||
| 292 | slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; | 330 | slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; |
| 293 | if buffer.len() == 0 { | 331 | if buffer.len() == 0 { |
| 294 | return Err(Error::BufferZeroLength); | 332 | return Err(Error::BufferZeroLength); |
diff --git a/embassy-nrf/src/util.rs b/embassy-nrf/src/util.rs index b24bc452f..cd0f59490 100644 --- a/embassy-nrf/src/util.rs +++ b/embassy-nrf/src/util.rs | |||
| @@ -19,10 +19,11 @@ pub(crate) fn slice_in_ram<T>(slice: *const [T]) -> bool { | |||
| 19 | ptr >= SRAM_LOWER && (ptr + len * core::mem::size_of::<T>()) < SRAM_UPPER | 19 | ptr >= SRAM_LOWER && (ptr + len * core::mem::size_of::<T>()) < SRAM_UPPER |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | /// Return an error if slice is not in RAM. | 22 | /// Return an error if slice is not in RAM. Skips check if slice is zero-length. |
| 23 | #[cfg(not(feature = "nrf51"))] | 23 | #[cfg(not(feature = "nrf51"))] |
| 24 | pub(crate) fn slice_in_ram_or<T, E>(slice: *const [T], err: E) -> Result<(), E> { | 24 | pub(crate) fn slice_in_ram_or<T, E>(slice: *const [T], err: E) -> Result<(), E> { |
| 25 | if slice_in_ram(slice) { | 25 | let (_, len) = slice_ptr_parts(slice); |
| 26 | if len == 0 || slice_in_ram(slice) { | ||
| 26 | Ok(()) | 27 | Ok(()) |
| 27 | } else { | 28 | } else { |
| 28 | Err(err) | 29 | Err(err) |
