Fun Learning about the RP2040 UART impl!
This commit is contained in:
parent
a7b5643049
commit
fb026434bf
@ -545,54 +545,55 @@ 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, but we ALSO
|
// This is the address where the DMA would write to next
|
||||||
// got a line break, so take an offset of 1
|
let next_addr = ch.regs().write_addr().read() as usize;
|
||||||
let mut next_addr = ch.regs().write_addr().read() as usize;
|
|
||||||
|
|
||||||
// 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.
|
||||||
|
// Note that it's okay that `eval` is one past the end of the slice, as
|
||||||
|
// this is where the write pointer will end up at the end of a full
|
||||||
|
// transfer.
|
||||||
if (next_addr < sval) || (next_addr > eval) {
|
if (next_addr < sval) || (next_addr > eval) {
|
||||||
unreachable!("UART DMA reported invalid `write_addr`");
|
unreachable!("UART DMA reported invalid `write_addr`");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 regs = T::regs();
|
||||||
let is_end = next_addr == eval;
|
let all_full = 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);
|
// NOTE: This is off label usage of RSR! See the issue below for
|
||||||
defmt::println!("lg: {=bool}, is_end: {=bool}, not_empty: {=bool}, is_break: {=bool}", last_good, is_end, not_empty, is_break);
|
// why I am not checking if there is an "extra" FIFO byte, and why
|
||||||
|
// I am checking RSR directly (it seems to report the status of the LAST
|
||||||
|
// POPPED value, rather than the NEXT TO POP value like the datasheet
|
||||||
|
// suggests!)
|
||||||
|
//
|
||||||
|
// issue: https://github.com/raspberrypi/pico-feedback/issues/367
|
||||||
|
let last_was_break = regs.uartrsr().read().be();
|
||||||
|
|
||||||
if is_end && not_empty && !is_break {
|
return match (all_full, last_was_break) {
|
||||||
let val = regs.uartdr().read();
|
(true, true) | (false, _) => {
|
||||||
let tb = regs.uartrsr().read().be();
|
// We got less than the full amount + a break, or the full amount
|
||||||
let te = regs.uartfr().read().rxfe();
|
// and the last byte was a break. Subtract the break off.
|
||||||
defmt::println!("THEN: {=bool}, {=bool}", tb, te);
|
Ok((next_addr - 1) - sval)
|
||||||
if val.be() {
|
|
||||||
panic!("Oh what the hell");
|
|
||||||
}
|
}
|
||||||
|
(true, false) => {
|
||||||
|
// We finished the whole DMA, and the last DMA'd byte was NOT a break
|
||||||
|
// character. This is an error.
|
||||||
|
//
|
||||||
|
// NOTE: we COULD potentially return Ok(buffer.len()) here, since we
|
||||||
|
// know a line break occured at SOME POINT after the DMA completed.
|
||||||
|
//
|
||||||
|
// However, we have no way of knowing if there was extra data BEFORE
|
||||||
|
// that line break, so instead return an Err to signal to the caller
|
||||||
|
// that there are "leftovers", and they'll catch the actual line break
|
||||||
|
// on the next call.
|
||||||
|
//
|
||||||
|
// Doing it like this also avoids racyness: now whether you finished
|
||||||
|
// the full read BEFORE the line break occurred or AFTER the line break
|
||||||
|
// occurs, you still get `MissingBreak(buffer.len())` instead of sometimes
|
||||||
|
// getting `Ok(buffer.len())` if you were "late enough" to observe the
|
||||||
|
// line break.
|
||||||
|
Err(ReadToBreakError::MissingBreak(buffer.len()))
|
||||||
}
|
}
|
||||||
|
};
|
||||||
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(ReadToBreakError::Other(Error::Overrun));
|
return Err(ReadToBreakError::Other(Error::Overrun));
|
||||||
} else if errors.peris() {
|
} else if errors.peris() {
|
||||||
|
Loading…
Reference in New Issue
Block a user