Skip to content

Commit

Permalink
Fix Uart::flush_async (#3186)
Browse files Browse the repository at this point in the history
* Fix Uart::flush_async

* Apply single-byte workaround
  • Loading branch information
bugadani authored Feb 28, 2025
1 parent 1050ce5 commit 502d41f
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 5 deletions.
1 change: 1 addition & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

- Full-duplex SPI works when mixed with half-duplex SPI (#3176)
- `Uart::flush_async` should no longer return prematurely (#3186)

### Removed

Expand Down
21 changes: 16 additions & 5 deletions esp-hal/src/uart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -632,12 +632,18 @@ where
#[instability::unstable]
pub fn flush(&mut self) -> Result<(), TxError> {
while self.tx_fifo_count() > 0 {}
// The FSM is in the Idle state for a short while after the last byte is moved
// out of the FIFO. It is unclear how long this takes, but 10us seems to be a
// good enough duration to wait, for both fast and slow baud rates.
self.flush_last_byte();
Ok(())
}

fn flush_last_byte(&mut self) {
// This function handles an edge case that happens when the TX FIFO count
// changes to 0. The FSM is in the Idle state for a short while after
// the last byte is moved out of the FIFO. It is unclear how long this
// takes, but 10us seems to be a good enough duration to wait, for both
// fast and slow baud rates.
crate::rom::ets_delay_us(10);
while !self.is_tx_idle() {}
Ok(())
}

/// Checks if the TX line is idle for this UART instance.
Expand Down Expand Up @@ -1935,10 +1941,15 @@ impl UartTx<'_, Async> {
///
/// This function is cancellation safe.
pub async fn flush_async(&mut self) -> Result<(), TxError> {
if self.tx_fifo_count() > 0 {
// Nothing is guaranteed to clear the Done status, so let's loop here in case Tx
// was Done before the last write operation that pushed data into the
// FIFO.
while self.tx_fifo_count() > 0 {
UartTxFuture::new(self.uart.reborrow(), TxEvent::Done).await;
}

self.flush_last_byte();

Ok(())
}
}
Expand Down
37 changes: 37 additions & 0 deletions hil-test/tests/uart_tx_rx_async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,43 @@ mod tests {
}
}

#[test]
async fn flush_async_does_not_return_prematurely(mut ctx: Context) {
// Force TX_DONE to be set
ctx.tx.write_async(&[0u8; 10]).await.unwrap();

let mut read = [0u8; 10];
ctx.rx.read_exact_async(&mut read).await.unwrap();

// The flush should not return until the data is actually sent, regardless of
// previous TX_DONE status.
for _ in 0..10 {
ctx.tx.write_async(&[1u8; 10]).await.unwrap();
ctx.tx.flush_async().await.unwrap();

let read_count = ctx.rx.read_buffered(&mut read).unwrap();

assert_eq!(read_count, 10);
assert_eq!(&read, &[1u8; 10]);
}
}

#[test]
async fn flush_async_does_not_return_prematurely_even_for_short_writes(mut ctx: Context) {
let mut read = [0u8; 2];
// The flush should not return until the data is actually sent, regardless of
// previous TX_DONE status.
for i in 0..10 {
ctx.tx.write_async(&[i as u8]).await.unwrap();
ctx.tx.flush_async().await.unwrap();

let read_count = ctx.rx.read_buffered(&mut read).unwrap();

assert_eq!(read_count, 1);
assert_eq!(read[0], i as u8);
}
}

#[test]
async fn rx_overflow_is_detected(mut ctx: Context) {
let mut to_send: &[u8] = &[0; 250];
Expand Down

0 comments on commit 502d41f

Please sign in to comment.