aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpennae <[email protected]>2023-04-30 04:21:11 +0200
committerpennae <[email protected]>2023-05-01 15:32:58 +0200
commit861f49cfd4380d89aa13d507234a97b30a84473d (patch)
treec04ff6eb09a61ad5e2c549c9f044d6709963c3b4
parent7ab9fe0522de81583592bd09c5c57910bed1c181 (diff)
rp/uart: report errors from buffered uart
this reports errors at the same location the blocking uart would, which works out to being mostly exact (except in the case of overruns, where one extra character is dropped). this is actually easier than going nuclear in the case of errors and nuking both the buffer contents and the rx fifo, both of which are things we'd have to do in addition to what's added here, and neither are needed for correctness.
-rw-r--r--embassy-rp/src/uart/buffered.rs86
-rw-r--r--tests/rp/src/bin/uart_buffered.rs249
2 files changed, 301 insertions, 34 deletions
diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs
index 3fe0c8c4e..58fede765 100644
--- a/embassy-rp/src/uart/buffered.rs
+++ b/embassy-rp/src/uart/buffered.rs
@@ -2,6 +2,7 @@ use core::future::{poll_fn, Future};
2use core::slice; 2use core::slice;
3use core::task::Poll; 3use core::task::Poll;
4 4
5use atomic_polyfill::{AtomicU8, Ordering};
5use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; 6use embassy_cortex_m::interrupt::{Interrupt, InterruptExt};
6use embassy_hal_common::atomic_ring_buffer::RingBuffer; 7use embassy_hal_common::atomic_ring_buffer::RingBuffer;
7use embassy_sync::waitqueue::AtomicWaker; 8use embassy_sync::waitqueue::AtomicWaker;
@@ -16,8 +17,15 @@ pub struct State {
16 tx_buf: RingBuffer, 17 tx_buf: RingBuffer,
17 rx_waker: AtomicWaker, 18 rx_waker: AtomicWaker,
18 rx_buf: RingBuffer, 19 rx_buf: RingBuffer,
20 rx_error: AtomicU8,
19} 21}
20 22
23// these must match bits 8..11 in UARTDR
24const RXE_OVERRUN: u8 = 8;
25const RXE_BREAK: u8 = 4;
26const RXE_PARITY: u8 = 2;
27const RXE_FRAMING: u8 = 1;
28
21impl State { 29impl State {
22 pub const fn new() -> Self { 30 pub const fn new() -> Self {
23 Self { 31 Self {
@@ -25,6 +33,7 @@ impl State {
25 tx_buf: RingBuffer::new(), 33 tx_buf: RingBuffer::new(),
26 rx_waker: AtomicWaker::new(), 34 rx_waker: AtomicWaker::new(),
27 tx_waker: AtomicWaker::new(), 35 tx_waker: AtomicWaker::new(),
36 rx_error: AtomicU8::new(0),
28 } 37 }
29 } 38 }
30} 39}
@@ -196,7 +205,25 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> {
196 }) 205 })
197 } 206 }
198 207
199 fn try_read(buf: &mut [u8]) -> Poll<Result<usize, Error>> { 208 fn get_rx_error() -> Option<Error> {
209 let errs = T::buffered_state().rx_error.swap(0, Ordering::Relaxed);
210 if errs & RXE_OVERRUN != 0 {
211 Some(Error::Overrun)
212 } else if errs & RXE_BREAK != 0 {
213 Some(Error::Break)
214 } else if errs & RXE_PARITY != 0 {
215 Some(Error::Parity)
216 } else if errs & RXE_FRAMING != 0 {
217 Some(Error::Framing)
218 } else {
219 None
220 }
221 }
222
223 fn try_read(buf: &mut [u8]) -> Poll<Result<usize, Error>>
224 where
225 T: 'd,
226 {
200 if buf.is_empty() { 227 if buf.is_empty() {
201 return Poll::Ready(Ok(0)); 228 return Poll::Ready(Ok(0));
202 } 229 }
@@ -210,13 +237,16 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> {
210 }); 237 });
211 238
212 let result = if n == 0 { 239 let result = if n == 0 {
213 return Poll::Pending; 240 match Self::get_rx_error() {
241 None => return Poll::Pending,
242 Some(e) => Err(e),
243 }
214 } else { 244 } else {
215 Ok(n) 245 Ok(n)
216 }; 246 };
217 247
218 // (Re-)Enable the interrupt to receive more data in case it was 248 // (Re-)Enable the interrupt to receive more data in case it was
219 // disabled because the buffer was full. 249 // disabled because the buffer was full or errors were detected.
220 let regs = T::regs(); 250 let regs = T::regs();
221 unsafe { 251 unsafe {
222 regs.uartimsc().write_set(|w| { 252 regs.uartimsc().write_set(|w| {
@@ -237,18 +267,28 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> {
237 } 267 }
238 } 268 }
239 269
240 fn fill_buf<'a>() -> impl Future<Output = Result<&'a [u8], Error>> { 270 fn fill_buf<'a>() -> impl Future<Output = Result<&'a [u8], Error>>
271 where
272 T: 'd,
273 {
241 poll_fn(move |cx| { 274 poll_fn(move |cx| {
242 let state = T::buffered_state(); 275 let state = T::buffered_state();
243 let mut rx_reader = unsafe { state.rx_buf.reader() }; 276 let mut rx_reader = unsafe { state.rx_buf.reader() };
244 let (p, n) = rx_reader.pop_buf(); 277 let (p, n) = rx_reader.pop_buf();
245 if n == 0 { 278 let result = if n == 0 {
246 state.rx_waker.register(cx.waker()); 279 match Self::get_rx_error() {
247 return Poll::Pending; 280 None => {
248 } 281 state.rx_waker.register(cx.waker());
282 return Poll::Pending;
283 }
284 Some(e) => Err(e),
285 }
286 } else {
287 let buf = unsafe { slice::from_raw_parts(p, n) };
288 Ok(buf)
289 };
249 290
250 let buf = unsafe { slice::from_raw_parts(p, n) }; 291 Poll::Ready(result)
251 Poll::Ready(Ok(buf))
252 }) 292 })
253 } 293 }
254 294
@@ -258,7 +298,7 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> {
258 rx_reader.pop_done(amt); 298 rx_reader.pop_done(amt);
259 299
260 // (Re-)Enable the interrupt to receive more data in case it was 300 // (Re-)Enable the interrupt to receive more data in case it was
261 // disabled because the buffer was full. 301 // disabled because the buffer was full or errors were detected.
262 let regs = T::regs(); 302 let regs = T::regs();
263 unsafe { 303 unsafe {
264 regs.uartimsc().write_set(|w| { 304 regs.uartimsc().write_set(|w| {
@@ -478,19 +518,37 @@ pub(crate) unsafe fn on_interrupt<T: Instance>(_: *mut ()) {
478 let mut rx_writer = s.rx_buf.writer(); 518 let mut rx_writer = s.rx_buf.writer();
479 let rx_buf = rx_writer.push_slice(); 519 let rx_buf = rx_writer.push_slice();
480 let mut n_read = 0; 520 let mut n_read = 0;
521 let mut error = false;
481 for rx_byte in rx_buf { 522 for rx_byte in rx_buf {
482 if r.uartfr().read().rxfe() { 523 if r.uartfr().read().rxfe() {
483 break; 524 break;
484 } 525 }
485 *rx_byte = r.uartdr().read().data(); 526 let dr = r.uartdr().read();
527 if (dr.0 >> 8) != 0 {
528 s.rx_error.fetch_or((dr.0 >> 8) as u8, Ordering::Relaxed);
529 error = true;
530 // only fill the buffer with valid characters. the current character is fine
531 // if the error is an overrun, but if we add it to the buffer we'll report
532 // the overrun one character too late. drop it instead and pretend we were
533 // a bit slower at draining the rx fifo than we actually were.
534 // this is consistent with blocking uart error reporting.
535 break;
536 }
537 *rx_byte = dr.data();
486 n_read += 1; 538 n_read += 1;
487 } 539 }
488 if n_read > 0 { 540 if n_read > 0 {
489 rx_writer.push_done(n_read); 541 rx_writer.push_done(n_read);
490 s.rx_waker.wake(); 542 s.rx_waker.wake();
543 } else if error {
544 s.rx_waker.wake();
491 } 545 }
492 // Disable any further RX interrupts when the buffer becomes full. 546 // Disable any further RX interrupts when the buffer becomes full or
493 if s.rx_buf.is_full() { 547 // errors have occured. this lets us buffer additional errors in the
548 // fifo without needing more error storage locations, and most applications
549 // will want to do a full reset of their uart state anyway once an error
550 // has happened.
551 if s.rx_buf.is_full() || error {
494 r.uartimsc().write_clear(|w| { 552 r.uartimsc().write_clear(|w| {
495 w.set_rxim(true); 553 w.set_rxim(true);
496 w.set_rtim(true); 554 w.set_rtim(true);
diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs
index a1f0e2bf3..1deb22ce6 100644
--- a/tests/rp/src/bin/uart_buffered.rs
+++ b/tests/rp/src/bin/uart_buffered.rs
@@ -2,39 +2,248 @@
2#![no_main] 2#![no_main]
3#![feature(type_alias_impl_trait)] 3#![feature(type_alias_impl_trait)]
4 4
5use defmt::{assert_eq, *}; 5use defmt::{assert_eq, panic, *};
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_rp::gpio::{Level, Output};
7use embassy_rp::interrupt; 8use embassy_rp::interrupt;
8use embassy_rp::uart::{BufferedUart, Config}; 9use embassy_rp::uart::{BufferedUart, BufferedUartRx, Config, Error, Instance, Parity};
9use embedded_io::asynch::{Read, Write}; 10use embassy_time::{Duration, Timer};
11use embedded_io::asynch::{Read, ReadExactError, Write};
10use {defmt_rtt as _, panic_probe as _}; 12use {defmt_rtt as _, panic_probe as _};
11 13
14async fn read<const N: usize>(uart: &mut BufferedUart<'_, impl Instance>) -> Result<[u8; N], Error> {
15 let mut buf = [255; N];
16 match uart.read_exact(&mut buf).await {
17 Ok(()) => Ok(buf),
18 // we should not ever produce an Eof condition
19 Err(ReadExactError::UnexpectedEof) => panic!(),
20 Err(ReadExactError::Other(e)) => Err(e),
21 }
22}
23
24async fn read1<const N: usize>(uart: &mut BufferedUartRx<'_, impl Instance>) -> Result<[u8; N], Error> {
25 let mut buf = [255; N];
26 match uart.read_exact(&mut buf).await {
27 Ok(()) => Ok(buf),
28 // we should not ever produce an Eof condition
29 Err(ReadExactError::UnexpectedEof) => panic!(),
30 Err(ReadExactError::Other(e)) => Err(e),
31 }
32}
33
34async fn send(pin: &mut Output<'_, impl embassy_rp::gpio::Pin>, v: u8, parity: Option<bool>) {
35 pin.set_low();
36 Timer::after(Duration::from_millis(1)).await;
37 for i in 0..8 {
38 if v & (1 << i) == 0 {
39 pin.set_low();
40 } else {
41 pin.set_high();
42 }
43 Timer::after(Duration::from_millis(1)).await;
44 }
45 if let Some(b) = parity {
46 if b {
47 pin.set_high();
48 } else {
49 pin.set_low();
50 }
51 Timer::after(Duration::from_millis(1)).await;
52 }
53 pin.set_high();
54 Timer::after(Duration::from_millis(1)).await;
55}
56
12#[embassy_executor::main] 57#[embassy_executor::main]
13async fn main(_spawner: Spawner) { 58async fn main(_spawner: Spawner) {
14 let p = embassy_rp::init(Default::default()); 59 let p = embassy_rp::init(Default::default());
15 info!("Hello World!"); 60 info!("Hello World!");
16 61
17 let (tx, rx, uart) = (p.PIN_0, p.PIN_1, p.UART0); 62 let (mut tx, mut rx, mut uart) = (p.PIN_0, p.PIN_1, p.UART0);
63 let mut irq = interrupt::take!(UART0_IRQ);
64
65 {
66 let config = Config::default();
67 let tx_buf = &mut [0u8; 16];
68 let rx_buf = &mut [0u8; 16];
69 let mut uart = BufferedUart::new(&mut uart, &mut irq, &mut tx, &mut rx, tx_buf, rx_buf, config);
70
71 // Make sure we send more bytes than fits in the FIFO, to test the actual
72 // bufferedUart.
73
74 let data = [
75 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
76 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
77 ];
78 uart.write_all(&data).await.unwrap();
79 info!("Done writing");
80
81 assert_eq!(read(&mut uart).await.unwrap(), data);
82 }
83
84 info!("test overflow detection");
85 {
86 let config = Config::default();
87 let tx_buf = &mut [0u8; 16];
88 let rx_buf = &mut [0u8; 16];
89 let mut uart = BufferedUart::new(&mut uart, &mut irq, &mut tx, &mut rx, tx_buf, rx_buf, config);
90
91 // Make sure we send more bytes than fits in the FIFO, to test the actual
92 // bufferedUart.
93
94 let data = [
95 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
96 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
97 ];
98 let overflow = [
99 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
100 ];
101 // give each block time to settle into the fifo. we want the overrun to occur at a well-defined point.
102 uart.write_all(&data).await.unwrap();
103 uart.blocking_flush().unwrap();
104 while uart.busy() {}
105 uart.write_all(&overflow).await.unwrap();
106 uart.blocking_flush().unwrap();
107 while uart.busy() {}
108
109 // already buffered/fifod prefix is valid
110 assert_eq!(read(&mut uart).await.unwrap(), data);
111 // next received character causes overrun error and is discarded
112 uart.write_all(&[1, 2, 3]).await.unwrap();
113 uart.blocking_flush().unwrap();
114 assert_eq!(read::<1>(&mut uart).await.unwrap_err(), Error::Overrun);
115 assert_eq!(read(&mut uart).await.unwrap(), [2, 3]);
116 }
117
118 info!("test break detection");
119 {
120 let mut config = Config::default();
121 config.baudrate = 1000;
122 let tx_buf = &mut [0u8; 16];
123 let rx_buf = &mut [0u8; 16];
124 let mut uart = BufferedUart::new(&mut uart, &mut irq, &mut tx, &mut rx, tx_buf, rx_buf, config);
125
126 // break on empty buffer
127 uart.send_break(20).await;
128 assert_eq!(read::<1>(&mut uart).await.unwrap_err(), Error::Break);
129 uart.write_all(&[64]).await.unwrap();
130 assert_eq!(read(&mut uart).await.unwrap(), [64]);
131
132 // break on partially filled buffer
133 uart.write_all(&[65; 2]).await.unwrap();
134 uart.send_break(20).await;
135 uart.write_all(&[66]).await.unwrap();
136 assert_eq!(read(&mut uart).await.unwrap(), [65; 2]);
137 assert_eq!(read::<1>(&mut uart).await.unwrap_err(), Error::Break);
138 assert_eq!(read(&mut uart).await.unwrap(), [66]);
139
140 // break on full buffer
141 uart.write_all(&[64; 16]).await.unwrap();
142 uart.send_break(20).await;
143 uart.write_all(&[65]).await.unwrap();
144 assert_eq!(read(&mut uart).await.unwrap(), [64; 16]);
145 assert_eq!(read::<1>(&mut uart).await.unwrap_err(), Error::Break);
146 assert_eq!(read(&mut uart).await.unwrap(), [65]);
147 }
148
149 // parity detection. here we bitbang to not require two uarts.
150 info!("test parity error detection");
151 {
152 let mut pin = Output::new(&mut tx, Level::High);
153 // choose a very slow baud rate to make tests reliable even with O0
154 let mut config = Config::default();
155 config.baudrate = 1000;
156 config.parity = Parity::ParityEven;
157 let rx_buf = &mut [0u8; 16];
158 let mut uart = BufferedUartRx::new(&mut uart, &mut irq, &mut rx, rx_buf, config);
159
160 async fn chr(pin: &mut Output<'_, impl embassy_rp::gpio::Pin>, v: u8, parity: u32) {
161 send(pin, v, Some(parity != 0)).await;
162 }
163
164 // first check that we can send correctly
165 chr(&mut pin, 64, 1).await;
166 assert_eq!(read1(&mut uart).await.unwrap(), [64]);
167
168 // parity on empty buffer
169 chr(&mut pin, 64, 0).await;
170 chr(&mut pin, 4, 1).await;
171 assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Parity);
172 assert_eq!(read1(&mut uart).await.unwrap(), [4]);
173
174 // parity on partially filled buffer
175 chr(&mut pin, 64, 1).await;
176 chr(&mut pin, 32, 1).await;
177 chr(&mut pin, 64, 0).await;
178 chr(&mut pin, 65, 0).await;
179 assert_eq!(read1(&mut uart).await.unwrap(), [64, 32]);
180 assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Parity);
181 assert_eq!(read1(&mut uart).await.unwrap(), [65]);
182
183 // parity on full buffer
184 for i in 0..16 {
185 chr(&mut pin, i, i.count_ones() % 2).await;
186 }
187 chr(&mut pin, 64, 0).await;
188 chr(&mut pin, 65, 0).await;
189 assert_eq!(
190 read1(&mut uart).await.unwrap(),
191 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
192 );
193 assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Parity);
194 assert_eq!(read1(&mut uart).await.unwrap(), [65]);
195 }
196
197 // framing error detection. here we bitbang because there's no other way.
198 info!("test framing error detection");
199 {
200 let mut pin = Output::new(&mut tx, Level::High);
201 // choose a very slow baud rate to make tests reliable even with O0
202 let mut config = Config::default();
203 config.baudrate = 1000;
204 let rx_buf = &mut [0u8; 16];
205 let mut uart = BufferedUartRx::new(&mut uart, &mut irq, &mut rx, rx_buf, config);
206
207 async fn chr(pin: &mut Output<'_, impl embassy_rp::gpio::Pin>, v: u8, good: bool) {
208 if good {
209 send(pin, v, None).await;
210 } else {
211 send(pin, v, Some(false)).await;
212 }
213 }
18 214
19 let config = Config::default(); 215 // first check that we can send correctly
20 let irq = interrupt::take!(UART0_IRQ); 216 chr(&mut pin, 64, true).await;
21 let tx_buf = &mut [0u8; 16]; 217 assert_eq!(read1(&mut uart).await.unwrap(), [64]);
22 let rx_buf = &mut [0u8; 16];
23 let mut uart = BufferedUart::new(uart, irq, tx, rx, tx_buf, rx_buf, config);
24 218
25 // Make sure we send more bytes than fits in the FIFO, to test the actual 219 // framing on empty buffer
26 // bufferedUart. 220 chr(&mut pin, 64, false).await;
221 assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Framing);
222 chr(&mut pin, 65, true).await;
223 assert_eq!(read1(&mut uart).await.unwrap(), [65]);
27 224
28 let data = [ 225 // framing on partially filled buffer
29 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 226 chr(&mut pin, 64, true).await;
30 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 227 chr(&mut pin, 32, true).await;
31 ]; 228 chr(&mut pin, 64, false).await;
32 uart.write_all(&data).await.unwrap(); 229 chr(&mut pin, 65, true).await;
33 info!("Done writing"); 230 assert_eq!(read1(&mut uart).await.unwrap(), [64, 32]);
231 assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Framing);
232 assert_eq!(read1(&mut uart).await.unwrap(), [65]);
34 233
35 let mut buf = [0; 48]; 234 // framing on full buffer
36 uart.read_exact(&mut buf).await.unwrap(); 235 for i in 0..16 {
37 assert_eq!(buf, data); 236 chr(&mut pin, i, true).await;
237 }
238 chr(&mut pin, 64, false).await;
239 chr(&mut pin, 65, true).await;
240 assert_eq!(
241 read1(&mut uart).await.unwrap(),
242 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
243 );
244 assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Framing);
245 assert_eq!(read1(&mut uart).await.unwrap(), [65]);
246 }
38 247
39 info!("Test OK"); 248 info!("Test OK");
40 cortex_m::asm::bkpt(); 249 cortex_m::asm::bkpt();