aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2022-10-28 12:19:56 +0000
committerGitHub <[email protected]>2022-10-28 12:19:56 +0000
commite7fdd500d8354a03fcd105c8298cf7b4798a4107 (patch)
tree8f0ccc58ed21a79a5b70fd38e57c8c2f30ebc00c
parent1f246d0e37f9044d8949358081b75e3cca0c4800 (diff)
parentbc21b6efafe607e6ed582b048baedb7803483ee7 (diff)
Merge #951
951: (embassy-rp): Implementation of generic flash mutation access r=Dirbaio a=MathiasKoch I have attempted to utilize the work done in `rp2040-flash` by implementing `embedded-storage` traits on top, for RP2040. Concerns: 1. ~~Should the DMA be paused where I have put a FIXME note? `DMA_CHx.ctrl_trig().write(|w| { w.set_en(false) })`? If so, how to properly do that without have control over the peripheral for the DMA channels? And if so, I assume we should only re-enable/unpause the ones that were enabled before?~~ 2. ~~Should I make sure core2 is halted as part of this code? I am not sure if https://github.com/jannic/rp2040-flash/blob/ea8ab1ac807a7ab2b28a18bb5ca2e42495bb744d/examples/flash_example.rs#L103-L109 is heavy/slow code to run?~~ 3. ~~Any good way of making this configurable over `FLASH_SIZE`, `WRITE_SIZE` and `ERASE_SIZE` without doing it as generics or parameters, as those make it possible to do differing configs throughout the same program, which feels wrong? Preferably, a compile-time option?~~ **EDIT:** I have implemented the flash API here under the assumption that all external QSPI nor flashes are infact `Multiwrite` capable, as this makes it possible to use the ROM function for writes of 1 bytes at a time. I have also added a HIL test for this, but because HIL tests are running 100% from RAM and I wanted to make sure it still works when running from flash, I have also added an example testing erase/write cycles of entire sectors, as well as single bytes in multi-write style. Ping `@Dirbaio` Co-authored-by: Mathias <[email protected]> Co-authored-by: Vincent Stakenburg <[email protected]> Co-authored-by: Joakim Hulthe <[email protected]> Co-authored-by: Alex Martens <[email protected]> Co-authored-by: Ulf Lilleengen <[email protected]> Co-authored-by: Dario Nieuwenhuis <[email protected]>
-rw-r--r--embassy-rp/Cargo.toml1
-rw-r--r--embassy-rp/src/dma.rs2
-rw-r--r--embassy-rp/src/flash.rs463
-rw-r--r--embassy-rp/src/lib.rs3
-rw-r--r--examples/rp/Cargo.toml1
-rw-r--r--examples/rp/src/bin/flash.rs89
-rw-r--r--tests/rp/Cargo.toml1
-rw-r--r--tests/rp/src/bin/flash.rs54
8 files changed, 613 insertions, 1 deletions
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml
index d2196be76..04b0c13ce 100644
--- a/embassy-rp/Cargo.toml
+++ b/embassy-rp/Cargo.toml
@@ -54,6 +54,7 @@ critical-section = "1.1"
54futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 54futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
55chrono = { version = "0.4", default-features = false, optional = true } 55chrono = { version = "0.4", default-features = false, optional = true }
56embedded-io = { version = "0.3.1", features = ["async"], optional = true } 56embedded-io = { version = "0.3.1", features = ["async"], optional = true }
57embedded-storage = { version = "0.3" }
57 58
58rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] } 59rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] }
59#rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] } 60#rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] }
diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs
index 410c48666..fd281fd5d 100644
--- a/embassy-rp/src/dma.rs
+++ b/embassy-rp/src/dma.rs
@@ -191,7 +191,7 @@ impl<'a, C: Channel> Future for Transfer<'a, C> {
191 } 191 }
192} 192}
193 193
194const CHANNEL_COUNT: usize = 12; 194pub(crate) const CHANNEL_COUNT: usize = 12;
195const NEW_AW: AtomicWaker = AtomicWaker::new(); 195const NEW_AW: AtomicWaker = AtomicWaker::new();
196static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT]; 196static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT];
197 197
diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs
new file mode 100644
index 000000000..d09cc62fb
--- /dev/null
+++ b/embassy-rp/src/flash.rs
@@ -0,0 +1,463 @@
1use core::marker::PhantomData;
2
3use embassy_hal_common::Peripheral;
4use embedded_storage::nor_flash::{
5 check_erase, check_read, check_write, ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind,
6 ReadNorFlash,
7};
8
9use crate::peripherals::FLASH;
10
11pub const FLASH_BASE: usize = 0x10000000;
12
13// **NOTE**:
14//
15// These limitations are currently enforced because of using the
16// RP2040 boot-rom flash functions, that are optimized for flash compatibility
17// rather than performance.
18pub const PAGE_SIZE: usize = 256;
19pub const WRITE_SIZE: usize = 1;
20pub const READ_SIZE: usize = 1;
21pub const ERASE_SIZE: usize = 4096;
22
23/// Error type for NVMC operations.
24#[derive(Debug, Copy, Clone, PartialEq, Eq)]
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26pub enum Error {
27 /// Opration using a location not in flash.
28 OutOfBounds,
29 /// Unaligned operation or using unaligned buffers.
30 Unaligned,
31 Other,
32}
33
34impl From<NorFlashErrorKind> for Error {
35 fn from(e: NorFlashErrorKind) -> Self {
36 match e {
37 NorFlashErrorKind::NotAligned => Self::Unaligned,
38 NorFlashErrorKind::OutOfBounds => Self::OutOfBounds,
39 _ => Self::Other,
40 }
41 }
42}
43
44impl NorFlashError for Error {
45 fn kind(&self) -> NorFlashErrorKind {
46 match self {
47 Self::OutOfBounds => NorFlashErrorKind::OutOfBounds,
48 Self::Unaligned => NorFlashErrorKind::NotAligned,
49 Self::Other => NorFlashErrorKind::Other,
50 }
51 }
52}
53
54pub struct Flash<'d, T: Instance, const FLASH_SIZE: usize>(PhantomData<&'d mut T>);
55
56impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> {
57 pub fn new(_flash: impl Peripheral<P = T> + 'd) -> Self {
58 Self(PhantomData)
59 }
60
61 pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
62 check_read(self, offset, bytes.len())?;
63
64 let flash_data = unsafe { core::slice::from_raw_parts((FLASH_BASE as u32 + offset) as *const u8, bytes.len()) };
65
66 bytes.copy_from_slice(flash_data);
67 Ok(())
68 }
69
70 pub fn capacity(&self) -> usize {
71 FLASH_SIZE
72 }
73
74 pub fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
75 check_erase(self, from, to)?;
76
77 trace!(
78 "Erasing from 0x{:x} to 0x{:x}",
79 FLASH_BASE as u32 + from,
80 FLASH_BASE as u32 + to
81 );
82
83 let len = to - from;
84
85 unsafe { self.in_ram(|| ram_helpers::flash_range_erase(from, len, true)) };
86
87 Ok(())
88 }
89
90 pub fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
91 check_write(self, offset, bytes.len())?;
92
93 trace!("Writing {:?} bytes to 0x{:x}", bytes.len(), FLASH_BASE as u32 + offset);
94
95 let end_offset = offset as usize + bytes.len();
96
97 let padded_offset = (offset as *const u8).align_offset(PAGE_SIZE);
98 let start_padding = core::cmp::min(padded_offset, bytes.len());
99
100 // Pad in the beginning
101 if start_padding > 0 {
102 let start = PAGE_SIZE - padded_offset;
103 let end = start + start_padding;
104
105 let mut pad_buf = [0xFF_u8; PAGE_SIZE];
106 pad_buf[start..end].copy_from_slice(&bytes[..start_padding]);
107
108 let unaligned_offset = offset as usize - start;
109
110 unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true)) }
111 }
112
113 let remaining_len = bytes.len() - start_padding;
114 let end_padding = start_padding + PAGE_SIZE * (remaining_len / PAGE_SIZE);
115
116 // Write aligned slice of length in multiples of 256 bytes
117 // If the remaining bytes to be written is more than a full page.
118 if remaining_len >= PAGE_SIZE {
119 let mut aligned_offset = if start_padding > 0 {
120 offset as usize + padded_offset
121 } else {
122 offset as usize
123 };
124
125 if bytes.as_ptr() as usize >= 0x2000_0000 {
126 let aligned_data = &bytes[start_padding..end_padding];
127
128 unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data, true)) }
129 } else {
130 for chunk in bytes[start_padding..end_padding].chunks_exact(PAGE_SIZE) {
131 let mut ram_buf = [0xFF_u8; PAGE_SIZE];
132 ram_buf.copy_from_slice(chunk);
133 unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf, true)) }
134 aligned_offset += PAGE_SIZE;
135 }
136 }
137 }
138
139 // Pad in the end
140 let rem_offset = (end_offset as *const u8).align_offset(PAGE_SIZE);
141 let rem_padding = remaining_len % PAGE_SIZE;
142 if rem_padding > 0 {
143 let mut pad_buf = [0xFF_u8; PAGE_SIZE];
144 pad_buf[..rem_padding].copy_from_slice(&bytes[end_padding..]);
145
146 let unaligned_offset = end_offset - (PAGE_SIZE - rem_offset);
147
148 unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true)) }
149 }
150
151 Ok(())
152 }
153
154 /// Make sure to uphold the contract points with rp2040-flash.
155 /// - interrupts must be disabled
156 /// - DMA must not access flash memory
157 unsafe fn in_ram(&mut self, operation: impl FnOnce()) {
158 let dma_status = &mut [false; crate::dma::CHANNEL_COUNT];
159
160 // TODO: Make sure CORE1 is paused during the entire duration of the RAM function
161
162 critical_section::with(|_| {
163 // Pause all DMA channels for the duration of the ram operation
164 for (number, status) in dma_status.iter_mut().enumerate() {
165 let ch = crate::pac::DMA.ch(number as _);
166 *status = ch.ctrl_trig().read().en();
167 if *status {
168 ch.ctrl_trig().modify(|w| w.set_en(false));
169 }
170 }
171
172 // Run our flash operation in RAM
173 operation();
174
175 // Re-enable previously enabled DMA channels
176 for (number, status) in dma_status.iter().enumerate() {
177 let ch = crate::pac::DMA.ch(number as _);
178 if *status {
179 ch.ctrl_trig().modify(|w| w.set_en(true));
180 }
181 }
182 });
183 }
184}
185
186impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, FLASH_SIZE> {
187 type Error = Error;
188}
189
190impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, FLASH_SIZE> {
191 const READ_SIZE: usize = READ_SIZE;
192
193 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
194 self.read(offset, bytes)
195 }
196
197 fn capacity(&self) -> usize {
198 self.capacity()
199 }
200}
201
202impl<'d, T: Instance, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, FLASH_SIZE> {}
203
204impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_SIZE> {
205 const WRITE_SIZE: usize = WRITE_SIZE;
206
207 const ERASE_SIZE: usize = ERASE_SIZE;
208
209 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
210 self.erase(from, to)
211 }
212
213 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
214 self.write(offset, bytes)
215 }
216}
217
218#[allow(dead_code)]
219mod ram_helpers {
220 use core::marker::PhantomData;
221
222 use crate::rom_data;
223
224 #[repr(C)]
225 struct FlashFunctionPointers<'a> {
226 connect_internal_flash: unsafe extern "C" fn() -> (),
227 flash_exit_xip: unsafe extern "C" fn() -> (),
228 flash_range_erase: Option<unsafe extern "C" fn(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> ()>,
229 flash_range_program: Option<unsafe extern "C" fn(addr: u32, data: *const u8, count: usize) -> ()>,
230 flash_flush_cache: unsafe extern "C" fn() -> (),
231 flash_enter_cmd_xip: unsafe extern "C" fn() -> (),
232 phantom: PhantomData<&'a ()>,
233 }
234
235 #[allow(unused)]
236 fn flash_function_pointers(erase: bool, write: bool) -> FlashFunctionPointers<'static> {
237 FlashFunctionPointers {
238 connect_internal_flash: rom_data::connect_internal_flash::ptr(),
239 flash_exit_xip: rom_data::flash_exit_xip::ptr(),
240 flash_range_erase: if erase {
241 Some(rom_data::flash_range_erase::ptr())
242 } else {
243 None
244 },
245 flash_range_program: if write {
246 Some(rom_data::flash_range_program::ptr())
247 } else {
248 None
249 },
250 flash_flush_cache: rom_data::flash_flush_cache::ptr(),
251 flash_enter_cmd_xip: rom_data::flash_enter_cmd_xip::ptr(),
252 phantom: PhantomData,
253 }
254 }
255
256 #[allow(unused)]
257 /// # Safety
258 ///
259 /// `boot2` must contain a valid 2nd stage boot loader which can be called to re-initialize XIP mode
260 unsafe fn flash_function_pointers_with_boot2(erase: bool, write: bool, boot2: &[u32; 64]) -> FlashFunctionPointers {
261 let boot2_fn_ptr = (boot2 as *const u32 as *const u8).offset(1);
262 let boot2_fn: unsafe extern "C" fn() -> () = core::mem::transmute(boot2_fn_ptr);
263 FlashFunctionPointers {
264 connect_internal_flash: rom_data::connect_internal_flash::ptr(),
265 flash_exit_xip: rom_data::flash_exit_xip::ptr(),
266 flash_range_erase: if erase {
267 Some(rom_data::flash_range_erase::ptr())
268 } else {
269 None
270 },
271 flash_range_program: if write {
272 Some(rom_data::flash_range_program::ptr())
273 } else {
274 None
275 },
276 flash_flush_cache: rom_data::flash_flush_cache::ptr(),
277 flash_enter_cmd_xip: boot2_fn,
278 phantom: PhantomData,
279 }
280 }
281
282 /// Erase a flash range starting at `addr` with length `len`.
283 ///
284 /// `addr` and `len` must be multiples of 4096
285 ///
286 /// If `use_boot2` is `true`, a copy of the 2nd stage boot loader
287 /// is used to re-initialize the XIP engine after flashing.
288 ///
289 /// # Safety
290 ///
291 /// Nothing must access flash while this is running.
292 /// Usually this means:
293 /// - interrupts must be disabled
294 /// - 2nd core must be running code from RAM or ROM with interrupts disabled
295 /// - DMA must not access flash memory
296 ///
297 /// `addr` and `len` parameters must be valid and are not checked.
298 pub unsafe fn flash_range_erase(addr: u32, len: u32, use_boot2: bool) {
299 let mut boot2 = [0u32; 256 / 4];
300 let ptrs = if use_boot2 {
301 rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256);
302 flash_function_pointers_with_boot2(true, false, &boot2)
303 } else {
304 flash_function_pointers(true, false)
305 };
306
307 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
308
309 write_flash_inner(addr, len, None, &ptrs as *const FlashFunctionPointers);
310 }
311
312 /// Erase and rewrite a flash range starting at `addr` with data `data`.
313 ///
314 /// `addr` and `data.len()` must be multiples of 4096
315 ///
316 /// If `use_boot2` is `true`, a copy of the 2nd stage boot loader
317 /// is used to re-initialize the XIP engine after flashing.
318 ///
319 /// # Safety
320 ///
321 /// Nothing must access flash while this is running.
322 /// Usually this means:
323 /// - interrupts must be disabled
324 /// - 2nd core must be running code from RAM or ROM with interrupts disabled
325 /// - DMA must not access flash memory
326 ///
327 /// `addr` and `len` parameters must be valid and are not checked.
328 pub unsafe fn flash_range_erase_and_program(addr: u32, data: &[u8], use_boot2: bool) {
329 let mut boot2 = [0u32; 256 / 4];
330 let ptrs = if use_boot2 {
331 rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256);
332 flash_function_pointers_with_boot2(true, true, &boot2)
333 } else {
334 flash_function_pointers(true, true)
335 };
336
337 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
338
339 write_flash_inner(
340 addr,
341 data.len() as u32,
342 Some(data),
343 &ptrs as *const FlashFunctionPointers,
344 );
345 }
346
347 /// Write a flash range starting at `addr` with data `data`.
348 ///
349 /// `addr` and `data.len()` must be multiples of 256
350 ///
351 /// If `use_boot2` is `true`, a copy of the 2nd stage boot loader
352 /// is used to re-initialize the XIP engine after flashing.
353 ///
354 /// # Safety
355 ///
356 /// Nothing must access flash while this is running.
357 /// Usually this means:
358 /// - interrupts must be disabled
359 /// - 2nd core must be running code from RAM or ROM with interrupts disabled
360 /// - DMA must not access flash memory
361 ///
362 /// `addr` and `len` parameters must be valid and are not checked.
363 pub unsafe fn flash_range_program(addr: u32, data: &[u8], use_boot2: bool) {
364 let mut boot2 = [0u32; 256 / 4];
365 let ptrs = if use_boot2 {
366 rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256);
367 flash_function_pointers_with_boot2(false, true, &boot2)
368 } else {
369 flash_function_pointers(false, true)
370 };
371
372 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
373
374 write_flash_inner(
375 addr,
376 data.len() as u32,
377 Some(data),
378 &ptrs as *const FlashFunctionPointers,
379 );
380 }
381
382 /// # Safety
383 ///
384 /// Nothing must access flash while this is running.
385 /// Usually this means:
386 /// - interrupts must be disabled
387 /// - 2nd core must be running code from RAM or ROM with interrupts disabled
388 /// - DMA must not access flash memory
389 /// Length of data must be a multiple of 4096
390 /// addr must be aligned to 4096
391 #[inline(never)]
392 #[link_section = ".data.ram_func"]
393 unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) {
394 /*
395 Should be equivalent to:
396 rom_data::connect_internal_flash();
397 rom_data::flash_exit_xip();
398 rom_data::flash_range_erase(addr, len, 1 << 31, 0); // if selected
399 rom_data::flash_range_program(addr, data as *const _, len); // if selected
400 rom_data::flash_flush_cache();
401 rom_data::flash_enter_cmd_xip();
402 */
403 #[cfg(target_arch = "arm")]
404 core::arch::asm!(
405 "mov r8, r0",
406 "mov r9, r2",
407 "mov r10, r1",
408 "ldr r4, [{ptrs}, #0]",
409 "blx r4", // connect_internal_flash()
410
411 "ldr r4, [{ptrs}, #4]",
412 "blx r4", // flash_exit_xip()
413
414 "mov r0, r8", // r0 = addr
415 "mov r1, r10", // r1 = len
416 "movs r2, #1",
417 "lsls r2, r2, #31", // r2 = 1 << 31
418 "movs r3, #0", // r3 = 0
419 "ldr r4, [{ptrs}, #8]",
420 "cmp r4, #0",
421 "beq 1f",
422 "blx r4", // flash_range_erase(addr, len, 1 << 31, 0)
423 "1:",
424
425 "mov r0, r8", // r0 = addr
426 "mov r1, r9", // r0 = data
427 "mov r2, r10", // r2 = len
428 "ldr r4, [{ptrs}, #12]",
429 "cmp r4, #0",
430 "beq 1f",
431 "blx r4", // flash_range_program(addr, data, len);
432 "1:",
433
434 "ldr r4, [{ptrs}, #16]",
435 "blx r4", // flash_flush_cache();
436
437 "ldr r4, [{ptrs}, #20]",
438 "blx r4", // flash_enter_cmd_xip();
439 ptrs = in(reg) ptrs,
440 // Registers r8-r15 are not allocated automatically,
441 // so assign them manually. We need to use them as
442 // otherwise there are not enough registers available.
443 in("r0") addr,
444 in("r2") data.map(|d| d.as_ptr()).unwrap_or(core::ptr::null()),
445 in("r1") len,
446 out("r3") _,
447 out("r4") _,
448 lateout("r8") _,
449 lateout("r9") _,
450 lateout("r10") _,
451 clobber_abi("C"),
452 );
453 }
454}
455
456mod sealed {
457 pub trait Instance {}
458}
459
460pub trait Instance: sealed::Instance {}
461
462impl sealed::Instance for FLASH {}
463impl Instance for FLASH {}
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index e784399d4..f608f1768 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -20,6 +20,7 @@ pub mod uart;
20pub mod usb; 20pub mod usb;
21 21
22mod clocks; 22mod clocks;
23pub mod flash;
23mod reset; 24mod reset;
24 25
25// Reexports 26// Reexports
@@ -95,6 +96,8 @@ embassy_hal_common::peripherals! {
95 USB, 96 USB,
96 97
97 RTC, 98 RTC,
99
100 FLASH,
98} 101}
99 102
100#[link_section = ".boot2"] 103#[link_section = ".boot2"]
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index ec9896b77..31f688305 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -30,6 +30,7 @@ byte-slice-cast = { version = "1.2.0", default-features = false }
30embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } 30embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" }
31embedded-hal-async = { version = "0.1.0-alpha.3" } 31embedded-hal-async = { version = "0.1.0-alpha.3" }
32embedded-io = { version = "0.3.1", features = ["async", "defmt"] } 32embedded-io = { version = "0.3.1", features = ["async", "defmt"] }
33embedded-storage = { version = "0.3" }
33static_cell = "1.0.0" 34static_cell = "1.0.0"
34 35
35[profile.release] 36[profile.release]
diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs
new file mode 100644
index 000000000..8d6b379f4
--- /dev/null
+++ b/examples/rp/src/bin/flash.rs
@@ -0,0 +1,89 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE};
8use embassy_rp::peripherals::FLASH;
9use embassy_time::{Duration, Timer};
10use {defmt_rtt as _, panic_probe as _};
11
12const ADDR_OFFSET: u32 = 0x100000;
13const FLASH_SIZE: usize = 2 * 1024 * 1024;
14
15#[embassy_executor::main]
16async fn main(_spawner: Spawner) {
17 let p = embassy_rp::init(Default::default());
18 info!("Hello World!");
19
20 // add some delay to give an attached debug probe time to parse the
21 // defmt RTT header. Reading that header might touch flash memory, which
22 // interferes with flash write operations.
23 // https://github.com/knurling-rs/defmt/pull/683
24 Timer::after(Duration::from_millis(10)).await;
25
26 let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH);
27 erase_write_sector(&mut flash, 0x00);
28
29 multiwrite_bytes(&mut flash, ERASE_SIZE as u32);
30
31 loop {}
32}
33
34fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) {
35 info!(">>>> [multiwrite_bytes]");
36 let mut read_buf = [0u8; ERASE_SIZE];
37 defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf));
38
39 info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
40 info!("Contents start with {=[u8]}", read_buf[0..4]);
41
42 defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
43
44 defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf));
45 info!("Contents after erase starts with {=[u8]}", read_buf[0..4]);
46 if read_buf.iter().any(|x| *x != 0xFF) {
47 defmt::panic!("unexpected");
48 }
49
50 defmt::unwrap!(flash.write(ADDR_OFFSET + offset, &[0x01]));
51 defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 1, &[0x02]));
52 defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 2, &[0x03]));
53 defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 3, &[0x04]));
54
55 defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf));
56 info!("Contents after write starts with {=[u8]}", read_buf[0..4]);
57 if &read_buf[0..4] != &[0x01, 0x02, 0x03, 0x04] {
58 defmt::panic!("unexpected");
59 }
60}
61
62fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) {
63 info!(">>>> [erase_write_sector]");
64 let mut buf = [0u8; ERASE_SIZE];
65 defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf));
66
67 info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
68 info!("Contents start with {=[u8]}", buf[0..4]);
69
70 defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
71
72 defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf));
73 info!("Contents after erase starts with {=[u8]}", buf[0..4]);
74 if buf.iter().any(|x| *x != 0xFF) {
75 defmt::panic!("unexpected");
76 }
77
78 for b in buf.iter_mut() {
79 *b = 0xDA;
80 }
81
82 defmt::unwrap!(flash.write(ADDR_OFFSET + offset, &buf));
83
84 defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf));
85 info!("Contents after write starts with {=[u8]}", buf[0..4]);
86 if buf.iter().any(|x| *x != 0xDA) {
87 defmt::panic!("unexpected");
88 }
89}
diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml
index e1a6dce41..069b7fb8c 100644
--- a/tests/rp/Cargo.toml
+++ b/tests/rp/Cargo.toml
@@ -22,6 +22,7 @@ embedded-hal-async = { version = "=0.1.0-alpha.3" }
22panic-probe = { version = "0.3.0", features = ["print-defmt"] } 22panic-probe = { version = "0.3.0", features = ["print-defmt"] }
23futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 23futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
24embedded-io = { version = "0.3.1", features = ["async"] } 24embedded-io = { version = "0.3.1", features = ["async"] }
25embedded-storage = { version = "0.3" }
25 26
26[profile.dev] 27[profile.dev]
27debug = 2 28debug = 2
diff --git a/tests/rp/src/bin/flash.rs b/tests/rp/src/bin/flash.rs
new file mode 100644
index 000000000..897e3804f
--- /dev/null
+++ b/tests/rp/src/bin/flash.rs
@@ -0,0 +1,54 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE};
8use embassy_time::{Duration, Timer};
9use {defmt_rtt as _, panic_probe as _};
10
11const ADDR_OFFSET: u32 = 0x4000;
12
13#[embassy_executor::main]
14async fn main(_spawner: Spawner) {
15 let p = embassy_rp::init(Default::default());
16 info!("Hello World!");
17
18 // add some delay to give an attached debug probe time to parse the
19 // defmt RTT header. Reading that header might touch flash memory, which
20 // interferes with flash write operations.
21 // https://github.com/knurling-rs/defmt/pull/683
22 Timer::after(Duration::from_millis(10)).await;
23
24 let mut flash = embassy_rp::flash::Flash::<_, { 2 * 1024 * 1024 }>::new(p.FLASH);
25
26 let mut buf = [0u8; ERASE_SIZE];
27 defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf));
28
29 info!("Addr of flash block is {:x}", ADDR_OFFSET + FLASH_BASE as u32);
30 info!("Contents start with {=[u8]}", buf[0..4]);
31
32 defmt::unwrap!(flash.erase(ADDR_OFFSET, ADDR_OFFSET + ERASE_SIZE as u32));
33
34 defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf));
35 info!("Contents after erase starts with {=[u8]}", buf[0..4]);
36 if buf.iter().any(|x| *x != 0xFF) {
37 defmt::panic!("unexpected");
38 }
39
40 for b in buf.iter_mut() {
41 *b = 0xDA;
42 }
43
44 defmt::unwrap!(flash.write(ADDR_OFFSET, &mut buf));
45
46 defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf));
47 info!("Contents after write starts with {=[u8]}", buf[0..4]);
48 if buf.iter().any(|x| *x != 0xDA) {
49 defmt::panic!("unexpected");
50 }
51
52 info!("Test OK");
53 cortex_m::asm::bkpt();
54}