diff options
| -rw-r--r-- | embassy-boot/rp/src/lib.rs | 14 | ||||
| -rw-r--r-- | embassy-rp/Cargo.toml | 3 | ||||
| -rw-r--r-- | embassy-rp/src/flash.rs | 203 | ||||
| -rw-r--r-- | examples/boot/application/rp/src/bin/a.rs | 4 | ||||
| -rw-r--r-- | examples/rp/src/bin/flash.rs | 42 | ||||
| -rw-r--r-- | tests/rp/src/bin/flash.rs | 14 |
6 files changed, 254 insertions, 26 deletions
diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index 25329f9e9..35fc104ec 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs | |||
| @@ -6,7 +6,7 @@ mod fmt; | |||
| 6 | #[cfg(feature = "nightly")] | 6 | #[cfg(feature = "nightly")] |
| 7 | pub use embassy_boot::FirmwareUpdater; | 7 | pub use embassy_boot::FirmwareUpdater; |
| 8 | pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State}; | 8 | pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State}; |
| 9 | use embassy_rp::flash::{Flash, ERASE_SIZE}; | 9 | use embassy_rp::flash::{Blocking, Flash, ERASE_SIZE}; |
| 10 | use embassy_rp::peripherals::{FLASH, WATCHDOG}; | 10 | use embassy_rp::peripherals::{FLASH, WATCHDOG}; |
| 11 | use embassy_rp::watchdog::Watchdog; | 11 | use embassy_rp::watchdog::Watchdog; |
| 12 | use embassy_time::Duration; | 12 | use embassy_time::Duration; |
| @@ -58,14 +58,14 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize> | |||
| 58 | 58 | ||
| 59 | /// A flash implementation that will feed a watchdog when touching flash. | 59 | /// A flash implementation that will feed a watchdog when touching flash. |
| 60 | pub struct WatchdogFlash<'d, const SIZE: usize> { | 60 | pub struct WatchdogFlash<'d, const SIZE: usize> { |
| 61 | flash: Flash<'d, FLASH, SIZE>, | 61 | flash: Flash<'d, FLASH, Blocking, SIZE>, |
| 62 | watchdog: Watchdog, | 62 | watchdog: Watchdog, |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> { | 65 | impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> { |
| 66 | /// Start a new watchdog with a given flash and watchdog peripheral and a timeout | 66 | /// Start a new watchdog with a given flash and watchdog peripheral and a timeout |
| 67 | pub fn start(flash: FLASH, watchdog: WATCHDOG, timeout: Duration) -> Self { | 67 | pub fn start(flash: FLASH, watchdog: WATCHDOG, timeout: Duration) -> Self { |
| 68 | let flash: Flash<'_, FLASH, SIZE> = Flash::new(flash); | 68 | let flash = Flash::<_, Blocking, SIZE>::new(flash); |
| 69 | let mut watchdog = Watchdog::new(watchdog); | 69 | let mut watchdog = Watchdog::new(watchdog); |
| 70 | watchdog.start(timeout); | 70 | watchdog.start(timeout); |
| 71 | Self { flash, watchdog } | 71 | Self { flash, watchdog } |
| @@ -73,12 +73,12 @@ impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> { | |||
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | impl<'d, const SIZE: usize> ErrorType for WatchdogFlash<'d, SIZE> { | 75 | impl<'d, const SIZE: usize> ErrorType for WatchdogFlash<'d, SIZE> { |
| 76 | type Error = <Flash<'d, FLASH, SIZE> as ErrorType>::Error; | 76 | type Error = <Flash<'d, FLASH, Blocking, SIZE> as ErrorType>::Error; |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | impl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> { | 79 | impl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> { |
| 80 | const WRITE_SIZE: usize = <Flash<'d, FLASH, SIZE> as NorFlash>::WRITE_SIZE; | 80 | const WRITE_SIZE: usize = <Flash<'d, FLASH, Blocking, SIZE> as NorFlash>::WRITE_SIZE; |
| 81 | const ERASE_SIZE: usize = <Flash<'d, FLASH, SIZE> as NorFlash>::ERASE_SIZE; | 81 | const ERASE_SIZE: usize = <Flash<'d, FLASH, Blocking, SIZE> as NorFlash>::ERASE_SIZE; |
| 82 | 82 | ||
| 83 | fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { | 83 | fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { |
| 84 | self.watchdog.feed(); | 84 | self.watchdog.feed(); |
| @@ -91,7 +91,7 @@ impl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> { | |||
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | impl<'d, const SIZE: usize> ReadNorFlash for WatchdogFlash<'d, SIZE> { | 93 | impl<'d, const SIZE: usize> ReadNorFlash for WatchdogFlash<'d, SIZE> { |
| 94 | const READ_SIZE: usize = <Flash<'d, FLASH, SIZE> as ReadNorFlash>::READ_SIZE; | 94 | const READ_SIZE: usize = <Flash<'d, FLASH, Blocking, SIZE> as ReadNorFlash>::READ_SIZE; |
| 95 | fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> { | 95 | fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> { |
| 96 | self.watchdog.feed(); | 96 | self.watchdog.feed(); |
| 97 | self.flash.read(offset, data) | 97 | self.flash.read(offset, data) |
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index b53c7a01a..6310ffb62 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml | |||
| @@ -48,7 +48,7 @@ boot2-w25x10cl = [] | |||
| 48 | run-from-ram = [] | 48 | run-from-ram = [] |
| 49 | 49 | ||
| 50 | # Enable nightly-only features | 50 | # Enable nightly-only features |
| 51 | nightly = ["embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] | 51 | nightly = ["embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] |
| 52 | 52 | ||
| 53 | # Implement embedded-hal 1.0 alpha traits. | 53 | # Implement embedded-hal 1.0 alpha traits. |
| 54 | # Implement embedded-hal-async traits if `nightly` is set as well. | 54 | # Implement embedded-hal-async traits if `nightly` is set as well. |
| @@ -73,6 +73,7 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa | |||
| 73 | chrono = { version = "0.4", default-features = false, optional = true } | 73 | chrono = { version = "0.4", default-features = false, optional = true } |
| 74 | embedded-io = { version = "0.4.0", features = ["async"], optional = true } | 74 | embedded-io = { version = "0.4.0", features = ["async"], optional = true } |
| 75 | embedded-storage = { version = "0.3" } | 75 | embedded-storage = { version = "0.3" } |
| 76 | embedded-storage-async = { version = "0.4.0", optional = true } | ||
| 76 | rand_core = "0.6.4" | 77 | rand_core = "0.6.4" |
| 77 | fixed = "1.23.1" | 78 | fixed = "1.23.1" |
| 78 | 79 | ||
diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 0ed6808eb..70d867319 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs | |||
| @@ -1,11 +1,15 @@ | |||
| 1 | use core::future::Future; | ||
| 1 | use core::marker::PhantomData; | 2 | use core::marker::PhantomData; |
| 3 | use core::pin::Pin; | ||
| 4 | use core::task::{Context, Poll}; | ||
| 2 | 5 | ||
| 3 | use embassy_hal_internal::Peripheral; | 6 | use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; |
| 4 | use embedded_storage::nor_flash::{ | 7 | use embedded_storage::nor_flash::{ |
| 5 | check_erase, check_read, check_write, ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, | 8 | check_erase, check_read, check_write, ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, |
| 6 | ReadNorFlash, | 9 | ReadNorFlash, |
| 7 | }; | 10 | }; |
| 8 | 11 | ||
| 12 | use crate::dma::{AnyChannel, Channel, Transfer}; | ||
| 9 | use crate::pac; | 13 | use crate::pac; |
| 10 | use crate::peripherals::FLASH; | 14 | use crate::peripherals::FLASH; |
| 11 | 15 | ||
| @@ -24,6 +28,7 @@ pub const PAGE_SIZE: usize = 256; | |||
| 24 | pub const WRITE_SIZE: usize = 1; | 28 | pub const WRITE_SIZE: usize = 1; |
| 25 | pub const READ_SIZE: usize = 1; | 29 | pub const READ_SIZE: usize = 1; |
| 26 | pub const ERASE_SIZE: usize = 4096; | 30 | pub const ERASE_SIZE: usize = 4096; |
| 31 | pub const ASYNC_READ_SIZE: usize = 4; | ||
| 27 | 32 | ||
| 28 | /// Error type for NVMC operations. | 33 | /// Error type for NVMC operations. |
| 29 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | 34 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
| @@ -57,13 +62,46 @@ impl NorFlashError for Error { | |||
| 57 | } | 62 | } |
| 58 | } | 63 | } |
| 59 | 64 | ||
| 60 | pub struct Flash<'d, T: Instance, const FLASH_SIZE: usize>(PhantomData<&'d mut T>); | 65 | /// Future that waits for completion of a background read |
| 66 | #[must_use = "futures do nothing unless you `.await` or poll them"] | ||
| 67 | pub struct BackgroundRead<'a, 'd, T: Instance, const FLASH_SIZE: usize> { | ||
| 68 | flash: PhantomData<&'a mut Flash<'d, T, Async, FLASH_SIZE>>, | ||
| 69 | transfer: Transfer<'a, AnyChannel>, | ||
| 70 | } | ||
| 61 | 71 | ||
| 62 | impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { | 72 | impl<'a, 'd, T: Instance, const FLASH_SIZE: usize> Future for BackgroundRead<'a, 'd, T, FLASH_SIZE> { |
| 63 | pub fn new(_flash: impl Peripheral<P = T> + 'd) -> Self { | 73 | type Output = (); |
| 64 | Self(PhantomData) | 74 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
| 75 | Pin::new(&mut self.transfer).poll(cx) | ||
| 65 | } | 76 | } |
| 77 | } | ||
| 66 | 78 | ||
| 79 | impl<'a, 'd, T: Instance, const FLASH_SIZE: usize> Drop for BackgroundRead<'a, 'd, T, FLASH_SIZE> { | ||
| 80 | fn drop(&mut self) { | ||
| 81 | if pac::XIP_CTRL.stream_ctr().read().0 == 0 { | ||
| 82 | return; | ||
| 83 | } | ||
| 84 | pac::XIP_CTRL | ||
| 85 | .stream_ctr() | ||
| 86 | .write_value(pac::xip_ctrl::regs::StreamCtr(0)); | ||
| 87 | core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); | ||
| 88 | // Errata RP2040-E8: Perform an uncached read to make sure there's not a transfer in | ||
| 89 | // flight that might effect an address written to start a new transfer. This stalls | ||
| 90 | // until after any transfer is complete, so the address will not change anymore. | ||
| 91 | const XIP_NOCACHE_NOALLOC_BASE: *const u32 = 0x13000000 as *const _; | ||
| 92 | unsafe { | ||
| 93 | core::ptr::read_volatile(XIP_NOCACHE_NOALLOC_BASE); | ||
| 94 | } | ||
| 95 | core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | pub struct Flash<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> { | ||
| 100 | dma: Option<PeripheralRef<'d, AnyChannel>>, | ||
| 101 | phantom: PhantomData<(&'d mut T, M)>, | ||
| 102 | } | ||
| 103 | |||
| 104 | impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SIZE> { | ||
| 67 | pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { | 105 | pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { |
| 68 | trace!( | 106 | trace!( |
| 69 | "Reading from 0x{:x} to 0x{:x}", | 107 | "Reading from 0x{:x} to 0x{:x}", |
| @@ -182,6 +220,8 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { | |||
| 182 | let ch = crate::pac::DMA.ch(n); | 220 | let ch = crate::pac::DMA.ch(n); |
| 183 | while ch.read_addr().read() < SRAM_LOWER && ch.ctrl_trig().read().busy() {} | 221 | while ch.read_addr().read() < SRAM_LOWER && ch.ctrl_trig().read().busy() {} |
| 184 | } | 222 | } |
| 223 | // Wait for completion of any background reads | ||
| 224 | while pac::XIP_CTRL.stream_ctr().read().0 > 0 {} | ||
| 185 | 225 | ||
| 186 | // Run our flash operation in RAM | 226 | // Run our flash operation in RAM |
| 187 | operation(); | 227 | operation(); |
| @@ -210,11 +250,73 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { | |||
| 210 | } | 250 | } |
| 211 | } | 251 | } |
| 212 | 252 | ||
| 213 | impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, FLASH_SIZE> { | 253 | impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Blocking, FLASH_SIZE> { |
| 254 | pub fn new(_flash: impl Peripheral<P = T> + 'd) -> Self { | ||
| 255 | Self { | ||
| 256 | dma: None, | ||
| 257 | phantom: PhantomData, | ||
| 258 | } | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Async, FLASH_SIZE> { | ||
| 263 | pub fn new(_flash: impl Peripheral<P = T> + 'd, dma: impl Peripheral<P = impl Channel> + 'd) -> Self { | ||
| 264 | into_ref!(dma); | ||
| 265 | Self { | ||
| 266 | dma: Some(dma.map_into()), | ||
| 267 | phantom: PhantomData, | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | pub fn background_read<'a>( | ||
| 272 | &'a mut self, | ||
| 273 | offset: u32, | ||
| 274 | data: &'a mut [u32], | ||
| 275 | ) -> Result<BackgroundRead<'a, 'd, T, FLASH_SIZE>, Error> { | ||
| 276 | trace!( | ||
| 277 | "Reading in background from 0x{:x} to 0x{:x}", | ||
| 278 | FLASH_BASE as u32 + offset, | ||
| 279 | FLASH_BASE as u32 + offset + (data.len() * 4) as u32 | ||
| 280 | ); | ||
| 281 | // Can't use check_read because we need to enforce 4-byte alignment | ||
| 282 | let offset = offset as usize; | ||
| 283 | let length = data.len() * 4; | ||
| 284 | if length > self.capacity() || offset > self.capacity() - length { | ||
| 285 | return Err(Error::OutOfBounds); | ||
| 286 | } | ||
| 287 | if offset % 4 != 0 { | ||
| 288 | return Err(Error::Unaligned); | ||
| 289 | } | ||
| 290 | |||
| 291 | while !pac::XIP_CTRL.stat().read().fifo_empty() { | ||
| 292 | pac::XIP_CTRL.stream_fifo().read(); | ||
| 293 | } | ||
| 294 | |||
| 295 | pac::XIP_CTRL | ||
| 296 | .stream_addr() | ||
| 297 | .write_value(pac::xip_ctrl::regs::StreamAddr(FLASH_BASE as u32 + offset as u32)); | ||
| 298 | pac::XIP_CTRL | ||
| 299 | .stream_ctr() | ||
| 300 | .write_value(pac::xip_ctrl::regs::StreamCtr(data.len() as u32)); | ||
| 301 | |||
| 302 | // Use the XIP AUX bus port, rather than the FIFO register access (e.x. | ||
| 303 | // pac::XIP_CTRL.stream_fifo().as_ptr()) to avoid DMA stalling on | ||
| 304 | // general XIP access. | ||
| 305 | const XIP_AUX_BASE: *const u32 = 0x50400000 as *const _; | ||
| 306 | let transfer = unsafe { crate::dma::read(self.dma.as_mut().unwrap(), XIP_AUX_BASE, data, 37) }; | ||
| 307 | |||
| 308 | Ok(BackgroundRead { | ||
| 309 | flash: PhantomData, | ||
| 310 | transfer, | ||
| 311 | }) | ||
| 312 | } | ||
| 313 | } | ||
| 314 | |||
| 315 | impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, M, FLASH_SIZE> { | ||
| 214 | type Error = Error; | 316 | type Error = Error; |
| 215 | } | 317 | } |
| 216 | 318 | ||
| 217 | impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, FLASH_SIZE> { | 319 | impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, M, FLASH_SIZE> { |
| 218 | const READ_SIZE: usize = READ_SIZE; | 320 | const READ_SIZE: usize = READ_SIZE; |
| 219 | 321 | ||
| 220 | fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { | 322 | fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { |
| @@ -226,9 +328,9 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, FLA | |||
| 226 | } | 328 | } |
| 227 | } | 329 | } |
| 228 | 330 | ||
| 229 | impl<'d, T: Instance, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, FLASH_SIZE> {} | 331 | impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, M, FLASH_SIZE> {} |
| 230 | 332 | ||
| 231 | impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_SIZE> { | 333 | impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, M, FLASH_SIZE> { |
| 232 | const WRITE_SIZE: usize = WRITE_SIZE; | 334 | const WRITE_SIZE: usize = WRITE_SIZE; |
| 233 | 335 | ||
| 234 | const ERASE_SIZE: usize = ERASE_SIZE; | 336 | const ERASE_SIZE: usize = ERASE_SIZE; |
| @@ -242,6 +344,74 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_S | |||
| 242 | } | 344 | } |
| 243 | } | 345 | } |
| 244 | 346 | ||
| 347 | #[cfg(feature = "nightly")] | ||
| 348 | impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::ReadNorFlash | ||
| 349 | for Flash<'d, T, Async, FLASH_SIZE> | ||
| 350 | { | ||
| 351 | const READ_SIZE: usize = ASYNC_READ_SIZE; | ||
| 352 | |||
| 353 | async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { | ||
| 354 | use core::mem::MaybeUninit; | ||
| 355 | |||
| 356 | // Checked early to simplify address validity checks | ||
| 357 | if bytes.len() % 4 != 0 { | ||
| 358 | return Err(Error::Unaligned); | ||
| 359 | } | ||
| 360 | |||
| 361 | // If the destination address is already aligned, then we can just DMA directly | ||
| 362 | if (bytes.as_ptr() as u32) % 4 == 0 { | ||
| 363 | // Safety: alignment and size have been checked for compatibility | ||
| 364 | let mut buf: &mut [u32] = | ||
| 365 | unsafe { core::slice::from_raw_parts_mut(bytes.as_mut_ptr() as *mut u32, bytes.len() / 4) }; | ||
| 366 | self.background_read(offset, &mut buf)?.await; | ||
| 367 | return Ok(()); | ||
| 368 | } | ||
| 369 | |||
| 370 | // Destination address is unaligned, so use an intermediate buffer | ||
| 371 | const REALIGN_CHUNK: usize = PAGE_SIZE; | ||
| 372 | // Safety: MaybeUninit requires no initialization | ||
| 373 | let mut buf: [MaybeUninit<u32>; REALIGN_CHUNK / 4] = unsafe { MaybeUninit::uninit().assume_init() }; | ||
| 374 | let mut chunk_offset: usize = 0; | ||
| 375 | while chunk_offset < bytes.len() { | ||
| 376 | let chunk_size = (bytes.len() - chunk_offset).min(REALIGN_CHUNK); | ||
| 377 | let buf = &mut buf[..(chunk_size / 4)]; | ||
| 378 | |||
| 379 | // Safety: this is written to completely by DMA before any reads | ||
| 380 | let buf = unsafe { &mut *(buf as *mut [MaybeUninit<u32>] as *mut [u32]) }; | ||
| 381 | self.background_read(offset + chunk_offset as u32, buf)?.await; | ||
| 382 | |||
| 383 | // Safety: [u8] has more relaxed alignment and size requirements than [u32], so this is just aliasing | ||
| 384 | let buf = unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const _, buf.len() * 4) }; | ||
| 385 | bytes[chunk_offset..(chunk_offset + chunk_size)].copy_from_slice(&buf[..chunk_size]); | ||
| 386 | |||
| 387 | chunk_offset += chunk_size; | ||
| 388 | } | ||
| 389 | |||
| 390 | Ok(()) | ||
| 391 | } | ||
| 392 | |||
| 393 | fn capacity(&self) -> usize { | ||
| 394 | self.capacity() | ||
| 395 | } | ||
| 396 | } | ||
| 397 | |||
| 398 | #[cfg(feature = "nightly")] | ||
| 399 | impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::NorFlash | ||
| 400 | for Flash<'d, T, Async, FLASH_SIZE> | ||
| 401 | { | ||
| 402 | const WRITE_SIZE: usize = WRITE_SIZE; | ||
| 403 | |||
| 404 | const ERASE_SIZE: usize = ERASE_SIZE; | ||
| 405 | |||
| 406 | async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { | ||
| 407 | self.erase(from, to) | ||
| 408 | } | ||
| 409 | |||
| 410 | async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { | ||
| 411 | self.write(offset, bytes) | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 245 | #[allow(dead_code)] | 415 | #[allow(dead_code)] |
| 246 | mod ram_helpers { | 416 | mod ram_helpers { |
| 247 | use core::marker::PhantomData; | 417 | use core::marker::PhantomData; |
| @@ -699,9 +869,24 @@ mod ram_helpers { | |||
| 699 | 869 | ||
| 700 | mod sealed { | 870 | mod sealed { |
| 701 | pub trait Instance {} | 871 | pub trait Instance {} |
| 872 | pub trait Mode {} | ||
| 702 | } | 873 | } |
| 703 | 874 | ||
| 704 | pub trait Instance: sealed::Instance {} | 875 | pub trait Instance: sealed::Instance {} |
| 876 | pub trait Mode: sealed::Mode {} | ||
| 705 | 877 | ||
| 706 | impl sealed::Instance for FLASH {} | 878 | impl sealed::Instance for FLASH {} |
| 707 | impl Instance for FLASH {} | 879 | impl Instance for FLASH {} |
| 880 | |||
| 881 | macro_rules! impl_mode { | ||
| 882 | ($name:ident) => { | ||
| 883 | impl sealed::Mode for $name {} | ||
| 884 | impl Mode for $name {} | ||
| 885 | }; | ||
| 886 | } | ||
| 887 | |||
| 888 | pub struct Blocking; | ||
| 889 | pub struct Async; | ||
| 890 | |||
| 891 | impl_mode!(Blocking); | ||
| 892 | impl_mode!(Async); | ||
diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index c8497494c..b5e1950cc 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs | |||
| @@ -7,7 +7,7 @@ use core::cell::RefCell; | |||
| 7 | use defmt_rtt as _; | 7 | use defmt_rtt as _; |
| 8 | use embassy_boot_rp::*; | 8 | use embassy_boot_rp::*; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_rp::flash::Flash; | 10 | use embassy_rp::flash::{self, Flash}; |
| 11 | use embassy_rp::gpio::{Level, Output}; | 11 | use embassy_rp::gpio::{Level, Output}; |
| 12 | use embassy_rp::watchdog::Watchdog; | 12 | use embassy_rp::watchdog::Watchdog; |
| 13 | use embassy_sync::blocking_mutex::Mutex; | 13 | use embassy_sync::blocking_mutex::Mutex; |
| @@ -34,7 +34,7 @@ async fn main(_s: Spawner) { | |||
| 34 | let mut watchdog = Watchdog::new(p.WATCHDOG); | 34 | let mut watchdog = Watchdog::new(p.WATCHDOG); |
| 35 | watchdog.start(Duration::from_secs(8)); | 35 | watchdog.start(Duration::from_secs(8)); |
| 36 | 36 | ||
| 37 | let flash: Flash<_, FLASH_SIZE> = Flash::new(p.FLASH); | 37 | let flash = Flash::<_, flash::Blocking, FLASH_SIZE>::new(p.FLASH); |
| 38 | let flash = Mutex::new(RefCell::new(flash)); | 38 | let flash = Mutex::new(RefCell::new(flash)); |
| 39 | 39 | ||
| 40 | let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); | 40 | let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); |
diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs index 4c4982acc..88bb931d2 100644 --- a/examples/rp/src/bin/flash.rs +++ b/examples/rp/src/bin/flash.rs | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | use defmt::*; | 7 | use defmt::*; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; | 9 | use embassy_rp::flash::{Async, ERASE_SIZE, FLASH_BASE}; |
| 10 | use embassy_rp::peripherals::FLASH; | 10 | use embassy_rp::peripherals::FLASH; |
| 11 | use embassy_time::{Duration, Timer}; | 11 | use embassy_time::{Duration, Timer}; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -25,7 +25,7 @@ async fn main(_spawner: Spawner) { | |||
| 25 | // https://github.com/knurling-rs/defmt/pull/683 | 25 | // https://github.com/knurling-rs/defmt/pull/683 |
| 26 | Timer::after(Duration::from_millis(10)).await; | 26 | Timer::after(Duration::from_millis(10)).await; |
| 27 | 27 | ||
| 28 | let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH); | 28 | let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0); |
| 29 | 29 | ||
| 30 | // Get JEDEC id | 30 | // Get JEDEC id |
| 31 | let jedec = flash.jedec_id().unwrap(); | 31 | let jedec = flash.jedec_id().unwrap(); |
| @@ -40,10 +40,12 @@ async fn main(_spawner: Spawner) { | |||
| 40 | 40 | ||
| 41 | multiwrite_bytes(&mut flash, ERASE_SIZE as u32); | 41 | multiwrite_bytes(&mut flash, ERASE_SIZE as u32); |
| 42 | 42 | ||
| 43 | background_read(&mut flash, (ERASE_SIZE * 2) as u32).await; | ||
| 44 | |||
| 43 | loop {} | 45 | loop {} |
| 44 | } | 46 | } |
| 45 | 47 | ||
| 46 | fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) { | 48 | fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { |
| 47 | info!(">>>> [multiwrite_bytes]"); | 49 | info!(">>>> [multiwrite_bytes]"); |
| 48 | let mut read_buf = [0u8; ERASE_SIZE]; | 50 | let mut read_buf = [0u8; ERASE_SIZE]; |
| 49 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); | 51 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); |
| @@ -71,7 +73,7 @@ fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, | |||
| 71 | } | 73 | } |
| 72 | } | 74 | } |
| 73 | 75 | ||
| 74 | fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) { | 76 | fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { |
| 75 | info!(">>>> [erase_write_sector]"); | 77 | info!(">>>> [erase_write_sector]"); |
| 76 | let mut buf = [0u8; ERASE_SIZE]; | 78 | let mut buf = [0u8; ERASE_SIZE]; |
| 77 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); | 79 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); |
| @@ -99,3 +101,35 @@ fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE | |||
| 99 | defmt::panic!("unexpected"); | 101 | defmt::panic!("unexpected"); |
| 100 | } | 102 | } |
| 101 | } | 103 | } |
| 104 | |||
| 105 | async fn background_read(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { | ||
| 106 | info!(">>>> [background_read]"); | ||
| 107 | |||
| 108 | let mut buf = [0u32; 8]; | ||
| 109 | defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await; | ||
| 110 | |||
| 111 | info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); | ||
| 112 | info!("Contents start with {=u32:x}", buf[0]); | ||
| 113 | |||
| 114 | defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); | ||
| 115 | |||
| 116 | defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await; | ||
| 117 | info!("Contents after erase starts with {=u32:x}", buf[0]); | ||
| 118 | if buf.iter().any(|x| *x != 0xFFFFFFFF) { | ||
| 119 | defmt::panic!("unexpected"); | ||
| 120 | } | ||
| 121 | |||
| 122 | for b in buf.iter_mut() { | ||
| 123 | *b = 0xDABA1234; | ||
| 124 | } | ||
| 125 | |||
| 126 | defmt::unwrap!(flash.write(ADDR_OFFSET + offset, unsafe { | ||
| 127 | core::slice::from_raw_parts(buf.as_ptr() as *const u8, buf.len() * 4) | ||
| 128 | })); | ||
| 129 | |||
| 130 | defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await; | ||
| 131 | info!("Contents after write starts with {=u32:x}", buf[0]); | ||
| 132 | if buf.iter().any(|x| *x != 0xDABA1234) { | ||
| 133 | defmt::panic!("unexpected"); | ||
| 134 | } | ||
| 135 | } | ||
diff --git a/tests/rp/src/bin/flash.rs b/tests/rp/src/bin/flash.rs index cf9b86df5..c31d6decf 100644 --- a/tests/rp/src/bin/flash.rs +++ b/tests/rp/src/bin/flash.rs | |||
| @@ -6,11 +6,11 @@ mod common; | |||
| 6 | 6 | ||
| 7 | use defmt::*; | 7 | use defmt::*; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; | 9 | use embassy_rp::flash::{Async, ERASE_SIZE, FLASH_BASE}; |
| 10 | use embassy_time::{Duration, Timer}; | 10 | use embassy_time::{Duration, Timer}; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 12 | ||
| 13 | const ADDR_OFFSET: u32 = 0x4000; | 13 | const ADDR_OFFSET: u32 = 0x8000; |
| 14 | 14 | ||
| 15 | #[embassy_executor::main] | 15 | #[embassy_executor::main] |
| 16 | async fn main(_spawner: Spawner) { | 16 | async fn main(_spawner: Spawner) { |
| @@ -23,7 +23,7 @@ async fn main(_spawner: Spawner) { | |||
| 23 | // https://github.com/knurling-rs/defmt/pull/683 | 23 | // https://github.com/knurling-rs/defmt/pull/683 |
| 24 | Timer::after(Duration::from_millis(10)).await; | 24 | Timer::after(Duration::from_millis(10)).await; |
| 25 | 25 | ||
| 26 | let mut flash = embassy_rp::flash::Flash::<_, { 2 * 1024 * 1024 }>::new(p.FLASH); | 26 | let mut flash = embassy_rp::flash::Flash::<_, Async, { 2 * 1024 * 1024 }>::new(p.FLASH, p.DMA_CH0); |
| 27 | 27 | ||
| 28 | // Get JEDEC id | 28 | // Get JEDEC id |
| 29 | let jedec = defmt::unwrap!(flash.jedec_id()); | 29 | let jedec = defmt::unwrap!(flash.jedec_id()); |
| @@ -60,6 +60,14 @@ async fn main(_spawner: Spawner) { | |||
| 60 | defmt::panic!("unexpected"); | 60 | defmt::panic!("unexpected"); |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | let mut buf = [0u32; ERASE_SIZE / 4]; | ||
| 64 | |||
| 65 | defmt::unwrap!(flash.background_read(ADDR_OFFSET, &mut buf)).await; | ||
| 66 | info!("Contents after write starts with {=u32:x}", buf[0]); | ||
| 67 | if buf.iter().any(|x| *x != 0xDADADADA) { | ||
| 68 | defmt::panic!("unexpected"); | ||
| 69 | } | ||
| 70 | |||
| 63 | info!("Test OK"); | 71 | info!("Test OK"); |
| 64 | cortex_m::asm::bkpt(); | 72 | cortex_m::asm::bkpt(); |
| 65 | } | 73 | } |
