aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMathias <[email protected]>2022-09-23 08:12:32 +0200
committerMathias <[email protected]>2022-09-23 08:12:32 +0200
commit18dc0dea636daa6e48aa9208b5a74cdfedc8b513 (patch)
treea5648caab29ac6156527cb2808d517b50221fbdf
parent9d674f0212bf2a51fccd192632a16f104ba9629c (diff)
Drop rp2040-flash as dependency, as they pull in rp2040-hal for rom-data functions, which are now part of this HAL as well
-rw-r--r--embassy-rp/src/dma.rs2
-rw-r--r--embassy-rp/src/flash.rs329
2 files changed, 292 insertions, 39 deletions
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
index 3b6405090..0fc19f5a2 100644
--- a/embassy-rp/src/flash.rs
+++ b/embassy-rp/src/flash.rs
@@ -1,7 +1,18 @@
1use embedded_storage::nor_flash::{ 1use embedded_storage::nor_flash::{
2 ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, 2 check_erase, check_read, check_write, ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash,
3}; 3};
4 4
5pub const FLASH_BASE: usize = 0x10000000;
6
7// **NOTE**:
8//
9// These limitations are currently enforced because of using the
10// RP2040 boot-rom flash functions, that are optimized for flash compatibility
11// rather than performance.
12pub const WRITE_SIZE: usize = 256;
13pub const READ_SIZE: usize = 1;
14pub const ERASE_SIZE: usize = 4096;
15
5/// Error type for NVMC operations. 16/// Error type for NVMC operations.
6#[derive(Debug, Copy, Clone, PartialEq, Eq)] 17#[derive(Debug, Copy, Clone, PartialEq, Eq)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))] 18#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -10,6 +21,17 @@ pub enum Error {
10 OutOfBounds, 21 OutOfBounds,
11 /// Unaligned operation or using unaligned buffers. 22 /// Unaligned operation or using unaligned buffers.
12 Unaligned, 23 Unaligned,
24 Other,
25}
26
27impl From<NorFlashErrorKind> for Error {
28 fn from(e: NorFlashErrorKind) -> Self {
29 match e {
30 NorFlashErrorKind::NotAligned => Self::Unaligned,
31 NorFlashErrorKind::OutOfBounds => Self::OutOfBounds,
32 _ => Self::Other,
33 }
34 }
13} 35}
14 36
15impl NorFlashError for Error { 37impl NorFlashError for Error {
@@ -17,27 +39,57 @@ impl NorFlashError for Error {
17 match self { 39 match self {
18 Self::OutOfBounds => NorFlashErrorKind::OutOfBounds, 40 Self::OutOfBounds => NorFlashErrorKind::OutOfBounds,
19 Self::Unaligned => NorFlashErrorKind::NotAligned, 41 Self::Unaligned => NorFlashErrorKind::NotAligned,
42 Self::Other => NorFlashErrorKind::Other,
20 } 43 }
21 } 44 }
22} 45}
23 46
24pub struct Flash<const FLASH_SIZE: usize>; 47pub struct Flash<const FLASH_SIZE: usize>;
25 48
49impl<const FLASH_SIZE: usize> Flash<FLASH_SIZE> {
50 /// Make sure to uphold the contract points with rp2040-flash.
51 /// - interrupts must be disabled
52 /// - DMA must not access flash memory
53 unsafe fn in_ram(&mut self, operation: impl FnOnce()) {
54 let dma_status = &mut [false; crate::dma::CHANNEL_COUNT];
55
56 // TODO: Make sure CORE1 is paused during the entire duration of the RAM function
57
58 critical_section::with(|_| {
59 // Pause all DMA channels for the duration of the ram operation
60 for (number, status) in dma_status.iter_mut().enumerate() {
61 let ch = crate::pac::DMA.ch(number as _);
62 *status = ch.ctrl_trig().read().en();
63 if *status {
64 ch.ctrl_trig().modify(|w| w.set_en(false));
65 }
66 }
67
68 // Run our flash operation in RAM
69 operation();
70
71 // Re-enable previously enabled DMA channels
72 for (number, status) in dma_status.iter().enumerate() {
73 let ch = crate::pac::DMA.ch(number as _);
74 if *status {
75 ch.ctrl_trig().modify(|w| w.set_en(true));
76 }
77 }
78 });
79 }
80}
26impl<const FLASH_SIZE: usize> ErrorType for Flash<FLASH_SIZE> { 81impl<const FLASH_SIZE: usize> ErrorType for Flash<FLASH_SIZE> {
27 type Error = Error; 82 type Error = Error;
28} 83}
29 84
30impl<const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<FLASH_SIZE> {}
31
32impl<const FLASH_SIZE: usize> ReadNorFlash for Flash<FLASH_SIZE> { 85impl<const FLASH_SIZE: usize> ReadNorFlash for Flash<FLASH_SIZE> {
33 const READ_SIZE: usize = 1; 86 const READ_SIZE: usize = READ_SIZE;
34 87
35 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { 88 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
36 if offset as usize >= FLASH_SIZE || offset as usize + bytes.len() > FLASH_SIZE { 89 check_read(self, offset, bytes.len())?;
37 return Err(Error::OutOfBounds); 90
38 } 91 let flash_data = unsafe { core::slice::from_raw_parts((FLASH_BASE as u32 + offset) as *const u8, bytes.len()) };
39 92
40 let flash_data = unsafe { core::slice::from_raw_parts(offset as *const u8, bytes.len()) };
41 bytes.copy_from_slice(flash_data); 93 bytes.copy_from_slice(flash_data);
42 Ok(()) 94 Ok(())
43 } 95 }
@@ -48,53 +100,254 @@ impl<const FLASH_SIZE: usize> ReadNorFlash for Flash<FLASH_SIZE> {
48} 100}
49 101
50impl<const FLASH_SIZE: usize> NorFlash for Flash<FLASH_SIZE> { 102impl<const FLASH_SIZE: usize> NorFlash for Flash<FLASH_SIZE> {
51 const WRITE_SIZE: usize = 4; 103 const WRITE_SIZE: usize = WRITE_SIZE;
52 104
53 const ERASE_SIZE: usize = 4096; 105 const ERASE_SIZE: usize = ERASE_SIZE;
54 106
55 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { 107 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
56 if to < from || to as usize > FLASH_SIZE { 108 check_erase(self, from, to)?;
57 return Err(Error::OutOfBounds);
58 }
59 if from as usize % Self::ERASE_SIZE != 0 || to as usize % Self::ERASE_SIZE != 0 {
60 return Err(Error::Unaligned);
61 }
62 109
63 let len = to - from; 110 let len = to - from;
64 111
65 // Make sure to uphold the contract point with rp2040-flash. 112 unsafe { self.in_ram(|| ram_helpers::flash_range_erase(from, len, true)) };
66 // - interrupts must be disabled
67 // - DMA must not access flash memory
68 // FIXME: Pause all DMA channels for the duration of the flash_write?
69 113
70 critical_section::with(|_| { 114 Ok(())
71 unsafe { rp2040_flash::flash::flash_range_erase(from, len, true) }; 115 }
72 });
73 116
74 // Re-enable DMA channels 117 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
118 check_write(self, offset, bytes.len())?;
119
120 trace!("Writing {:?} bytes to 0x{:x}", bytes.len(), offset);
121
122 unsafe { self.in_ram(|| ram_helpers::flash_range_program(offset, bytes, true)) };
75 123
76 Ok(()) 124 Ok(())
77 } 125 }
126}
78 127
79 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { 128mod ram_helpers {
80 if offset as usize + bytes.len() > FLASH_SIZE { 129 use core::marker::PhantomData;
81 return Err(Error::OutOfBounds); 130
131 use crate::rom_data;
132
133 #[repr(C)]
134 struct FlashFunctionPointers<'a> {
135 connect_internal_flash: unsafe extern "C" fn() -> (),
136 flash_exit_xip: unsafe extern "C" fn() -> (),
137 flash_range_erase: Option<unsafe extern "C" fn(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> ()>,
138 flash_range_program: Option<unsafe extern "C" fn(addr: u32, data: *const u8, count: usize) -> ()>,
139 flash_flush_cache: unsafe extern "C" fn() -> (),
140 flash_enter_cmd_xip: unsafe extern "C" fn() -> (),
141 phantom: PhantomData<&'a ()>,
142 }
143
144 #[allow(unused)]
145 fn flash_function_pointers(erase: bool, write: bool) -> FlashFunctionPointers<'static> {
146 FlashFunctionPointers {
147 connect_internal_flash: rom_data::connect_internal_flash::ptr(),
148 flash_exit_xip: rom_data::flash_exit_xip::ptr(),
149 flash_range_erase: if erase {
150 Some(rom_data::flash_range_erase::ptr())
151 } else {
152 None
153 },
154 flash_range_program: if write {
155 Some(rom_data::flash_range_program::ptr())
156 } else {
157 None
158 },
159 flash_flush_cache: rom_data::flash_flush_cache::ptr(),
160 flash_enter_cmd_xip: rom_data::flash_enter_cmd_xip::ptr(),
161 phantom: PhantomData,
82 } 162 }
83 if offset as usize % 4 != 0 || bytes.len() as usize % 4 != 0 { 163 }
84 return Err(Error::Unaligned); 164
165 #[allow(unused)]
166 /// # Safety
167 ///
168 /// `boot2` must contain a valid 2nd stage boot loader which can be called to re-initialize XIP mode
169 unsafe fn flash_function_pointers_with_boot2(erase: bool, write: bool, boot2: &[u32; 64]) -> FlashFunctionPointers {
170 let boot2_fn_ptr = (boot2 as *const u32 as *const u8).offset(1);
171 let boot2_fn: unsafe extern "C" fn() -> () = core::mem::transmute(boot2_fn_ptr);
172 FlashFunctionPointers {
173 connect_internal_flash: rom_data::connect_internal_flash::ptr(),
174 flash_exit_xip: rom_data::flash_exit_xip::ptr(),
175 flash_range_erase: if erase {
176 Some(rom_data::flash_range_erase::ptr())
177 } else {
178 None
179 },
180 flash_range_program: if write {
181 Some(rom_data::flash_range_program::ptr())
182 } else {
183 None
184 },
185 flash_flush_cache: rom_data::flash_flush_cache::ptr(),
186 flash_enter_cmd_xip: boot2_fn,
187 phantom: PhantomData,
85 } 188 }
189 }
86 190
87 // Make sure to uphold the contract point with rp2040-flash. 191 /// Erase a flash range starting at `addr` with length `len`.
88 // - interrupts must be disabled 192 ///
89 // - DMA must not access flash memory 193 /// `addr` and `len` must be multiples of 4096
90 // FIXME: Pause all DMA channels for the duration of the flash_write? 194 ///
195 /// If `use_boot2` is `true`, a copy of the 2nd stage boot loader
196 /// is used to re-initialize the XIP engine after flashing.
197 ///
198 /// # Safety
199 ///
200 /// Nothing must access flash while this is running.
201 /// Usually this means:
202 /// - interrupts must be disabled
203 /// - 2nd core must be running code from RAM or ROM with interrupts disabled
204 /// - DMA must not access flash memory
205 ///
206 /// `addr` and `len` parameters must be valid and are not checked.
207 pub unsafe fn flash_range_erase(addr: u32, len: u32, use_boot2: bool) {
208 let mut boot2 = [0u32; 256 / 4];
209 let ptrs = if use_boot2 {
210 rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256);
211 flash_function_pointers_with_boot2(true, false, &boot2)
212 } else {
213 flash_function_pointers(true, false)
214 };
215 write_flash_inner(addr, len, None, &ptrs as *const FlashFunctionPointers);
216 }
91 217
92 critical_section::with(|_| { 218 /// Erase and rewrite a flash range starting at `addr` with data `data`.
93 unsafe { rp2040_flash::flash::flash_range_program(offset, bytes, true) }; 219 ///
94 }); 220 /// `addr` and `data.len()` must be multiples of 4096
221 ///
222 /// If `use_boot2` is `true`, a copy of the 2nd stage boot loader
223 /// is used to re-initialize the XIP engine after flashing.
224 ///
225 /// # Safety
226 ///
227 /// Nothing must access flash while this is running.
228 /// Usually this means:
229 /// - interrupts must be disabled
230 /// - 2nd core must be running code from RAM or ROM with interrupts disabled
231 /// - DMA must not access flash memory
232 ///
233 /// `addr` and `len` parameters must be valid and are not checked.
234 pub unsafe fn flash_range_erase_and_program(addr: u32, data: &[u8], use_boot2: bool) {
235 let mut boot2 = [0u32; 256 / 4];
236 let ptrs = if use_boot2 {
237 rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256);
238 flash_function_pointers_with_boot2(true, true, &boot2)
239 } else {
240 flash_function_pointers(true, true)
241 };
242 write_flash_inner(
243 addr,
244 data.len() as u32,
245 Some(data),
246 &ptrs as *const FlashFunctionPointers,
247 );
248 }
95 249
96 // Re-enable DMA channels 250 /// Write a flash range starting at `addr` with data `data`.
251 ///
252 /// `addr` and `data.len()` must be multiples of 256
253 ///
254 /// If `use_boot2` is `true`, a copy of the 2nd stage boot loader
255 /// is used to re-initialize the XIP engine after flashing.
256 ///
257 /// # Safety
258 ///
259 /// Nothing must access flash while this is running.
260 /// Usually this means:
261 /// - interrupts must be disabled
262 /// - 2nd core must be running code from RAM or ROM with interrupts disabled
263 /// - DMA must not access flash memory
264 ///
265 /// `addr` and `len` parameters must be valid and are not checked.
266 pub unsafe fn flash_range_program(addr: u32, data: &[u8], use_boot2: bool) {
267 let mut boot2 = [0u32; 256 / 4];
268 let ptrs = if use_boot2 {
269 rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256);
270 flash_function_pointers_with_boot2(false, true, &boot2)
271 } else {
272 flash_function_pointers(false, true)
273 };
274 write_flash_inner(
275 addr,
276 data.len() as u32,
277 Some(data),
278 &ptrs as *const FlashFunctionPointers,
279 );
280 }
97 281
98 Ok(()) 282 /// # Safety
283 ///
284 /// Nothing must access flash while this is running.
285 /// Usually this means:
286 /// - interrupts must be disabled
287 /// - 2nd core must be running code from RAM or ROM with interrupts disabled
288 /// - DMA must not access flash memory
289 /// Length of data must be a multiple of 4096
290 /// addr must be aligned to 4096
291 #[inline(never)]
292 #[link_section = ".data.ram_func"]
293 unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) {
294 /*
295 Should be equivalent to:
296 rom_data::connect_internal_flash();
297 rom_data::flash_exit_xip();
298 rom_data::flash_range_erase(addr, len, 1 << 31, 0); // if selected
299 rom_data::flash_range_program(addr, data as *const _, len); // if selected
300 rom_data::flash_flush_cache();
301 rom_data::flash_enter_cmd_xip();
302 */
303 core::arch::asm!(
304 "mov r8, r0",
305 "mov r9, r2",
306 "mov r10, r1",
307 "ldr r4, [{ptrs}, #0]",
308 "blx r4", // connect_internal_flash()
309
310 "ldr r4, [{ptrs}, #4]",
311 "blx r4", // flash_exit_xip()
312
313 "mov r0, r8", // r0 = addr
314 "mov r1, r10", // r1 = len
315 "movs r2, #1",
316 "lsls r2, r2, #31", // r2 = 1 << 31
317 "movs r3, #0", // r3 = 0
318 "ldr r4, [{ptrs}, #8]",
319 "cmp r4, #0",
320 "beq 1f",
321 "blx r4", // flash_range_erase(addr, len, 1 << 31, 0)
322 "1:",
323
324 "mov r0, r8", // r0 = addr
325 "mov r1, r9", // r0 = data
326 "mov r2, r10", // r2 = len
327 "ldr r4, [{ptrs}, #12]",
328 "cmp r4, #0",
329 "beq 1f",
330 "blx r4", // flash_range_program(addr, data, len);
331 "1:",
332
333 "ldr r4, [{ptrs}, #16]",
334 "blx r4", // flash_flush_cache();
335
336 "ldr r4, [{ptrs}, #20]",
337 "blx r4", // flash_enter_cmd_xip();
338 ptrs = in(reg) ptrs,
339 // Registers r8-r15 are not allocated automatically,
340 // so assign them manually. We need to use them as
341 // otherwise there are not enough registers available.
342 in("r0") addr,
343 in("r2") data.map(|d| d.as_ptr()).unwrap_or(core::ptr::null()),
344 in("r1") len,
345 out("r3") _,
346 out("r4") _,
347 lateout("r8") _,
348 lateout("r9") _,
349 lateout("r10") _,
350 clobber_abi("C"),
351 );
99 } 352 }
100} 353}