diff options
| author | James Munns <[email protected]> | 2023-12-20 17:27:58 +0100 |
|---|---|---|
| committer | James Munns <[email protected]> | 2024-01-19 14:02:17 +0100 |
| commit | 1ce96f79fb356b29b882a3571415f347e48e89c9 (patch) | |
| tree | f8e7f3a79e23e9771e65d1ac491ff8dfd5c37055 | |
| parent | 94290981c359cfc4bb2355055a8a5d1497cf09aa (diff) | |
Fun Learning about the RP2040 UART impl!
| -rw-r--r-- | embassy-rp/src/uart/mod.rs | 81 |
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() { |
