From 19e332c6cf47a2749bc9d3feba48ce5d60d4a920 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 6 Jan 2021 13:48:26 +0100 Subject: [PATCH 01/19] Add embedded_hal::serial implementation for uarte This adds a sub-module named "serial" that splits TX and RX parts of UART into separate types that implements the embedded_hal::serial interfaces. This allows using the nRF UART for drivers that rely on the embedded_hal::serial traits. The buffer management is primitive in order to support the semantics of the more restrictive embedded_hal traits. --- nrf-hal-common/src/uarte.rs | 235 +++++++++++++++++++++++++++++++++++- 1 file changed, 232 insertions(+), 3 deletions(-) diff --git a/nrf-hal-common/src/uarte.rs b/nrf-hal-common/src/uarte.rs index 7390e31e..62fd600a 100644 --- a/nrf-hal-common/src/uarte.rs +++ b/nrf-hal-common/src/uarte.rs @@ -370,6 +370,13 @@ where }, ) } + + // Split into implementations of embedded_hal::serial traits + pub fn split(self) -> (UarteTx, UarteRx) { + let tx = UarteTx::new(); + let rx = UarteRx::new(); + (tx, rx) + } } impl fmt::Write for Uarte @@ -406,18 +413,240 @@ pub enum Error { BufferNotInRAM, } -pub trait Instance: Deref + sealed::Sealed {} +pub trait Instance: Deref + sealed::Sealed { + fn ptr() -> *const uarte0::RegisterBlock; +} mod sealed { pub trait Sealed {} } impl sealed::Sealed for UARTE0 {} -impl Instance for UARTE0 {} +impl Instance for UARTE0 { + fn ptr() -> *const uarte0::RegisterBlock { + UARTE0::ptr() + } +} #[cfg(any(feature = "52833", feature = "52840", feature = "9160"))] mod _uarte1 { use super::*; impl sealed::Sealed for UARTE1 {} - impl Instance for UARTE1 {} + impl Instance for UARTE1 { + fn ptr() -> *const uarte0::RegisterBlock { + UARTE1::ptr() + } + } +} + +/// Interface for the TX part of a UART instance that can be used independently of the RX part. +pub struct UarteTx { + _marker: core::marker::PhantomData, + tx_buf: [u8; 1], +} + +/// Interface for the RX part of a UART instance that can be used independently of the TX part. +pub struct UarteRx { + _marker: core::marker::PhantomData, + rx_buf: [u8; 1], +} + +impl UarteTx +where + T: Instance, +{ + fn new() -> UarteTx { + let tx = UarteTx { + _marker: core::marker::PhantomData, + tx_buf: [0; 1], + }; + tx + } +} + +impl UarteRx +where + T: Instance, +{ + fn new() -> UarteRx { + let rx = UarteRx { + _marker: core::marker::PhantomData, + rx_buf: [0; 1], + }; + rx + } +} + +pub mod serial { + + ///! Implementation of the embedded_hal::serial::* traits for UartTx and UartRx. + use super::*; + use embedded_hal::serial; + use nb; + + impl serial::Write for UarteTx + where + T: Instance, + { + type Error = Error; + + /// Write a single byte non-blocking. Returns nb::Error::WouldBlock if not yet done. + fn write(&mut self, b: u8) -> nb::Result<(), Self::Error> { + let uarte = unsafe { &*T::ptr() }; + + // If txstarted is set, we are in the process of transmitting. + let in_progress = uarte.events_txstarted.read().bits() == 1; + + if in_progress { + self.flush() + } else { + // Start a new transmission, copy value into transmit buffer. + + let tx_buffer = &mut self.tx_buf; + tx_buffer[0] = b; + + // 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(SeqCst); + + // Reset the events. + uarte.events_endtx.reset(); + uarte.events_txstopped.reset(); + + // Set up the DMA write. + // We're giving the register a pointer to the tx buffer. + // + // The PTR field is a full 32 bits wide and accepts the full range + // of values. + uarte + .txd + .ptr + .write(|w| unsafe { w.ptr().bits(tx_buffer.as_ptr() as u32) }); + + // We're giving it the length of the buffer, so no danger of + // accessing invalid memory. We have verified that the length of the + // buffer fits in an `u8`, so the cast to `u8` is also fine. + // + // The MAXCNT field is 8 bits wide and accepts the full range of + // values. + uarte + .txd + .maxcnt + .write(|w| unsafe { w.maxcnt().bits(tx_buffer.len() as _) }); + + // Start UARTE Transmit transaction. + // `1` is a valid value to write to task registers. + uarte.tasks_starttx.write(|w| unsafe { w.bits(1) }); + Err(nb::Error::WouldBlock) + } + } + + /// Flush the TX buffer non-blocking. Returns nb::Error::WouldBlock if not yet flushed. + fn flush(&mut self) -> nb::Result<(), Self::Error> { + let uarte = unsafe { &*T::ptr() }; + + let in_progress = uarte.events_txstarted.read().bits() == 1; + let endtx = uarte.events_endtx.read().bits() != 0; + let txstopped = uarte.events_txstopped.read().bits() != 0; + if in_progress { + if endtx || txstopped { + // We are done, cleanup the state. + uarte.events_txstarted.reset(); + // 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(SeqCst); + + if txstopped { + return Err(nb::Error::Other(Error::Transmit)); + } + + // Lower power consumption by disabling the transmitter once we're + // finished. + // `1` is a valid value to write to task registers. + uarte.tasks_stoptx.write(|w| unsafe { w.bits(1) }); + Ok(()) + } else { + // Still not done, don't block. + Err(nb::Error::WouldBlock) + } + } else { + Ok(()) + } + } + } + + impl core::fmt::Write for UarteTx + where + T: Instance, + { + fn write_str(&mut self, s: &str) -> fmt::Result { + s.as_bytes() + .iter() + .try_for_each(|c| nb::block!(self.write(*c))) + .map_err(|_| core::fmt::Error) + } + } + + impl serial::Read for UarteRx + where + T: Instance, + { + type Error = Error; + fn read(&mut self) -> nb::Result { + let uarte = unsafe { &*T::ptr() }; + + compiler_fence(SeqCst); + + let in_progress = uarte.events_rxstarted.read().bits() == 1; + if in_progress && uarte.events_endrx.read().bits() == 0 { + return Err(nb::Error::WouldBlock); + } + + if in_progress { + let b = self.rx_buf[0]; + uarte.events_rxstarted.write(|w| w); + uarte.events_endrx.write(|w| w); + + compiler_fence(SeqCst); + if uarte.rxd.amount.read().bits() != 1 as u32 { + return Err(nb::Error::Other(Error::Receive)); + } + Ok(b) + } else { + let rx_buf = &mut self.rx_buf; + + // We're giving the register a pointer to the rx buffer. + // + // The PTR field is a full 32 bits wide and accepts the full range + // of values. + uarte + .rxd + .ptr + .write(|w| unsafe { w.ptr().bits(rx_buf.as_ptr() as u32) }); + + // We're giving it the length of the buffer, so no danger of + // accessing invalid memory. + // + // The MAXCNT field is at least 8 bits wide and accepts the full + // range of values. + uarte + .rxd + .maxcnt + .write(|w| unsafe { w.maxcnt().bits(rx_buf.len() as _) }); + + // Start UARTE Receive transaction. + // `1` is a valid value to write to task registers. + uarte.tasks_startrx.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(SeqCst); + + Err(nb::Error::WouldBlock) + } + } + } } From 4ed85a575c99c20ed5e32d13845ba58526388eee Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 7 Jan 2021 08:20:39 +0100 Subject: [PATCH 02/19] Pass TX and RX buffers to split() to ensure move of UartTx and UartRx does not affect memory location for DMA. --- nrf-hal-common/src/uarte.rs | 91 +++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/nrf-hal-common/src/uarte.rs b/nrf-hal-common/src/uarte.rs index 62fd600a..e161e7c2 100644 --- a/nrf-hal-common/src/uarte.rs +++ b/nrf-hal-common/src/uarte.rs @@ -371,11 +371,16 @@ where ) } - // Split into implementations of embedded_hal::serial traits - pub fn split(self) -> (UarteTx, UarteRx) { - let tx = UarteTx::new(); - let rx = UarteRx::new(); - (tx, rx) + // Split into implementations of embedded_hal::serial traits. The buffers passed here must outlive any DMA transfers + // that are initiated by the UartTx and UartRx. + pub fn split<'a>( + self, + tx_buf: &'a mut [u8], + rx_buf: &'a mut [u8], + ) -> Result<(UarteTx<'a, T>, UarteRx<'a, T>), Error> { + let tx = UarteTx::new(tx_buf)?; + let rx = UarteRx::new(rx_buf)?; + Ok((tx, rx)) } } @@ -405,6 +410,8 @@ pub struct Pins { #[derive(Debug)] pub enum Error { + TxBufferTooSmall, + RxBufferTooSmall, TxBufferTooLong, RxBufferTooLong, Transmit, @@ -440,40 +447,46 @@ mod _uarte1 { } /// Interface for the TX part of a UART instance that can be used independently of the RX part. -pub struct UarteTx { +pub struct UarteTx<'a, T> { _marker: core::marker::PhantomData, - tx_buf: [u8; 1], + tx_buf: &'a mut [u8], } /// Interface for the RX part of a UART instance that can be used independently of the TX part. -pub struct UarteRx { +pub struct UarteRx<'a, T> { _marker: core::marker::PhantomData, - rx_buf: [u8; 1], + rx_buf: &'a mut [u8], } -impl UarteTx +impl<'a, T> UarteTx<'a, T> where T: Instance, { - fn new() -> UarteTx { - let tx = UarteTx { - _marker: core::marker::PhantomData, - tx_buf: [0; 1], - }; - tx + fn new(tx_buf: &'a mut [u8]) -> Result, Error> { + if tx_buf.len() > 0 { + Ok(UarteTx { + _marker: core::marker::PhantomData, + tx_buf, + }) + } else { + Err(Error::TxBufferTooSmall) + } } } -impl UarteRx +impl<'a, T> UarteRx<'a, T> where T: Instance, { - fn new() -> UarteRx { - let rx = UarteRx { - _marker: core::marker::PhantomData, - rx_buf: [0; 1], - }; - rx + fn new(rx_buf: &'a mut [u8]) -> Result, Error> { + if rx_buf.len() > 0 { + Ok(UarteRx { + _marker: core::marker::PhantomData, + rx_buf, + }) + } else { + Err(Error::RxBufferTooSmall) + } } } @@ -484,7 +497,7 @@ pub mod serial { use embedded_hal::serial; use nb; - impl serial::Write for UarteTx + impl<'a, T> serial::Write for UarteTx<'a, T> where T: Instance, { @@ -502,8 +515,7 @@ pub mod serial { } else { // Start a new transmission, copy value into transmit buffer. - let tx_buffer = &mut self.tx_buf; - tx_buffer[0] = b; + self.tx_buf[0] = b; // Conservative compiler fence to prevent optimizations that do not // take in to account actions by DMA. The fence has been placed here, @@ -522,18 +534,13 @@ pub mod serial { uarte .txd .ptr - .write(|w| unsafe { w.ptr().bits(tx_buffer.as_ptr() as u32) }); + .write(|w| unsafe { w.ptr().bits(self.tx_buf.as_ptr() as u32) }); - // We're giving it the length of the buffer, so no danger of - // accessing invalid memory. We have verified that the length of the - // buffer fits in an `u8`, so the cast to `u8` is also fine. + // We're giving it a length of 1 to transmit 1 byte at a time. // // The MAXCNT field is 8 bits wide and accepts the full range of // values. - uarte - .txd - .maxcnt - .write(|w| unsafe { w.maxcnt().bits(tx_buffer.len() as _) }); + uarte.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(1) }); // Start UARTE Transmit transaction. // `1` is a valid value to write to task registers. @@ -577,7 +584,7 @@ pub mod serial { } } - impl core::fmt::Write for UarteTx + impl<'a, T> core::fmt::Write for UarteTx<'a, T> where T: Instance, { @@ -589,7 +596,7 @@ pub mod serial { } } - impl serial::Read for UarteRx + impl<'a, T> serial::Read for UarteRx<'a, T> where T: Instance, { @@ -615,8 +622,6 @@ pub mod serial { } Ok(b) } else { - let rx_buf = &mut self.rx_buf; - // We're giving the register a pointer to the rx buffer. // // The PTR field is a full 32 bits wide and accepts the full range @@ -624,17 +629,13 @@ pub mod serial { uarte .rxd .ptr - .write(|w| unsafe { w.ptr().bits(rx_buf.as_ptr() as u32) }); + .write(|w| unsafe { w.ptr().bits(self.rx_buf.as_ptr() as u32) }); - // We're giving it the length of the buffer, so no danger of - // accessing invalid memory. + // We're giving it a length of 1 to read only 1 byte. // // The MAXCNT field is at least 8 bits wide and accepts the full // range of values. - uarte - .rxd - .maxcnt - .write(|w| unsafe { w.maxcnt().bits(rx_buf.len() as _) }); + uarte.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(1) }); // Start UARTE Receive transaction. // `1` is a valid value to write to task registers. From a45bd6fb7b320cb6ead293729af4f6a9dbbcff47 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 7 Jan 2021 08:29:40 +0100 Subject: [PATCH 03/19] Check that buffers are in RAM --- nrf-hal-common/src/uarte.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nrf-hal-common/src/uarte.rs b/nrf-hal-common/src/uarte.rs index e161e7c2..3b885d99 100644 --- a/nrf-hal-common/src/uarte.rs +++ b/nrf-hal-common/src/uarte.rs @@ -463,6 +463,7 @@ where T: Instance, { fn new(tx_buf: &'a mut [u8]) -> Result, Error> { + slice_in_ram_or(tx_buf, Error::BufferNotInRAM)?; if tx_buf.len() > 0 { Ok(UarteTx { _marker: core::marker::PhantomData, @@ -479,6 +480,7 @@ where T: Instance, { fn new(rx_buf: &'a mut [u8]) -> Result, Error> { + slice_in_ram_or(rx_buf, Error::BufferNotInRAM)?; if rx_buf.len() > 0 { Ok(UarteRx { _marker: core::marker::PhantomData, From ff3defea2ec4c23abc373d955af886327cd8df43 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 7 Jan 2021 08:33:02 +0100 Subject: [PATCH 04/19] Implement Drop trait to ensure DMA is stopped --- nrf-hal-common/src/uarte.rs | 67 +++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/nrf-hal-common/src/uarte.rs b/nrf-hal-common/src/uarte.rs index 3b885d99..4d67f305 100644 --- a/nrf-hal-common/src/uarte.rs +++ b/nrf-hal-common/src/uarte.rs @@ -447,13 +447,19 @@ mod _uarte1 { } /// Interface for the TX part of a UART instance that can be used independently of the RX part. -pub struct UarteTx<'a, T> { +pub struct UarteTx<'a, T> +where + T: Instance, +{ _marker: core::marker::PhantomData, tx_buf: &'a mut [u8], } /// Interface for the RX part of a UART instance that can be used independently of the TX part. -pub struct UarteRx<'a, T> { +pub struct UarteRx<'a, T> +where + T: Instance, +{ _marker: core::marker::PhantomData, rx_buf: &'a mut [u8], } @@ -492,6 +498,63 @@ where } } +impl<'a, T> Drop for UarteTx<'a, T> +where + T: Instance, +{ + fn drop(&mut self) { + let uarte = unsafe { &*T::ptr() }; + + let in_progress = uarte.events_txstarted.read().bits() == 1; + // Stop any ongoing transmission + if in_progress { + uarte.tasks_stoptx.write(|w| unsafe { w.bits(1) }); + + // Wait for transmitter is stopped. + while uarte.events_txstopped.read().bits() == 0 {} + + // Reset events + uarte.events_endtx.reset(); + uarte.events_txstopped.reset(); + + // Ensure the above is done + compiler_fence(SeqCst); + } + } +} + +impl<'a, T> Drop for UarteRx<'a, T> +where + T: Instance, +{ + fn drop(&mut self) { + let uarte = unsafe { &*T::ptr() }; + + let in_progress = uarte.events_rxstarted.read().bits() == 1; + // Stop any ongoing reception + if in_progress { + uarte.tasks_stoprx.write(|w| unsafe { w.bits(1) }); + + // Wait for receive to be done to ensure memory is untouched. + while uarte.events_rxto.read().bits() == 0 {} + + uarte.events_rxto.reset(); + + // Flush DMA + uarte.tasks_flushrx.write(|w| unsafe { w.bits(1) }); + + // Wait for the flush to complete. + while uarte.events_endrx.read().bits() == 0 {} + + // Reset events + uarte.events_endrx.reset(); + + // Ensure the above is done + compiler_fence(SeqCst); + } + } +} + pub mod serial { ///! Implementation of the embedded_hal::serial::* traits for UartTx and UartRx. From 5da3e9b7850b96581daf718b0c6d47848f67756f Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 11 Jan 2021 10:11:28 +0100 Subject: [PATCH 05/19] Require DMA buffers to have static lifetime * Allow making multi-byte DMA transfers by starting DMA only on flush(). Read is still limited by the embedded_hal trait, so will still read only 1 byte at a time. * Auto derive blocking trait from non-blocking version. --- nrf-hal-common/src/uarte.rs | 136 +++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 58 deletions(-) diff --git a/nrf-hal-common/src/uarte.rs b/nrf-hal-common/src/uarte.rs index 4d67f305..c52fd8af 100644 --- a/nrf-hal-common/src/uarte.rs +++ b/nrf-hal-common/src/uarte.rs @@ -371,12 +371,12 @@ where ) } - // Split into implementations of embedded_hal::serial traits. The buffers passed here must outlive any DMA transfers + // Split into implementations of embedded_hal::serial traits. The size of the slices passed to this method will determine the size of the DMA transfers performed. // that are initiated by the UartTx and UartRx. pub fn split<'a>( self, - tx_buf: &'a mut [u8], - rx_buf: &'a mut [u8], + tx_buf: &'static mut [u8], + rx_buf: &'static mut [u8], ) -> Result<(UarteTx<'a, T>, UarteRx<'a, T>), Error> { let tx = UarteTx::new(tx_buf)?; let rx = UarteRx::new(rx_buf)?; @@ -453,6 +453,7 @@ where { _marker: core::marker::PhantomData, tx_buf: &'a mut [u8], + written: u16, } /// Interface for the RX part of a UART instance that can be used independently of the TX part. @@ -470,14 +471,19 @@ where { fn new(tx_buf: &'a mut [u8]) -> Result, Error> { slice_in_ram_or(tx_buf, Error::BufferNotInRAM)?; - if tx_buf.len() > 0 { - Ok(UarteTx { - _marker: core::marker::PhantomData, - tx_buf, - }) - } else { - Err(Error::TxBufferTooSmall) + if tx_buf.len() == 0 { + return Err(Error::TxBufferTooSmall); } + + if tx_buf.len() > EASY_DMA_SIZE { + return Err(Error::TxBufferTooLong); + } + + Ok(UarteTx { + _marker: core::marker::PhantomData, + tx_buf, + written: 0, + }) } } @@ -487,14 +493,18 @@ where { fn new(rx_buf: &'a mut [u8]) -> Result, Error> { slice_in_ram_or(rx_buf, Error::BufferNotInRAM)?; - if rx_buf.len() > 0 { - Ok(UarteRx { - _marker: core::marker::PhantomData, - rx_buf, - }) - } else { - Err(Error::RxBufferTooSmall) + if rx_buf.len() == 0 { + return Err(Error::RxBufferTooSmall); + } + + if rx_buf.len() > EASY_DMA_SIZE { + return Err(Error::RxBufferTooLong); } + + Ok(UarteRx { + _marker: core::marker::PhantomData, + rx_buf, + }) } } @@ -557,8 +567,9 @@ where pub mod serial { - ///! Implementation of the embedded_hal::serial::* traits for UartTx and UartRx. + ///! Implementation of the embedded_hal::serial::* and embedded_hal::blocking::serial::* traits for UartTx and UartRx. use super::*; + use embedded_hal::blocking::serial as bserial; use embedded_hal::serial; use nb; @@ -568,48 +579,21 @@ pub mod serial { { type Error = Error; - /// Write a single byte non-blocking. Returns nb::Error::WouldBlock if not yet done. + /// Write a single byte to the internal buffer. Returns nb::Error::WouldBlock if buffer is full. fn write(&mut self, b: u8) -> nb::Result<(), Self::Error> { let uarte = unsafe { &*T::ptr() }; - // If txstarted is set, we are in the process of transmitting. - let in_progress = uarte.events_txstarted.read().bits() == 1; + // Prevent writing to buffer while DMA transfer is in progress. + if uarte.events_txstarted.read().bits() == 1 { + return Err(nb::Error::WouldBlock); + } - if in_progress { - self.flush() + let written = self.written as usize; + if written < self.tx_buf.len() { + self.tx_buf[written] = b; + self.written += 1; + Ok(()) } else { - // Start a new transmission, copy value into transmit buffer. - - self.tx_buf[0] = b; - - // 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(SeqCst); - - // Reset the events. - uarte.events_endtx.reset(); - uarte.events_txstopped.reset(); - - // Set up the DMA write. - // We're giving the register a pointer to the tx buffer. - // - // The PTR field is a full 32 bits wide and accepts the full range - // of values. - uarte - .txd - .ptr - .write(|w| unsafe { w.ptr().bits(self.tx_buf.as_ptr() as u32) }); - - // We're giving it a length of 1 to transmit 1 byte at a time. - // - // The MAXCNT field is 8 bits wide and accepts the full range of - // values. - uarte.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(1) }); - - // Start UARTE Transmit transaction. - // `1` is a valid value to write to task registers. - uarte.tasks_starttx.write(|w| unsafe { w.bits(1) }); Err(nb::Error::WouldBlock) } } @@ -618,10 +602,12 @@ pub mod serial { fn flush(&mut self) -> nb::Result<(), Self::Error> { let uarte = unsafe { &*T::ptr() }; + // If txstarted is set, we are in the process of transmitting. let in_progress = uarte.events_txstarted.read().bits() == 1; - let endtx = uarte.events_endtx.read().bits() != 0; - let txstopped = uarte.events_txstopped.read().bits() != 0; + if in_progress { + let endtx = uarte.events_endtx.read().bits() != 0; + let txstopped = uarte.events_txstopped.read().bits() != 0; if endtx || txstopped { // We are done, cleanup the state. uarte.events_txstarted.reset(); @@ -644,11 +630,45 @@ pub mod serial { Err(nb::Error::WouldBlock) } } else { - Ok(()) + // 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(SeqCst); + + // Reset the events. + uarte.events_endtx.reset(); + uarte.events_txstopped.reset(); + + // Set up the DMA write. + // We're giving the register a pointer to the tx buffer. + // + // The PTR field is a full 32 bits wide and accepts the full range + // of values. + uarte + .txd + .ptr + .write(|w| unsafe { w.ptr().bits(self.tx_buf.as_ptr() as u32) }); + + // We're giving it a length of the number of bytes written to the buffer. + // + // The MAXCNT field is 8 bits wide and accepts the full range of + // values. + uarte + .txd + .maxcnt + .write(|w| unsafe { w.maxcnt().bits(self.written) }); + + // Start UARTE Transmit transaction. + // `1` is a valid value to write to task registers. + uarte.tasks_starttx.write(|w| unsafe { w.bits(1) }); + Err(nb::Error::WouldBlock) } } } + // Auto-implement the blocking variant + impl<'a, T> bserial::write::Default for UarteTx<'a, T> where T: Instance {} + impl<'a, T> core::fmt::Write for UarteTx<'a, T> where T: Instance, From d84e1fe6d6a71acef632a42784b6f49248d66bcc Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 11 Jan 2021 10:21:25 +0100 Subject: [PATCH 06/19] Ensure write pointer is reset --- nrf-hal-common/src/uarte.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/nrf-hal-common/src/uarte.rs b/nrf-hal-common/src/uarte.rs index c52fd8af..6fa25223 100644 --- a/nrf-hal-common/src/uarte.rs +++ b/nrf-hal-common/src/uarte.rs @@ -611,6 +611,7 @@ pub mod serial { if endtx || txstopped { // We are done, cleanup the state. uarte.events_txstarted.reset(); + self.written = 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. From c333210b2ba3d93532d6f9d3bc730b0166717a6b Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 11 Jan 2021 10:39:54 +0100 Subject: [PATCH 07/19] Avoid initiating transfer if no bytes have been written --- nrf-hal-common/src/uarte.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nrf-hal-common/src/uarte.rs b/nrf-hal-common/src/uarte.rs index 6fa25223..209405ed 100644 --- a/nrf-hal-common/src/uarte.rs +++ b/nrf-hal-common/src/uarte.rs @@ -631,6 +631,11 @@ pub mod serial { Err(nb::Error::WouldBlock) } } else { + // No need to trigger transmit if we don't have anything written + if self.written == 0 { + return Ok(()); + } + // 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. From 99616c7098104e353a50360067292cbc33d0c558 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 11 Jan 2021 10:46:25 +0100 Subject: [PATCH 08/19] Fix conversion --- nrf-hal-common/src/uarte.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/nrf-hal-common/src/uarte.rs b/nrf-hal-common/src/uarte.rs index 209405ed..be473270 100644 --- a/nrf-hal-common/src/uarte.rs +++ b/nrf-hal-common/src/uarte.rs @@ -453,7 +453,7 @@ where { _marker: core::marker::PhantomData, tx_buf: &'a mut [u8], - written: u16, + written: usize, } /// Interface for the RX part of a UART instance that can be used independently of the TX part. @@ -588,9 +588,8 @@ pub mod serial { return Err(nb::Error::WouldBlock); } - let written = self.written as usize; - if written < self.tx_buf.len() { - self.tx_buf[written] = b; + if self.written < self.tx_buf.len() { + self.tx_buf[self.written] = b; self.written += 1; Ok(()) } else { @@ -662,7 +661,7 @@ pub mod serial { uarte .txd .maxcnt - .write(|w| unsafe { w.maxcnt().bits(self.written) }); + .write(|w| unsafe { w.maxcnt().bits(self.written as _) }); // Start UARTE Transmit transaction. // `1` is a valid value to write to task registers. From 7fef6b23fec9a796e56535daf5aac3bdf0ada753 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 12 Jan 2021 11:50:53 +0100 Subject: [PATCH 09/19] Address review comments * Remove explicit lifetime for tx/rx structs * Refactor write/read logic into functions that are reused in the existing combined Uarte implementation and the UartTx/UarteRx implementation. --- nrf-hal-common/src/uarte.rs | 362 +++++++++++++++--------------------- 1 file changed, 153 insertions(+), 209 deletions(-) diff --git a/nrf-hal-common/src/uarte.rs b/nrf-hal-common/src/uarte.rs index be473270..f2ad38c2 100644 --- a/nrf-hal-common/src/uarte.rs +++ b/nrf-hal-common/src/uarte.rs @@ -110,37 +110,7 @@ where // We can only DMA out of RAM. slice_in_ram_or(tx_buffer, Error::BufferNotInRAM)?; - // 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(SeqCst); - - // Reset the events. - self.0.events_endtx.reset(); - self.0.events_txstopped.reset(); - - // Set up the DMA write. - self.0.txd.ptr.write(|w| - // We're giving the register a pointer to the stack. Since we're - // waiting for the UARTE transaction to end before this stack pointer - // becomes invalid, there's nothing wrong here. - // - // The PTR field is a full 32 bits wide and accepts the full range - // of values. - unsafe { w.ptr().bits(tx_buffer.as_ptr() as u32) }); - self.0.txd.maxcnt.write(|w| - // We're giving it the length of the buffer, so no danger of - // accessing invalid memory. We have verified that the length of the - // buffer fits in an `u8`, so the cast to `u8` is also fine. - // - // The MAXCNT field is 8 bits wide and accepts the full range of - // values. - unsafe { w.maxcnt().bits(tx_buffer.len() as _) }); - - // Start UARTE Transmit transaction. - self.0.tasks_starttx.write(|w| - // `1` is a valid value to write to task registers. - unsafe { w.bits(1) }); + start_write(&*self.0, tx_buffer); // Wait for transmission to end. let mut endtx; @@ -162,12 +132,7 @@ where return Err(Error::Transmit); } - // Lower power consumption by disabling the transmitter once we're - // finished. - self.0.tasks_stoptx.write(|w| - // `1` is a valid value to write to task registers. - unsafe { w.bits(1) }); - + stop_write(&*self.0); Ok(()) } @@ -178,12 +143,12 @@ where /// /// The buffer must have a length of at most 255 bytes. pub fn read(&mut self, rx_buffer: &mut [u8]) -> Result<(), Error> { - self.start_read(rx_buffer)?; + start_read(&*self.0, rx_buffer)?; // Wait for transmission to end. while self.0.events_endrx.read().bits() == 0 {} - self.finalize_read(); + finalize_read(&*self.0); if self.0.rxd.amount.read().bits() != rx_buffer.len() as u32 { return Err(Error::Receive); @@ -216,7 +181,7 @@ where I: timer::Instance, { // Start the read. - self.start_read(rx_buffer)?; + start_read(&self.0, rx_buffer)?; // Start the timeout timer. timer.start(cycles); @@ -235,11 +200,11 @@ where if !event_complete { // Cancel the reception if it did not complete until now. - self.cancel_read(); + cancel_read(&self.0); } // Cleanup, even in the error case. - self.finalize_read(); + finalize_read(&self.0); let bytes_read = self.0.rxd.amount.read().bits() as usize; @@ -254,78 +219,6 @@ where Ok(()) } - /// Start a UARTE read transaction by setting the control - /// values and triggering a read task. - fn start_read(&mut self, rx_buffer: &mut [u8]) -> Result<(), Error> { - if rx_buffer.len() > EASY_DMA_SIZE { - return Err(Error::RxBufferTooLong); - } - - // NOTE: RAM slice check 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(SeqCst); - - // Set up the DMA read - self.0.rxd.ptr.write(|w| - // We're giving the register a pointer to the stack. Since we're - // waiting for the UARTE transaction to end before this stack pointer - // becomes invalid, there's nothing wrong here. - // - // The PTR field is a full 32 bits wide and accepts the full range - // of values. - unsafe { w.ptr().bits(rx_buffer.as_ptr() as u32) }); - self.0.rxd.maxcnt.write(|w| - // We're giving it the length of the buffer, so no danger of - // accessing invalid memory. We have verified that the length of the - // buffer fits in an `u8`, so the cast to `u8` is also fine. - // - // The MAXCNT field is at least 8 bits wide and accepts the full - // range of values. - unsafe { w.maxcnt().bits(rx_buffer.len() as _) }); - - // Start UARTE Receive transaction. - self.0.tasks_startrx.write(|w| - // `1` is a valid value to write to task registers. - unsafe { w.bits(1) }); - - Ok(()) - } - - /// Finalize a UARTE read transaction by clearing the event. - fn finalize_read(&mut self) { - // Reset the event, otherwise it will always read `1` from now on. - self.0.events_endrx.write(|w| w); - - // 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(SeqCst); - } - - /// Stop an unfinished UART read transaction and flush FIFO to DMA buffer. - fn cancel_read(&mut self) { - // Stop reception. - self.0.tasks_stoprx.write(|w| unsafe { w.bits(1) }); - - // Wait for the reception to have stopped. - while self.0.events_rxto.read().bits() == 0 {} - - // Reset the event flag. - self.0.events_rxto.write(|w| w); - - // Ask UART to flush FIFO to DMA buffer. - self.0.tasks_flushrx.write(|w| unsafe { w.bits(1) }); - - // Wait for the flush to complete. - while self.0.events_endrx.read().bits() == 0 {} - - // The event flag itself is later reset by `finalize_read`. - } - /// Return the raw interface to the underlying UARTE peripheral. pub fn free(self) -> (T, Pins) { let rxd = self.0.psel.rxd.read(); @@ -371,19 +264,136 @@ where ) } - // Split into implementations of embedded_hal::serial traits. The size of the slices passed to this method will determine the size of the DMA transfers performed. - // that are initiated by the UartTx and UartRx. - pub fn split<'a>( + /// Split into implementations of embedded_hal::serial traits. The size of the slices passed to this + /// method will determine the size of the DMA transfers performed. + pub fn split( self, tx_buf: &'static mut [u8], rx_buf: &'static mut [u8], - ) -> Result<(UarteTx<'a, T>, UarteRx<'a, T>), Error> { + ) -> Result<(UarteTx, UarteRx), Error> { let tx = UarteTx::new(tx_buf)?; let rx = UarteRx::new(rx_buf)?; Ok((tx, rx)) } } +/// Write via UARTE. +/// +/// This method uses transmits all bytes in `tx_buffer`. +fn start_write(uarte: &uarte0::RegisterBlock, tx_buffer: &[u8]) { + // 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(SeqCst); + + // Reset the events. + uarte.events_endtx.reset(); + uarte.events_txstopped.reset(); + + // Set up the DMA write. + uarte.txd.ptr.write(|w| + // We're giving the register a pointer to the stack. Since we're + // waiting for the UARTE transaction to end before this stack pointer + // becomes invalid, there's nothing wrong here. + // + // The PTR field is a full 32 bits wide and accepts the full range + // of values. + unsafe { w.ptr().bits(tx_buffer.as_ptr() as u32) }); + uarte.txd.maxcnt.write(|w| + // We're giving it the length of the buffer, so no danger of + // accessing invalid memory. We have verified that the length of the + // buffer fits in an `u8`, so the cast to `u8` is also fine. + // + // The MAXCNT field is 8 bits wide and accepts the full range of + // values. + unsafe { w.maxcnt().bits(tx_buffer.len() as _) }); + + // Start UARTE Transmit transaction. + uarte.tasks_starttx.write(|w| + // `1` is a valid value to write to task registers. + unsafe { w.bits(1) }); +} + +fn stop_write(uarte: &uarte0::RegisterBlock) { + // `1` is a valid value to write to task registers. + uarte.tasks_stoptx.write(|w| unsafe { w.bits(1) }); + + // Wait for transmitter is stopped. + while uarte.events_txstopped.read().bits() == 0 {} +} + +/// Start a UARTE read transaction by setting the control +/// values and triggering a read task. +fn start_read(uarte: &uarte0::RegisterBlock, rx_buffer: &mut [u8]) -> Result<(), Error> { + if rx_buffer.len() > EASY_DMA_SIZE { + return Err(Error::RxBufferTooLong); + } + + // NOTE: RAM slice check 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(SeqCst); + + // Set up the DMA read + uarte.rxd.ptr.write(|w| + // We're giving the register a pointer to the stack. Since we're + // waiting for the UARTE transaction to end before this stack pointer + // becomes invalid, there's nothing wrong here. + // + // The PTR field is a full 32 bits wide and accepts the full range + // of values. + unsafe { w.ptr().bits(rx_buffer.as_ptr() as u32) }); + uarte.rxd.maxcnt.write(|w| + // We're giving it the length of the buffer, so no danger of + // accessing invalid memory. We have verified that the length of the + // buffer fits in an `u8`, so the cast to `u8` is also fine. + // + // The MAXCNT field is at least 8 bits wide and accepts the full + // range of values. + unsafe { w.maxcnt().bits(rx_buffer.len() as _) }); + + // Start UARTE Receive transaction. + uarte.tasks_startrx.write(|w| + // `1` is a valid value to write to task registers. + unsafe { w.bits(1) }); + + Ok(()) +} + +/// Stop an unfinished UART read transaction and flush FIFO to DMA buffer. +fn cancel_read(uarte: &uarte0::RegisterBlock) { + // Stop reception. + uarte.tasks_stoprx.write(|w| unsafe { w.bits(1) }); + + // Wait for the reception to have stopped. + while uarte.events_rxto.read().bits() == 0 {} + + // Reset the event flag. + uarte.events_rxto.write(|w| w); + + // Ask UART to flush FIFO to DMA buffer. + uarte.tasks_flushrx.write(|w| unsafe { w.bits(1) }); + + // Wait for the flush to complete. + while uarte.events_endrx.read().bits() == 0 {} + + // The event flag itself is later reset by `finalize_read`. +} + +/// Finalize a UARTE read transaction by clearing the event. +fn finalize_read(uarte: &uarte0::RegisterBlock) { + // Reset the event, otherwise it will always read `1` from now on. + uarte.events_endrx.write(|w| w); + + // 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(SeqCst); +} + impl fmt::Write for Uarte where T: Instance, @@ -447,30 +457,29 @@ mod _uarte1 { } /// Interface for the TX part of a UART instance that can be used independently of the RX part. -pub struct UarteTx<'a, T> +pub struct UarteTx where T: Instance, { _marker: core::marker::PhantomData, - tx_buf: &'a mut [u8], + tx_buf: &'static mut [u8], written: usize, } /// Interface for the RX part of a UART instance that can be used independently of the TX part. -pub struct UarteRx<'a, T> +pub struct UarteRx where T: Instance, { _marker: core::marker::PhantomData, - rx_buf: &'a mut [u8], + rx_buf: &'static mut [u8], } -impl<'a, T> UarteTx<'a, T> +impl UarteTx where T: Instance, { - fn new(tx_buf: &'a mut [u8]) -> Result, Error> { - slice_in_ram_or(tx_buf, Error::BufferNotInRAM)?; + fn new(tx_buf: &'static mut [u8]) -> Result, Error> { if tx_buf.len() == 0 { return Err(Error::TxBufferTooSmall); } @@ -487,12 +496,11 @@ where } } -impl<'a, T> UarteRx<'a, T> +impl UarteRx where T: Instance, { - fn new(rx_buf: &'a mut [u8]) -> Result, Error> { - slice_in_ram_or(rx_buf, Error::BufferNotInRAM)?; + fn new(rx_buf: &'static mut [u8]) -> Result, Error> { if rx_buf.len() == 0 { return Err(Error::RxBufferTooSmall); } @@ -508,7 +516,7 @@ where } } -impl<'a, T> Drop for UarteTx<'a, T> +impl Drop for UarteTx where T: Instance, { @@ -518,10 +526,7 @@ where let in_progress = uarte.events_txstarted.read().bits() == 1; // Stop any ongoing transmission if in_progress { - uarte.tasks_stoptx.write(|w| unsafe { w.bits(1) }); - - // Wait for transmitter is stopped. - while uarte.events_txstopped.read().bits() == 0 {} + stop_write(uarte); // Reset events uarte.events_endtx.reset(); @@ -533,7 +538,7 @@ where } } -impl<'a, T> Drop for UarteRx<'a, T> +impl Drop for UarteRx where T: Instance, { @@ -543,18 +548,7 @@ where let in_progress = uarte.events_rxstarted.read().bits() == 1; // Stop any ongoing reception if in_progress { - uarte.tasks_stoprx.write(|w| unsafe { w.bits(1) }); - - // Wait for receive to be done to ensure memory is untouched. - while uarte.events_rxto.read().bits() == 0 {} - - uarte.events_rxto.reset(); - - // Flush DMA - uarte.tasks_flushrx.write(|w| unsafe { w.bits(1) }); - - // Wait for the flush to complete. - while uarte.events_endrx.read().bits() == 0 {} + cancel_read(uarte); // Reset events uarte.events_endrx.reset(); @@ -573,7 +567,7 @@ pub mod serial { use embedded_hal::serial; use nb; - impl<'a, T> serial::Write for UarteTx<'a, T> + impl serial::Write for UarteTx where T: Instance, { @@ -611,6 +605,7 @@ pub mod serial { // We are done, cleanup the state. uarte.events_txstarted.reset(); self.written = 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. @@ -622,8 +617,9 @@ pub mod serial { // Lower power consumption by disabling the transmitter once we're // finished. - // `1` is a valid value to write to task registers. - uarte.tasks_stoptx.write(|w| unsafe { w.bits(1) }); + stop_write(uarte); + + compiler_fence(SeqCst); Ok(()) } else { // Still not done, don't block. @@ -635,46 +631,17 @@ pub mod serial { return Ok(()); } - // 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(SeqCst); - - // Reset the events. - uarte.events_endtx.reset(); - uarte.events_txstopped.reset(); - - // Set up the DMA write. - // We're giving the register a pointer to the tx buffer. - // - // The PTR field is a full 32 bits wide and accepts the full range - // of values. - uarte - .txd - .ptr - .write(|w| unsafe { w.ptr().bits(self.tx_buf.as_ptr() as u32) }); - - // We're giving it a length of the number of bytes written to the buffer. - // - // The MAXCNT field is 8 bits wide and accepts the full range of - // values. - uarte - .txd - .maxcnt - .write(|w| unsafe { w.maxcnt().bits(self.written as _) }); - - // Start UARTE Transmit transaction. - // `1` is a valid value to write to task registers. - uarte.tasks_starttx.write(|w| unsafe { w.bits(1) }); + start_write(uarte, &self.tx_buf[0..self.written]); + Err(nb::Error::WouldBlock) } } } // Auto-implement the blocking variant - impl<'a, T> bserial::write::Default for UarteTx<'a, T> where T: Instance {} + impl bserial::write::Default for UarteTx where T: Instance {} - impl<'a, T> core::fmt::Write for UarteTx<'a, T> + impl core::fmt::Write for UarteTx where T: Instance, { @@ -686,7 +653,7 @@ pub mod serial { } } - impl<'a, T> serial::Read for UarteRx<'a, T> + impl serial::Read for UarteRx where T: Instance, { @@ -704,38 +671,15 @@ pub mod serial { if in_progress { let b = self.rx_buf[0]; uarte.events_rxstarted.write(|w| w); - uarte.events_endrx.write(|w| w); - compiler_fence(SeqCst); + finalize_read(uarte); + if uarte.rxd.amount.read().bits() != 1 as u32 { return Err(nb::Error::Other(Error::Receive)); } Ok(b) } else { - // We're giving the register a pointer to the rx buffer. - // - // The PTR field is a full 32 bits wide and accepts the full range - // of values. - uarte - .rxd - .ptr - .write(|w| unsafe { w.ptr().bits(self.rx_buf.as_ptr() as u32) }); - - // We're giving it a length of 1 to read only 1 byte. - // - // The MAXCNT field is at least 8 bits wide and accepts the full - // range of values. - uarte.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(1) }); - - // Start UARTE Receive transaction. - // `1` is a valid value to write to task registers. - uarte.tasks_startrx.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(SeqCst); - + start_read(&uarte, self.rx_buf)?; Err(nb::Error::WouldBlock) } } From d97613737d833fd8a9b44036c76eb82690470cd6 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 12 Jan 2021 14:36:38 +0100 Subject: [PATCH 10/19] Update nrf-hal-common/src/uarte.rs Co-authored-by: Jonas Schievink --- nrf-hal-common/src/uarte.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nrf-hal-common/src/uarte.rs b/nrf-hal-common/src/uarte.rs index f2ad38c2..e4a390ab 100644 --- a/nrf-hal-common/src/uarte.rs +++ b/nrf-hal-common/src/uarte.rs @@ -559,9 +559,8 @@ where } } +/// Implementation of the embedded_hal::serial::* and embedded_hal::blocking::serial::* traits for UartTx and UartRx. pub mod serial { - - ///! Implementation of the embedded_hal::serial::* and embedded_hal::blocking::serial::* traits for UartTx and UartRx. use super::*; use embedded_hal::blocking::serial as bserial; use embedded_hal::serial; From 18c876334848e01266ba0318c5e6e16e8a1a3d90 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 12 Jan 2021 14:39:22 +0100 Subject: [PATCH 11/19] Move into parent module --- nrf-hal-common/src/uarte.rs | 188 ++++++++++++++++++------------------ 1 file changed, 92 insertions(+), 96 deletions(-) diff --git a/nrf-hal-common/src/uarte.rs b/nrf-hal-common/src/uarte.rs index e4a390ab..603b3285 100644 --- a/nrf-hal-common/src/uarte.rs +++ b/nrf-hal-common/src/uarte.rs @@ -8,7 +8,11 @@ use core::fmt; use core::ops::Deref; use core::sync::atomic::{compiler_fence, Ordering::SeqCst}; +use embedded_hal::blocking::serial as bserial; use embedded_hal::digital::v2::OutputPin; +use embedded_hal::serial; + +use nb; #[cfg(any(feature = "52833", feature = "52840"))] use crate::pac::UARTE1; @@ -559,128 +563,120 @@ where } } -/// Implementation of the embedded_hal::serial::* and embedded_hal::blocking::serial::* traits for UartTx and UartRx. -pub mod serial { - use super::*; - use embedded_hal::blocking::serial as bserial; - use embedded_hal::serial; - use nb; - - impl serial::Write for UarteTx - where - T: Instance, - { - type Error = Error; - - /// Write a single byte to the internal buffer. Returns nb::Error::WouldBlock if buffer is full. - fn write(&mut self, b: u8) -> nb::Result<(), Self::Error> { - let uarte = unsafe { &*T::ptr() }; +impl serial::Write for UarteTx +where + T: Instance, +{ + type Error = Error; - // Prevent writing to buffer while DMA transfer is in progress. - if uarte.events_txstarted.read().bits() == 1 { - return Err(nb::Error::WouldBlock); - } + /// Write a single byte to the internal buffer. Returns nb::Error::WouldBlock if buffer is full. + fn write(&mut self, b: u8) -> nb::Result<(), Self::Error> { + let uarte = unsafe { &*T::ptr() }; - if self.written < self.tx_buf.len() { - self.tx_buf[self.written] = b; - self.written += 1; - Ok(()) - } else { - Err(nb::Error::WouldBlock) - } + // Prevent writing to buffer while DMA transfer is in progress. + if uarte.events_txstarted.read().bits() == 1 { + return Err(nb::Error::WouldBlock); } - /// Flush the TX buffer non-blocking. Returns nb::Error::WouldBlock if not yet flushed. - fn flush(&mut self) -> nb::Result<(), Self::Error> { - let uarte = unsafe { &*T::ptr() }; - - // If txstarted is set, we are in the process of transmitting. - let in_progress = uarte.events_txstarted.read().bits() == 1; + if self.written < self.tx_buf.len() { + self.tx_buf[self.written] = b; + self.written += 1; + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } + } - if in_progress { - let endtx = uarte.events_endtx.read().bits() != 0; - let txstopped = uarte.events_txstopped.read().bits() != 0; - if endtx || txstopped { - // We are done, cleanup the state. - uarte.events_txstarted.reset(); - self.written = 0; + /// Flush the TX buffer non-blocking. Returns nb::Error::WouldBlock if not yet flushed. + fn flush(&mut self) -> nb::Result<(), Self::Error> { + let uarte = unsafe { &*T::ptr() }; - // 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(SeqCst); + // If txstarted is set, we are in the process of transmitting. + let in_progress = uarte.events_txstarted.read().bits() == 1; - if txstopped { - return Err(nb::Error::Other(Error::Transmit)); - } + if in_progress { + let endtx = uarte.events_endtx.read().bits() != 0; + let txstopped = uarte.events_txstopped.read().bits() != 0; + if endtx || txstopped { + // We are done, cleanup the state. + uarte.events_txstarted.reset(); + self.written = 0; - // Lower power consumption by disabling the transmitter once we're - // finished. - stop_write(uarte); + // 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(SeqCst); - compiler_fence(SeqCst); - Ok(()) - } else { - // Still not done, don't block. - Err(nb::Error::WouldBlock) - } - } else { - // No need to trigger transmit if we don't have anything written - if self.written == 0 { - return Ok(()); + if txstopped { + return Err(nb::Error::Other(Error::Transmit)); } - start_write(uarte, &self.tx_buf[0..self.written]); + // Lower power consumption by disabling the transmitter once we're + // finished. + stop_write(uarte); + compiler_fence(SeqCst); + Ok(()) + } else { + // Still not done, don't block. Err(nb::Error::WouldBlock) } + } else { + // No need to trigger transmit if we don't have anything written + if self.written == 0 { + return Ok(()); + } + + start_write(uarte, &self.tx_buf[0..self.written]); + + Err(nb::Error::WouldBlock) } } +} - // Auto-implement the blocking variant - impl bserial::write::Default for UarteTx where T: Instance {} +// Auto-implement the blocking variant +impl bserial::write::Default for UarteTx where T: Instance {} - impl core::fmt::Write for UarteTx - where - T: Instance, - { - fn write_str(&mut self, s: &str) -> fmt::Result { - s.as_bytes() - .iter() - .try_for_each(|c| nb::block!(self.write(*c))) - .map_err(|_| core::fmt::Error) - } +impl core::fmt::Write for UarteTx +where + T: Instance, +{ + fn write_str(&mut self, s: &str) -> fmt::Result { + s.as_bytes() + .iter() + .try_for_each(|c| nb::block!(self.write(*c))) + .map_err(|_| core::fmt::Error) } +} - impl serial::Read for UarteRx - where - T: Instance, - { - type Error = Error; - fn read(&mut self) -> nb::Result { - let uarte = unsafe { &*T::ptr() }; +impl serial::Read for UarteRx +where + T: Instance, +{ + type Error = Error; + fn read(&mut self) -> nb::Result { + let uarte = unsafe { &*T::ptr() }; - compiler_fence(SeqCst); + compiler_fence(SeqCst); - let in_progress = uarte.events_rxstarted.read().bits() == 1; - if in_progress && uarte.events_endrx.read().bits() == 0 { - return Err(nb::Error::WouldBlock); - } + let in_progress = uarte.events_rxstarted.read().bits() == 1; + if in_progress && uarte.events_endrx.read().bits() == 0 { + return Err(nb::Error::WouldBlock); + } - if in_progress { - let b = self.rx_buf[0]; - uarte.events_rxstarted.write(|w| w); + if in_progress { + let b = self.rx_buf[0]; + uarte.events_rxstarted.write(|w| w); - finalize_read(uarte); + finalize_read(uarte); - if uarte.rxd.amount.read().bits() != 1 as u32 { - return Err(nb::Error::Other(Error::Receive)); - } - Ok(b) - } else { - start_read(&uarte, self.rx_buf)?; - Err(nb::Error::WouldBlock) + if uarte.rxd.amount.read().bits() != 1 as u32 { + return Err(nb::Error::Other(Error::Receive)); } + Ok(b) + } else { + start_read(&uarte, self.rx_buf)?; + Err(nb::Error::WouldBlock) } } } From 3d78cde98401cae4a17cbcff80d95f35b5283ed6 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 12 Jan 2021 14:59:02 +0100 Subject: [PATCH 12/19] Remove unneeded import --- nrf-hal-common/src/uarte.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/nrf-hal-common/src/uarte.rs b/nrf-hal-common/src/uarte.rs index 603b3285..7dacbe15 100644 --- a/nrf-hal-common/src/uarte.rs +++ b/nrf-hal-common/src/uarte.rs @@ -12,8 +12,6 @@ use embedded_hal::blocking::serial as bserial; use embedded_hal::digital::v2::OutputPin; use embedded_hal::serial; -use nb; - #[cfg(any(feature = "52833", feature = "52840"))] use crate::pac::UARTE1; From 1a58c9db4f6b251055d1ab58391a63960adcaa01 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 12 Jan 2021 16:29:54 +0100 Subject: [PATCH 13/19] Update nrf-hal-common/src/uarte.rs Co-authored-by: Jonas Schievink --- nrf-hal-common/src/uarte.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nrf-hal-common/src/uarte.rs b/nrf-hal-common/src/uarte.rs index 7dacbe15..6ece9e0d 100644 --- a/nrf-hal-common/src/uarte.rs +++ b/nrf-hal-common/src/uarte.rs @@ -664,7 +664,7 @@ where if in_progress { let b = self.rx_buf[0]; - uarte.events_rxstarted.write(|w| w); + uarte.events_rxstarted.reset(); finalize_read(uarte); From d0dc119d58625e312296e8b74de625b65e0cbb78 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 12 Jan 2021 16:25:49 +0100 Subject: [PATCH 14/19] Call flush if write buffer is full --- nrf-hal-common/src/uarte.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nrf-hal-common/src/uarte.rs b/nrf-hal-common/src/uarte.rs index 6ece9e0d..2c1db82c 100644 --- a/nrf-hal-common/src/uarte.rs +++ b/nrf-hal-common/src/uarte.rs @@ -581,7 +581,7 @@ where self.written += 1; Ok(()) } else { - Err(nb::Error::WouldBlock) + self.flush() } } From 42dc2ee423f1f333eca21d7239f1adbe9407691a Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 12 Jan 2021 17:51:32 +0100 Subject: [PATCH 15/19] Restrict serial API to read only 1 byte at a time to avoid DMA issues. --- nrf-hal-common/src/uarte.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/nrf-hal-common/src/uarte.rs b/nrf-hal-common/src/uarte.rs index 2c1db82c..39d5ffea 100644 --- a/nrf-hal-common/src/uarte.rs +++ b/nrf-hal-common/src/uarte.rs @@ -145,7 +145,7 @@ where /// /// The buffer must have a length of at most 255 bytes. pub fn read(&mut self, rx_buffer: &mut [u8]) -> Result<(), Error> { - start_read(&*self.0, rx_buffer)?; + start_read(&*self.0, rx_buffer, rx_buffer.len())?; // Wait for transmission to end. while self.0.events_endrx.read().bits() == 0 {} @@ -183,7 +183,7 @@ where I: timer::Instance, { // Start the read. - start_read(&self.0, rx_buffer)?; + start_read(&self.0, rx_buffer, rx_buffer.len())?; // Start the timeout timer. timer.start(cycles); @@ -326,7 +326,11 @@ fn stop_write(uarte: &uarte0::RegisterBlock) { /// Start a UARTE read transaction by setting the control /// values and triggering a read task. -fn start_read(uarte: &uarte0::RegisterBlock, rx_buffer: &mut [u8]) -> Result<(), Error> { +fn start_read( + uarte: &uarte0::RegisterBlock, + rx_buffer: &mut [u8], + nbytes: usize, +) -> Result<(), Error> { if rx_buffer.len() > EASY_DMA_SIZE { return Err(Error::RxBufferTooLong); } @@ -355,7 +359,7 @@ fn start_read(uarte: &uarte0::RegisterBlock, rx_buffer: &mut [u8]) -> Result<(), // // The MAXCNT field is at least 8 bits wide and accepts the full // range of values. - unsafe { w.maxcnt().bits(rx_buffer.len() as _) }); + unsafe { w.maxcnt().bits(nbytes.min(rx_buffer.len()) as _) }); // Start UARTE Receive transaction. uarte.tasks_startrx.write(|w| @@ -663,7 +667,6 @@ where } if in_progress { - let b = self.rx_buf[0]; uarte.events_rxstarted.reset(); finalize_read(uarte); @@ -671,9 +674,9 @@ where if uarte.rxd.amount.read().bits() != 1 as u32 { return Err(nb::Error::Other(Error::Receive)); } - Ok(b) + Ok(self.rx_buf[0]) } else { - start_read(&uarte, self.rx_buf)?; + start_read(&uarte, self.rx_buf, 1)?; Err(nb::Error::WouldBlock) } } From 655e41be4b0c38fab8d8cd5488af4551006c6ec0 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 13 Jan 2021 14:35:50 +0100 Subject: [PATCH 16/19] Simplify by passing slice only --- nrf-hal-common/src/uarte.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/nrf-hal-common/src/uarte.rs b/nrf-hal-common/src/uarte.rs index 39d5ffea..e6ba1cad 100644 --- a/nrf-hal-common/src/uarte.rs +++ b/nrf-hal-common/src/uarte.rs @@ -145,7 +145,7 @@ where /// /// The buffer must have a length of at most 255 bytes. pub fn read(&mut self, rx_buffer: &mut [u8]) -> Result<(), Error> { - start_read(&*self.0, rx_buffer, rx_buffer.len())?; + start_read(&*self.0, rx_buffer)?; // Wait for transmission to end. while self.0.events_endrx.read().bits() == 0 {} @@ -183,7 +183,7 @@ where I: timer::Instance, { // Start the read. - start_read(&self.0, rx_buffer, rx_buffer.len())?; + start_read(&self.0, rx_buffer)?; // Start the timeout timer. timer.start(cycles); @@ -326,11 +326,7 @@ fn stop_write(uarte: &uarte0::RegisterBlock) { /// Start a UARTE read transaction by setting the control /// values and triggering a read task. -fn start_read( - uarte: &uarte0::RegisterBlock, - rx_buffer: &mut [u8], - nbytes: usize, -) -> Result<(), Error> { +fn start_read(uarte: &uarte0::RegisterBlock, rx_buffer: &mut [u8]) -> Result<(), Error> { if rx_buffer.len() > EASY_DMA_SIZE { return Err(Error::RxBufferTooLong); } @@ -359,7 +355,7 @@ fn start_read( // // The MAXCNT field is at least 8 bits wide and accepts the full // range of values. - unsafe { w.maxcnt().bits(nbytes.min(rx_buffer.len()) as _) }); + unsafe { w.maxcnt().bits(rx_buffer.len() as _) }); // Start UARTE Receive transaction. uarte.tasks_startrx.write(|w| @@ -676,7 +672,7 @@ where } Ok(self.rx_buf[0]) } else { - start_read(&uarte, self.rx_buf, 1)?; + start_read(&uarte, &mut self.rx_buf[..1])?; Err(nb::Error::WouldBlock) } } From d25a7460b75fb054e71e7d5acd8730e0f50453f2 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 13 Jan 2021 14:38:28 +0100 Subject: [PATCH 17/19] Add comment --- nrf-hal-common/src/uarte.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nrf-hal-common/src/uarte.rs b/nrf-hal-common/src/uarte.rs index e6ba1cad..7d5c06e3 100644 --- a/nrf-hal-common/src/uarte.rs +++ b/nrf-hal-common/src/uarte.rs @@ -672,6 +672,8 @@ where } Ok(self.rx_buf[0]) } else { + // We can only read 1 byte at a time, otherwise ENDTX might not be raised, + // causing the read to stall forever. start_read(&uarte, &mut self.rx_buf[..1])?; Err(nb::Error::WouldBlock) } From cf11f38e3ed4c9db094d978cf8f83aadc31060dc Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 13 Jan 2021 17:43:54 +0100 Subject: [PATCH 18/19] Require a slice of size 1 for rx_buf --- nrf-hal-common/src/uarte.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nrf-hal-common/src/uarte.rs b/nrf-hal-common/src/uarte.rs index 7d5c06e3..0d226bf0 100644 --- a/nrf-hal-common/src/uarte.rs +++ b/nrf-hal-common/src/uarte.rs @@ -271,7 +271,7 @@ where pub fn split( self, tx_buf: &'static mut [u8], - rx_buf: &'static mut [u8], + rx_buf: &'static mut [u8; 1], ) -> Result<(UarteTx, UarteRx), Error> { let tx = UarteTx::new(tx_buf)?; let rx = UarteRx::new(rx_buf)?; @@ -674,7 +674,7 @@ where } else { // We can only read 1 byte at a time, otherwise ENDTX might not be raised, // causing the read to stall forever. - start_read(&uarte, &mut self.rx_buf[..1])?; + start_read(&uarte, self.rx_buf)?; Err(nb::Error::WouldBlock) } } From 8688eb696a6dddf82362f786ca8a9b1a6de92bba Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 16 Feb 2021 08:06:18 +0100 Subject: [PATCH 19/19] Consistent buffer length check behavior --- nrf-hal-common/src/uarte.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/nrf-hal-common/src/uarte.rs b/nrf-hal-common/src/uarte.rs index 0d226bf0..3bd8b675 100644 --- a/nrf-hal-common/src/uarte.rs +++ b/nrf-hal-common/src/uarte.rs @@ -105,6 +105,10 @@ where /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. pub fn write(&mut self, tx_buffer: &[u8]) -> Result<(), Error> { + if tx_buffer.len() == 0 { + return Err(Error::TxBufferTooSmall); + } + if tx_buffer.len() > EASY_DMA_SIZE { return Err(Error::TxBufferTooLong); } @@ -327,6 +331,10 @@ fn stop_write(uarte: &uarte0::RegisterBlock) { /// Start a UARTE read transaction by setting the control /// values and triggering a read task. fn start_read(uarte: &uarte0::RegisterBlock, rx_buffer: &mut [u8]) -> Result<(), Error> { + if rx_buffer.len() == 0 { + return Err(Error::RxBufferTooSmall); + } + if rx_buffer.len() > EASY_DMA_SIZE { return Err(Error::RxBufferTooLong); }