mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-25 16:23:10 +00:00
Debugging RSR
This commit is contained in:
parent
fe172109be
commit
94290981c3
@ -116,10 +116,16 @@ pub enum Error {
|
|||||||
Parity,
|
Parity,
|
||||||
/// Triggered when the received character didn't have a valid stop bit.
|
/// Triggered when the received character didn't have a valid stop bit.
|
||||||
Framing,
|
Framing,
|
||||||
/// There was an issue when calculating the number of transferred items
|
}
|
||||||
/// in an aborted DMA transaction. This is likely an error in the
|
|
||||||
/// driver implementation, please open an embassy issue.
|
/// Read To Break error
|
||||||
Calculation,
|
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum ReadToBreakError {
|
||||||
|
/// Read this many bytes, but never received a line break.
|
||||||
|
MissingBreak(usize),
|
||||||
|
Other(Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Internal DMA state of UART RX.
|
/// Internal DMA state of UART RX.
|
||||||
@ -432,12 +438,10 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let mut did_finish = false;
|
|
||||||
let errors = match transfer_result {
|
let errors = match transfer_result {
|
||||||
Either::First(()) => {
|
Either::First(()) => {
|
||||||
// We're here because the DMA finished, BUT if an error occurred on the LAST
|
// We're here because the DMA finished, BUT if an error occurred on the LAST
|
||||||
// byte, then we may still need to grab the error state!
|
// byte, then we may still need to grab the error state!
|
||||||
did_finish = true;
|
|
||||||
Uartris(T::dma_state().rx_errs.swap(0, Ordering::Relaxed) as u32)
|
Uartris(T::dma_state().rx_errs.swap(0, Ordering::Relaxed) as u32)
|
||||||
}
|
}
|
||||||
Either::Second(e) => {
|
Either::Second(e) => {
|
||||||
@ -452,12 +456,7 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we DID get an error, and DID finish, we'll have one error byte left in the FIFO.
|
// If we DID get an error, we need to figure out which one it was.
|
||||||
// Pop it since we are reporting the error on THIS transaction.
|
|
||||||
if did_finish {
|
|
||||||
let _ = T::regs().uartdr().read();
|
|
||||||
}
|
|
||||||
|
|
||||||
if errors.oeris() {
|
if errors.oeris() {
|
||||||
return Err(Error::Overrun);
|
return Err(Error::Overrun);
|
||||||
} else if errors.beris() {
|
} else if errors.beris() {
|
||||||
@ -470,15 +469,27 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
|
|||||||
unreachable!("unrecognized rx error");
|
unreachable!("unrecognized rx error");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read from the UART, until one of the following occurs:
|
/// Read from the UART, waiting for a line break.
|
||||||
|
///
|
||||||
|
/// We read until one of the following occurs:
|
||||||
///
|
///
|
||||||
/// * We read `buffer.len()` bytes without a line break
|
/// * We read `buffer.len()` bytes without a line break
|
||||||
/// * returns `Ok(buffer)`
|
/// * returns `Err(ReadToBreakError::MissingBreak(buffer.len()))`
|
||||||
/// * We read `n` bytes then a line break occurs
|
/// * We read `n` bytes then a line break occurs
|
||||||
/// * returns `Ok(&mut buffer[..n])`
|
/// * returns `Ok(n)`
|
||||||
/// * We encounter some error OTHER than a line break
|
/// * We encounter some error OTHER than a line break
|
||||||
/// * returns `Err(Error)`
|
/// * returns `Err(ReadToBreakError::Other(error))`
|
||||||
pub async fn read_to_break<'a>(&mut self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], Error> {
|
///
|
||||||
|
/// **NOTE**: you MUST provide a buffer one byte larger than your largest expected
|
||||||
|
/// message to reliably detect the framing on one single call to `read_to_break()`.
|
||||||
|
///
|
||||||
|
/// * If you expect a message of 20 bytes + line break, and provide a 20-byte buffer:
|
||||||
|
/// * The first call to `read_to_break()` will return `Err(ReadToBreakError::MissingBreak(20))`
|
||||||
|
/// * The next call to `read_to_break()` will immediately return `Ok(0)`, from the "stale" line break
|
||||||
|
/// * If you expect a message of 20 bytes + line break, and provide a 21-byte buffer:
|
||||||
|
/// * The first call to `read_to_break()` will return `Ok(20)`.
|
||||||
|
/// * The next call to `read_to_break()` will work as expected
|
||||||
|
pub async fn read_to_break(&mut self, buffer: &mut [u8]) -> Result<usize, ReadToBreakError> {
|
||||||
// clear error flags before we drain the fifo. errors that have accumulated
|
// clear error flags before we drain the fifo. errors that have accumulated
|
||||||
// in the flags will also be present in the fifo.
|
// in the flags will also be present in the fifo.
|
||||||
T::dma_state().rx_errs.store(0, Ordering::Relaxed);
|
T::dma_state().rx_errs.store(0, Ordering::Relaxed);
|
||||||
@ -498,11 +509,11 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
|
|||||||
// Drained fifo, still some room left!
|
// Drained fifo, still some room left!
|
||||||
Ok(len) if len < buffer.len() => &mut buffer[len..],
|
Ok(len) if len < buffer.len() => &mut buffer[len..],
|
||||||
// Drained (some/all of the fifo), no room left
|
// Drained (some/all of the fifo), no room left
|
||||||
Ok(_) => return Ok(buffer),
|
Ok(len) => return Err(ReadToBreakError::MissingBreak(len)),
|
||||||
// We got a break WHILE draining the FIFO, return what we did get before the break
|
// We got a break WHILE draining the FIFO, return what we did get before the break
|
||||||
Err((i, Error::Break)) => return Ok(&mut buffer[..i]),
|
Err((i, Error::Break)) => return Ok(i),
|
||||||
// Some other error, just return the error
|
// Some other error, just return the error
|
||||||
Err((_i, e)) => return Err(e),
|
Err((_i, e)) => return Err(ReadToBreakError::Other(e)),
|
||||||
};
|
};
|
||||||
|
|
||||||
// start a dma transfer. if errors have happened in the interim some error
|
// start a dma transfer. if errors have happened in the interim some error
|
||||||
@ -538,14 +549,11 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let mut did_finish = false;
|
|
||||||
|
|
||||||
// Figure out our error state
|
// Figure out our error state
|
||||||
let errors = match transfer_result {
|
let errors = match transfer_result {
|
||||||
Either::First(()) => {
|
Either::First(()) => {
|
||||||
// We're here because the DMA finished, BUT if an error occurred on the LAST
|
// We're here because the DMA finished, BUT if an error occurred on the LAST
|
||||||
// byte, then we may still need to grab the error state!
|
// byte, then we may still need to grab the error state!
|
||||||
did_finish = true;
|
|
||||||
Uartris(T::dma_state().rx_errs.swap(0, Ordering::Relaxed) as u32)
|
Uartris(T::dma_state().rx_errs.swap(0, Ordering::Relaxed) as u32)
|
||||||
}
|
}
|
||||||
Either::Second(e) => {
|
Either::Second(e) => {
|
||||||
@ -557,7 +565,8 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
|
|||||||
|
|
||||||
if errors.0 == 0 {
|
if errors.0 == 0 {
|
||||||
// No errors? That means we filled the buffer without a line break.
|
// No errors? That means we filled the buffer without a line break.
|
||||||
return Ok(buffer);
|
// For THIS function, that's a problem.
|
||||||
|
return Err(ReadToBreakError::MissingBreak(buffer.len()));
|
||||||
} else if errors.beris() {
|
} else if errors.beris() {
|
||||||
// We got a Line Break! By this point, we've finished/aborted the DMA
|
// We got a Line Break! By this point, we've finished/aborted the DMA
|
||||||
// transaction, which means that we need to figure out where it left off
|
// transaction, which means that we need to figure out where it left off
|
||||||
@ -568,36 +577,60 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
|
|||||||
let sval = buffer.as_ptr() as usize;
|
let sval = buffer.as_ptr() as usize;
|
||||||
let eval = sval + buffer.len();
|
let eval = sval + buffer.len();
|
||||||
|
|
||||||
// Note: the `write_addr()` is where the NEXT write would be.
|
// Note: the `write_addr()` is where the NEXT write would be, but we ALSO
|
||||||
let mut last_written = ch.regs().write_addr().read() as usize;
|
// got a line break, so take an offset of 1
|
||||||
|
let mut next_addr = ch.regs().write_addr().read() as usize;
|
||||||
// Did we finish the whole DMA transfer?
|
|
||||||
if !did_finish {
|
|
||||||
// No, we did not! We stopped because we got a line break. That means the
|
|
||||||
// DMA transferred one "garbage byte" from the FIFO that held an error.
|
|
||||||
last_written -= 1;
|
|
||||||
} else {
|
|
||||||
// We did finish and got a "late break", where the interrupt error fired AFTER
|
|
||||||
// we got the last byte. Pop that from the FIFO so we don't trip on it next time.
|
|
||||||
let dr = T::regs().uartdr().read();
|
|
||||||
if !dr.be() {
|
|
||||||
// Got an error after DMA but no error in the FIFO?
|
|
||||||
return Err(Error::Calculation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we DON'T end up inside the range, something has gone really wrong.
|
// If we DON'T end up inside the range, something has gone really wrong.
|
||||||
if (last_written < sval) || (last_written > eval) {
|
if (next_addr < sval) || (next_addr > eval) {
|
||||||
return Err(Error::Calculation);
|
unreachable!("UART DMA reported invalid `write_addr`");
|
||||||
}
|
}
|
||||||
let taken = last_written - sval;
|
|
||||||
return Ok(&mut buffer[..taken]);
|
// If we finished the full DMA, AND the FIFO is not-empty, AND that
|
||||||
|
// byte reports a break error, THAT byte caused the error, and not data
|
||||||
|
// in the DMA transfer! Otherwise: our DMA grabbed one "bad" byte.
|
||||||
|
//
|
||||||
|
// Note: even though we COULD detect this and return `Ok(buffer.len())`,
|
||||||
|
// we DON'T, as that is racy: if we read the error state AFTER the data
|
||||||
|
// was transferred but BEFORE the line break interrupt fired, we'd return
|
||||||
|
// `MissingBreak`. Ignoring the fact that there's a line break in the FIFO
|
||||||
|
// means callers consistently see the same error regardless of
|
||||||
|
let regs = T::regs();
|
||||||
|
let is_end = next_addr == eval;
|
||||||
|
let not_empty = !regs.uartfr().read().rxfe();
|
||||||
|
let is_break = regs.uartrsr().read().be();
|
||||||
|
let last_good = is_end && not_empty && is_break;
|
||||||
|
|
||||||
|
defmt::println!("next: {=usize}, sval: {=usize}, eval: {=usize}", next_addr, sval, eval);
|
||||||
|
defmt::println!("lg: {=bool}, is_end: {=bool}, not_empty: {=bool}, is_break: {=bool}", last_good, is_end, not_empty, is_break);
|
||||||
|
|
||||||
|
if is_end && not_empty && !is_break {
|
||||||
|
let val = regs.uartdr().read();
|
||||||
|
let tb = regs.uartrsr().read().be();
|
||||||
|
let te = regs.uartfr().read().rxfe();
|
||||||
|
defmt::println!("THEN: {=bool}, {=bool}", tb, te);
|
||||||
|
if val.be() {
|
||||||
|
panic!("Oh what the hell");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !last_good {
|
||||||
|
defmt::println!("Last not good!");
|
||||||
|
// The last is NOT good (it's the line-break `0x00`), so elide it
|
||||||
|
next_addr -= 1;
|
||||||
|
} else {
|
||||||
|
defmt::println!("last good!");
|
||||||
|
}
|
||||||
|
|
||||||
|
defmt::println!("->{=usize}", next_addr - sval);
|
||||||
|
|
||||||
|
return Ok(next_addr - sval);
|
||||||
} else if errors.oeris() {
|
} else if errors.oeris() {
|
||||||
return Err(Error::Overrun);
|
return Err(ReadToBreakError::Other(Error::Overrun));
|
||||||
} else if errors.peris() {
|
} else if errors.peris() {
|
||||||
return Err(Error::Parity);
|
return Err(ReadToBreakError::Other(Error::Parity));
|
||||||
} else if errors.feris() {
|
} else if errors.feris() {
|
||||||
return Err(Error::Framing);
|
return Err(ReadToBreakError::Other(Error::Framing));
|
||||||
}
|
}
|
||||||
unreachable!("unrecognized rx error");
|
unreachable!("unrecognized rx error");
|
||||||
}
|
}
|
||||||
@ -902,7 +935,7 @@ impl<'d, T: Instance> Uart<'d, T, Async> {
|
|||||||
self.rx.read(buffer).await
|
self.rx.read(buffer).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_to_break<'a>(&mut self, buf: &'a mut [u8]) -> Result<&'a mut [u8], Error> {
|
pub async fn read_to_break<'a>(&mut self, buf: &'a mut [u8]) -> Result<usize, ReadToBreakError> {
|
||||||
self.rx.read_to_break(buf).await
|
self.rx.read_to_break(buf).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1004,7 +1037,6 @@ impl embedded_hal_nb::serial::Error for Error {
|
|||||||
Self::Break => embedded_hal_nb::serial::ErrorKind::Other,
|
Self::Break => embedded_hal_nb::serial::ErrorKind::Other,
|
||||||
Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,
|
Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,
|
||||||
Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity,
|
Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity,
|
||||||
Self::Calculation => embedded_hal_nb::serial::ErrorKind::Other,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user