aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2022-03-09 01:47:52 +0000
committerGitHub <[email protected]>2022-03-09 01:47:52 +0000
commit13247897b06e4abce8b76b7fa9a545b859cf80b6 (patch)
tree545f2c4988c0836f9fe1dc3eb0e3846712ff7546
parent3047098c554075d97a5def7acf0d248ea4df315b (diff)
parent63030bf998b0787b421f30af4f75159e2cb5c99f (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.rs36
-rw-r--r--embassy-nrf/src/spim.rs75
-rw-r--r--embassy-nrf/src/twim.rs130
-rw-r--r--embassy-nrf/src/uarte.rs42
-rw-r--r--embassy-nrf/src/util.rs5
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;
8use embassy_hal_common::unborrow; 8use embassy_hal_common::unborrow;
9use futures::future::poll_fn; 9use futures::future::poll_fn;
10 10
11use crate::chip::FORCE_COPY_BUFFER_SIZE;
11use crate::gpio::sealed::Pin as _; 12use crate::gpio::sealed::Pin as _;
12use crate::gpio::{self, AnyPin}; 13use crate::gpio::{self, AnyPin};
13use crate::gpio::{Pin as GpioPin, PselBits}; 14use 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.
31pub struct Spim<'d, T: Instance> { 35pub 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
289impl<'d, T: Instance> Drop for Spim<'d, T> { 356impl<'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.
68pub struct Twim<'d, T: Instance> { 70pub 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
506impl<'a, T: Instance> Drop for Twim<'a, T> { 556impl<'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;
22use embassy_hal_common::unborrow; 22use embassy_hal_common::unborrow;
23use futures::future::poll_fn; 23use futures::future::poll_fn;
24 24
25use crate::chip::EASY_DMA_SIZE; 25use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
26use crate::gpio::sealed::Pin as _; 26use crate::gpio::sealed::Pin as _;
27use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; 27use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits};
28use crate::interrupt::Interrupt; 28use 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.
64pub struct Uarte<'d, T: Instance> { 66pub 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
236impl<'d, T: Instance> UarteTx<'d, T> { 248impl<'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"))]
24pub(crate) fn slice_in_ram_or<T, E>(slice: *const [T], err: E) -> Result<(), E> { 24pub(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)