diff options
| -rw-r--r-- | embassy-rp/src/dma.rs | 2 | ||||
| -rw-r--r-- | embassy-rp/src/flash.rs | 329 |
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 | ||
| 194 | const CHANNEL_COUNT: usize = 12; | 194 | pub(crate) const CHANNEL_COUNT: usize = 12; |
| 195 | const NEW_AW: AtomicWaker = AtomicWaker::new(); | 195 | const NEW_AW: AtomicWaker = AtomicWaker::new(); |
| 196 | static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT]; | 196 | static 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 @@ | |||
| 1 | use embedded_storage::nor_flash::{ | 1 | use 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 | ||
| 5 | pub 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. | ||
| 12 | pub const WRITE_SIZE: usize = 256; | ||
| 13 | pub const READ_SIZE: usize = 1; | ||
| 14 | pub 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 | |||
| 27 | impl 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 | ||
| 15 | impl NorFlashError for Error { | 37 | impl 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 | ||
| 24 | pub struct Flash<const FLASH_SIZE: usize>; | 47 | pub struct Flash<const FLASH_SIZE: usize>; |
| 25 | 48 | ||
| 49 | impl<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 | } | ||
| 26 | impl<const FLASH_SIZE: usize> ErrorType for Flash<FLASH_SIZE> { | 81 | impl<const FLASH_SIZE: usize> ErrorType for Flash<FLASH_SIZE> { |
| 27 | type Error = Error; | 82 | type Error = Error; |
| 28 | } | 83 | } |
| 29 | 84 | ||
| 30 | impl<const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<FLASH_SIZE> {} | ||
| 31 | |||
| 32 | impl<const FLASH_SIZE: usize> ReadNorFlash for Flash<FLASH_SIZE> { | 85 | impl<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 | ||
| 50 | impl<const FLASH_SIZE: usize> NorFlash for Flash<FLASH_SIZE> { | 102 | impl<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> { | 128 | mod 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 | } |
