aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxoviat <[email protected]>2025-12-12 18:51:51 -0600
committerxoviat <[email protected]>2025-12-12 18:51:51 -0600
commit74f5ec6efdd62969c6edad706ab3313f4d535a6a (patch)
tree72cc3c6a9572e673626c3503d2143f37d7661013
parente8d0b69e8c416cc2b9922acbf277b520d80f3134 (diff)
parent9f501479921ea9bbf5fef8ecdc9ac625225ed019 (diff)
Merge branch 'main' of https://github.com/embassy-rs/embassy into wpan
-rwxr-xr-x.github/ci/build-xtensa.sh2
-rw-r--r--embassy-executor/CHANGELOG.md1
-rw-r--r--embassy-executor/src/arch/std.rs15
-rw-r--r--embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr10
-rw-r--r--embassy-imxrt/Cargo.toml4
-rw-r--r--embassy-imxrt/src/flexcomm/mod.rs1
-rw-r--r--embassy-imxrt/src/flexcomm/spi.rs1011
-rw-r--r--embassy-nrf/CHANGELOG.md1
-rw-r--r--embassy-nrf/src/lib.rs4
-rw-r--r--embassy-nrf/src/rramc.rs173
-rw-r--r--embassy-nrf/src/twim.rs2
-rw-r--r--embassy-stm32/src/sdmmc/mod.rs64
-rw-r--r--embassy-stm32/src/sdmmc/sd.rs58
-rw-r--r--embassy-stm32/src/sdmmc/sdio.rs27
-rw-r--r--examples/mimxrt6/Cargo.toml2
-rw-r--r--examples/mimxrt6/src/bin/dma.rs26
-rw-r--r--examples/mimxrt6/src/bin/spi-async.rs35
-rw-r--r--examples/mimxrt6/src/bin/spi.rs51
-rw-r--r--examples/nrf54l15/src/bin/rramc.rs (renamed from examples/nrf54l15/src/bin/nvmc.rs)0
-rw-r--r--examples/nrf54l15/src/bin/rramc_buffered.rs59
-rw-r--r--examples/std/src/bin/net_ppp.rs9
-rw-r--r--examples/std/src/bin/tick_cancel.rs47
-rw-r--r--rust-toolchain-nightly.toml2
-rw-r--r--rust-toolchain.toml2
24 files changed, 1526 insertions, 80 deletions
diff --git a/.github/ci/build-xtensa.sh b/.github/ci/build-xtensa.sh
index f07816861..cc56adb26 100755
--- a/.github/ci/build-xtensa.sh
+++ b/.github/ci/build-xtensa.sh
@@ -14,7 +14,7 @@ export PATH=$CARGO_HOME/bin:$PATH
14export CARGO_NET_GIT_FETCH_WITH_CLI=true 14export CARGO_NET_GIT_FETCH_WITH_CLI=true
15 15
16cargo install espup --locked 16cargo install espup --locked
17espup install --toolchain-version 1.88.0.0 17espup install --toolchain-version 1.90.0.0
18 18
19# Restore lockfiles 19# Restore lockfiles
20if [ -f /ci/cache/lockfiles.tar ]; then 20if [ -f /ci/cache/lockfiles.tar ]; then
diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md
index 5fbb8cf13..8f1db7de7 100644
--- a/embassy-executor/CHANGELOG.md
+++ b/embassy-executor/CHANGELOG.md
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
15- Added optional "earliest deadline first" EDF scheduling 15- Added optional "earliest deadline first" EDF scheduling
16- Migrate `cortex-ar` to `aarch32-cpu`. The feature name `arch-cortex-ar` remains the same and 16- Migrate `cortex-ar` to `aarch32-cpu`. The feature name `arch-cortex-ar` remains the same and
17 legacy ARM architectures are not supported. 17 legacy ARM architectures are not supported.
18- Added `run_until` to `arch-std` variant of `Executor`.
18 19
19## 0.9.1 - 2025-08-31 20## 0.9.1 - 2025-08-31
20 21
diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs
index c62ab723b..d4057144e 100644
--- a/embassy-executor/src/arch/std.rs
+++ b/embassy-executor/src/arch/std.rs
@@ -55,11 +55,24 @@ mod thread {
55 /// 55 ///
56 /// This function never returns. 56 /// This function never returns.
57 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 57 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
58 self.run_until(init, || false);
59 unreachable!()
60 }
61
62 /// Run the executor until a flag is raised.
63 ///
64 /// This function is identical to `Executor::run()` apart from offering a `done` flag to stop execution.
65 pub fn run_until(&'static mut self, init: impl FnOnce(Spawner), mut done: impl FnMut() -> bool) {
58 init(self.inner.spawner()); 66 init(self.inner.spawner());
59 67
60 loop { 68 loop {
61 unsafe { self.inner.poll() }; 69 unsafe { self.inner.poll() };
62 self.signaler.wait() 70
71 if done() {
72 break;
73 }
74
75 self.signaler.wait();
63 } 76 }
64 } 77 }
65 } 78 }
diff --git a/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr b/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr
index 3c3c9503b..e5e069ade 100644
--- a/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr
+++ b/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr
@@ -5,6 +5,10 @@ error[E0277]: task futures must resolve to `()` or `!`
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskReturnValue` is not implemented for `u32` 5 | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskReturnValue` is not implemented for `u32`
6 | 6 |
7 = note: use `async fn` or change the return type to `impl Future<Output = ()>` 7 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
8 = help: the following other types implement trait `TaskReturnValue`: 8help: the following other types implement trait `TaskReturnValue`
9 () 9 --> src/lib.rs
10 <fn() -> ! as HasOutput>::Output 10 |
11 | impl TaskReturnValue for () {}
12 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `()`
13 | impl TaskReturnValue for Never {}
14 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `<fn() -> ! as HasOutput>::Output`
diff --git a/embassy-imxrt/Cargo.toml b/embassy-imxrt/Cargo.toml
index c47756f10..81377579b 100644
--- a/embassy-imxrt/Cargo.toml
+++ b/embassy-imxrt/Cargo.toml
@@ -103,5 +103,5 @@ document-features = "0.2.7"
103paste = "1.0" 103paste = "1.0"
104 104
105# PACs 105# PACs
106mimxrt685s-pac = { version = "0.4.0", optional = true, features = ["rt", "critical-section"] } 106mimxrt685s-pac = { version = "0.5.0", optional = true, features = ["rt", "critical-section"] }
107mimxrt633s-pac = { version = "0.4.0", optional = true, features = ["rt", "critical-section"] } 107mimxrt633s-pac = { version = "0.5.0", optional = true, features = ["rt", "critical-section"] }
diff --git a/embassy-imxrt/src/flexcomm/mod.rs b/embassy-imxrt/src/flexcomm/mod.rs
index 27794042b..ed87c7fb4 100644
--- a/embassy-imxrt/src/flexcomm/mod.rs
+++ b/embassy-imxrt/src/flexcomm/mod.rs
@@ -1,5 +1,6 @@
1//! Implements Flexcomm interface wrapper for easier usage across modules 1//! Implements Flexcomm interface wrapper for easier usage across modules
2 2
3pub mod spi;
3pub mod uart; 4pub mod uart;
4 5
5use paste::paste; 6use paste::paste;
diff --git a/embassy-imxrt/src/flexcomm/spi.rs b/embassy-imxrt/src/flexcomm/spi.rs
new file mode 100644
index 000000000..9dd776ac7
--- /dev/null
+++ b/embassy-imxrt/src/flexcomm/spi.rs
@@ -0,0 +1,1011 @@
1//! Serial Peripheral Interface (SPI) driver.
2
3use core::future::{Future, poll_fn};
4use core::marker::PhantomData;
5use core::task::Poll;
6
7use embassy_embedded_hal::SetConfig;
8use embassy_hal_internal::{Peri, PeripheralType};
9use embassy_sync::waitqueue::AtomicWaker;
10pub use embedded_hal_1::spi::{MODE_0, MODE_1, MODE_2, MODE_3, Mode, Phase, Polarity};
11use paste::paste;
12
13use crate::flexcomm::Clock;
14use crate::gpio::{AnyPin, GpioPin as Pin};
15use crate::interrupt;
16use crate::interrupt::typelevel::Interrupt;
17use crate::iopctl::{DriveMode, DriveStrength, Inverter, IopctlPin, Pull, SlewRate};
18use crate::pac::spi0::cfg::{Cpha, Cpol};
19
20/// Driver move trait.
21#[allow(private_bounds)]
22pub trait IoMode: sealed::Sealed {}
23
24/// Blocking mode.
25pub struct Blocking;
26impl sealed::Sealed for Blocking {}
27impl IoMode for Blocking {}
28
29/// Async mode.
30pub struct Async;
31impl sealed::Sealed for Async {}
32impl IoMode for Async {}
33
34/// Spi errors.
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36#[cfg_attr(feature = "defmt", derive(defmt::Format))]
37#[non_exhaustive]
38pub enum Error {
39 // No errors for now.
40}
41
42/// Spi driver.
43pub struct Spi<'a, M: IoMode> {
44 info: Info,
45 _phantom: PhantomData<&'a M>,
46}
47
48impl<'a> Spi<'a, Blocking> {
49 /// Create a SPI driver in blocking mode.
50 pub fn new_blocking<T: Instance>(
51 _inner: Peri<'a, T>,
52 sck: Peri<'a, impl SckPin<T> + 'a>,
53 mosi: Peri<'a, impl MosiPin<T> + 'a>,
54 miso: Peri<'a, impl MisoPin<T> + 'a>,
55 config: Config,
56 ) -> Self {
57 sck.as_sck();
58 mosi.as_mosi();
59 miso.as_miso();
60
61 Self::new_inner(_inner, Some(sck.into()), Some(mosi.into()), Some(miso.into()), config)
62 }
63
64 /// Create a TX-only SPI driver in blocking mode.
65 pub fn new_blocking_txonly<T: Instance>(
66 _inner: Peri<'a, T>,
67 sck: Peri<'a, impl SckPin<T> + 'a>,
68 mosi: Peri<'a, impl MosiPin<T> + 'a>,
69 config: Config,
70 ) -> Self {
71 sck.as_sck();
72 mosi.as_mosi();
73
74 Self::new_inner(_inner, Some(sck.into()), Some(mosi.into()), None, config)
75 }
76
77 /// Create an RX-only SPI driver in blocking mode.
78 pub fn new_blocking_rxonly<T: Instance>(
79 _inner: Peri<'a, T>,
80 sck: Peri<'a, impl SckPin<T> + 'a>,
81 miso: Peri<'a, impl MisoPin<T> + 'a>,
82 config: Config,
83 ) -> Self {
84 sck.as_sck();
85 miso.as_miso();
86
87 Self::new_inner(_inner, Some(sck.into()), None, Some(miso.into()), config)
88 }
89
90 /// Create an internal-loopback SPI driver in blocking mode.
91 ///
92 /// WARNING: This is only useful for testing as it doesn't use any
93 /// external pins.
94 pub fn new_blocking_loopback<T: Instance>(_inner: Peri<'a, T>, config: Config) -> Self {
95 Self::new_inner(_inner, None, None, None, config)
96 }
97}
98
99impl<'a, M: IoMode> Spi<'a, M> {
100 /// Read data from Spi blocking execution until done.
101 pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<(), Error> {
102 critical_section::with(|_| {
103 self.info
104 .regs
105 .fifostat()
106 .modify(|_, w| w.txerr().set_bit().rxerr().set_bit());
107
108 for word in data.iter_mut() {
109 // wait until we have data in the RxFIFO.
110 while self.info.regs.fifostat().read().rxnotempty().bit_is_clear() {}
111
112 self.info
113 .regs
114 .fifowr()
115 .write(|w| unsafe { w.txdata().bits(*word as u16).len().bits(7) });
116
117 *word = self.info.regs.fiford().read().rxdata().bits() as u8;
118 }
119 });
120
121 self.flush()
122 }
123
124 /// Write data to Spi blocking execution until done.
125 pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> {
126 critical_section::with(|_| {
127 self.info
128 .regs
129 .fifostat()
130 .modify(|_, w| w.txerr().set_bit().rxerr().set_bit());
131
132 for (i, word) in data.iter().enumerate() {
133 // wait until we have space in the TxFIFO.
134 while self.info.regs.fifostat().read().txnotfull().bit_is_clear() {}
135
136 self.info.regs.fifowr().write(|w| {
137 unsafe { w.txdata().bits(*word as u16).len().bits(7) }
138 .rxignore()
139 .set_bit();
140
141 if i == data.len() - 1 {
142 w.eot().set_bit();
143 }
144
145 w
146 });
147 }
148 });
149
150 self.flush()
151 }
152
153 /// Transfer data to SPI blocking execution until done.
154 pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
155 let len = read.len().max(write.len());
156
157 critical_section::with(|_| {
158 self.info
159 .regs
160 .fifostat()
161 .modify(|_, w| w.txerr().set_bit().rxerr().set_bit());
162
163 for i in 0..len {
164 let wb = write.get(i).copied().unwrap_or(0);
165
166 // wait until we have space in the TxFIFO.
167 while self.info.regs.fifostat().read().txnotfull().bit_is_clear() {}
168
169 self.info.regs.fifowr().write(|w| {
170 unsafe { w.txdata().bits(wb as u16).len().bits(7) };
171
172 if i == len - 1 {
173 w.eot().set_bit();
174 }
175
176 w
177 });
178
179 // wait until we have data in the RxFIFO.
180 while self.info.regs.fifostat().read().rxnotempty().bit_is_clear() {}
181
182 let rb = self.info.regs.fiford().read().rxdata().bits() as u8;
183
184 if let Some(r) = read.get_mut(i) {
185 *r = rb;
186 }
187 }
188 });
189
190 self.flush()
191 }
192
193 /// Transfer data in place to SPI blocking execution until done.
194 pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> {
195 critical_section::with(|_| {
196 self.info
197 .regs
198 .fifostat()
199 .modify(|_, w| w.txerr().set_bit().rxerr().set_bit());
200
201 for word in data {
202 // wait until we have space in the TxFIFO.
203 while self.info.regs.fifostat().read().txnotfull().bit_is_clear() {}
204 self.info
205 .regs
206 .fifowr()
207 .write(|w| unsafe { w.txdata().bits(*word as u16) });
208
209 // wait until we have data in the RxFIFO.
210 while self.info.regs.fifostat().read().rxnotempty().bit_is_clear() {}
211 *word = self.info.regs.fiford().read().rxdata().bits() as u8;
212 }
213 });
214
215 self.flush()
216 }
217
218 /// Block execution until Spi is done.
219 pub fn flush(&mut self) -> Result<(), Error> {
220 let regs = self.info.regs;
221 while regs.stat().read().mstidle().bit_is_clear() {}
222 Ok(())
223 }
224}
225
226impl<'a> Spi<'a, Async> {
227 /// Create a SPI driver in async mode.
228 pub fn new_async<T: Instance>(
229 _inner: Peri<'a, T>,
230 sck: Peri<'a, impl SckPin<T> + 'a>,
231 mosi: Peri<'a, impl MosiPin<T> + 'a>,
232 miso: Peri<'a, impl MisoPin<T> + 'a>,
233 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'a,
234 config: Config,
235 ) -> Self {
236 sck.as_sck();
237 mosi.as_mosi();
238 miso.as_miso();
239
240 T::Interrupt::unpend();
241 unsafe { T::Interrupt::enable() };
242
243 Self::new_inner(_inner, Some(sck.into()), Some(mosi.into()), Some(miso.into()), config)
244 }
245
246 /// Create a TX-only SPI driver in async mode.
247 pub fn new_async_txonly<T: Instance>(
248 _inner: Peri<'a, T>,
249 sck: Peri<'a, impl SckPin<T> + 'a>,
250 mosi: Peri<'a, impl MosiPin<T> + 'a>,
251 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'a,
252 config: Config,
253 ) -> Self {
254 sck.as_sck();
255 mosi.as_mosi();
256
257 T::Interrupt::unpend();
258 unsafe { T::Interrupt::enable() };
259
260 Self::new_inner(_inner, Some(sck.into()), Some(mosi.into()), None, config)
261 }
262
263 /// Create an RX-only SPI driver in async mode.
264 pub fn new_async_rxonly<T: Instance>(
265 _inner: Peri<'a, T>,
266 sck: Peri<'a, impl SckPin<T> + 'a>,
267 miso: Peri<'a, impl MisoPin<T> + 'a>,
268 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'a,
269 config: Config,
270 ) -> Self {
271 sck.as_sck();
272 miso.as_miso();
273
274 T::Interrupt::unpend();
275 unsafe { T::Interrupt::enable() };
276
277 Self::new_inner(_inner, Some(sck.into()), None, Some(miso.into()), config)
278 }
279
280 /// Create an internal-loopback SPI driver in async mode.
281 ///
282 /// WARNING: This is only useful for testing as it doesn't use any
283 /// external pins.
284 pub fn new_async_loopback<T: Instance>(
285 _inner: Peri<'a, T>,
286 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'a,
287 config: Config,
288 ) -> Self {
289 T::Interrupt::unpend();
290 unsafe { T::Interrupt::enable() };
291
292 Self::new_inner(_inner, None, None, None, config)
293 }
294
295 /// Read data from Spi async execution until done.
296 pub async fn async_read(&mut self, data: &mut [u8]) -> Result<(), Error> {
297 critical_section::with(|_| {
298 self.info
299 .regs
300 .fifostat()
301 .modify(|_, w| w.txerr().set_bit().rxerr().set_bit());
302 });
303
304 for word in data.iter_mut() {
305 // wait until we have data in the RxFIFO.
306 self.wait_for(
307 |me| {
308 if me.info.regs.fifostat().read().rxnotempty().bit_is_set() {
309 Poll::Ready(())
310 } else {
311 Poll::Pending
312 }
313 },
314 |me| {
315 me.info
316 .regs
317 .fifointenset()
318 .write(|w| w.rxlvl().set_bit().rxerr().set_bit());
319 },
320 )
321 .await;
322
323 self.info
324 .regs
325 .fifowr()
326 .write(|w| unsafe { w.txdata().bits(*word as u16).len().bits(7) });
327
328 *word = self.info.regs.fiford().read().rxdata().bits() as u8;
329 }
330
331 self.async_flush().await;
332
333 Ok(())
334 }
335
336 /// Write data to Spi async execution until done.
337 pub async fn async_write(&mut self, data: &[u8]) -> Result<(), Error> {
338 critical_section::with(|_| {
339 self.info
340 .regs
341 .fifostat()
342 .modify(|_, w| w.txerr().set_bit().rxerr().set_bit());
343 });
344
345 for (i, word) in data.iter().enumerate() {
346 // wait until we have space in the TxFIFO.
347 self.wait_for(
348 |me| {
349 if me.info.regs.fifostat().read().txnotfull().bit_is_set() {
350 Poll::Ready(())
351 } else {
352 Poll::Pending
353 }
354 },
355 |me| {
356 me.info
357 .regs
358 .fifointenset()
359 .write(|w| w.txlvl().set_bit().txerr().set_bit());
360 },
361 )
362 .await;
363
364 self.info.regs.fifowr().write(|w| {
365 unsafe { w.txdata().bits(*word as u16).len().bits(7) }
366 .rxignore()
367 .set_bit();
368
369 if i == data.len() - 1 {
370 w.eot().set_bit();
371 }
372
373 w
374 });
375 }
376
377 self.async_flush().await;
378
379 Ok(())
380 }
381
382 /// Transfer data to SPI async execution until done.
383 pub async fn async_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
384 let len = read.len().max(write.len());
385
386 critical_section::with(|_| {
387 self.info
388 .regs
389 .fifostat()
390 .modify(|_, w| w.txerr().set_bit().rxerr().set_bit());
391 });
392
393 for i in 0..len {
394 let wb = write.get(i).copied().unwrap_or(0);
395
396 // wait until we have space in the TxFIFO.
397 self.wait_for(
398 |me| {
399 if me.info.regs.fifostat().read().txnotfull().bit_is_set() {
400 Poll::Ready(())
401 } else {
402 Poll::Pending
403 }
404 },
405 |me| {
406 me.info.regs.fifotrig().write(|w| w.txlvlena().set_bit());
407 me.info
408 .regs
409 .fifointenset()
410 .write(|w| w.txlvl().set_bit().txerr().set_bit());
411 },
412 )
413 .await;
414
415 self.info.regs.fifowr().write(|w| {
416 unsafe { w.txdata().bits(wb as u16).len().bits(7) };
417
418 if i == len - 1 {
419 w.eot().set_bit();
420 }
421
422 w
423 });
424
425 // wait until we have data in the RxFIFO.
426 self.wait_for(
427 |me| {
428 if me.info.regs.fifostat().read().rxnotempty().bit_is_set() {
429 Poll::Ready(())
430 } else {
431 Poll::Pending
432 }
433 },
434 |me| {
435 me.info.regs.fifotrig().write(|w| w.rxlvlena().set_bit());
436 me.info
437 .regs
438 .fifointenset()
439 .write(|w| w.rxlvl().set_bit().rxerr().set_bit());
440 },
441 )
442 .await;
443
444 let rb = self.info.regs.fiford().read().rxdata().bits() as u8;
445
446 if let Some(r) = read.get_mut(i) {
447 *r = rb;
448 }
449 }
450
451 self.async_flush().await;
452
453 Ok(())
454 }
455
456 /// Transfer data in place to SPI async execution until done.
457 pub async fn async_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> {
458 critical_section::with(|_| {
459 self.info
460 .regs
461 .fifostat()
462 .modify(|_, w| w.txerr().set_bit().rxerr().set_bit());
463 });
464
465 for word in data {
466 // wait until we have space in the TxFIFO.
467 self.wait_for(
468 |me| {
469 if me.info.regs.fifostat().read().txnotfull().bit_is_set() {
470 Poll::Ready(())
471 } else {
472 Poll::Pending
473 }
474 },
475 |me| {
476 me.info
477 .regs
478 .fifointenset()
479 .write(|w| w.txlvl().set_bit().txerr().set_bit());
480 },
481 )
482 .await;
483
484 self.info
485 .regs
486 .fifowr()
487 .write(|w| unsafe { w.txdata().bits(*word as u16) });
488
489 // wait until we have data in the RxFIFO.
490 self.wait_for(
491 |me| {
492 if me.info.regs.fifostat().read().rxnotempty().bit_is_set() {
493 Poll::Ready(())
494 } else {
495 Poll::Pending
496 }
497 },
498 |me| {
499 me.info
500 .regs
501 .fifointenset()
502 .write(|w| w.rxlvl().set_bit().rxerr().set_bit());
503 },
504 )
505 .await;
506
507 *word = self.info.regs.fiford().read().rxdata().bits() as u8;
508 }
509
510 self.async_flush().await;
511
512 Ok(())
513 }
514
515 /// Async flush.
516 pub fn async_flush(&mut self) -> impl Future<Output = ()> + use<'_, 'a> {
517 self.wait_for(
518 |me| {
519 if me.info.regs.stat().read().mstidle().bit_is_set() {
520 Poll::Ready(())
521 } else {
522 Poll::Pending
523 }
524 },
525 |me| {
526 me.info.regs.intenset().write(|w| w.mstidleen().set_bit());
527 },
528 )
529 }
530
531 /// Calls `f` to check if we are ready or not.
532 /// If not, `g` is called once the waker is set (to eg enable the required interrupts).
533 fn wait_for<F, U, G>(&mut self, mut f: F, mut g: G) -> impl Future<Output = U> + use<'_, 'a, F, U, G>
534 where
535 F: FnMut(&mut Self) -> Poll<U>,
536 G: FnMut(&mut Self),
537 {
538 poll_fn(move |cx| {
539 // Register waker before checking condition, to ensure that wakes/interrupts
540 // aren't lost between f() and g()
541 self.info.waker.register(cx.waker());
542 let r = f(self);
543
544 if r.is_pending() {
545 g(self);
546 }
547
548 r
549 })
550 }
551}
552
553impl<'a, M: IoMode> Spi<'a, M> {
554 fn new_inner<T: Instance>(
555 _inner: Peri<'a, T>,
556 sck: Option<Peri<'a, AnyPin>>,
557 mosi: Option<Peri<'a, AnyPin>>,
558 miso: Option<Peri<'a, AnyPin>>,
559 config: Config,
560 ) -> Self {
561 // REVISIT: allow selecting from multiple clocks.
562 let clk = Self::clock(&config);
563
564 T::enable(clk);
565 T::into_spi();
566
567 Self::apply_config(T::info().regs, &config);
568
569 let info = T::info();
570 let regs = info.regs;
571
572 critical_section::with(|_| match (sck.is_some(), mosi.is_some(), miso.is_some()) {
573 (true, true, true) => {
574 regs.fifocfg().modify(|_, w| {
575 w.enabletx()
576 .set_bit()
577 .emptytx()
578 .set_bit()
579 .enablerx()
580 .set_bit()
581 .emptyrx()
582 .set_bit()
583 });
584 }
585 (true, false, true) => {
586 regs.fifocfg().modify(|_, w| {
587 w.enabletx()
588 .set_bit()
589 .emptytx()
590 .clear_bit()
591 .enablerx()
592 .set_bit()
593 .emptyrx()
594 .set_bit()
595 });
596 }
597 (true, true, false) => {
598 regs.fifocfg().modify(|_, w| {
599 w.enabletx()
600 .set_bit()
601 .emptytx()
602 .set_bit()
603 .enablerx()
604 .clear_bit()
605 .emptyrx()
606 .set_bit()
607 });
608 }
609 (false, _, _) => {
610 regs.fifocfg().modify(|_, w| {
611 w.enabletx()
612 .set_bit()
613 .emptytx()
614 .set_bit()
615 .enablerx()
616 .set_bit()
617 .emptyrx()
618 .set_bit()
619 });
620 regs.cfg().modify(|_, w| w.loop_().enabled());
621 }
622 _ => {}
623 });
624
625 Self {
626 info,
627 _phantom: PhantomData,
628 }
629 }
630
631 fn set_config(&mut self, config: &Config) {
632 Self::apply_config(self.info.regs, config);
633 }
634
635 fn clock(config: &Config) -> Clock {
636 const SFRO_CLOCK_SPEED_HZ: u32 = 16_000_000;
637
638 if config.frequency > SFRO_CLOCK_SPEED_HZ {
639 Clock::Ffro
640 } else {
641 Clock::Sfro
642 }
643 }
644
645 fn clock_frequency(clock: Clock) -> u32 {
646 match clock {
647 Clock::Sfro => 16_000_000,
648 Clock::Ffro => 48_000_000,
649 _ => unreachable!(),
650 }
651 }
652
653 fn apply_config(regs: &'static crate::pac::spi0::RegisterBlock, config: &Config) {
654 let polarity = if config.mode.polarity == Polarity::IdleLow {
655 Cpol::Low
656 } else {
657 Cpol::High
658 };
659
660 let phase = if config.mode.phase == Phase::CaptureOnFirstTransition {
661 Cpha::Change
662 } else {
663 Cpha::Capture
664 };
665
666 let clk = Self::clock(config);
667 let div = Self::clock_frequency(clk) / config.frequency - 1;
668
669 critical_section::with(|_| {
670 // disable SPI every time we need to modify configuration.
671 regs.cfg().modify(|_, w| w.enable().disabled());
672
673 regs.cfg().modify(|_, w| {
674 w.cpha()
675 .variant(phase)
676 .cpol()
677 .variant(polarity)
678 .loop_()
679 .disabled()
680 .master()
681 .master_mode()
682 });
683
684 regs.div().write(|w| unsafe { w.divval().bits(div as u16) });
685
686 regs.cfg().modify(|_, w| w.enable().enabled());
687 });
688 }
689}
690
691/// Spi config.
692#[derive(Clone)]
693pub struct Config {
694 /// Frequency in Hertz.
695 pub frequency: u32,
696 /// SPI operating mode.
697 pub mode: Mode,
698}
699
700impl Default for Config {
701 fn default() -> Self {
702 Self {
703 frequency: 1_000_000,
704 mode: MODE_0,
705 }
706 }
707}
708
709struct Info {
710 regs: &'static crate::pac::spi0::RegisterBlock,
711 waker: &'static AtomicWaker,
712}
713
714// SAFETY: safety for Send here is the same as the other accessors to
715// unsafe blocks: it must be done from a single executor context.
716//
717// This is a temporary workaround -- a better solution might be to
718// refactor Info to no longer maintain a reference to regs, but
719// instead look up the correct register set and then perform
720// operations within an unsafe block as we do for other peripherals
721unsafe impl Send for Info {}
722
723trait SealedInstance {
724 fn info() -> Info;
725}
726
727/// Spi interrupt handler.
728pub struct InterruptHandler<T: Instance> {
729 _phantom: PhantomData<T>,
730}
731
732impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
733 unsafe fn on_interrupt() {
734 let waker = T::info().waker;
735 let stat = T::info().regs.fifointstat().read();
736
737 if stat.perint().bit_is_set() {
738 T::info().regs.intenclr().write(|w| w.mstidle().clear_bit_by_one());
739 }
740
741 if stat.txlvl().bit_is_set() {
742 T::info().regs.fifointenclr().write(|w| w.txlvl().set_bit());
743 }
744
745 if stat.txerr().bit_is_set() {
746 T::info().regs.fifointenclr().write(|w| w.txerr().set_bit());
747 }
748
749 if stat.rxlvl().bit_is_set() {
750 T::info().regs.fifointenclr().write(|w| w.rxlvl().set_bit());
751 }
752
753 if stat.rxerr().bit_is_set() {
754 T::info().regs.fifointenclr().write(|w| w.rxerr().set_bit());
755 }
756
757 waker.wake();
758 }
759}
760
761/// Spi instance trait.
762#[allow(private_bounds)]
763pub trait Instance: crate::flexcomm::IntoSpi + SealedInstance + PeripheralType + 'static + Send {
764 /// Interrupt for this Spi instance.
765 type Interrupt: interrupt::typelevel::Interrupt;
766}
767
768macro_rules! impl_instance {
769 ($($n:expr),*) => {
770 $(
771 paste!{
772 impl SealedInstance for crate::peripherals::[<FLEXCOMM $n>] {
773 #[inline]
774 fn info() -> Info {
775 static WAKER: AtomicWaker = AtomicWaker::new();
776
777 Info {
778 regs: unsafe { &*crate::pac::[<Spi $n>]::ptr() },
779 waker: &WAKER,
780 }
781 }
782 }
783
784 impl Instance for crate::peripherals::[<FLEXCOMM $n>] {
785 type Interrupt = crate::interrupt::typelevel::[<FLEXCOMM $n>];
786 }
787 }
788 )*
789 }
790}
791
792impl_instance!(0, 1, 2, 3, 4, 5, 6, 7, 14);
793
794mod sealed {
795 /// Seal a trait
796 pub trait Sealed {}
797}
798
799impl<T: Pin> sealed::Sealed for T {}
800
801/// IO configuration trait for Spi clk
802pub trait SckPin<T: Instance>: Pin + sealed::Sealed + PeripheralType {
803 /// convert the pin to appropriate function for Spi clk usage.
804 fn as_sck(&self);
805}
806
807/// IO configuration trait for Spi mosi
808pub trait MosiPin<T: Instance>: Pin + sealed::Sealed + PeripheralType {
809 /// convert the pin to appropriate function for Spi mosi usage.
810 fn as_mosi(&self);
811}
812
813/// IO configuration trait for Spi miso
814pub trait MisoPin<T: Instance>: Pin + sealed::Sealed + PeripheralType {
815 /// convert the pin to appropriate function for Spi miso usage.
816 fn as_miso(&self);
817}
818
819macro_rules! impl_pin_trait {
820 ($fcn:ident, $mode:ident, $($pin:ident, $fn:ident),*) => {
821 paste! {
822 $(
823 impl [<$mode:camel Pin>]<crate::peripherals::$fcn> for crate::peripherals::$pin {
824 fn [<as_ $mode>](&self) {
825 // UM11147 table 530 pg 518
826 self.set_function(crate::iopctl::Function::$fn)
827 .set_pull(Pull::None)
828 .enable_input_buffer()
829 .set_slew_rate(SlewRate::Standard)
830 .set_drive_strength(DriveStrength::Normal)
831 .disable_analog_multiplex()
832 .set_drive_mode(DriveMode::PushPull)
833 .set_input_inverter(Inverter::Disabled);
834 }
835 }
836 )*
837 }
838 }
839}
840
841// FLEXCOMM0
842impl_pin_trait!(FLEXCOMM0, sck, PIO0_0, F1, PIO3_0, F5);
843impl_pin_trait!(FLEXCOMM0, miso, PIO0_1, F1, PIO3_1, F5);
844impl_pin_trait!(FLEXCOMM0, mosi, PIO0_2, F1, PIO3_2, F5);
845
846// FLEXCOMM1
847impl_pin_trait!(FLEXCOMM1, sck, PIO0_7, F1, PIO7_25, F1);
848impl_pin_trait!(FLEXCOMM1, miso, PIO0_8, F1, PIO7_26, F1);
849impl_pin_trait!(FLEXCOMM1, mosi, PIO0_9, F1, PIO7_28, F1);
850
851// FLEXCOMM2
852impl_pin_trait!(FLEXCOMM2, sck, PIO0_14, F1, PIO7_29, F5);
853impl_pin_trait!(FLEXCOMM2, miso, PIO0_15, F1, PIO7_30, F5);
854impl_pin_trait!(FLEXCOMM2, mosi, PIO0_16, F1, PIO7_31, F5);
855
856// FLEXCOMM3
857impl_pin_trait!(FLEXCOMM3, sck, PIO0_21, F1);
858impl_pin_trait!(FLEXCOMM3, miso, PIO0_22, F1);
859impl_pin_trait!(FLEXCOMM3, mosi, PIO0_23, F1);
860
861// FLEXCOMM4
862impl_pin_trait!(FLEXCOMM4, sck, PIO0_28, F1);
863impl_pin_trait!(FLEXCOMM4, miso, PIO0_29, F1);
864impl_pin_trait!(FLEXCOMM4, mosi, PIO0_30, F1);
865
866// FLEXCOMM5
867impl_pin_trait!(FLEXCOMM5, sck, PIO1_3, F1, PIO3_15, F5);
868impl_pin_trait!(FLEXCOMM5, miso, PIO1_4, F1, PIO3_16, F5);
869impl_pin_trait!(FLEXCOMM5, mosi, PIO1_5, F1, PIO3_17, F5);
870
871// FLEXCOMM6
872impl_pin_trait!(FLEXCOMM6, sck, PIO3_25, F1);
873impl_pin_trait!(FLEXCOMM6, miso, PIO3_26, F1);
874impl_pin_trait!(FLEXCOMM6, mosi, PIO3_27, F1);
875
876// FLEXCOMM7
877impl_pin_trait!(FLEXCOMM7, sck, PIO4_0, F1);
878impl_pin_trait!(FLEXCOMM7, miso, PIO4_1, F1);
879impl_pin_trait!(FLEXCOMM7, mosi, PIO4_2, F1);
880
881// FLEXCOMM14
882impl_pin_trait!(FLEXCOMM14, sck, PIO1_11, F1);
883impl_pin_trait!(FLEXCOMM14, miso, PIO1_12, F1);
884impl_pin_trait!(FLEXCOMM14, mosi, PIO1_13, F1);
885
886/// Spi Tx DMA trait.
887#[allow(private_bounds)]
888pub trait TxDma<T: Instance>: crate::dma::Channel {}
889
890/// Spi Rx DMA trait.
891#[allow(private_bounds)]
892pub trait RxDma<T: Instance>: crate::dma::Channel {}
893
894macro_rules! impl_dma {
895 ($fcn:ident, $mode:ident, $dma:ident) => {
896 paste! {
897 impl [<$mode Dma>]<crate::peripherals::$fcn> for crate::peripherals::$dma {}
898 }
899 };
900}
901
902impl_dma!(FLEXCOMM0, Rx, DMA0_CH0);
903impl_dma!(FLEXCOMM0, Tx, DMA0_CH1);
904
905impl_dma!(FLEXCOMM1, Rx, DMA0_CH2);
906impl_dma!(FLEXCOMM1, Tx, DMA0_CH3);
907
908impl_dma!(FLEXCOMM2, Rx, DMA0_CH4);
909impl_dma!(FLEXCOMM2, Tx, DMA0_CH5);
910
911impl_dma!(FLEXCOMM3, Rx, DMA0_CH6);
912impl_dma!(FLEXCOMM3, Tx, DMA0_CH7);
913
914impl_dma!(FLEXCOMM4, Rx, DMA0_CH8);
915impl_dma!(FLEXCOMM4, Tx, DMA0_CH9);
916
917impl_dma!(FLEXCOMM5, Rx, DMA0_CH10);
918impl_dma!(FLEXCOMM5, Tx, DMA0_CH11);
919
920impl_dma!(FLEXCOMM6, Rx, DMA0_CH12);
921impl_dma!(FLEXCOMM6, Tx, DMA0_CH13);
922
923impl_dma!(FLEXCOMM7, Rx, DMA0_CH14);
924impl_dma!(FLEXCOMM7, Tx, DMA0_CH15);
925
926impl_dma!(FLEXCOMM14, Rx, DMA0_CH16);
927impl_dma!(FLEXCOMM14, Tx, DMA0_CH17);
928
929// ==============================
930
931impl<'d, M: IoMode> embedded_hal_02::blocking::spi::Transfer<u8> for Spi<'d, M> {
932 type Error = Error;
933 fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
934 self.blocking_transfer_in_place(words)?;
935 Ok(words)
936 }
937}
938
939impl<'d, M: IoMode> embedded_hal_02::blocking::spi::Write<u8> for Spi<'d, M> {
940 type Error = Error;
941
942 fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
943 self.blocking_write(words)
944 }
945}
946
947impl embedded_hal_1::spi::Error for Error {
948 fn kind(&self) -> embedded_hal_1::spi::ErrorKind {
949 match *self {}
950 }
951}
952
953impl<'d, M: IoMode> embedded_hal_1::spi::ErrorType for Spi<'d, M> {
954 type Error = Error;
955}
956
957impl<'d, M: IoMode> embedded_hal_1::spi::SpiBus<u8> for Spi<'d, M> {
958 fn flush(&mut self) -> Result<(), Self::Error> {
959 self.flush()
960 }
961
962 fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
963 self.blocking_read(words)
964 }
965
966 fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
967 self.blocking_write(words)
968 }
969
970 fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
971 self.blocking_transfer(read, write)
972 }
973
974 fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
975 self.blocking_transfer_in_place(words)
976 }
977}
978
979impl<'d> embedded_hal_async::spi::SpiBus<u8> for Spi<'d, Async> {
980 async fn flush(&mut self) -> Result<(), Self::Error> {
981 self.async_flush().await;
982
983 Ok(())
984 }
985
986 async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
987 self.async_write(words).await
988 }
989
990 async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
991 self.async_read(words).await
992 }
993
994 async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
995 self.async_transfer(read, write).await
996 }
997
998 async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
999 self.async_transfer_in_place(words).await
1000 }
1001}
1002
1003impl<'d, M: IoMode> SetConfig for Spi<'d, M> {
1004 type Config = Config;
1005 type ConfigError = ();
1006 fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
1007 self.set_config(config);
1008
1009 Ok(())
1010 }
1011}
diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md
index 9c53e66a7..56bb15f42 100644
--- a/embassy-nrf/CHANGELOG.md
+++ b/embassy-nrf/CHANGELOG.md
@@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
32* added: support for nrf54l10 and nrf54l05 32* added: support for nrf54l10 and nrf54l05
33* added: expose uicr write functions 33* added: expose uicr write functions
34* added: support for nrf54lm20a 34* added: support for nrf54lm20a
35- added: support buffered rram for nrf54
35 36
36## 0.8.0 - 2025-09-30 37## 0.8.0 - 2025-09-30
37 38
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index db71dee10..584d0a0be 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -845,9 +845,9 @@ pub fn init(config: config::Config) -> Peripherals {
845 // Chips with a certain chip type-specific build code or higher have an 845 // Chips with a certain chip type-specific build code or higher have an
846 // improved APPROTECT ("hardware and software controlled access port protection") 846 // improved APPROTECT ("hardware and software controlled access port protection")
847 // which needs explicit action by the firmware to keep it unlocked 847 // which needs explicit action by the firmware to keep it unlocked
848 // See https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/working-with-the-nrf52-series-improved-approtect 848 // See https://docs.nordicsemi.com/bundle/ps_nrf52840/page/dif.html#d402e184
849 849
850 // UICR.APPROTECT = SwDisabled 850 // UICR.APPROTECT = HwDisabled
851 let res = uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_DISABLED); 851 let res = uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_DISABLED);
852 needs_reset |= res == WriteResult::Written; 852 needs_reset |= res == WriteResult::Written;
853 // APPROTECT.DISABLE = SwDisabled 853 // APPROTECT.DISABLE = SwDisabled
diff --git a/embassy-nrf/src/rramc.rs b/embassy-nrf/src/rramc.rs
index 521ac4ee7..961e0a535 100644
--- a/embassy-nrf/src/rramc.rs
+++ b/embassy-nrf/src/rramc.rs
@@ -1,5 +1,6 @@
1//! Resistive Random-Access Memory Controller driver. 1//! Resistive Random-Access Memory Controller driver.
2 2
3use core::marker::PhantomData;
3use core::{ptr, slice}; 4use core::{ptr, slice};
4 5
5use embedded_storage::nor_flash::{ 6use embedded_storage::nor_flash::{
@@ -9,11 +10,28 @@ use embedded_storage::nor_flash::{
9use crate::peripherals::RRAMC; 10use crate::peripherals::RRAMC;
10use crate::{Peri, pac}; 11use crate::{Peri, pac};
11 12
13/// Unbuffered RRAMC mode.
14pub struct Unbuffered;
15
16/// Buffered RRAMC mode.
17pub struct Buffered<const BUFFER_SIZE_BYTES: usize>;
18
19trait SealedRramMode {}
20
21/// Operating modes for RRAMC
22#[allow(private_bounds)]
23pub trait RramMode: SealedRramMode {}
24
25impl SealedRramMode for Unbuffered {}
26impl RramMode for Unbuffered {}
27impl<const BUFFER_SIZE_BYTES: usize> SealedRramMode for Buffered<BUFFER_SIZE_BYTES> {}
28impl<const BUFFER_SIZE_BYTES: usize> RramMode for Buffered<BUFFER_SIZE_BYTES> {}
29
12// 30//
13// Export Nvmc alias and page size for downstream compatibility 31// Export Nvmc alias and page size for downstream compatibility
14// 32//
15/// RRAM-backed `Nvmc` compatibile driver. 33/// RRAM-backed `Nvmc` compatibile driver.
16pub type Nvmc<'d> = Rramc<'d>; 34pub type Nvmc<'d> = Rramc<'d, Unbuffered>;
17/// Emulated page size. RRAM does not use pages. This exists only for downstream compatibility. 35/// Emulated page size. RRAM does not use pages. This exists only for downstream compatibility.
18pub const PAGE_SIZE: usize = 4096; 36pub const PAGE_SIZE: usize = 4096;
19 37
@@ -44,16 +62,27 @@ impl NorFlashError for Error {
44 62
45/// Resistive Random-Access Memory Controller (RRAMC) that implements the `embedded-storage` 63/// Resistive Random-Access Memory Controller (RRAMC) that implements the `embedded-storage`
46/// traits. 64/// traits.
47pub struct Rramc<'d> { 65pub struct Rramc<'d, MODE: RramMode> {
48 _p: Peri<'d, RRAMC>, 66 _p: Peri<'d, RRAMC>,
67 _d: PhantomData<MODE>,
68}
69
70impl<'d> Rramc<'d, Unbuffered> {
71 /// Create Rramc driver.
72 pub fn new(_p: Peri<'d, RRAMC>) -> Rramc<'d, Unbuffered> {
73 Self { _p, _d: PhantomData }
74 }
49} 75}
50 76
51impl<'d> Rramc<'d> { 77impl<'d, const BUFFER_SIZE_BYTES: usize> Rramc<'d, Buffered<BUFFER_SIZE_BYTES>> {
52 /// Create Rramc driver. 78 /// Create Rramc driver.
53 pub fn new(_p: Peri<'d, RRAMC>) -> Self { 79 pub fn new_buffered(_p: Peri<'d, RRAMC>) -> Rramc<'d, Buffered<BUFFER_SIZE_BYTES>> {
54 Self { _p } 80 assert!(BUFFER_SIZE_BYTES > 0 && BUFFER_SIZE_BYTES <= 512);
81 Self { _p, _d: PhantomData }
55 } 82 }
83}
56 84
85impl<'d, MODE: RramMode> Rramc<'d, MODE> {
57 fn regs() -> pac::rramc::Rramc { 86 fn regs() -> pac::rramc::Rramc {
58 pac::RRAMC 87 pac::RRAMC
59 } 88 }
@@ -63,18 +92,48 @@ impl<'d> Rramc<'d> {
63 while !p.ready().read().ready() {} 92 while !p.ready().read().ready() {}
64 } 93 }
65 94
95 fn enable_read(&self) {
96 Self::regs().config().write(|w| w.set_wen(false));
97 }
98
99 fn finish_write(&mut self) {
100 self.enable_read();
101 self.wait_ready();
102 }
103}
104
105impl<'d> Rramc<'d, Unbuffered> {
106 fn wait_ready_write(&mut self) {
107 let p = Self::regs();
108 while !p.readynext().read().readynext() {}
109 }
110
111 fn enable_write(&self) {
112 Self::regs().config().write(|w| {
113 w.set_wen(true);
114 w.set_writebufsize(pac::rramc::vals::Writebufsize::UNBUFFERED)
115 });
116 }
117}
118
119impl<'d, const SIZE: usize> Rramc<'d, Buffered<SIZE>> {
66 fn wait_ready_write(&mut self) { 120 fn wait_ready_write(&mut self) {
67 let p = Self::regs(); 121 let p = Self::regs();
68 while !p.readynext().read().readynext() {} 122 while !p.readynext().read().readynext() {}
69 while !p.bufstatus().writebufempty().read().empty() {} 123 while !p.bufstatus().writebufempty().read().empty() {}
70 } 124 }
71 125
72 fn enable_read(&self) { 126 fn commit(&self) {
73 Self::regs().config().write(|w| w.set_wen(false)); 127 let p = Self::regs();
128 p.tasks_commitwritebuf().write_value(1);
129 while !p.bufstatus().writebufempty().read().empty() {}
74 } 130 }
75 131
76 fn enable_write(&self) { 132 fn enable_write(&self) {
77 Self::regs().config().write(|w| w.set_wen(true)); 133 Self::regs().config().write(|w| {
134 w.set_wen(true);
135 w.set_writebufsize(pac::rramc::vals::Writebufsize::from_bits(SIZE as _))
136 });
78 } 137 }
79} 138}
80 139
@@ -83,13 +142,13 @@ impl<'d> Rramc<'d> {
83// implement the traits for downstream compatibility. 142// implement the traits for downstream compatibility.
84// 143//
85 144
86impl<'d> MultiwriteNorFlash for Rramc<'d> {} 145impl<'d> MultiwriteNorFlash for Rramc<'d, Unbuffered> {}
87 146
88impl<'d> ErrorType for Rramc<'d> { 147impl<'d> ErrorType for Rramc<'d, Unbuffered> {
89 type Error = Error; 148 type Error = Error;
90} 149}
91 150
92impl<'d> ReadNorFlash for Rramc<'d> { 151impl<'d> ReadNorFlash for Rramc<'d, Unbuffered> {
93 const READ_SIZE: usize = 1; 152 const READ_SIZE: usize = 1;
94 153
95 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { 154 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
@@ -107,7 +166,7 @@ impl<'d> ReadNorFlash for Rramc<'d> {
107 } 166 }
108} 167}
109 168
110impl<'d> NorFlash for Rramc<'d> { 169impl<'d> NorFlash for Rramc<'d, Unbuffered> {
111 const WRITE_SIZE: usize = WRITE_LINE_SIZE; 170 const WRITE_SIZE: usize = WRITE_LINE_SIZE;
112 const ERASE_SIZE: usize = PAGE_SIZE; 171 const ERASE_SIZE: usize = PAGE_SIZE;
113 172
@@ -139,11 +198,100 @@ impl<'d> NorFlash for Rramc<'d> {
139 self.wait_ready_write(); 198 self.wait_ready_write();
140 } 199 }
141 } 200 }
201 self.finish_write();
202 Ok(())
203 }
204
205 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
206 if offset as usize + bytes.len() > FLASH_SIZE {
207 return Err(Error::OutOfBounds);
208 }
209 if offset as usize % Self::WRITE_SIZE != 0 || bytes.len() % Self::WRITE_SIZE != 0 {
210 return Err(Error::Unaligned);
211 }
212
213 self.enable_write();
214 self.wait_ready();
215
216 unsafe {
217 let p_src = bytes.as_ptr() as *const u32;
218 let p_dst = offset as *mut u32;
219 let words = bytes.len() / 4;
220 for i in 0..words {
221 let w = ptr::read_unaligned(p_src.add(i));
222 ptr::write_volatile(p_dst.add(i), w);
223 if (i + 1) % (Self::WRITE_SIZE / 4) == 0 {
224 self.wait_ready_write();
225 }
226 }
227 }
142 228
143 self.enable_read(); 229 self.enable_read();
144 self.wait_ready(); 230 self.wait_ready();
145 Ok(()) 231 Ok(())
146 } 232 }
233}
234
235impl<'d, const BUFFER_SIZE_BYTES: usize> MultiwriteNorFlash for Rramc<'d, Buffered<BUFFER_SIZE_BYTES>> {}
236
237impl<'d, const BUFFER_SIZE_BYTES: usize> ErrorType for Rramc<'d, Buffered<BUFFER_SIZE_BYTES>> {
238 type Error = Error;
239}
240
241impl<'d, const BUFFER_SIZE_BYTES: usize> ReadNorFlash for Rramc<'d, Buffered<BUFFER_SIZE_BYTES>> {
242 const READ_SIZE: usize = 1;
243
244 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
245 if offset as usize >= FLASH_SIZE || offset as usize + bytes.len() > FLASH_SIZE {
246 return Err(Error::OutOfBounds);
247 }
248
249 let flash_data = unsafe { slice::from_raw_parts(offset as *const u8, bytes.len()) };
250 bytes.copy_from_slice(flash_data);
251 Ok(())
252 }
253
254 fn capacity(&self) -> usize {
255 FLASH_SIZE
256 }
257}
258
259impl<'d, const BUFFER_SIZE_BYTES: usize> NorFlash for Rramc<'d, Buffered<BUFFER_SIZE_BYTES>> {
260 const WRITE_SIZE: usize = WRITE_LINE_SIZE;
261 const ERASE_SIZE: usize = PAGE_SIZE;
262
263 // RRAM can overwrite in-place, so emulate page erases by overwriting the page bytes with 0xFF.
264 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
265 if to < from || to as usize > FLASH_SIZE {
266 return Err(Error::OutOfBounds);
267 }
268 if from as usize % Self::ERASE_SIZE != 0 || to as usize % Self::ERASE_SIZE != 0 {
269 return Err(Error::Unaligned);
270 }
271
272 self.enable_write();
273 self.wait_ready();
274
275 // Treat each emulated page separately so callers can rely on post‑erase read‑back
276 // returning 0xFF just like on real NOR flash.
277 let buf = [0xFFu8; BUFFER_SIZE_BYTES];
278 for page_addr in (from..to).step_by(Self::ERASE_SIZE) {
279 let page_end = page_addr + Self::ERASE_SIZE as u32;
280 for line_addr in (page_addr..page_end).step_by(BUFFER_SIZE_BYTES) {
281 unsafe {
282 let src = buf.as_ptr() as *const u32;
283 let dst = line_addr as *mut u32;
284 for i in 0..(Self::WRITE_SIZE / 4) {
285 core::ptr::write_volatile(dst.add(i), core::ptr::read_unaligned(src.add(i)));
286 }
287 }
288 self.wait_ready_write();
289 }
290 }
291 self.commit();
292 self.finish_write();
293 Ok(())
294 }
147 295
148 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { 296 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
149 if offset as usize + bytes.len() > FLASH_SIZE { 297 if offset as usize + bytes.len() > FLASH_SIZE {
@@ -169,6 +317,7 @@ impl<'d> NorFlash for Rramc<'d> {
169 } 317 }
170 } 318 }
171 319
320 self.commit();
172 self.enable_read(); 321 self.enable_read();
173 self.wait_ready(); 322 self.wait_ready();
174 Ok(()) 323 Ok(())
diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs
index abf9a923f..6c38ab3fe 100644
--- a/embassy-nrf/src/twim.rs
+++ b/embassy-nrf/src/twim.rs
@@ -174,7 +174,7 @@ impl<'d> Twim<'d> {
174 }); 174 });
175 w.set_drive1(gpiovals::Drive::D); 175 w.set_drive1(gpiovals::Drive::D);
176 } 176 }
177 if config.sda_pullup { 177 if config.scl_pullup {
178 w.set_pull(gpiovals::Pull::PULLUP); 178 w.set_pull(gpiovals::Pull::PULLUP);
179 } 179 }
180 }); 180 });
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs
index 12086cd3a..e716fc348 100644
--- a/embassy-stm32/src/sdmmc/mod.rs
+++ b/embassy-stm32/src/sdmmc/mod.rs
@@ -7,6 +7,7 @@ use core::marker::PhantomData;
7use core::slice; 7use core::slice;
8use core::task::Poll; 8use core::task::Poll;
9 9
10use aligned::{A4, Aligned};
10use embassy_hal_internal::{Peri, PeripheralType}; 11use embassy_hal_internal::{Peri, PeripheralType};
11use embassy_sync::waitqueue::AtomicWaker; 12use embassy_sync::waitqueue::AtomicWaker;
12use sdio_host::Cmd; 13use sdio_host::Cmd;
@@ -139,16 +140,38 @@ impl Default for Signalling {
139 } 140 }
140} 141}
141 142
143const fn aligned_mut(x: &mut [u32]) -> &mut Aligned<A4, [u8]> {
144 let len = x.len() * 4;
145 unsafe { core::mem::transmute(slice::from_raw_parts_mut(x.as_mut_ptr() as _, len)) }
146}
147
142const fn slice8_mut(x: &mut [u32]) -> &mut [u8] { 148const fn slice8_mut(x: &mut [u32]) -> &mut [u8] {
143 let len = x.len() * 4; 149 let len = x.len() * 4;
144 unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } 150 unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) }
145} 151}
146 152
153#[allow(unused)]
154const fn slice32_mut(x: &mut Aligned<A4, [u8]>) -> &mut [u32] {
155 let len = (size_of_val(x) + 4 - 1) / 4;
156 unsafe { slice::from_raw_parts_mut(x as *mut Aligned<A4, [u8]> as *mut _, len) }
157}
158
159const fn aligned_ref(x: &[u32]) -> &Aligned<A4, [u8]> {
160 let len = x.len() * 4;
161 unsafe { core::mem::transmute(slice::from_raw_parts(x.as_ptr() as _, len)) }
162}
163
147const fn slice8_ref(x: &[u32]) -> &[u8] { 164const fn slice8_ref(x: &[u32]) -> &[u8] {
148 let len = x.len() * 4; 165 let len = x.len() * 4;
149 unsafe { slice::from_raw_parts(x.as_ptr() as _, len) } 166 unsafe { slice::from_raw_parts(x.as_ptr() as _, len) }
150} 167}
151 168
169#[allow(unused)]
170const fn slice32_ref(x: &Aligned<A4, [u8]>) -> &[u32] {
171 let len = (size_of_val(x) + 4 - 1) / 4;
172 unsafe { slice::from_raw_parts(x as *const Aligned<A4, [u8]> as *const _, len) }
173}
174
152/// Errors 175/// Errors
153#[non_exhaustive] 176#[non_exhaustive]
154#[derive(Debug, Copy, Clone, PartialEq, Eq)] 177#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -187,6 +210,11 @@ enum PowerCtrl {
187 On = 0b11, 210 On = 0b11,
188} 211}
189 212
213enum DatapathMode {
214 Block(BlockSize),
215 Byte,
216}
217
190fn get_waitresp_val(rlen: ResponseLen) -> u8 { 218fn get_waitresp_val(rlen: ResponseLen) -> u8 {
191 match rlen { 219 match rlen {
192 common_cmd::ResponseLen::Zero => 0, 220 common_cmd::ResponseLen::Zero => 0,
@@ -768,12 +796,16 @@ impl<'d> Sdmmc<'d> {
768 #[allow(unused_variables)] 796 #[allow(unused_variables)]
769 fn prepare_datapath_read<'a>( 797 fn prepare_datapath_read<'a>(
770 &'a self, 798 &'a self,
771 buffer: &'a mut [u32], 799 buffer: &'a mut Aligned<A4, [u8]>,
772 block_size: BlockSize, 800 mode: DatapathMode,
773 byte_mode: bool,
774 ) -> WrappedTransfer<'a> { 801 ) -> WrappedTransfer<'a> {
775 let regs = self.info.regs; 802 let regs = self.info.regs;
776 803
804 let (byte_mode, block_size) = match mode {
805 DatapathMode::Block(block_size) => (false, block_size as u8),
806 DatapathMode::Byte => (true, 0),
807 };
808
777 // Command AND Data state machines must be idle 809 // Command AND Data state machines must be idle
778 self.wait_idle(); 810 self.wait_idle();
779 self.clear_interrupt_flags(); 811 self.clear_interrupt_flags();
@@ -783,8 +815,11 @@ impl<'d> Sdmmc<'d> {
783 // SAFETY: No other functions use the dma 815 // SAFETY: No other functions use the dma
784 #[cfg(sdmmc_v1)] 816 #[cfg(sdmmc_v1)]
785 let transfer = unsafe { 817 let transfer = unsafe {
786 self.dma 818 self.dma.read_unchecked(
787 .read_unchecked(regs.fifor().as_ptr() as *mut u32, buffer, DMA_TRANSFER_OPTIONS) 819 regs.fifor().as_ptr() as *mut u32,
820 slice32_mut(buffer),
821 DMA_TRANSFER_OPTIONS,
822 )
788 }; 823 };
789 #[cfg(sdmmc_v2)] 824 #[cfg(sdmmc_v2)]
790 let transfer = { 825 let transfer = {
@@ -817,14 +852,14 @@ impl<'d> Sdmmc<'d> {
817 /// # Safety 852 /// # Safety
818 /// 853 ///
819 /// `buffer` must be valid for the whole transfer and word aligned 854 /// `buffer` must be valid for the whole transfer and word aligned
820 fn prepare_datapath_write<'a>( 855 fn prepare_datapath_write<'a>(&'a self, buffer: &'a Aligned<A4, [u8]>, mode: DatapathMode) -> WrappedTransfer<'a> {
821 &'a self,
822 buffer: &'a [u32],
823 block_size: BlockSize,
824 byte_mode: bool,
825 ) -> WrappedTransfer<'a> {
826 let regs = self.info.regs; 856 let regs = self.info.regs;
827 857
858 let (byte_mode, block_size) = match mode {
859 DatapathMode::Block(block_size) => (false, block_size as u8),
860 DatapathMode::Byte => (true, 0),
861 };
862
828 // Command AND Data state machines must be idle 863 // Command AND Data state machines must be idle
829 self.wait_idle(); 864 self.wait_idle();
830 self.clear_interrupt_flags(); 865 self.clear_interrupt_flags();
@@ -834,8 +869,11 @@ impl<'d> Sdmmc<'d> {
834 // SAFETY: No other functions use the dma 869 // SAFETY: No other functions use the dma
835 #[cfg(sdmmc_v1)] 870 #[cfg(sdmmc_v1)]
836 let transfer = unsafe { 871 let transfer = unsafe {
837 self.dma 872 self.dma.write_unchecked(
838 .write_unchecked(buffer, regs.fifor().as_ptr() as *mut u32, DMA_TRANSFER_OPTIONS) 873 slice32_ref(buffer),
874 regs.fifor().as_ptr() as *mut u32,
875 DMA_TRANSFER_OPTIONS,
876 )
839 }; 877 };
840 #[cfg(sdmmc_v2)] 878 #[cfg(sdmmc_v2)]
841 let transfer = { 879 let transfer = {
diff --git a/embassy-stm32/src/sdmmc/sd.rs b/embassy-stm32/src/sdmmc/sd.rs
index 6190226b8..20318bbfa 100644
--- a/embassy-stm32/src/sdmmc/sd.rs
+++ b/embassy-stm32/src/sdmmc/sd.rs
@@ -5,7 +5,10 @@ use sdio_host::emmc::{EMMC, ExtCSD};
5use sdio_host::sd::{BusWidth, CIC, CID, CSD, CardCapacity, CardStatus, CurrentState, OCR, RCA, SCR, SD, SDStatus}; 5use sdio_host::sd::{BusWidth, CIC, CID, CSD, CardCapacity, CardStatus, CurrentState, OCR, RCA, SCR, SD, SDStatus};
6use sdio_host::{common_cmd, emmc_cmd, sd_cmd}; 6use sdio_host::{common_cmd, emmc_cmd, sd_cmd};
7 7
8use crate::sdmmc::{BlockSize, Error, Sdmmc, Signalling, block_size, bus_width_vals, slice8_mut, slice8_ref}; 8use crate::sdmmc::{
9 BlockSize, DatapathMode, Error, Sdmmc, Signalling, aligned_mut, aligned_ref, block_size, bus_width_vals,
10 slice8_mut, slice8_ref,
11};
9use crate::time::{Hertz, mhz}; 12use crate::time::{Hertz, mhz};
10 13
11/// Aligned data block for SDMMC transfers. 14/// Aligned data block for SDMMC transfers.
@@ -230,10 +233,8 @@ impl<'a, 'b> StorageDevice<'a, 'b, Card> {
230 }; 233 };
231 234
232 let buffer = &mut cmd_block.0[..64 / 4]; 235 let buffer = &mut cmd_block.0[..64 / 4];
233 236 let mode = DatapathMode::Block(block_size(size_of_val(buffer)));
234 let transfer = self 237 let transfer = self.sdmmc.prepare_datapath_read(aligned_mut(buffer), mode);
235 .sdmmc
236 .prepare_datapath_read(buffer, block_size(size_of_val(buffer)), false);
237 238
238 self.sdmmc.cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 239 self.sdmmc.cmd(sd_cmd::cmd6(set_function), true)?; // CMD6
239 240
@@ -272,7 +273,9 @@ impl<'a, 'b> StorageDevice<'a, 'b, Card> {
272 273
273 // Arm `OnDrop` after the buffer, so it will be dropped first 274 // Arm `OnDrop` after the buffer, so it will be dropped first
274 275
275 let transfer = self.sdmmc.prepare_datapath_read(scr, BlockSize::Size8, false); 276 let transfer = self
277 .sdmmc
278 .prepare_datapath_read(aligned_mut(scr), DatapathMode::Block(BlockSize::Size8));
276 self.sdmmc.cmd(sd_cmd::send_scr(), true)?; 279 self.sdmmc.cmd(sd_cmd::send_scr(), true)?;
277 280
278 self.sdmmc.complete_datapath_transfer(transfer, true).await?; 281 self.sdmmc.complete_datapath_transfer(transfer, true).await?;
@@ -290,10 +293,9 @@ impl<'a, 'b> StorageDevice<'a, 'b, Card> {
290 self.sdmmc.cmd(common_cmd::app_cmd(rca), false)?; // APP 293 self.sdmmc.cmd(common_cmd::app_cmd(rca), false)?; // APP
291 294
292 let buffer = &mut cmd_block.as_mut()[..64 / 4]; 295 let buffer = &mut cmd_block.as_mut()[..64 / 4];
296 let mode = DatapathMode::Block(block_size(size_of_val(buffer)));
293 297
294 let transfer = self 298 let transfer = self.sdmmc.prepare_datapath_read(aligned_mut(buffer), mode);
295 .sdmmc
296 .prepare_datapath_read(buffer, block_size(size_of_val(buffer)), false);
297 self.sdmmc.cmd(sd_cmd::sd_status(), true)?; 299 self.sdmmc.cmd(sd_cmd::sd_status(), true)?;
298 300
299 self.sdmmc.complete_datapath_transfer(transfer, true).await?; 301 self.sdmmc.complete_datapath_transfer(transfer, true).await?;
@@ -398,9 +400,10 @@ impl<'a, 'b> StorageDevice<'a, 'b, Emmc> {
398 .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false) 400 .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)
399 .unwrap(); // CMD16 401 .unwrap(); // CMD16
400 402
401 let transfer = self 403 let transfer = self.sdmmc.prepare_datapath_read(
402 .sdmmc 404 aligned_mut(&mut data_block.0),
403 .prepare_datapath_read(&mut data_block.0, block_size(size_of::<DataBlock>()), false); 405 DatapathMode::Block(block_size(size_of::<DataBlock>())),
406 );
404 self.sdmmc.cmd(emmc_cmd::send_ext_csd(), true)?; 407 self.sdmmc.cmd(emmc_cmd::send_ext_csd(), true)?;
405 408
406 self.sdmmc.complete_datapath_transfer(transfer, true).await?; 409 self.sdmmc.complete_datapath_transfer(transfer, true).await?;
@@ -418,7 +421,7 @@ impl<'a, 'b, A: Addressable> StorageDevice<'a, 'b, A> {
418 421
419 /// Read a data block. 422 /// Read a data block.
420 #[inline] 423 #[inline]
421 pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { 424 pub async fn read_block(&mut self, block_idx: u32, data_block: &mut DataBlock) -> Result<(), Error> {
422 let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); 425 let _scoped_block_stop = self.sdmmc.info.rcc.block_stop();
423 let card_capacity = self.info.get_capacity(); 426 let card_capacity = self.info.get_capacity();
424 427
@@ -431,9 +434,10 @@ impl<'a, 'b, A: Addressable> StorageDevice<'a, 'b, A> {
431 self.sdmmc 434 self.sdmmc
432 .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16 435 .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16
433 436
434 let transfer = self 437 let transfer = self.sdmmc.prepare_datapath_read(
435 .sdmmc 438 aligned_mut(&mut data_block.0),
436 .prepare_datapath_read(&mut buffer.0, block_size(size_of::<DataBlock>()), false); 439 DatapathMode::Block(block_size(size_of::<DataBlock>())),
440 );
437 self.sdmmc.cmd(common_cmd::read_single_block(address), true)?; 441 self.sdmmc.cmd(common_cmd::read_single_block(address), true)?;
438 442
439 self.sdmmc.complete_datapath_transfer(transfer, true).await?; 443 self.sdmmc.complete_datapath_transfer(transfer, true).await?;
@@ -464,9 +468,10 @@ impl<'a, 'b, A: Addressable> StorageDevice<'a, 'b, A> {
464 self.sdmmc 468 self.sdmmc
465 .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16 469 .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16
466 470
467 let transfer = self 471 let transfer = self.sdmmc.prepare_datapath_read(
468 .sdmmc 472 aligned_mut(buffer),
469 .prepare_datapath_read(buffer, block_size(size_of::<DataBlock>()), false); 473 DatapathMode::Block(block_size(size_of::<DataBlock>())),
474 );
470 self.sdmmc.cmd(common_cmd::read_multiple_blocks(address), true)?; 475 self.sdmmc.cmd(common_cmd::read_multiple_blocks(address), true)?;
471 476
472 self.sdmmc.complete_datapath_transfer(transfer, false).await?; 477 self.sdmmc.complete_datapath_transfer(transfer, false).await?;
@@ -497,9 +502,10 @@ impl<'a, 'b, A: Addressable> StorageDevice<'a, 'b, A> {
497 #[cfg(sdmmc_v1)] 502 #[cfg(sdmmc_v1)]
498 self.sdmmc.cmd(common_cmd::write_single_block(address), true)?; 503 self.sdmmc.cmd(common_cmd::write_single_block(address), true)?;
499 504
500 let transfer = self 505 let transfer = self.sdmmc.prepare_datapath_write(
501 .sdmmc 506 aligned_ref(&buffer.0),
502 .prepare_datapath_write(&buffer.0, block_size(size_of::<DataBlock>()), false); 507 DatapathMode::Block(block_size(size_of::<DataBlock>())),
508 );
503 509
504 #[cfg(sdmmc_v2)] 510 #[cfg(sdmmc_v2)]
505 self.sdmmc.cmd(common_cmd::write_single_block(address), true)?; 511 self.sdmmc.cmd(common_cmd::write_single_block(address), true)?;
@@ -548,10 +554,10 @@ impl<'a, 'b, A: Addressable> StorageDevice<'a, 'b, A> {
548 self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 554 self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25
549 555
550 // Setup write command 556 // Setup write command
551 let transfer = self 557 let transfer = self.sdmmc.prepare_datapath_write(
552 .sdmmc 558 aligned_ref(buffer),
553 .prepare_datapath_write(buffer, block_size(size_of::<DataBlock>()), false); 559 DatapathMode::Block(block_size(size_of::<DataBlock>())),
554 560 );
555 #[cfg(sdmmc_v2)] 561 #[cfg(sdmmc_v2)]
556 self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 562 self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25
557 563
diff --git a/embassy-stm32/src/sdmmc/sdio.rs b/embassy-stm32/src/sdmmc/sdio.rs
index 1412b21fc..e436d68cb 100644
--- a/embassy-stm32/src/sdmmc/sdio.rs
+++ b/embassy-stm32/src/sdmmc/sdio.rs
@@ -1,10 +1,11 @@
1use core::ops::{Deref, DerefMut}; 1use core::ops::{Deref, DerefMut};
2 2
3use aligned::{A4, Aligned};
3use sdio_host::common_cmd::{R1, Rz, cmd}; 4use sdio_host::common_cmd::{R1, Rz, cmd};
4use sdio_host::sd::BusWidth; 5use sdio_host::sd::BusWidth;
5use sdio_host::sd_cmd; 6use sdio_host::sd_cmd;
6 7
7use crate::sdmmc::{Error, Sdmmc, block_size, slice8_mut, slice8_ref}; 8use crate::sdmmc::{DatapathMode, Error, Sdmmc, aligned_mut, aligned_ref, block_size, slice8_mut, slice8_ref};
8use crate::time::Hertz; 9use crate::time::Hertz;
9 10
10/// Aligned data block for SDMMC transfers. 11/// Aligned data block for SDMMC transfers.
@@ -39,7 +40,7 @@ impl DerefMut for DataBlock {
39/// Storage Device 40/// Storage Device
40pub struct SerialDataInterface<'a, 'b> { 41pub struct SerialDataInterface<'a, 'b> {
41 /// Inner member 42 /// Inner member
42 pub sdmmc: &'a mut Sdmmc<'b>, 43 sdmmc: &'a mut Sdmmc<'b>,
43} 44}
44 45
45/// Card Storage Device 46/// Card Storage Device
@@ -101,9 +102,10 @@ impl<'a, 'b> SerialDataInterface<'a, 'b> {
101 ) 102 )
102 }; 103 };
103 104
104 let transfer = self 105 let transfer = self.sdmmc.prepare_datapath_read(
105 .sdmmc 106 aligned_mut(buffer),
106 .prepare_datapath_read(buffer, block_size(size_of::<DataBlock>()), false); 107 DatapathMode::Block(block_size(size_of::<DataBlock>())),
108 );
107 self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; 109 self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?;
108 110
109 self.sdmmc.complete_datapath_transfer(transfer, false).await?; 111 self.sdmmc.complete_datapath_transfer(transfer, false).await?;
@@ -113,12 +115,10 @@ impl<'a, 'b> SerialDataInterface<'a, 'b> {
113 } 115 }
114 116
115 /// Read in multibyte mode using cmd53 117 /// Read in multibyte mode using cmd53
116 pub async fn cmd53_byte_read(&mut self, arg: u32, buffer: &mut [u32]) -> Result<(), Error> { 118 pub async fn cmd53_byte_read(&mut self, arg: u32, buffer: &mut Aligned<A4, [u8]>) -> Result<(), Error> {
117 let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); 119 let _scoped_block_stop = self.sdmmc.info.rcc.block_stop();
118 120
119 let transfer = self 121 let transfer = self.sdmmc.prepare_datapath_read(buffer, DatapathMode::Byte);
120 .sdmmc
121 .prepare_datapath_read(buffer, block_size(size_of::<DataBlock>()), true);
122 self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; 122 self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?;
123 123
124 self.sdmmc.complete_datapath_transfer(transfer, false).await?; 124 self.sdmmc.complete_datapath_transfer(transfer, false).await?;
@@ -142,9 +142,10 @@ impl<'a, 'b> SerialDataInterface<'a, 'b> {
142 #[cfg(sdmmc_v1)] 142 #[cfg(sdmmc_v1)]
143 self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; 143 self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?;
144 144
145 let transfer = self 145 let transfer = self.sdmmc.prepare_datapath_write(
146 .sdmmc 146 aligned_ref(buffer),
147 .prepare_datapath_read(buffer, block_size(size_of::<DataBlock>()), false); 147 DatapathMode::Block(block_size(size_of::<DataBlock>())),
148 );
148 149
149 #[cfg(sdmmc_v2)] 150 #[cfg(sdmmc_v2)]
150 self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; 151 self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?;
@@ -164,7 +165,7 @@ impl<'a, 'b> SerialDataInterface<'a, 'b> {
164 165
165 let transfer = self 166 let transfer = self
166 .sdmmc 167 .sdmmc
167 .prepare_datapath_write(buffer, block_size(size_of::<DataBlock>()), true); 168 .prepare_datapath_write(aligned_ref(buffer), DatapathMode::Byte);
168 169
169 #[cfg(sdmmc_v2)] 170 #[cfg(sdmmc_v2)]
170 self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; 171 self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?;
diff --git a/examples/mimxrt6/Cargo.toml b/examples/mimxrt6/Cargo.toml
index dc09e97e7..ada112833 100644
--- a/examples/mimxrt6/Cargo.toml
+++ b/examples/mimxrt6/Cargo.toml
@@ -21,6 +21,8 @@ embedded-hal-async = "1.0.0"
21 21
22mimxrt600-fcb = "0.2.2" 22mimxrt600-fcb = "0.2.2"
23panic-probe = { version = "1.0.0", features = ["print-defmt"] } 23panic-probe = { version = "1.0.0", features = ["print-defmt"] }
24embedded-hal-bus = "0.3.0"
25is31fl3743b-driver = { version = "0.1.1", features = ["is_blocking"] }
24 26
25# cargo build/run 27# cargo build/run
26[profile.dev] 28[profile.dev]
diff --git a/examples/mimxrt6/src/bin/dma.rs b/examples/mimxrt6/src/bin/dma.rs
new file mode 100644
index 000000000..b490efc6b
--- /dev/null
+++ b/examples/mimxrt6/src/bin/dma.rs
@@ -0,0 +1,26 @@
1#![no_std]
2#![no_main]
3
4extern crate embassy_imxrt_examples;
5
6use defmt::info;
7use embassy_executor::Spawner;
8use embassy_imxrt::dma::copy;
9use {defmt_rtt as _, panic_probe as _};
10
11const BUFLEN: usize = 1024;
12
13#[embassy_executor::main]
14async fn main(_spawner: Spawner) {
15 let p = embassy_imxrt::init(Default::default());
16
17 info!("Test memory-to-memory DMA transfers");
18
19 let src = [0x55u8; BUFLEN];
20 let mut dst = [0u8; BUFLEN];
21
22 unsafe { copy(p.DMA0_CH0, &src, &mut dst) }.await;
23 assert!(dst == src);
24
25 info!("DMA copy succeeded");
26}
diff --git a/examples/mimxrt6/src/bin/spi-async.rs b/examples/mimxrt6/src/bin/spi-async.rs
new file mode 100644
index 000000000..aa56f7c42
--- /dev/null
+++ b/examples/mimxrt6/src/bin/spi-async.rs
@@ -0,0 +1,35 @@
1#![no_std]
2#![no_main]
3
4use defmt::info;
5use embassy_executor::Spawner;
6use embassy_imxrt::bind_interrupts;
7use embassy_imxrt::flexcomm::spi::{InterruptHandler, Spi};
8use embassy_imxrt::peripherals::FLEXCOMM5;
9use {defmt_rtt as _, embassy_imxrt_examples as _, panic_probe as _};
10
11bind_interrupts!(struct Irqs {
12 FLEXCOMM5 => InterruptHandler<FLEXCOMM5>;
13});
14
15const BUFLEN: usize = 1024;
16
17#[embassy_executor::main]
18async fn main(_spawner: Spawner) {
19 let p = embassy_imxrt::init(Default::default());
20
21 info!("Initializing SPI");
22
23 let mut spi = Spi::new_async(p.FLEXCOMM5, p.PIO1_3, p.PIO1_5, p.PIO1_4, Irqs, Default::default());
24
25 let mut rxbuf = [0x55; BUFLEN];
26 let txbuf = [0xaa; BUFLEN];
27
28 for _ in 0..10 {
29 spi.async_transfer(&mut rxbuf, &txbuf).await.unwrap();
30 assert!(rxbuf.iter().all(|b| *b == 0xaa));
31 rxbuf.fill(0x55);
32 }
33
34 info!("SPI transfers succeeded");
35}
diff --git a/examples/mimxrt6/src/bin/spi.rs b/examples/mimxrt6/src/bin/spi.rs
new file mode 100644
index 000000000..4854432e8
--- /dev/null
+++ b/examples/mimxrt6/src/bin/spi.rs
@@ -0,0 +1,51 @@
1#![no_std]
2#![no_main]
3
4use defmt::info;
5use embassy_executor::Spawner;
6use embassy_imxrt::flexcomm::spi::Spi;
7use embassy_imxrt::gpio;
8use embassy_time::{Delay, Timer};
9use embedded_hal_bus::spi::ExclusiveDevice;
10use is31fl3743b_driver::{CSy, Is31fl3743b, SWx};
11use {defmt_rtt as _, embassy_imxrt_examples as _, panic_probe as _};
12
13#[embassy_executor::main]
14async fn main(_spawner: Spawner) {
15 let p = embassy_imxrt::init(Default::default());
16
17 info!("Initializing SPI");
18
19 let cs = gpio::Output::new(
20 p.PIO1_6,
21 gpio::Level::Low,
22 gpio::DriveMode::PushPull,
23 gpio::DriveStrength::Normal,
24 gpio::SlewRate::Standard,
25 );
26
27 let spi = Spi::new_blocking(p.FLEXCOMM5, p.PIO1_3, p.PIO1_5, p.PIO1_4, Default::default());
28 let delay = Delay;
29
30 // One SPI device only on the SPI bus
31 let spi_dev = ExclusiveDevice::new(spi, cs, delay).unwrap();
32
33 // Instantiate IS31FL3743B device
34 let mut driver = Is31fl3743b::new(spi_dev).unwrap();
35
36 // Enable phase delay to help reduce power noise
37 let _ = driver.enable_phase_delay();
38 // Set global current, check method documentation for more info
39 let _ = driver.set_global_current(90);
40
41 let _ = driver.set_led_peak_current_bulk(SWx::SW1, CSy::CS1, &[100; 11 * 18]);
42
43 // Driver is fully set up, we can now start turning on LEDs!
44 // Create a white breathing effect
45 loop {
46 for brightness in (0..=255_u8).chain((0..=255).rev()) {
47 let _ = driver.set_led_brightness_bulk(SWx::SW1, CSy::CS1, &[brightness; 11 * 18]);
48 Timer::after_micros(1).await;
49 }
50 }
51}
diff --git a/examples/nrf54l15/src/bin/nvmc.rs b/examples/nrf54l15/src/bin/rramc.rs
index f990604cd..f990604cd 100644
--- a/examples/nrf54l15/src/bin/nvmc.rs
+++ b/examples/nrf54l15/src/bin/rramc.rs
diff --git a/examples/nrf54l15/src/bin/rramc_buffered.rs b/examples/nrf54l15/src/bin/rramc_buffered.rs
new file mode 100644
index 000000000..06c9585ed
--- /dev/null
+++ b/examples/nrf54l15/src/bin/rramc_buffered.rs
@@ -0,0 +1,59 @@
1#![no_std]
2#![no_main]
3
4use defmt::{info, unwrap};
5use embassy_executor::Spawner;
6use embassy_nrf::rramc::{Buffered, PAGE_SIZE, Rramc};
7use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
8use {defmt_rtt as _, panic_probe as _};
9
10#[embassy_executor::main]
11async fn main(_spawner: Spawner) {
12 let p = embassy_nrf::init(Default::default());
13 info!("Hello RRAMC NVMC!");
14
15 // Buffer 8 word lines
16 let mut f: Rramc<'_, Buffered<128>> = Rramc::new_buffered(p.RRAMC);
17
18 const ADDR: u32 = 0x80000;
19 let mut buf = [0u8; 4];
20
21 info!("Reading...");
22 unwrap!(f.read(ADDR, &mut buf));
23 info!("Read: {=[u8]:x}", buf);
24
25 info!("Erasing...");
26 unwrap!(f.erase(ADDR, ADDR + PAGE_SIZE as u32));
27
28 info!("Reading...");
29 unwrap!(f.read(ADDR, &mut buf));
30 info!("Read: {=[u8]:x}", buf);
31
32 info!("Writing...");
33 // 16 B (128-bit) write minimum
34 let out: [u8; 256] = [
35 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa,
36 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa,
37 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb,
38 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb,
39 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc,
40 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc,
41 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd,
42 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd,
43 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa,
44 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa,
45 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb,
46 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb,
47 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc,
48 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc,
49 0xdd, 0xdd, 0xdd, 0xdd,
50 ];
51 unwrap!(f.write(ADDR, &out));
52
53 info!("Reading...");
54 // Can read arbitrary sizes
55 for addr in (ADDR..ADDR + 256).step_by(4) {
56 unwrap!(f.read(addr, &mut buf));
57 info!("Read: {=[u8]:x}", buf);
58 }
59}
diff --git a/examples/std/src/bin/net_ppp.rs b/examples/std/src/bin/net_ppp.rs
index 82272c798..685dbf3d3 100644
--- a/examples/std/src/bin/net_ppp.rs
+++ b/examples/std/src/bin/net_ppp.rs
@@ -52,7 +52,7 @@ async fn ppp_task(stack: Stack<'static>, mut runner: Runner<'static>, port: Seri
52 password: b"mypass", 52 password: b"mypass",
53 }; 53 };
54 54
55 runner 55 let r = runner
56 .run(port, config, |ipv4| { 56 .run(port, config, |ipv4| {
57 let Some(addr) = ipv4.address else { 57 let Some(addr) = ipv4.address else {
58 warn!("PPP did not provide an IP address."); 58 warn!("PPP did not provide an IP address.");
@@ -69,9 +69,10 @@ async fn ppp_task(stack: Stack<'static>, mut runner: Runner<'static>, port: Seri
69 }); 69 });
70 stack.set_config_v4(config); 70 stack.set_config_v4(config);
71 }) 71 })
72 .await 72 .await;
73 .unwrap(); 73 match r {
74 unreachable!() 74 Err(e) => panic!("{:?}", e),
75 }
75} 76}
76 77
77#[embassy_executor::task] 78#[embassy_executor::task]
diff --git a/examples/std/src/bin/tick_cancel.rs b/examples/std/src/bin/tick_cancel.rs
new file mode 100644
index 000000000..54e44790f
--- /dev/null
+++ b/examples/std/src/bin/tick_cancel.rs
@@ -0,0 +1,47 @@
1use std::sync::atomic::{AtomicBool, Ordering};
2use std::thread;
3use std::time::Duration;
4
5use embassy_executor::Executor;
6use embassy_time::Timer;
7use log::*;
8use static_cell::StaticCell;
9
10#[embassy_executor::task]
11async fn run() {
12 loop {
13 info!("tick");
14 Timer::after_secs(1).await;
15 }
16}
17
18static DONE: StaticCell<AtomicBool> = StaticCell::new();
19static EXECUTOR: StaticCell<Executor> = StaticCell::new();
20
21fn main() {
22 env_logger::builder()
23 .filter_level(log::LevelFilter::Debug)
24 .format_timestamp_nanos()
25 .init();
26
27 let done = DONE.init(AtomicBool::new(false));
28 let done_cb = || done.load(Ordering::Relaxed);
29
30 let server_thread = thread::spawn(move || {
31 let executor = EXECUTOR.init(Executor::new());
32 executor.run_until(
33 |spawner| {
34 spawner.spawn(run().unwrap());
35 },
36 done_cb,
37 );
38 info!("Executor finished");
39 });
40
41 thread::sleep(Duration::from_secs(5));
42
43 info!("Cancelling executor");
44 done.store(true, Ordering::Relaxed);
45
46 server_thread.join().unwrap();
47}
diff --git a/rust-toolchain-nightly.toml b/rust-toolchain-nightly.toml
index dde431bba..2a46473b7 100644
--- a/rust-toolchain-nightly.toml
+++ b/rust-toolchain-nightly.toml
@@ -1,5 +1,5 @@
1[toolchain] 1[toolchain]
2channel = "nightly-2025-09-26" 2channel = "nightly-2025-12-11"
3components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ] 3components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ]
4targets = [ 4targets = [
5 "thumbv7em-none-eabi", 5 "thumbv7em-none-eabi",
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
index 5d925c934..1f47bee1e 100644
--- a/rust-toolchain.toml
+++ b/rust-toolchain.toml
@@ -1,5 +1,5 @@
1[toolchain] 1[toolchain]
2channel = "1.90" 2channel = "1.92"
3components = [ "rust-src", "rustfmt", "llvm-tools" ] 3components = [ "rust-src", "rustfmt", "llvm-tools" ]
4targets = [ 4targets = [
5 "thumbv7em-none-eabi", 5 "thumbv7em-none-eabi",