aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Munns <[email protected]>2023-12-20 17:27:58 +0100
committerJames Munns <[email protected]>2024-01-19 14:02:17 +0100
commit1ce96f79fb356b29b882a3571415f347e48e89c9 (patch)
treef8e7f3a79e23e9771e65d1ac491ff8dfd5c37055
parent94290981c359cfc4bb2355055a8a5d1497cf09aa (diff)
Fun Learning about the RP2040 UART impl!
-rw-r--r--embassy-rp/src/uart/mod.rs81
1 files changed, 41 insertions, 40 deletions
diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs
index 61d3af5de..a9ae6c3f4 100644
--- a/embassy-rp/src/uart/mod.rs
+++ b/embassy-rp/src/uart/mod.rs
@@ -577,54 +577,55 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
577 let sval = buffer.as_ptr() as usize; 577 let sval = buffer.as_ptr() as usize;
578 let eval = sval + buffer.len(); 578 let eval = sval + buffer.len();
579 579
580 // Note: the `write_addr()` is where the NEXT write would be, but we ALSO 580 // This is the address where the DMA would write to next
581 // got a line break, so take an offset of 1 581 let next_addr = ch.regs().write_addr().read() as usize;
582 let mut next_addr = ch.regs().write_addr().read() as usize;
583 582
584 // If we DON'T end up inside the range, something has gone really wrong. 583 // If we DON'T end up inside the range, something has gone really wrong.
584 // Note that it's okay that `eval` is one past the end of the slice, as
585 // this is where the write pointer will end up at the end of a full
586 // transfer.
585 if (next_addr < sval) || (next_addr > eval) { 587 if (next_addr < sval) || (next_addr > eval) {
586 unreachable!("UART DMA reported invalid `write_addr`"); 588 unreachable!("UART DMA reported invalid `write_addr`");
587 } 589 }
588 590
589 // If we finished the full DMA, AND the FIFO is not-empty, AND that
590 // byte reports a break error, THAT byte caused the error, and not data
591 // in the DMA transfer! Otherwise: our DMA grabbed one "bad" byte.
592 //
593 // Note: even though we COULD detect this and return `Ok(buffer.len())`,
594 // we DON'T, as that is racy: if we read the error state AFTER the data
595 // was transferred but BEFORE the line break interrupt fired, we'd return
596 // `MissingBreak`. Ignoring the fact that there's a line break in the FIFO
597 // means callers consistently see the same error regardless of
598 let regs = T::regs(); 591 let regs = T::regs();
599 let is_end = next_addr == eval; 592 let all_full = next_addr == eval;
600 let not_empty = !regs.uartfr().read().rxfe();
601 let is_break = regs.uartrsr().read().be();
602 let last_good = is_end && not_empty && is_break;
603
604 defmt::println!("next: {=usize}, sval: {=usize}, eval: {=usize}", next_addr, sval, eval);
605 defmt::println!("lg: {=bool}, is_end: {=bool}, not_empty: {=bool}, is_break: {=bool}", last_good, is_end, not_empty, is_break);
606
607 if is_end && not_empty && !is_break {
608 let val = regs.uartdr().read();
609 let tb = regs.uartrsr().read().be();
610 let te = regs.uartfr().read().rxfe();
611 defmt::println!("THEN: {=bool}, {=bool}", tb, te);
612 if val.be() {
613 panic!("Oh what the hell");
614 }
615 }
616
617 if !last_good {
618 defmt::println!("Last not good!");
619 // The last is NOT good (it's the line-break `0x00`), so elide it
620 next_addr -= 1;
621 } else {
622 defmt::println!("last good!");
623 }
624
625 defmt::println!("->{=usize}", next_addr - sval);
626 593
627 return Ok(next_addr - sval); 594 // NOTE: This is off label usage of RSR! See the issue below for
595 // why I am not checking if there is an "extra" FIFO byte, and why
596 // I am checking RSR directly (it seems to report the status of the LAST
597 // POPPED value, rather than the NEXT TO POP value like the datasheet
598 // suggests!)
599 //
600 // issue: https://github.com/raspberrypi/pico-feedback/issues/367
601 let last_was_break = regs.uartrsr().read().be();
602
603 return match (all_full, last_was_break) {
604 (true, true) | (false, _) => {
605 // We got less than the full amount + a break, or the full amount
606 // and the last byte was a break. Subtract the break off.
607 Ok((next_addr - 1) - sval)
608 }
609 (true, false) => {
610 // We finished the whole DMA, and the last DMA'd byte was NOT a break
611 // character. This is an error.
612 //
613 // NOTE: we COULD potentially return Ok(buffer.len()) here, since we
614 // know a line break occured at SOME POINT after the DMA completed.
615 //
616 // However, we have no way of knowing if there was extra data BEFORE
617 // that line break, so instead return an Err to signal to the caller
618 // that there are "leftovers", and they'll catch the actual line break
619 // on the next call.
620 //
621 // Doing it like this also avoids racyness: now whether you finished
622 // the full read BEFORE the line break occurred or AFTER the line break
623 // occurs, you still get `MissingBreak(buffer.len())` instead of sometimes
624 // getting `Ok(buffer.len())` if you were "late enough" to observe the
625 // line break.
626 Err(ReadToBreakError::MissingBreak(buffer.len()))
627 }
628 };
628 } else if errors.oeris() { 629 } else if errors.oeris() {
629 return Err(ReadToBreakError::Other(Error::Overrun)); 630 return Err(ReadToBreakError::Other(Error::Overrun));
630 } else if errors.peris() { 631 } else if errors.peris() {