aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2023-03-05 02:56:15 +0100
committerGitHub <[email protected]>2023-03-05 02:56:15 +0100
commitd91efe3e6252336a6e226700ca72afef2e13abc0 (patch)
treea940edf957ef7facb29d9881351abafd3f802747
parentbef559307c2b63540e73539e3ba906f4c370a131 (diff)
parent7650fea5f2bed1c39a0ff6c5934709d316547a23 (diff)
Merge pull request #1208 from embassy-rs/nrf-uarte-lockfree
nrf/buffered_uarte: make it work without rts/cts, and lock-free.
-rwxr-xr-xci.sh1
-rw-r--r--embassy-nrf/src/buffered_uarte.rs825
-rw-r--r--embassy-nrf/src/lib.rs1
-rw-r--r--embassy-nrf/src/ppi/dppi.rs2
-rw-r--r--embassy-nrf/src/ppi/mod.rs104
-rw-r--r--embassy-nrf/src/ppi/ppi.rs2
-rw-r--r--embassy-nrf/src/timer.rs43
-rw-r--r--embassy-nrf/src/uarte.rs5
-rw-r--r--examples/nrf52840/src/bin/buffered_uart.rs14
-rw-r--r--tests/nrf/.cargo/config.toml9
-rw-r--r--tests/nrf/Cargo.toml20
-rw-r--r--tests/nrf/build.rs16
-rw-r--r--tests/nrf/link_ram.x254
-rw-r--r--tests/nrf/memory.x5
-rw-r--r--tests/nrf/src/bin/buffered_uart.rs74
-rw-r--r--tests/nrf/src/bin/buffered_uart_spam.rs86
16 files changed, 1096 insertions, 365 deletions
diff --git a/ci.sh b/ci.sh
index 417937d07..bbcb26bdb 100755
--- a/ci.sh
+++ b/ci.sh
@@ -133,6 +133,7 @@ cargo batch \
133 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ 133 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \
134 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \ 134 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \
135 --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ 135 --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \
136 --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \
136 $BUILD_EXTRA 137 $BUILD_EXTRA
137 138
138 139
diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs
index 112f084c1..ab639aeea 100644
--- a/embassy-nrf/src/buffered_uarte.rs
+++ b/embassy-nrf/src/buffered_uarte.rs
@@ -1,10 +1,5 @@
1//! Async buffered UART driver. 1//! Async buffered UART driver.
2//! 2//!
3//! WARNING!!! The functionality provided here is intended to be used only
4//! in situations where hardware flow control are available i.e. CTS and RTS.
5//! This is a problem that should be addressed at a later stage and can be
6//! fully explained at <https://github.com/embassy-rs/embassy/issues/536>.
7//!
8//! Note that discarding a future from a read or write operation may lead to losing 3//! Note that discarding a future from a read or write operation may lead to losing
9//! data. For example, when using `futures_util::future::select` and completion occurs 4//! data. For example, when using `futures_util::future::select` and completion occurs
10//! on the "other" future, you should capture the incomplete future and continue to use 5//! on the "other" future, you should capture the incomplete future and continue to use
@@ -13,82 +8,128 @@
13//! 8//!
14//! Please also see [crate::uarte] to understand when [BufferedUarte] should be used. 9//! Please also see [crate::uarte] to understand when [BufferedUarte] should be used.
15 10
16use core::cell::RefCell;
17use core::cmp::min; 11use core::cmp::min;
18use core::future::poll_fn; 12use core::future::poll_fn;
19use core::sync::atomic::{compiler_fence, Ordering}; 13use core::slice;
14use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering};
20use core::task::Poll; 15use core::task::Poll;
21 16
22use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; 17use embassy_cortex_m::interrupt::Interrupt;
23use embassy_hal_common::ring_buffer::RingBuffer; 18use embassy_hal_common::atomic_ring_buffer::RingBuffer;
24use embassy_hal_common::{into_ref, PeripheralRef}; 19use embassy_hal_common::{into_ref, PeripheralRef};
25use embassy_sync::waitqueue::WakerRegistration; 20use embassy_sync::waitqueue::AtomicWaker;
26// Re-export SVD variants to allow user to directly set values 21// Re-export SVD variants to allow user to directly set values
27pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; 22pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};
28 23
29use crate::gpio::{self, Pin as GpioPin}; 24use crate::gpio::sealed::Pin;
25use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits};
30use crate::interrupt::InterruptExt; 26use crate::interrupt::InterruptExt;
31use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; 27use crate::ppi::{
32use crate::timer::{Frequency, Instance as TimerInstance, Timer}; 28 self, AnyConfigurableChannel, AnyGroup, Channel, ConfigurableChannel, Event, Group, Ppi, PpiGroup, Task,
29};
30use crate::timer::{Instance as TimerInstance, Timer};
33use crate::uarte::{apply_workaround_for_enable_anomaly, Config, Instance as UarteInstance}; 31use crate::uarte::{apply_workaround_for_enable_anomaly, Config, Instance as UarteInstance};
34use crate::{pac, Peripheral}; 32use crate::{pac, Peripheral};
35 33
36#[derive(Copy, Clone, Debug, PartialEq)] 34mod sealed {
37enum RxState { 35 use super::*;
38 Idle,
39 Receiving,
40}
41 36
42#[derive(Copy, Clone, Debug, PartialEq)] 37 pub struct State {
43enum TxState { 38 pub tx_waker: AtomicWaker,
44 Idle, 39 pub tx_buf: RingBuffer,
45 Transmitting(usize), 40 pub tx_count: AtomicUsize,
46}
47 41
48/// A type for storing the state of the UARTE peripheral that can be stored in a static. 42 pub rx_waker: AtomicWaker,
49pub struct State<'d, U: UarteInstance, T: TimerInstance>(StateStorage<StateInner<'d, U, T>>); 43 pub rx_buf: RingBuffer,
50impl<'d, U: UarteInstance, T: TimerInstance> State<'d, U, T> { 44 pub rx_bufs: AtomicU8,
51 /// Create an instance for storing UARTE peripheral state. 45 pub rx_ppi_ch: AtomicU8,
52 pub fn new() -> Self {
53 Self(StateStorage::new())
54 } 46 }
55} 47}
56 48
57struct StateInner<'d, U: UarteInstance, T: TimerInstance> { 49/// UART error.
58 _peri: PeripheralRef<'d, U>, 50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59 timer: Timer<'d, T>, 51#[cfg_attr(feature = "defmt", derive(defmt::Format))]
60 _ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>, 52#[non_exhaustive]
61 _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>, 53pub enum Error {
54 // No errors for now
55}
62 56
63 rx: RingBuffer<'d>, 57pub(crate) use sealed::State;
64 rx_state: RxState,
65 rx_waker: WakerRegistration,
66 58
67 tx: RingBuffer<'d>, 59impl State {
68 tx_state: TxState, 60 pub(crate) const fn new() -> Self {
69 tx_waker: WakerRegistration, 61 Self {
62 tx_waker: AtomicWaker::new(),
63 tx_buf: RingBuffer::new(),
64 tx_count: AtomicUsize::new(0),
65
66 rx_waker: AtomicWaker::new(),
67 rx_buf: RingBuffer::new(),
68 rx_bufs: AtomicU8::new(0),
69 rx_ppi_ch: AtomicU8::new(0),
70 }
71 }
70} 72}
71 73
72/// Buffered UARTE driver. 74/// Buffered UARTE driver.
73pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> { 75pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> {
74 inner: RefCell<PeripheralMutex<'d, StateInner<'d, U, T>>>, 76 _peri: PeripheralRef<'d, U>,
77 timer: Timer<'d, T>,
78 _ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 1>,
79 _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 2>,
80 _ppi_group: PpiGroup<'d, AnyGroup>,
75} 81}
76 82
77impl<'d, U: UarteInstance, T: TimerInstance> Unpin for BufferedUarte<'d, U, T> {} 83impl<'d, U: UarteInstance, T: TimerInstance> Unpin for BufferedUarte<'d, U, T> {}
78 84
79impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { 85impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
80 /// Create a new instance of a BufferedUarte. 86 /// Create a new BufferedUarte without hardware flow control.
81 /// 87 ///
82 /// See the [module documentation](crate::buffered_uarte) for more details about the intended use. 88 /// # Panics
83 /// 89 ///
84 /// The BufferedUarte uses the provided state to store the buffers and peripheral state. The timer and ppi channels are used to 'emulate' idle line detection so that read operations 90 /// Panics if `rx_buffer.len()` is odd.
85 /// can return early if there is no data to receive.
86 pub fn new( 91 pub fn new(
87 state: &'d mut State<'d, U, T>, 92 uarte: impl Peripheral<P = U> + 'd,
88 peri: impl Peripheral<P = U> + 'd, 93 timer: impl Peripheral<P = T> + 'd,
94 ppi_ch1: impl Peripheral<P = impl ConfigurableChannel> + 'd,
95 ppi_ch2: impl Peripheral<P = impl ConfigurableChannel> + 'd,
96 ppi_group: impl Peripheral<P = impl Group> + 'd,
97 irq: impl Peripheral<P = U::Interrupt> + 'd,
98 rxd: impl Peripheral<P = impl GpioPin> + 'd,
99 txd: impl Peripheral<P = impl GpioPin> + 'd,
100 config: Config,
101 rx_buffer: &'d mut [u8],
102 tx_buffer: &'d mut [u8],
103 ) -> Self {
104 into_ref!(rxd, txd, ppi_ch1, ppi_ch2, ppi_group);
105 Self::new_inner(
106 uarte,
107 timer,
108 ppi_ch1.map_into(),
109 ppi_ch2.map_into(),
110 ppi_group.map_into(),
111 irq,
112 rxd.map_into(),
113 txd.map_into(),
114 None,
115 None,
116 config,
117 rx_buffer,
118 tx_buffer,
119 )
120 }
121
122 /// Create a new BufferedUarte with hardware flow control (RTS/CTS)
123 ///
124 /// # Panics
125 ///
126 /// Panics if `rx_buffer.len()` is odd.
127 pub fn new_with_rtscts(
128 uarte: impl Peripheral<P = U> + 'd,
89 timer: impl Peripheral<P = T> + 'd, 129 timer: impl Peripheral<P = T> + 'd,
90 ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd, 130 ppi_ch1: impl Peripheral<P = impl ConfigurableChannel> + 'd,
91 ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd, 131 ppi_ch2: impl Peripheral<P = impl ConfigurableChannel> + 'd,
132 ppi_group: impl Peripheral<P = impl Group> + 'd,
92 irq: impl Peripheral<P = U::Interrupt> + 'd, 133 irq: impl Peripheral<P = U::Interrupt> + 'd,
93 rxd: impl Peripheral<P = impl GpioPin> + 'd, 134 rxd: impl Peripheral<P = impl GpioPin> + 'd,
94 txd: impl Peripheral<P = impl GpioPin> + 'd, 135 txd: impl Peripheral<P = impl GpioPin> + 'd,
@@ -98,11 +139,44 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
98 rx_buffer: &'d mut [u8], 139 rx_buffer: &'d mut [u8],
99 tx_buffer: &'d mut [u8], 140 tx_buffer: &'d mut [u8],
100 ) -> Self { 141 ) -> Self {
101 into_ref!(peri, ppi_ch1, ppi_ch2, irq, rxd, txd, cts, rts); 142 into_ref!(rxd, txd, cts, rts, ppi_ch1, ppi_ch2, ppi_group);
143 Self::new_inner(
144 uarte,
145 timer,
146 ppi_ch1.map_into(),
147 ppi_ch2.map_into(),
148 ppi_group.map_into(),
149 irq,
150 rxd.map_into(),
151 txd.map_into(),
152 Some(cts.map_into()),
153 Some(rts.map_into()),
154 config,
155 rx_buffer,
156 tx_buffer,
157 )
158 }
102 159
103 let r = U::regs(); 160 fn new_inner(
161 peri: impl Peripheral<P = U> + 'd,
162 timer: impl Peripheral<P = T> + 'd,
163 ppi_ch1: PeripheralRef<'d, AnyConfigurableChannel>,
164 ppi_ch2: PeripheralRef<'d, AnyConfigurableChannel>,
165 ppi_group: PeripheralRef<'d, AnyGroup>,
166 irq: impl Peripheral<P = U::Interrupt> + 'd,
167 rxd: PeripheralRef<'d, AnyPin>,
168 txd: PeripheralRef<'d, AnyPin>,
169 cts: Option<PeripheralRef<'d, AnyPin>>,
170 rts: Option<PeripheralRef<'d, AnyPin>>,
171 config: Config,
172 rx_buffer: &'d mut [u8],
173 tx_buffer: &'d mut [u8],
174 ) -> Self {
175 into_ref!(peri, timer, irq);
176
177 assert!(rx_buffer.len() % 2 == 0);
104 178
105 let mut timer = Timer::new(timer); 179 let r = U::regs();
106 180
107 rxd.conf().write(|w| w.input().connect().drive().h0h1()); 181 rxd.conf().write(|w| w.input().connect().drive().h0h1());
108 r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) }); 182 r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) });
@@ -111,92 +185,200 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
111 txd.conf().write(|w| w.dir().output().drive().h0h1()); 185 txd.conf().write(|w| w.dir().output().drive().h0h1());
112 r.psel.txd.write(|w| unsafe { w.bits(txd.psel_bits()) }); 186 r.psel.txd.write(|w| unsafe { w.bits(txd.psel_bits()) });
113 187
114 cts.conf().write(|w| w.input().connect().drive().h0h1()); 188 if let Some(pin) = &cts {
189 pin.conf().write(|w| w.input().connect().drive().h0h1());
190 }
115 r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) }); 191 r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) });
116 192
117 rts.set_high(); 193 if let Some(pin) = &rts {
118 rts.conf().write(|w| w.dir().output().drive().h0h1()); 194 pin.set_high();
195 pin.conf().write(|w| w.dir().output().drive().h0h1());
196 }
119 r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) }); 197 r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) });
120 198
121 r.baudrate.write(|w| w.baudrate().variant(config.baudrate)); 199 // Initialize state
122 r.config.write(|w| w.parity().variant(config.parity)); 200 let s = U::buffered_state();
201 s.tx_count.store(0, Ordering::Relaxed);
202 s.rx_bufs.store(0, Ordering::Relaxed);
203 let len = tx_buffer.len();
204 unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) };
205 let len = rx_buffer.len();
206 unsafe { s.rx_buf.init(rx_buffer.as_mut_ptr(), len) };
123 207
124 // Configure 208 // Configure
125 r.config.write(|w| { 209 r.config.write(|w| {
126 w.hwfc().bit(true); 210 w.hwfc().bit(false);
127 w.parity().variant(config.parity); 211 w.parity().variant(config.parity);
128 w 212 w
129 }); 213 });
130 r.baudrate.write(|w| w.baudrate().variant(config.baudrate)); 214 r.baudrate.write(|w| w.baudrate().variant(config.baudrate));
131 215
132 // Enable interrupts 216 // clear errors
133 r.intenset.write(|w| w.endrx().set().endtx().set()); 217 let errors = r.errorsrc.read().bits();
218 r.errorsrc.write(|w| unsafe { w.bits(errors) });
134 219
135 // Disable the irq, let the Registration enable it when everything is set up. 220 r.events_rxstarted.reset();
136 irq.disable(); 221 r.events_txstarted.reset();
137 irq.pend(); 222 r.events_error.reset();
223 r.events_endrx.reset();
224 r.events_endtx.reset();
225
226 // Enable interrupts
227 r.intenclr.write(|w| unsafe { w.bits(!0) });
228 r.intenset.write(|w| {
229 w.endtx().set();
230 w.rxstarted().set();
231 w.error().set();
232 w
233 });
138 234
139 // Enable UARTE instance 235 // Enable UARTE instance
140 apply_workaround_for_enable_anomaly(&r); 236 apply_workaround_for_enable_anomaly(&r);
141 r.enable.write(|w| w.enable().enabled()); 237 r.enable.write(|w| w.enable().enabled());
142 238
143 // BAUDRATE register values are `baudrate * 2^32 / 16000000` 239 // Configure byte counter.
144 // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values 240 let mut timer = Timer::new_counter(timer);
145 // 241 timer.cc(1).write(rx_buffer.len() as u32 * 2);
146 // We want to stop RX if line is idle for 2 bytes worth of time 242 timer.cc(1).short_compare_clear();
147 // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit) 243 timer.clear();
148 // This gives us the amount of 16M ticks for 20 bits. 244 timer.start();
149 let timeout = 0x8000_0000 / (config.baudrate as u32 / 40);
150 245
151 timer.set_frequency(Frequency::F16MHz); 246 let mut ppi_ch1 = Ppi::new_one_to_one(ppi_ch1, Event::from_reg(&r.events_rxdrdy), timer.task_count());
152 timer.cc(0).write(timeout);
153 timer.cc(0).short_compare_clear();
154 timer.cc(0).short_compare_stop();
155
156 let mut ppi_ch1 = Ppi::new_one_to_two(
157 ppi_ch1.map_into(),
158 Event::from_reg(&r.events_rxdrdy),
159 timer.task_clear(),
160 timer.task_start(),
161 );
162 ppi_ch1.enable(); 247 ppi_ch1.enable();
163 248
164 let mut ppi_ch2 = Ppi::new_one_to_one( 249 s.rx_ppi_ch.store(ppi_ch2.number() as u8, Ordering::Relaxed);
165 ppi_ch2.map_into(), 250 let mut ppi_group = PpiGroup::new(ppi_group);
166 timer.cc(0).event_compare(), 251 let mut ppi_ch2 = Ppi::new_one_to_two(
167 Task::from_reg(&r.tasks_stoprx), 252 ppi_ch2,
253 Event::from_reg(&r.events_endrx),
254 Task::from_reg(&r.tasks_startrx),
255 ppi_group.task_disable_all(),
168 ); 256 );
169 ppi_ch2.enable(); 257 ppi_ch2.disable();
258 ppi_group.add_channel(&ppi_ch2);
259
260 irq.disable();
261 irq.set_handler(Self::on_interrupt);
262 irq.pend();
263 irq.enable();
170 264
171 Self { 265 Self {
172 inner: RefCell::new(PeripheralMutex::new(irq, &mut state.0, move || StateInner { 266 _peri: peri,
173 _peri: peri, 267 timer,
174 timer, 268 _ppi_ch1: ppi_ch1,
175 _ppi_ch1: ppi_ch1, 269 _ppi_ch2: ppi_ch2,
176 _ppi_ch2: ppi_ch2, 270 _ppi_group: ppi_group,
177
178 rx: RingBuffer::new(rx_buffer),
179 rx_state: RxState::Idle,
180 rx_waker: WakerRegistration::new(),
181
182 tx: RingBuffer::new(tx_buffer),
183 tx_state: TxState::Idle,
184 tx_waker: WakerRegistration::new(),
185 })),
186 } 271 }
187 } 272 }
188 273
189 /// Adjust the baud rate to the provided value. 274 fn pend_irq() {
190 pub fn set_baudrate(&mut self, baudrate: Baudrate) { 275 unsafe { <U::Interrupt as Interrupt>::steal() }.pend()
191 self.inner.borrow_mut().with(|state| { 276 }
192 let r = U::regs(); 277
278 fn on_interrupt(_: *mut ()) {
279 //trace!("irq: start");
280 let r = U::regs();
281 let s = U::buffered_state();
193 282
194 let timeout = 0x8000_0000 / (baudrate as u32 / 40); 283 let buf_len = s.rx_buf.len();
195 state.timer.cc(0).write(timeout); 284 let half_len = buf_len / 2;
196 state.timer.clear(); 285 let mut tx = unsafe { s.tx_buf.reader() };
286 let mut rx = unsafe { s.rx_buf.writer() };
197 287
198 r.baudrate.write(|w| w.baudrate().variant(baudrate)); 288 if r.events_error.read().bits() != 0 {
199 }); 289 r.events_error.reset();
290 let errs = r.errorsrc.read();
291 r.errorsrc.write(|w| unsafe { w.bits(errs.bits()) });
292
293 if errs.overrun().bit() {
294 panic!("BufferedUarte overrun");
295 }
296 }
297
298 // Received some bytes, wake task.
299 if r.inten.read().rxdrdy().bit_is_set() && r.events_rxdrdy.read().bits() != 0 {
300 r.intenclr.write(|w| w.rxdrdy().clear());
301 r.events_rxdrdy.reset();
302 s.rx_waker.wake();
303 }
304
305 // If not RXing, start.
306 if s.rx_bufs.load(Ordering::Relaxed) == 0 {
307 let (ptr, len) = rx.push_buf();
308 if len >= half_len {
309 //trace!(" irq_rx: starting {:?}", half_len);
310 s.rx_bufs.store(1, Ordering::Relaxed);
311
312 // Set up the DMA read
313 r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
314 r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(half_len as _) });
315
316 // Start UARTE Receive transaction
317 r.tasks_startrx.write(|w| unsafe { w.bits(1) });
318 rx.push_done(half_len);
319 r.intenset.write(|w| w.rxstarted().set());
320 }
321 }
322
323 if r.events_rxstarted.read().bits() != 0 {
324 //trace!(" irq_rx: rxstarted");
325 let (ptr, len) = rx.push_buf();
326 if len >= half_len {
327 //trace!(" irq_rx: starting second {:?}", half_len);
328
329 // Set up the DMA read
330 r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
331 r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(half_len as _) });
332
333 let chn = s.rx_ppi_ch.load(Ordering::Relaxed);
334
335 ppi::regs().chenset.write(|w| unsafe { w.bits(1 << chn) });
336
337 rx.push_done(half_len);
338
339 r.events_rxstarted.reset();
340 } else {
341 //trace!(" irq_rx: rxstarted no buf");
342 r.intenclr.write(|w| w.rxstarted().clear());
343 }
344 }
345
346 // =============================
347
348 // TX end
349 if r.events_endtx.read().bits() != 0 {
350 r.events_endtx.reset();
351
352 let n = s.tx_count.load(Ordering::Relaxed);
353 //trace!(" irq_tx: endtx {:?}", n);
354 tx.pop_done(n);
355 s.tx_waker.wake();
356 s.tx_count.store(0, Ordering::Relaxed);
357 }
358
359 // If not TXing, start.
360 if s.tx_count.load(Ordering::Relaxed) == 0 {
361 let (ptr, len) = tx.pop_buf();
362 if len != 0 {
363 //trace!(" irq_tx: starting {:?}", len);
364 s.tx_count.store(len, Ordering::Relaxed);
365
366 // Set up the DMA write
367 r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
368 r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
369
370 // Start UARTE Transmit transaction
371 r.tasks_starttx.write(|w| unsafe { w.bits(1) });
372 }
373 }
374
375 //trace!("irq: end");
376 }
377
378 /// Adjust the baud rate to the provided value.
379 pub fn set_baudrate(&mut self, baudrate: Baudrate) {
380 let r = U::regs();
381 r.baudrate.write(|w| w.baudrate().variant(baudrate));
200 } 382 }
201 383
202 /// Split the UART in reader and writer parts. 384 /// Split the UART in reader and writer parts.
@@ -206,120 +388,142 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
206 (BufferedUarteRx { inner: self }, BufferedUarteTx { inner: self }) 388 (BufferedUarteRx { inner: self }, BufferedUarteTx { inner: self })
207 } 389 }
208 390
209 async fn inner_read<'a>(&'a self, buf: &'a mut [u8]) -> Result<usize, core::convert::Infallible> { 391 async fn inner_read(&self, buf: &mut [u8]) -> Result<usize, Error> {
392 let data = self.inner_fill_buf().await?;
393 let n = data.len().min(buf.len());
394 buf[..n].copy_from_slice(&data[..n]);
395 self.inner_consume(n);
396 Ok(n)
397 }
398
399 async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result<usize, Error> {
210 poll_fn(move |cx| { 400 poll_fn(move |cx| {
211 let mut do_pend = false; 401 //trace!("poll_write: {:?}", buf.len());
212 let mut inner = self.inner.borrow_mut(); 402 let s = U::buffered_state();
213 let res = inner.with(|state| { 403 let mut tx = unsafe { s.tx_buf.writer() };
214 compiler_fence(Ordering::SeqCst); 404
215 trace!("poll_read"); 405 let tx_buf = tx.push_slice();
216 406 if tx_buf.is_empty() {
217 // We have data ready in buffer? Return it. 407 //trace!("poll_write: pending");
218 let data = state.rx.pop_buf(); 408 s.tx_waker.register(cx.waker());
219 if !data.is_empty() { 409 return Poll::Pending;
220 trace!(" got {:?} {:?}", data.as_ptr() as u32, data.len());
221 let len = data.len().min(buf.len());
222 buf[..len].copy_from_slice(&data[..len]);
223 state.rx.pop(len);
224 do_pend = true;
225 return Poll::Ready(Ok(len));
226 }
227
228 trace!(" empty");
229 state.rx_waker.register(cx.waker());
230 Poll::Pending
231 });
232 if do_pend {
233 inner.pend();
234 } 410 }
235 411
236 res 412 let n = min(tx_buf.len(), buf.len());
413 tx_buf[..n].copy_from_slice(&buf[..n]);
414 tx.push_done(n);
415
416 //trace!("poll_write: queued {:?}", n);
417
418 compiler_fence(Ordering::SeqCst);
419 Self::pend_irq();
420
421 Poll::Ready(Ok(n))
237 }) 422 })
238 .await 423 .await
239 } 424 }
240 425
241 async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result<usize, core::convert::Infallible> { 426 async fn inner_flush<'a>(&'a self) -> Result<(), Error> {
242 poll_fn(move |cx| { 427 poll_fn(move |cx| {
243 let mut inner = self.inner.borrow_mut(); 428 //trace!("poll_flush");
244 let res = inner.with(|state| { 429 let s = U::buffered_state();
245 trace!("poll_write: {:?}", buf.len()); 430 if !s.tx_buf.is_empty() {
431 //trace!("poll_flush: pending");
432 s.tx_waker.register(cx.waker());
433 return Poll::Pending;
434 }
246 435
247 let tx_buf = state.tx.push_buf(); 436 Poll::Ready(Ok(()))
248 if tx_buf.is_empty() { 437 })
249 trace!("poll_write: pending"); 438 .await
250 state.tx_waker.register(cx.waker()); 439 }
251 return Poll::Pending;
252 }
253 440
254 let n = min(tx_buf.len(), buf.len()); 441 async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], Error> {
255 tx_buf[..n].copy_from_slice(&buf[..n]); 442 poll_fn(move |cx| {
256 state.tx.push(n); 443 compiler_fence(Ordering::SeqCst);
444 //trace!("poll_read");
257 445
258 trace!("poll_write: queued {:?}", n); 446 let r = U::regs();
447 let s = U::buffered_state();
448
449 // Read the RXDRDY counter.
450 T::regs().tasks_capture[0].write(|w| unsafe { w.bits(1) });
451 let mut end = T::regs().cc[0].read().bits() as usize;
452 //trace!(" rxdrdy count = {:?}", end);
453
454 // We've set a compare channel that resets the counter to 0 when it reaches `len*2`.
455 // However, it's unclear if that's instant, or there's a small window where you can
456 // still read `len()*2`.
457 // This could happen if in one clock cycle the counter is updated, and in the next the
458 // clear takes effect. The docs are very sparse, they just say "Task delays: After TIMER
459 // is started, the CLEAR, COUNT, and STOP tasks are guaranteed to take effect within one
460 // clock cycle of the PCLK16M." :shrug:
461 // So, we wrap the counter ourselves, just in case.
462 if end > s.rx_buf.len() * 2 {
463 end = 0
464 }
259 465
260 compiler_fence(Ordering::SeqCst); 466 // This logic mirrors `atomic_ring_buffer::Reader::pop_buf()`
467 let mut start = s.rx_buf.start.load(Ordering::Relaxed);
468 let len = s.rx_buf.len();
469 if start == end {
470 //trace!(" empty");
471 s.rx_waker.register(cx.waker());
472 r.intenset.write(|w| w.rxdrdy().set_bit());
473 return Poll::Pending;
474 }
261 475
262 Poll::Ready(Ok(n)) 476 if start >= len {
263 }); 477 start -= len
478 }
479 if end >= len {
480 end -= len
481 }
264 482
265 inner.pend(); 483 let n = if end > start { end - start } else { len - start };
484 assert!(n != 0);
485 //trace!(" uarte ringbuf: pop_buf {:?}..{:?}", start, start + n);
266 486
267 res 487 let buf = s.rx_buf.buf.load(Ordering::Relaxed);
488 Poll::Ready(Ok(unsafe { slice::from_raw_parts(buf.add(start), n) }))
268 }) 489 })
269 .await 490 .await
270 } 491 }
271 492
272 async fn inner_flush<'a>(&'a self) -> Result<(), core::convert::Infallible> { 493 fn inner_consume(&self, amt: usize) {
273 poll_fn(move |cx| { 494 if amt == 0 {
274 self.inner.borrow_mut().with(|state| { 495 return;
275 trace!("poll_flush"); 496 }
276 497
277 if !state.tx.is_empty() { 498 let s = U::buffered_state();
278 trace!("poll_flush: pending"); 499 let mut rx = unsafe { s.rx_buf.reader() };
279 state.tx_waker.register(cx.waker()); 500 rx.pop_done(amt);
280 return Poll::Pending; 501 U::regs().intenset.write(|w| w.rxstarted().set());
281 } 502 }
282 503
283 Poll::Ready(Ok(())) 504 /// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
284 }) 505 pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
285 }) 506 self.inner_read(buf).await
286 .await
287 } 507 }
288 508
289 async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], core::convert::Infallible> { 509 /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty.
290 poll_fn(move |cx| { 510 pub async fn fill_buf(&mut self) -> Result<&[u8], Error> {
291 self.inner.borrow_mut().with(|state| { 511 self.inner_fill_buf().await
292 compiler_fence(Ordering::SeqCst);
293 trace!("fill_buf");
294
295 // We have data ready in buffer? Return it.
296 let buf = state.rx.pop_buf();
297 if !buf.is_empty() {
298 trace!(" got {:?} {:?}", buf.as_ptr() as u32, buf.len());
299 let buf: &[u8] = buf;
300 // Safety: buffer lives as long as uart
301 let buf: &[u8] = unsafe { core::mem::transmute(buf) };
302 return Poll::Ready(Ok(buf));
303 }
304
305 trace!(" empty");
306 state.rx_waker.register(cx.waker());
307 Poll::<Result<&[u8], core::convert::Infallible>>::Pending
308 })
309 })
310 .await
311 } 512 }
312 513
313 fn inner_consume(&self, amt: usize) { 514 /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`.
314 let mut inner = self.inner.borrow_mut(); 515 pub fn consume(&mut self, amt: usize) {
315 let signal = inner.with(|state| { 516 self.inner_consume(amt)
316 let full = state.rx.is_full(); 517 }
317 state.rx.pop(amt); 518
318 full 519 /// Write a buffer into this writer, returning how many bytes were written.
319 }); 520 pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
320 if signal { 521 self.inner_write(buf).await
321 inner.pend(); 522 }
322 } 523
524 /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.
525 pub async fn flush(&mut self) -> Result<(), Error> {
526 self.inner_flush().await
323 } 527 }
324} 528}
325 529
@@ -328,76 +532,116 @@ pub struct BufferedUarteTx<'u, 'd, U: UarteInstance, T: TimerInstance> {
328 inner: &'u BufferedUarte<'d, U, T>, 532 inner: &'u BufferedUarte<'d, U, T>,
329} 533}
330 534
535impl<'u, 'd, U: UarteInstance, T: TimerInstance> BufferedUarteTx<'u, 'd, U, T> {
536 /// Write a buffer into this writer, returning how many bytes were written.
537 pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
538 self.inner.inner_write(buf).await
539 }
540
541 /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.
542 pub async fn flush(&mut self) -> Result<(), Error> {
543 self.inner.inner_flush().await
544 }
545}
546
331/// Writer part of the buffered UARTE driver. 547/// Writer part of the buffered UARTE driver.
332pub struct BufferedUarteRx<'u, 'd, U: UarteInstance, T: TimerInstance> { 548pub struct BufferedUarteRx<'u, 'd, U: UarteInstance, T: TimerInstance> {
333 inner: &'u BufferedUarte<'d, U, T>, 549 inner: &'u BufferedUarte<'d, U, T>,
334} 550}
335 551
336impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> { 552impl<'u, 'd, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'u, 'd, U, T> {
337 type Error = core::convert::Infallible; 553 /// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
338} 554 pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
555 self.inner.inner_read(buf).await
556 }
339 557
340impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteRx<'u, 'd, U, T> { 558 /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty.
341 type Error = core::convert::Infallible; 559 pub async fn fill_buf(&mut self) -> Result<&[u8], Error> {
342} 560 self.inner.inner_fill_buf().await
561 }
343 562
344impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteTx<'u, 'd, U, T> { 563 /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`.
345 type Error = core::convert::Infallible; 564 pub fn consume(&mut self, amt: usize) {
565 self.inner.inner_consume(amt)
566 }
346} 567}
347 568
348impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> { 569#[cfg(feature = "nightly")]
349 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { 570mod _embedded_io {
350 self.inner_read(buf).await 571 use super::*;
572
573 impl embedded_io::Error for Error {
574 fn kind(&self) -> embedded_io::ErrorKind {
575 match *self {}
576 }
351 } 577 }
352}
353 578
354impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarteRx<'u, 'd, U, T> { 579 impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> {
355 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { 580 type Error = Error;
356 self.inner.inner_read(buf).await
357 } 581 }
358}
359 582
360impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarte<'d, U, T> { 583 impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteRx<'u, 'd, U, T> {
361 async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { 584 type Error = Error;
362 self.inner_fill_buf().await
363 } 585 }
364 586
365 fn consume(&mut self, amt: usize) { 587 impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteTx<'u, 'd, U, T> {
366 self.inner_consume(amt) 588 type Error = Error;
367 } 589 }
368}
369 590
370impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarteRx<'u, 'd, U, T> { 591 impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> {
371 async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { 592 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
372 self.inner.inner_fill_buf().await 593 self.inner_read(buf).await
594 }
373 } 595 }
374 596
375 fn consume(&mut self, amt: usize) { 597 impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarteRx<'u, 'd, U, T> {
376 self.inner.inner_consume(amt) 598 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
599 self.inner.inner_read(buf).await
600 }
377 } 601 }
378}
379 602
380impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> { 603 impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarte<'d, U, T> {
381 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { 604 async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
382 self.inner_write(buf).await 605 self.inner_fill_buf().await
606 }
607
608 fn consume(&mut self, amt: usize) {
609 self.inner_consume(amt)
610 }
383 } 611 }
384 612
385 async fn flush(&mut self) -> Result<(), Self::Error> { 613 impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarteRx<'u, 'd, U, T> {
386 self.inner_flush().await 614 async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
615 self.inner.inner_fill_buf().await
616 }
617
618 fn consume(&mut self, amt: usize) {
619 self.inner.inner_consume(amt)
620 }
387 } 621 }
388}
389 622
390impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarteTx<'u, 'd, U, T> { 623 impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> {
391 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { 624 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
392 self.inner.inner_write(buf).await 625 self.inner_write(buf).await
626 }
627
628 async fn flush(&mut self) -> Result<(), Self::Error> {
629 self.inner_flush().await
630 }
393 } 631 }
394 632
395 async fn flush(&mut self) -> Result<(), Self::Error> { 633 impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarteTx<'u, 'd, U, T> {
396 self.inner.inner_flush().await 634 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
635 self.inner.inner_write(buf).await
636 }
637
638 async fn flush(&mut self) -> Result<(), Self::Error> {
639 self.inner.inner_flush().await
640 }
397 } 641 }
398} 642}
399 643
400impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> { 644impl<'a, U: UarteInstance, T: TimerInstance> Drop for BufferedUarte<'a, U, T> {
401 fn drop(&mut self) { 645 fn drop(&mut self) {
402 let r = U::regs(); 646 let r = U::regs();
403 647
@@ -418,108 +662,11 @@ impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> {
418 gpio::deconfigure_pin(r.psel.txd.read().bits()); 662 gpio::deconfigure_pin(r.psel.txd.read().bits());
419 gpio::deconfigure_pin(r.psel.rts.read().bits()); 663 gpio::deconfigure_pin(r.psel.rts.read().bits());
420 gpio::deconfigure_pin(r.psel.cts.read().bits()); 664 gpio::deconfigure_pin(r.psel.cts.read().bits());
421 }
422}
423 665
424impl<'a, U: UarteInstance, T: TimerInstance> PeripheralState for StateInner<'a, U, T> { 666 let s = U::buffered_state();
425 type Interrupt = U::Interrupt; 667 unsafe {
426 fn on_interrupt(&mut self) { 668 s.rx_buf.deinit();
427 trace!("irq: start"); 669 s.tx_buf.deinit();
428 let r = U::regs();
429
430 loop {
431 match self.rx_state {
432 RxState::Idle => {
433 trace!(" irq_rx: in state idle");
434
435 let buf = self.rx.push_buf();
436 if !buf.is_empty() {
437 trace!(" irq_rx: starting {:?}", buf.len());
438 self.rx_state = RxState::Receiving;
439
440 // Set up the DMA read
441 r.rxd.ptr.write(|w|
442 // The PTR field is a full 32 bits wide and accepts the full range
443 // of values.
444 unsafe { w.ptr().bits(buf.as_ptr() as u32) });
445 r.rxd.maxcnt.write(|w|
446 // We're giving it the length of the buffer, so no danger of
447 // accessing invalid memory. We have verified that the length of the
448 // buffer fits in an `u8`, so the cast to `u8` is also fine.
449 //
450 // The MAXCNT field is at least 8 bits wide and accepts the full
451 // range of values.
452 unsafe { w.maxcnt().bits(buf.len() as _) });
453 trace!(" irq_rx: buf {:?} {:?}", buf.as_ptr() as u32, buf.len());
454
455 // Start UARTE Receive transaction
456 r.tasks_startrx.write(|w| unsafe { w.bits(1) });
457 }
458 break;
459 }
460 RxState::Receiving => {
461 trace!(" irq_rx: in state receiving");
462 if r.events_endrx.read().bits() != 0 {
463 self.timer.stop();
464
465 let n: usize = r.rxd.amount.read().amount().bits() as usize;
466 trace!(" irq_rx: endrx {:?}", n);
467 self.rx.push(n);
468
469 r.events_endrx.reset();
470
471 self.rx_waker.wake();
472 self.rx_state = RxState::Idle;
473 } else {
474 break;
475 }
476 }
477 }
478 }
479
480 loop {
481 match self.tx_state {
482 TxState::Idle => {
483 trace!(" irq_tx: in state Idle");
484 let buf = self.tx.pop_buf();
485 if !buf.is_empty() {
486 trace!(" irq_tx: starting {:?}", buf.len());
487 self.tx_state = TxState::Transmitting(buf.len());
488
489 // Set up the DMA write
490 r.txd.ptr.write(|w|
491 // The PTR field is a full 32 bits wide and accepts the full range
492 // of values.
493 unsafe { w.ptr().bits(buf.as_ptr() as u32) });
494 r.txd.maxcnt.write(|w|
495 // We're giving it the length of the buffer, so no danger of
496 // accessing invalid memory. We have verified that the length of the
497 // buffer fits in an `u8`, so the cast to `u8` is also fine.
498 //
499 // The MAXCNT field is 8 bits wide and accepts the full range of
500 // values.
501 unsafe { w.maxcnt().bits(buf.len() as _) });
502
503 // Start UARTE Transmit transaction
504 r.tasks_starttx.write(|w| unsafe { w.bits(1) });
505 }
506 break;
507 }
508 TxState::Transmitting(n) => {
509 trace!(" irq_tx: in state Transmitting");
510 if r.events_endtx.read().bits() != 0 {
511 r.events_endtx.reset();
512
513 trace!(" irq_tx: endtx {:?}", n);
514 self.tx.pop(n);
515 self.tx_waker.wake();
516 self.tx_state = TxState::Idle;
517 } else {
518 break;
519 }
520 }
521 }
522 } 670 }
523 trace!("irq: end");
524 } 671 }
525} 672}
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index a9683df44..6b7dc7791 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -37,7 +37,6 @@ pub(crate) mod util;
37#[cfg(feature = "_time-driver")] 37#[cfg(feature = "_time-driver")]
38mod time_driver; 38mod time_driver;
39 39
40#[cfg(feature = "nightly")]
41pub mod buffered_uarte; 40pub mod buffered_uarte;
42pub mod gpio; 41pub mod gpio;
43#[cfg(feature = "gpiote")] 42#[cfg(feature = "gpiote")]
diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs
index 0908cd7be..3a1e7f170 100644
--- a/embassy-nrf/src/ppi/dppi.rs
+++ b/embassy-nrf/src/ppi/dppi.rs
@@ -6,7 +6,7 @@ use crate::{pac, Peripheral};
6const DPPI_ENABLE_BIT: u32 = 0x8000_0000; 6const DPPI_ENABLE_BIT: u32 = 0x8000_0000;
7const DPPI_CHANNEL_MASK: u32 = 0x0000_00FF; 7const DPPI_CHANNEL_MASK: u32 = 0x0000_00FF;
8 8
9fn regs() -> &'static pac::dppic::RegisterBlock { 9pub(crate) fn regs() -> &'static pac::dppic::RegisterBlock {
10 unsafe { &*pac::DPPIC::ptr() } 10 unsafe { &*pac::DPPIC::ptr() }
11} 11}
12 12
diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs
index b76eccf0b..7c18da6ee 100644
--- a/embassy-nrf/src/ppi/mod.rs
+++ b/embassy-nrf/src/ppi/mod.rs
@@ -17,16 +17,16 @@
17 17
18use core::ptr::NonNull; 18use core::ptr::NonNull;
19 19
20use embassy_hal_common::{impl_peripheral, PeripheralRef}; 20use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef};
21 21
22use crate::{peripherals, Peripheral}; 22use crate::{peripherals, Peripheral};
23 23
24#[cfg(feature = "_dppi")] 24#[cfg_attr(feature = "_dppi", path = "dppi.rs")]
25mod dppi; 25#[cfg_attr(feature = "_ppi", path = "ppi.rs")]
26#[cfg(feature = "_ppi")] 26mod _version;
27mod ppi; 27pub(crate) use _version::*;
28 28
29/// An instance of the Programmable peripheral interconnect on nRF devices. 29/// PPI channel driver.
30pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> { 30pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> {
31 ch: PeripheralRef<'d, C>, 31 ch: PeripheralRef<'d, C>,
32 #[cfg(feature = "_dppi")] 32 #[cfg(feature = "_dppi")]
@@ -35,6 +35,88 @@ pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize
35 tasks: [Task; TASK_COUNT], 35 tasks: [Task; TASK_COUNT],
36} 36}
37 37
38/// PPI channel group driver.
39pub struct PpiGroup<'d, G: Group> {
40 g: PeripheralRef<'d, G>,
41}
42
43impl<'d, G: Group> PpiGroup<'d, G> {
44 /// Create a new PPI group driver.
45 ///
46 /// The group is initialized as containing no channels.
47 pub fn new(g: impl Peripheral<P = G> + 'd) -> Self {
48 into_ref!(g);
49
50 let r = regs();
51 let n = g.number();
52 r.chg[n].write(|w| unsafe { w.bits(0) });
53
54 Self { g }
55 }
56
57 /// Add a PPI channel to this group.
58 ///
59 /// If the channel is already in the group, this is a no-op.
60 pub fn add_channel<C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize>(
61 &mut self,
62 ch: &Ppi<'_, C, EVENT_COUNT, TASK_COUNT>,
63 ) {
64 let r = regs();
65 let ng = self.g.number();
66 let nc = ch.ch.number();
67 r.chg[ng].modify(|r, w| unsafe { w.bits(r.bits() | 1 << nc) });
68 }
69
70 /// Remove a PPI channel from this group.
71 ///
72 /// If the channel is already not in the group, this is a no-op.
73 pub fn remove_channel<C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize>(
74 &mut self,
75 ch: &Ppi<'_, C, EVENT_COUNT, TASK_COUNT>,
76 ) {
77 let r = regs();
78 let ng = self.g.number();
79 let nc = ch.ch.number();
80 r.chg[ng].modify(|r, w| unsafe { w.bits(r.bits() & !(1 << nc)) });
81 }
82
83 /// Enable all the channels in this group.
84 pub fn enable_all(&mut self) {
85 let n = self.g.number();
86 regs().tasks_chg[n].en.write(|w| unsafe { w.bits(1) });
87 }
88
89 /// Disable all the channels in this group.
90 pub fn disable_all(&mut self) {
91 let n = self.g.number();
92 regs().tasks_chg[n].dis.write(|w| unsafe { w.bits(1) });
93 }
94
95 /// Get a reference to the "enable all" task.
96 ///
97 /// When triggered, it will enable all the channels in this group.
98 pub fn task_enable_all(&self) -> Task {
99 let n = self.g.number();
100 Task::from_reg(&regs().tasks_chg[n].en)
101 }
102
103 /// Get a reference to the "disable all" task.
104 ///
105 /// When triggered, it will disable all the channels in this group.
106 pub fn task_disable_all(&self) -> Task {
107 let n = self.g.number();
108 Task::from_reg(&regs().tasks_chg[n].dis)
109 }
110}
111
112impl<'d, G: Group> Drop for PpiGroup<'d, G> {
113 fn drop(&mut self) {
114 let r = regs();
115 let n = self.g.number();
116 r.chg[n].write(|w| unsafe { w.bits(0) });
117 }
118}
119
38#[cfg(feature = "_dppi")] 120#[cfg(feature = "_dppi")]
39const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::<u32>(); 121const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::<u32>();
40 122
@@ -112,7 +194,7 @@ pub(crate) mod sealed {
112} 194}
113 195
114/// Interface for PPI channels. 196/// Interface for PPI channels.
115pub trait Channel: sealed::Channel + Peripheral<P = Self> + Sized { 197pub trait Channel: sealed::Channel + Peripheral<P = Self> + Sized + 'static {
116 /// Returns the number of the channel 198 /// Returns the number of the channel
117 fn number(&self) -> usize; 199 fn number(&self) -> usize;
118} 200}
@@ -130,7 +212,7 @@ pub trait StaticChannel: Channel + Into<AnyStaticChannel> {
130} 212}
131 213
132/// Interface for a group of PPI channels. 214/// Interface for a group of PPI channels.
133pub trait Group: sealed::Group + Sized { 215pub trait Group: sealed::Group + Peripheral<P = Self> + Into<AnyGroup> + Sized + 'static {
134 /// Returns the number of the group. 216 /// Returns the number of the group.
135 fn number(&self) -> usize; 217 fn number(&self) -> usize;
136 /// Convert into a type erased group. 218 /// Convert into a type erased group.
@@ -248,6 +330,12 @@ macro_rules! impl_group {
248 $number 330 $number
249 } 331 }
250 } 332 }
333
334 impl From<peripherals::$type> for crate::ppi::AnyGroup {
335 fn from(val: peripherals::$type) -> Self {
336 crate::ppi::Group::degrade(val)
337 }
338 }
251 }; 339 };
252} 340}
253 341
diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs
index a96ab50b7..f1eeaee1e 100644
--- a/embassy-nrf/src/ppi/ppi.rs
+++ b/embassy-nrf/src/ppi/ppi.rs
@@ -14,7 +14,7 @@ impl Event {
14 } 14 }
15} 15}
16 16
17fn regs() -> &'static pac::ppi::RegisterBlock { 17pub(crate) fn regs() -> &'static pac::ppi::RegisterBlock {
18 unsafe { &*pac::PPI::ptr() } 18 unsafe { &*pac::PPI::ptr() }
19} 19}
20 20
diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs
index d1ae57237..3b0d2f1ca 100644
--- a/embassy-nrf/src/timer.rs
+++ b/embassy-nrf/src/timer.rs
@@ -132,7 +132,21 @@ impl<'d, T: Instance> Timer<'d, T, Awaitable> {
132 irq.unpend(); 132 irq.unpend();
133 irq.enable(); 133 irq.enable();
134 134
135 Self::new_inner(timer) 135 Self::new_inner(timer, false)
136 }
137
138 /// Create a new async-capable timer driver in counter mode.
139 pub fn new_awaitable_counter(
140 timer: impl Peripheral<P = T> + 'd,
141 irq: impl Peripheral<P = T::Interrupt> + 'd,
142 ) -> Self {
143 into_ref!(irq);
144
145 irq.set_handler(Self::on_interrupt);
146 irq.unpend();
147 irq.enable();
148
149 Self::new_inner(timer, true)
136 } 150 }
137} 151}
138 152
@@ -142,7 +156,15 @@ impl<'d, T: Instance> Timer<'d, T, NotAwaitable> {
142 /// This can be useful for triggering tasks via PPI 156 /// This can be useful for triggering tasks via PPI
143 /// `Uarte` uses this internally. 157 /// `Uarte` uses this internally.
144 pub fn new(timer: impl Peripheral<P = T> + 'd) -> Self { 158 pub fn new(timer: impl Peripheral<P = T> + 'd) -> Self {
145 Self::new_inner(timer) 159 Self::new_inner(timer, false)
160 }
161
162 /// Create a `Timer` driver in counter mode without an interrupt, meaning `Cc::wait` won't work.
163 ///
164 /// This can be useful for triggering tasks via PPI
165 /// `Uarte` uses this internally.
166 pub fn new_counter(timer: impl Peripheral<P = T> + 'd) -> Self {
167 Self::new_inner(timer, true)
146 } 168 }
147} 169}
148 170
@@ -150,7 +172,7 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> {
150 /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work. 172 /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work.
151 /// 173 ///
152 /// This is used by the public constructors. 174 /// This is used by the public constructors.
153 fn new_inner(timer: impl Peripheral<P = T> + 'd) -> Self { 175 fn new_inner(timer: impl Peripheral<P = T> + 'd, is_counter: bool) -> Self {
154 into_ref!(timer); 176 into_ref!(timer);
155 177
156 let regs = T::regs(); 178 let regs = T::regs();
@@ -164,8 +186,11 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> {
164 // since changing BITMODE while running can cause 'unpredictable behaviour' according to the specification. 186 // since changing BITMODE while running can cause 'unpredictable behaviour' according to the specification.
165 this.stop(); 187 this.stop();
166 188
167 // Set the instance to timer mode. 189 if is_counter {
168 regs.mode.write(|w| w.mode().timer()); 190 regs.mode.write(|w| w.mode().counter());
191 } else {
192 regs.mode.write(|w| w.mode().timer());
193 }
169 194
170 // Make the counter's max value as high as possible. 195 // Make the counter's max value as high as possible.
171 // TODO: is there a reason someone would want to set this lower? 196 // TODO: is there a reason someone would want to set this lower?
@@ -225,6 +250,14 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> {
225 Task::from_reg(&T::regs().tasks_clear) 250 Task::from_reg(&T::regs().tasks_clear)
226 } 251 }
227 252
253 /// Returns the COUNT task, for use with PPI.
254 ///
255 /// When triggered, this task increments the timer's counter by 1.
256 /// Only works in counter mode.
257 pub fn task_count(&self) -> Task {
258 Task::from_reg(&T::regs().tasks_count)
259 }
260
228 /// Change the timer's frequency. 261 /// Change the timer's frequency.
229 /// 262 ///
230 /// This will stop the timer if it isn't already stopped, 263 /// This will stop the timer if it isn't already stopped,
diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs
index 48457744b..00afbd059 100644
--- a/embassy-nrf/src/uarte.rs
+++ b/embassy-nrf/src/uarte.rs
@@ -883,6 +883,7 @@ pub(crate) mod sealed {
883 pub trait Instance { 883 pub trait Instance {
884 fn regs() -> &'static pac::uarte0::RegisterBlock; 884 fn regs() -> &'static pac::uarte0::RegisterBlock;
885 fn state() -> &'static State; 885 fn state() -> &'static State;
886 fn buffered_state() -> &'static crate::buffered_uarte::State;
886 } 887 }
887} 888}
888 889
@@ -902,6 +903,10 @@ macro_rules! impl_uarte {
902 static STATE: crate::uarte::sealed::State = crate::uarte::sealed::State::new(); 903 static STATE: crate::uarte::sealed::State = crate::uarte::sealed::State::new();
903 &STATE 904 &STATE
904 } 905 }
906 fn buffered_state() -> &'static crate::buffered_uarte::State {
907 static STATE: crate::buffered_uarte::State = crate::buffered_uarte::State::new();
908 &STATE
909 }
905 } 910 }
906 impl crate::uarte::Instance for peripherals::$type { 911 impl crate::uarte::Instance for peripherals::$type {
907 type Interrupt = crate::interrupt::$irq; 912 type Interrupt = crate::interrupt::$irq;
diff --git a/examples/nrf52840/src/bin/buffered_uart.rs b/examples/nrf52840/src/bin/buffered_uart.rs
index ea566f4b2..5b934b7d6 100644
--- a/examples/nrf52840/src/bin/buffered_uart.rs
+++ b/examples/nrf52840/src/bin/buffered_uart.rs
@@ -4,10 +4,9 @@
4 4
5use defmt::*; 5use defmt::*;
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_nrf::buffered_uarte::{BufferedUarte, State}; 7use embassy_nrf::buffered_uarte::BufferedUarte;
8use embassy_nrf::{interrupt, uarte}; 8use embassy_nrf::{interrupt, uarte};
9use embedded_io::asynch::{BufRead, Write}; 9use embedded_io::asynch::Write;
10use futures::pin_mut;
11use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
12 11
13#[embassy_executor::main] 12#[embassy_executor::main]
@@ -21,24 +20,19 @@ async fn main(_spawner: Spawner) {
21 let mut rx_buffer = [0u8; 4096]; 20 let mut rx_buffer = [0u8; 4096];
22 21
23 let irq = interrupt::take!(UARTE0_UART0); 22 let irq = interrupt::take!(UARTE0_UART0);
24 let mut state = State::new(); 23 let mut u = BufferedUarte::new(
25 // Please note - important to have hardware flow control (https://github.com/embassy-rs/embassy/issues/536)
26 let u = BufferedUarte::new(
27 &mut state,
28 p.UARTE0, 24 p.UARTE0,
29 p.TIMER0, 25 p.TIMER0,
30 p.PPI_CH0, 26 p.PPI_CH0,
31 p.PPI_CH1, 27 p.PPI_CH1,
28 p.PPI_GROUP0,
32 irq, 29 irq,
33 p.P0_08, 30 p.P0_08,
34 p.P0_06, 31 p.P0_06,
35 p.P0_07,
36 p.P0_05,
37 config, 32 config,
38 &mut rx_buffer, 33 &mut rx_buffer,
39 &mut tx_buffer, 34 &mut tx_buffer,
40 ); 35 );
41 pin_mut!(u);
42 36
43 info!("uarte initialized!"); 37 info!("uarte initialized!");
44 38
diff --git a/tests/nrf/.cargo/config.toml b/tests/nrf/.cargo/config.toml
new file mode 100644
index 000000000..4eec189d4
--- /dev/null
+++ b/tests/nrf/.cargo/config.toml
@@ -0,0 +1,9 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2#runner = "teleprobe local run --chip nRF52840_xxAA --elf"
3runner = "teleprobe client run --target nrf52840-dk --elf"
4
5[build]
6target = "thumbv7em-none-eabi"
7
8[env]
9DEFMT_LOG = "trace"
diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml
new file mode 100644
index 000000000..2a4e8cf41
--- /dev/null
+++ b/tests/nrf/Cargo.toml
@@ -0,0 +1,20 @@
1[package]
2edition = "2021"
3name = "embassy-nrf-examples"
4version = "0.1.0"
5license = "MIT OR Apache-2.0"
6
7[dependencies]
8embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
9embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt", "nightly"] }
10embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] }
11embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] }
12embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
13embedded-io = { version = "0.4.0", features = ["async"] }
14
15defmt = "0.3"
16defmt-rtt = "0.4"
17
18cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
19cortex-m-rt = "0.7.0"
20panic-probe = { version = "0.3", features = ["print-defmt"] } \ No newline at end of file
diff --git a/tests/nrf/build.rs b/tests/nrf/build.rs
new file mode 100644
index 000000000..6f4872249
--- /dev/null
+++ b/tests/nrf/build.rs
@@ -0,0 +1,16 @@
1use std::error::Error;
2use std::path::PathBuf;
3use std::{env, fs};
4
5fn main() -> Result<(), Box<dyn Error>> {
6 let out = PathBuf::from(env::var("OUT_DIR").unwrap());
7 fs::write(out.join("link_ram.x"), include_bytes!("link_ram.x")).unwrap();
8 println!("cargo:rustc-link-search={}", out.display());
9 println!("cargo:rerun-if-changed=link_ram.x");
10
11 println!("cargo:rustc-link-arg-bins=--nmagic");
12 println!("cargo:rustc-link-arg-bins=-Tlink_ram.x");
13 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
14
15 Ok(())
16}
diff --git a/tests/nrf/link_ram.x b/tests/nrf/link_ram.x
new file mode 100644
index 000000000..26da86baa
--- /dev/null
+++ b/tests/nrf/link_ram.x
@@ -0,0 +1,254 @@
1/* ##### EMBASSY NOTE
2 Originally from https://github.com/rust-embedded/cortex-m-rt/blob/master/link.x.in
3 Adjusted to put everything in RAM
4*/
5
6/* # Developer notes
7
8- Symbols that start with a double underscore (__) are considered "private"
9
10- Symbols that start with a single underscore (_) are considered "semi-public"; they can be
11 overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" {
12 static mut __sbss }`).
13
14- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a
15 symbol if not dropped if it appears in or near the front of the linker arguments and "it's not
16 needed" by any of the preceding objects (linker arguments)
17
18- `PROVIDE` is used to provide default values that can be overridden by a user linker script
19
20- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and*
21 the LMA of .data are all 4-byte aligned. These alignments are assumed by the RAM initialization
22 routine. There's also a second benefit: 4-byte aligned boundaries means that you won't see
23 "Address (..) is out of bounds" in the disassembly produced by `objdump`.
24*/
25
26/* Provides information about the memory layout of the device */
27/* This will be provided by the user (see `memory.x`) or by a Board Support Crate */
28INCLUDE memory.x
29
30/* # Entry point = reset vector */
31EXTERN(__RESET_VECTOR);
32EXTERN(Reset);
33ENTRY(Reset);
34
35/* # Exception vectors */
36/* This is effectively weak aliasing at the linker level */
37/* The user can override any of these aliases by defining the corresponding symbol themselves (cf.
38 the `exception!` macro) */
39EXTERN(__EXCEPTIONS); /* depends on all the these PROVIDED symbols */
40
41EXTERN(DefaultHandler);
42
43PROVIDE(NonMaskableInt = DefaultHandler);
44EXTERN(HardFaultTrampoline);
45PROVIDE(MemoryManagement = DefaultHandler);
46PROVIDE(BusFault = DefaultHandler);
47PROVIDE(UsageFault = DefaultHandler);
48PROVIDE(SecureFault = DefaultHandler);
49PROVIDE(SVCall = DefaultHandler);
50PROVIDE(DebugMonitor = DefaultHandler);
51PROVIDE(PendSV = DefaultHandler);
52PROVIDE(SysTick = DefaultHandler);
53
54PROVIDE(DefaultHandler = DefaultHandler_);
55PROVIDE(HardFault = HardFault_);
56
57/* # Interrupt vectors */
58EXTERN(__INTERRUPTS); /* `static` variable similar to `__EXCEPTIONS` */
59
60/* # Pre-initialization function */
61/* If the user overrides this using the `pre_init!` macro or by creating a `__pre_init` function,
62 then the function this points to will be called before the RAM is initialized. */
63PROVIDE(__pre_init = DefaultPreInit);
64
65/* # Sections */
66SECTIONS
67{
68 PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM));
69
70 /* ## Sections in RAM */
71 /* ### Vector table */
72 .vector_table ORIGIN(RAM) :
73 {
74 /* Initial Stack Pointer (SP) value */
75 LONG(_stack_start);
76
77 /* Reset vector */
78 KEEP(*(.vector_table.reset_vector)); /* this is the `__RESET_VECTOR` symbol */
79 __reset_vector = .;
80
81 /* Exceptions */
82 KEEP(*(.vector_table.exceptions)); /* this is the `__EXCEPTIONS` symbol */
83 __eexceptions = .;
84
85 /* Device specific interrupts */
86 KEEP(*(.vector_table.interrupts)); /* this is the `__INTERRUPTS` symbol */
87 } > RAM
88
89 PROVIDE(_stext = ADDR(.vector_table) + SIZEOF(.vector_table));
90
91 /* ### .text */
92 .text _stext :
93 {
94 __stext = .;
95 *(.Reset);
96
97 *(.text .text.*);
98
99 /* The HardFaultTrampoline uses the `b` instruction to enter `HardFault`,
100 so must be placed close to it. */
101 *(.HardFaultTrampoline);
102 *(.HardFault.*);
103
104 . = ALIGN(4); /* Pad .text to the alignment to workaround overlapping load section bug in old lld */
105 __etext = .;
106 } > RAM
107
108 /* ### .rodata */
109 .rodata : ALIGN(4)
110 {
111 . = ALIGN(4);
112 __srodata = .;
113 *(.rodata .rodata.*);
114
115 /* 4-byte align the end (VMA) of this section.
116 This is required by LLD to ensure the LMA of the following .data
117 section will have the correct alignment. */
118 . = ALIGN(4);
119 __erodata = .;
120 } > RAM
121
122 /* ## Sections in RAM */
123 /* ### .data */
124 .data : ALIGN(4)
125 {
126 . = ALIGN(4);
127 __sdata = .;
128 __edata = .;
129 *(.data .data.*);
130 . = ALIGN(4); /* 4-byte align the end (VMA) of this section */
131 } > RAM
132 /* Allow sections from user `memory.x` injected using `INSERT AFTER .data` to
133 * use the .data loading mechanism by pushing __edata. Note: do not change
134 * output region or load region in those user sections! */
135 . = ALIGN(4);
136
137 /* LMA of .data */
138 __sidata = LOADADDR(.data);
139
140 /* ### .gnu.sgstubs
141 This section contains the TrustZone-M veneers put there by the Arm GNU linker. */
142 /* Security Attribution Unit blocks must be 32 bytes aligned. */
143 /* Note that this pads the RAM usage to 32 byte alignment. */
144 .gnu.sgstubs : ALIGN(32)
145 {
146 . = ALIGN(32);
147 __veneer_base = .;
148 *(.gnu.sgstubs*)
149 . = ALIGN(32);
150 __veneer_limit = .;
151 } > RAM
152
153 /* ### .bss */
154 .bss (NOLOAD) : ALIGN(4)
155 {
156 . = ALIGN(4);
157 __sbss = .;
158 *(.bss .bss.*);
159 *(COMMON); /* Uninitialized C statics */
160 . = ALIGN(4); /* 4-byte align the end (VMA) of this section */
161 } > RAM
162 /* Allow sections from user `memory.x` injected using `INSERT AFTER .bss` to
163 * use the .bss zeroing mechanism by pushing __ebss. Note: do not change
164 * output region or load region in those user sections! */
165 . = ALIGN(4);
166 __ebss = .;
167
168 /* ### .uninit */
169 .uninit (NOLOAD) : ALIGN(4)
170 {
171 . = ALIGN(4);
172 __suninit = .;
173 *(.uninit .uninit.*);
174 . = ALIGN(4);
175 __euninit = .;
176 } > RAM
177
178 /* Place the heap right after `.uninit` in RAM */
179 PROVIDE(__sheap = __euninit);
180
181 /* ## .got */
182 /* Dynamic relocations are unsupported. This section is only used to detect relocatable code in
183 the input files and raise an error if relocatable code is found */
184 .got (NOLOAD) :
185 {
186 KEEP(*(.got .got.*));
187 }
188
189 /* ## Discarded sections */
190 /DISCARD/ :
191 {
192 /* Unused exception related info that only wastes space */
193 *(.ARM.exidx);
194 *(.ARM.exidx.*);
195 *(.ARM.extab.*);
196 }
197}
198
199/* Do not exceed this mark in the error messages below | */
200/* # Alignment checks */
201ASSERT(ORIGIN(RAM) % 4 == 0, "
202ERROR(cortex-m-rt): the start of the RAM region must be 4-byte aligned");
203
204ASSERT(__sdata % 4 == 0 && __edata % 4 == 0, "
205BUG(cortex-m-rt): .data is not 4-byte aligned");
206
207ASSERT(__sidata % 4 == 0, "
208BUG(cortex-m-rt): the LMA of .data is not 4-byte aligned");
209
210ASSERT(__sbss % 4 == 0 && __ebss % 4 == 0, "
211BUG(cortex-m-rt): .bss is not 4-byte aligned");
212
213ASSERT(__sheap % 4 == 0, "
214BUG(cortex-m-rt): start of .heap is not 4-byte aligned");
215
216/* # Position checks */
217
218/* ## .vector_table */
219ASSERT(__reset_vector == ADDR(.vector_table) + 0x8, "
220BUG(cortex-m-rt): the reset vector is missing");
221
222ASSERT(__eexceptions == ADDR(.vector_table) + 0x40, "
223BUG(cortex-m-rt): the exception vectors are missing");
224
225ASSERT(SIZEOF(.vector_table) > 0x40, "
226ERROR(cortex-m-rt): The interrupt vectors are missing.
227Possible solutions, from most likely to less likely:
228- Link to a svd2rust generated device crate
229- Check that you actually use the device/hal/bsp crate in your code
230- Disable the 'device' feature of cortex-m-rt to build a generic application (a dependency
231may be enabling it)
232- Supply the interrupt handlers yourself. Check the documentation for details.");
233
234/* ## .text */
235ASSERT(ADDR(.vector_table) + SIZEOF(.vector_table) <= _stext, "
236ERROR(cortex-m-rt): The .text section can't be placed inside the .vector_table section
237Set _stext to an address greater than the end of .vector_table (See output of `nm`)");
238
239ASSERT(_stext + SIZEOF(.text) < ORIGIN(RAM) + LENGTH(RAM), "
240ERROR(cortex-m-rt): The .text section must be placed inside the RAM memory.
241Set _stext to an address smaller than 'ORIGIN(RAM) + LENGTH(RAM)'");
242
243/* # Other checks */
244ASSERT(SIZEOF(.got) == 0, "
245ERROR(cortex-m-rt): .got section detected in the input object files
246Dynamic relocations are not supported. If you are linking to C code compiled using
247the 'cc' crate then modify your build script to compile the C code _without_
248the -fPIC flag. See the documentation of the `cc::Build.pic` method for details.");
249/* Do not exceed this mark in the error messages above | */
250
251
252/* Provides weak aliases (cf. PROVIDED) for device specific interrupt handlers */
253/* This will usually be provided by a device crate generated using svd2rust (see `device.x`) */
254INCLUDE device.x \ No newline at end of file
diff --git a/tests/nrf/memory.x b/tests/nrf/memory.x
new file mode 100644
index 000000000..58900a7bd
--- /dev/null
+++ b/tests/nrf/memory.x
@@ -0,0 +1,5 @@
1MEMORY
2{
3 FLASH : ORIGIN = 0x00000000, LENGTH = 1024K
4 RAM : ORIGIN = 0x20000000, LENGTH = 256K
5}
diff --git a/tests/nrf/src/bin/buffered_uart.rs b/tests/nrf/src/bin/buffered_uart.rs
new file mode 100644
index 000000000..0550b0bb7
--- /dev/null
+++ b/tests/nrf/src/bin/buffered_uart.rs
@@ -0,0 +1,74 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{assert_eq, *};
6use embassy_executor::Spawner;
7use embassy_futures::join::join;
8use embassy_nrf::buffered_uarte::BufferedUarte;
9use embassy_nrf::{interrupt, uarte};
10use {defmt_rtt as _, panic_probe as _};
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) {
14 let p = embassy_nrf::init(Default::default());
15 let mut config = uarte::Config::default();
16 config.parity = uarte::Parity::EXCLUDED;
17 config.baudrate = uarte::Baudrate::BAUD1M;
18
19 let mut tx_buffer = [0u8; 1024];
20 let mut rx_buffer = [0u8; 1024];
21
22 let mut u = BufferedUarte::new(
23 p.UARTE0,
24 p.TIMER0,
25 p.PPI_CH0,
26 p.PPI_CH1,
27 p.PPI_GROUP0,
28 interrupt::take!(UARTE0_UART0),
29 p.P1_03,
30 p.P1_02,
31 config.clone(),
32 &mut rx_buffer,
33 &mut tx_buffer,
34 );
35
36 info!("uarte initialized!");
37
38 let (mut rx, mut tx) = u.split();
39
40 const COUNT: usize = 40_000;
41
42 let tx_fut = async {
43 let mut tx_buf = [0; 215];
44 let mut i = 0;
45 while i < COUNT {
46 let n = tx_buf.len().min(COUNT - i);
47 let tx_buf = &mut tx_buf[..n];
48 for (j, b) in tx_buf.iter_mut().enumerate() {
49 *b = (i + j) as u8;
50 }
51 let n = unwrap!(tx.write(tx_buf).await);
52 i += n;
53 }
54 };
55 let rx_fut = async {
56 let mut i = 0;
57 while i < COUNT {
58 let buf = unwrap!(rx.fill_buf().await);
59
60 for &b in buf {
61 assert_eq!(b, i as u8);
62 i = i + 1;
63 }
64
65 let n = buf.len();
66 rx.consume(n);
67 }
68 };
69
70 join(rx_fut, tx_fut).await;
71
72 info!("Test OK");
73 cortex_m::asm::bkpt();
74}
diff --git a/tests/nrf/src/bin/buffered_uart_spam.rs b/tests/nrf/src/bin/buffered_uart_spam.rs
new file mode 100644
index 000000000..57aaeca45
--- /dev/null
+++ b/tests/nrf/src/bin/buffered_uart_spam.rs
@@ -0,0 +1,86 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use core::mem;
6use core::ptr::NonNull;
7
8use defmt::{assert_eq, *};
9use embassy_executor::Spawner;
10use embassy_nrf::buffered_uarte::BufferedUarte;
11use embassy_nrf::gpio::{Level, Output, OutputDrive};
12use embassy_nrf::ppi::{Event, Ppi, Task};
13use embassy_nrf::uarte::Uarte;
14use embassy_nrf::{interrupt, pac, uarte};
15use embassy_time::{Duration, Timer};
16use {defmt_rtt as _, panic_probe as _};
17
18#[embassy_executor::main]
19async fn main(_spawner: Spawner) {
20 let mut p = embassy_nrf::init(Default::default());
21 let mut config = uarte::Config::default();
22 config.parity = uarte::Parity::EXCLUDED;
23 config.baudrate = uarte::Baudrate::BAUD1M;
24
25 let mut tx_buffer = [0u8; 1024];
26 let mut rx_buffer = [0u8; 1024];
27
28 mem::forget(Output::new(&mut p.P1_02, Level::High, OutputDrive::Standard));
29
30 let mut u = BufferedUarte::new(
31 p.UARTE0,
32 p.TIMER0,
33 p.PPI_CH0,
34 p.PPI_CH1,
35 p.PPI_GROUP0,
36 interrupt::take!(UARTE0_UART0),
37 p.P1_03,
38 p.P1_04,
39 config.clone(),
40 &mut rx_buffer,
41 &mut tx_buffer,
42 );
43
44 info!("uarte initialized!");
45
46 // uarte needs some quiet time to start rxing properly.
47 Timer::after(Duration::from_millis(10)).await;
48
49 // Tx spam in a loop.
50 const NSPAM: usize = 17;
51 static mut TX_BUF: [u8; NSPAM] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
52 let _spam = Uarte::new(p.UARTE1, interrupt::take!(UARTE1), p.P1_01, p.P1_02, config.clone());
53 let spam_peri: pac::UARTE1 = unsafe { mem::transmute(()) };
54 let event = unsafe { Event::new_unchecked(NonNull::new_unchecked(&spam_peri.events_endtx as *const _ as _)) };
55 let task = unsafe { Task::new_unchecked(NonNull::new_unchecked(&spam_peri.tasks_starttx as *const _ as _)) };
56 let mut spam_ppi = Ppi::new_one_to_one(p.PPI_CH2, event, task);
57 spam_ppi.enable();
58 let p = unsafe { TX_BUF.as_mut_ptr() };
59 spam_peri.txd.ptr.write(|w| unsafe { w.ptr().bits(p as u32) });
60 spam_peri.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(NSPAM as _) });
61 spam_peri.tasks_starttx.write(|w| unsafe { w.bits(1) });
62
63 let mut i = 0;
64 let mut total = 0;
65 while total < 256 * 1024 {
66 let buf = unwrap!(u.fill_buf().await);
67 //info!("rx {}", buf);
68
69 for &b in buf {
70 assert_eq!(b, unsafe { TX_BUF[i] });
71
72 i = i + 1;
73 if i == NSPAM {
74 i = 0;
75 }
76 }
77
78 // Read bytes have to be explicitly consumed, otherwise fill_buf() will return them again
79 let n = buf.len();
80 u.consume(n);
81 total += n;
82 }
83
84 info!("Test OK");
85 cortex_m::asm::bkpt();
86}