diff options
| author | Rasmus Melchior Jacobsen <[email protected]> | 2023-03-25 16:04:45 +0100 |
|---|---|---|
| committer | Rasmus Melchior Jacobsen <[email protected]> | 2023-03-25 16:04:45 +0100 |
| commit | bc69eb596e2496a5eb0cf1252ada12f2710aaff2 (patch) | |
| tree | d2a4c233d439cba49e2512fce076155b30d20d3c /embassy-stm32/src/flash | |
| parent | 245147634bfbdcd325eea20be19286708bb29c9f (diff) | |
Add is_eraseable_range and split write into consecutive parts
Diffstat (limited to 'embassy-stm32/src/flash')
| -rw-r--r-- | embassy-stm32/src/flash/f3.rs | 55 | ||||
| -rw-r--r-- | embassy-stm32/src/flash/f4.rs | 74 | ||||
| -rw-r--r-- | embassy-stm32/src/flash/f7.rs | 70 | ||||
| -rw-r--r-- | embassy-stm32/src/flash/h7.rs | 76 | ||||
| -rw-r--r-- | embassy-stm32/src/flash/l.rs | 54 | ||||
| -rw-r--r-- | embassy-stm32/src/flash/mod.rs | 85 |
6 files changed, 221 insertions, 193 deletions
diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index b24dfb4a7..99ac1a153 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs | |||
| @@ -1,13 +1,13 @@ | |||
| 1 | use core::convert::TryInto; | 1 | use core::convert::TryInto; |
| 2 | use core::mem::size_of; | ||
| 3 | use core::ptr::write_volatile; | 2 | use core::ptr::write_volatile; |
| 4 | 3 | ||
| 5 | use super::FlashRegion; | 4 | use atomic_polyfill::{fence, Ordering}; |
| 5 | |||
| 6 | use super::{FlashRegion, BANK1, WRITE_SIZE}; | ||
| 6 | use crate::flash::Error; | 7 | use crate::flash::Error; |
| 7 | use crate::pac; | 8 | use crate::pac; |
| 8 | 9 | ||
| 9 | pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1::WRITE_SIZE; | 10 | const ERASE_SIZE: usize = BANK1::ERASE_SIZE; |
| 10 | pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; | ||
| 11 | 11 | ||
| 12 | pub(crate) unsafe fn lock() { | 12 | pub(crate) unsafe fn lock() { |
| 13 | pac::FLASH.cr().modify(|w| w.set_lock(true)); | 13 | pac::FLASH.cr().modify(|w| w.set_lock(true)); |
| @@ -18,33 +18,35 @@ pub(crate) unsafe fn unlock() { | |||
| 18 | pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB)); | 18 | pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB)); |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { | 21 | pub(crate) unsafe fn begin_write() { |
| 22 | pac::FLASH.cr().write(|w| w.set_pg(true)); | 22 | assert_eq!(0, WRITE_SIZE % 2); |
| 23 | 23 | ||
| 24 | let ret = { | 24 | pac::FLASH.cr().write(|w| w.set_pg(true)); |
| 25 | let mut ret: Result<(), Error> = Ok(()); | 25 | } |
| 26 | let mut address = first_address; | ||
| 27 | let chunks = buf.chunks_exact(size_of::<u16>()); | ||
| 28 | assert!(chunks.remainder().is_empty()); | ||
| 29 | for chunk in chunks { | ||
| 30 | write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap())); | ||
| 31 | address += chunk.len() as u32; | ||
| 32 | |||
| 33 | ret = blocking_wait_ready(); | ||
| 34 | if ret.is_err() { | ||
| 35 | break; | ||
| 36 | } | ||
| 37 | } | ||
| 38 | ret | ||
| 39 | }; | ||
| 40 | 26 | ||
| 27 | pub(crate) unsafe fn end_write() { | ||
| 41 | pac::FLASH.cr().write(|w| w.set_pg(false)); | 28 | pac::FLASH.cr().write(|w| w.set_pg(false)); |
| 29 | } | ||
| 42 | 30 | ||
| 43 | ret | 31 | pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { |
| 32 | let mut address = start_address; | ||
| 33 | for chunk in buf.chunks(2) { | ||
| 34 | write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap())); | ||
| 35 | address += chunk.len() as u32; | ||
| 36 | |||
| 37 | // prevents parallelism errors | ||
| 38 | fence(Ordering::SeqCst); | ||
| 39 | } | ||
| 40 | |||
| 41 | blocking_wait_ready() | ||
| 44 | } | 42 | } |
| 45 | 43 | ||
| 46 | pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { | 44 | pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { |
| 47 | for page in (from_address..to_address).step_by(MAX_ERASE_SIZE) { | 45 | start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0 |
| 46 | } | ||
| 47 | |||
| 48 | pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { | ||
| 49 | for page in (start_address..end_address).step_by(ERASE_SIZE) { | ||
| 48 | pac::FLASH.cr().modify(|w| { | 50 | pac::FLASH.cr().modify(|w| { |
| 49 | w.set_per(true); | 51 | w.set_per(true); |
| 50 | }); | 52 | }); |
| @@ -71,7 +73,6 @@ pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Resul | |||
| 71 | return ret; | 73 | return ret; |
| 72 | } | 74 | } |
| 73 | } | 75 | } |
| 74 | |||
| 75 | Ok(()) | 76 | Ok(()) |
| 76 | } | 77 | } |
| 77 | 78 | ||
| @@ -89,7 +90,7 @@ pub(crate) unsafe fn clear_all_err() { | |||
| 89 | }); | 90 | }); |
| 90 | } | 91 | } |
| 91 | 92 | ||
| 92 | pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { | 93 | unsafe fn blocking_wait_ready() -> Result<(), Error> { |
| 93 | loop { | 94 | loop { |
| 94 | let sr = pac::FLASH.sr().read(); | 95 | let sr = pac::FLASH.sr().read(); |
| 95 | 96 | ||
diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 0d9d405ba..7428fd572 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs | |||
| @@ -1,23 +1,19 @@ | |||
| 1 | use core::convert::TryInto; | 1 | use core::convert::TryInto; |
| 2 | use core::mem::size_of; | ||
| 3 | use core::ptr::write_volatile; | 2 | use core::ptr::write_volatile; |
| 4 | use core::sync::atomic::{fence, Ordering}; | 3 | use core::sync::atomic::{fence, Ordering}; |
| 5 | 4 | ||
| 6 | use embassy_hal_common::stm32::flash::f4::{get_sector, SECOND_BANK_SECTOR_OFFSET}; | 5 | use embassy_hal_common::stm32::flash::f4::{get_sector, SECOND_BANK_SECTOR_OFFSET}; |
| 7 | 6 | ||
| 8 | use super::{FlashRegion, FLASH_SIZE}; | 7 | use super::{FLASH_SIZE, WRITE_SIZE}; |
| 9 | use crate::flash::Error; | 8 | use crate::flash::Error; |
| 10 | use crate::pac; | 9 | use crate::pac; |
| 11 | 10 | ||
| 12 | pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1_REGION3::WRITE_SIZE; | 11 | fn is_dual_bank() -> bool { |
| 13 | pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1_REGION3::ERASE_SIZE; | ||
| 14 | |||
| 15 | unsafe fn is_dual_bank() -> bool { | ||
| 16 | match FLASH_SIZE / 1024 { | 12 | match FLASH_SIZE / 1024 { |
| 17 | // 1 MB devices depend on configuration | 13 | // 1 MB devices depend on configuration |
| 18 | 1024 => { | 14 | 1024 => { |
| 19 | if cfg!(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)) { | 15 | if cfg!(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)) { |
| 20 | pac::FLASH.optcr().read().db1m() | 16 | unsafe { pac::FLASH.optcr().read().db1m() } |
| 21 | } else { | 17 | } else { |
| 22 | false | 18 | false |
| 23 | } | 19 | } |
| @@ -38,49 +34,53 @@ pub(crate) unsafe fn unlock() { | |||
| 38 | pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); | 34 | pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); |
| 39 | } | 35 | } |
| 40 | 36 | ||
| 41 | pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { | 37 | pub(crate) unsafe fn begin_write() { |
| 38 | assert_eq!(0, WRITE_SIZE % 4); | ||
| 39 | |||
| 42 | pac::FLASH.cr().write(|w| { | 40 | pac::FLASH.cr().write(|w| { |
| 43 | w.set_pg(true); | 41 | w.set_pg(true); |
| 44 | w.set_psize(pac::flash::vals::Psize::PSIZE32); | 42 | w.set_psize(pac::flash::vals::Psize::PSIZE32); |
| 45 | }); | 43 | }); |
| 44 | } | ||
| 46 | 45 | ||
| 47 | let ret = { | 46 | pub(crate) unsafe fn end_write() { |
| 48 | let mut ret: Result<(), Error> = Ok(()); | 47 | pac::FLASH.cr().write(|w| w.set_pg(false)); |
| 49 | let mut address = first_address; | 48 | } |
| 50 | for chunk in buf.chunks(MAX_WRITE_SIZE) { | ||
| 51 | let vals = chunk.chunks_exact(size_of::<u32>()); | ||
| 52 | assert!(vals.remainder().is_empty()); | ||
| 53 | for val in vals { | ||
| 54 | write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); | ||
| 55 | address += val.len() as u32; | ||
| 56 | |||
| 57 | // prevents parallelism errors | ||
| 58 | fence(Ordering::SeqCst); | ||
| 59 | } | ||
| 60 | 49 | ||
| 61 | ret = blocking_wait_ready(); | 50 | pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { |
| 62 | if ret.is_err() { | 51 | let mut address = start_address; |
| 63 | break; | 52 | for val in buf.chunks(4) { |
| 64 | } | 53 | write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); |
| 65 | } | 54 | address += val.len() as u32; |
| 66 | ret | ||
| 67 | }; | ||
| 68 | 55 | ||
| 69 | pac::FLASH.cr().write(|w| w.set_pg(false)); | 56 | // prevents parallelism errors |
| 57 | fence(Ordering::SeqCst); | ||
| 58 | } | ||
| 70 | 59 | ||
| 71 | ret | 60 | blocking_wait_ready() |
| 72 | } | 61 | } |
| 73 | 62 | ||
| 74 | pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { | 63 | pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { |
| 75 | let mut addr = from_address; | ||
| 76 | let dual_bank = is_dual_bank(); | 64 | let dual_bank = is_dual_bank(); |
| 65 | let mut address = start_address; | ||
| 66 | while address < end_address { | ||
| 67 | let sector = get_sector(address, dual_bank, FLASH_SIZE as u32); | ||
| 68 | if sector.start != address { | ||
| 69 | return false; | ||
| 70 | } | ||
| 71 | address += sector.size; | ||
| 72 | } | ||
| 73 | address == end_address | ||
| 74 | } | ||
| 77 | 75 | ||
| 78 | while addr < to_address { | 76 | pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { |
| 79 | let sector = get_sector(addr, dual_bank, FLASH_SIZE as u32); | 77 | let dual_bank = is_dual_bank(); |
| 78 | let mut address = start_address; | ||
| 79 | while address < end_address { | ||
| 80 | let sector = get_sector(address, dual_bank, FLASH_SIZE as u32); | ||
| 80 | erase_sector(sector.index)?; | 81 | erase_sector(sector.index)?; |
| 81 | addr += sector.size; | 82 | address += sector.size; |
| 82 | } | 83 | } |
| 83 | |||
| 84 | Ok(()) | 84 | Ok(()) |
| 85 | } | 85 | } |
| 86 | 86 | ||
| @@ -116,7 +116,7 @@ pub(crate) unsafe fn clear_all_err() { | |||
| 116 | }); | 116 | }); |
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { | 119 | unsafe fn blocking_wait_ready() -> Result<(), Error> { |
| 120 | loop { | 120 | loop { |
| 121 | let sr = pac::FLASH.sr().read(); | 121 | let sr = pac::FLASH.sr().read(); |
| 122 | 122 | ||
diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 8b8076e0c..16b684580 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs | |||
| @@ -1,17 +1,13 @@ | |||
| 1 | use core::convert::TryInto; | 1 | use core::convert::TryInto; |
| 2 | use core::mem::size_of; | ||
| 3 | use core::ptr::write_volatile; | 2 | use core::ptr::write_volatile; |
| 4 | use core::sync::atomic::{fence, Ordering}; | 3 | use core::sync::atomic::{fence, Ordering}; |
| 5 | 4 | ||
| 6 | use embassy_hal_common::stm32::flash::f7::get_sector; | 5 | use embassy_hal_common::stm32::flash::f7::get_sector; |
| 7 | 6 | ||
| 8 | use super::FlashRegion; | 7 | use super::WRITE_SIZE; |
| 9 | use crate::flash::Error; | 8 | use crate::flash::Error; |
| 10 | use crate::pac; | 9 | use crate::pac; |
| 11 | 10 | ||
| 12 | pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1_REGION3::WRITE_SIZE; | ||
| 13 | pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1_REGION3::ERASE_SIZE; | ||
| 14 | |||
| 15 | pub(crate) unsafe fn lock() { | 11 | pub(crate) unsafe fn lock() { |
| 16 | pac::FLASH.cr().modify(|w| w.set_lock(true)); | 12 | pac::FLASH.cr().modify(|w| w.set_lock(true)); |
| 17 | } | 13 | } |
| @@ -21,49 +17,51 @@ pub(crate) unsafe fn unlock() { | |||
| 21 | pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); | 17 | pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); |
| 22 | } | 18 | } |
| 23 | 19 | ||
| 24 | pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { | 20 | pub(crate) unsafe fn begin_write() { |
| 21 | assert_eq!(0, WRITE_SIZE % 4); | ||
| 22 | |||
| 25 | pac::FLASH.cr().write(|w| { | 23 | pac::FLASH.cr().write(|w| { |
| 26 | w.set_pg(true); | 24 | w.set_pg(true); |
| 27 | w.set_psize(pac::flash::vals::Psize::PSIZE32); | 25 | w.set_psize(pac::flash::vals::Psize::PSIZE32); |
| 28 | }); | 26 | }); |
| 27 | } | ||
| 29 | 28 | ||
| 30 | let ret = { | 29 | pub(crate) unsafe fn end_write() { |
| 31 | let mut ret: Result<(), Error> = Ok(()); | 30 | pac::FLASH.cr().write(|w| w.set_pg(false)); |
| 32 | let mut address = first_address; | 31 | } |
| 33 | for chunk in buf.chunks(MAX_WRITE_SIZE) { | ||
| 34 | let vals = chunk.chunks_exact(size_of::<u32>()); | ||
| 35 | assert!(vals.remainder().is_empty()); | ||
| 36 | for val in vals { | ||
| 37 | write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); | ||
| 38 | address += val.len() as u32; | ||
| 39 | |||
| 40 | // prevents parallelism errors | ||
| 41 | fence(Ordering::SeqCst); | ||
| 42 | } | ||
| 43 | 32 | ||
| 44 | ret = blocking_wait_ready(); | 33 | pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { |
| 45 | if ret.is_err() { | 34 | let mut address = start_address; |
| 46 | break; | 35 | for val in buf.chunks(4) { |
| 47 | } | 36 | write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); |
| 48 | } | 37 | address += val.len() as u32; |
| 49 | ret | ||
| 50 | }; | ||
| 51 | 38 | ||
| 52 | pac::FLASH.cr().write(|w| w.set_pg(false)); | 39 | // prevents parallelism errors |
| 40 | fence(Ordering::SeqCst); | ||
| 41 | } | ||
| 53 | 42 | ||
| 54 | ret | 43 | blocking_wait_ready() |
| 55 | } | 44 | } |
| 56 | 45 | ||
| 57 | pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { | 46 | pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { |
| 58 | let start_sector = get_sector(from_address); | 47 | let mut address = start_address; |
| 59 | let end_sector = get_sector(to_address); | 48 | while address < end_address { |
| 60 | for sector in start_sector.index..end_sector.index { | 49 | let sector = get_sector(address); |
| 61 | let ret = erase_sector(sector as u8); | 50 | if sector.start != address { |
| 62 | if ret.is_err() { | 51 | return false; |
| 63 | return ret; | ||
| 64 | } | 52 | } |
| 53 | address += sector.size; | ||
| 65 | } | 54 | } |
| 55 | address == end_address | ||
| 56 | } | ||
| 66 | 57 | ||
| 58 | pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { | ||
| 59 | let mut address = start_address; | ||
| 60 | while address < end_address { | ||
| 61 | let sector = get_sector(address); | ||
| 62 | erase_sector(sector.index)?; | ||
| 63 | address += sector.size; | ||
| 64 | } | ||
| 67 | Ok(()) | 65 | Ok(()) |
| 68 | } | 66 | } |
| 69 | 67 | ||
| @@ -106,7 +104,7 @@ pub(crate) unsafe fn clear_all_err() { | |||
| 106 | }); | 104 | }); |
| 107 | } | 105 | } |
| 108 | 106 | ||
| 109 | pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { | 107 | unsafe fn blocking_wait_ready() -> Result<(), Error> { |
| 110 | loop { | 108 | loop { |
| 111 | let sr = pac::FLASH.sr().read(); | 109 | let sr = pac::FLASH.sr().read(); |
| 112 | 110 | ||
diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 6ab8e7b74..21a9e45df 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs | |||
| @@ -1,15 +1,13 @@ | |||
| 1 | use core::convert::TryInto; | 1 | use core::convert::TryInto; |
| 2 | use core::mem::size_of; | ||
| 3 | use core::ptr::write_volatile; | 2 | use core::ptr::write_volatile; |
| 4 | 3 | ||
| 5 | use super::{FlashRegion, FLASH_SIZE}; | 4 | use atomic_polyfill::{fence, Ordering}; |
| 5 | |||
| 6 | use super::{FlashRegion, FLASH_SIZE, WRITE_SIZE}; | ||
| 6 | use crate::flash::Error; | 7 | use crate::flash::Error; |
| 7 | use crate::pac; | 8 | use crate::pac; |
| 8 | 9 | ||
| 9 | const WRITE_SIZE: usize = super::BANK1::WRITE_SIZE; | ||
| 10 | const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; | 10 | const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; |
| 11 | pub(crate) const MAX_WRITE_SIZE: usize = WRITE_SIZE; | ||
| 12 | pub(crate) const MAX_ERASE_SIZE: usize = ERASE_SIZE; | ||
| 13 | const SECOND_BANK_OFFSET: usize = 0x0010_0000; | 11 | const SECOND_BANK_OFFSET: usize = 0x0010_0000; |
| 14 | 12 | ||
| 15 | const fn is_dual_bank() -> bool { | 13 | const fn is_dual_bank() -> bool { |
| @@ -33,59 +31,60 @@ pub(crate) unsafe fn unlock() { | |||
| 33 | } | 31 | } |
| 34 | } | 32 | } |
| 35 | 33 | ||
| 36 | pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { | 34 | pub(crate) unsafe fn begin_write() { |
| 37 | let bank = if !is_dual_bank() || (first_address - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32 { | 35 | assert_eq!(0, WRITE_SIZE % 4); |
| 36 | } | ||
| 37 | |||
| 38 | pub(crate) unsafe fn end_write() {} | ||
| 39 | |||
| 40 | pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { | ||
| 41 | // We cannot have the write setup sequence in begin_write as it depends on the address | ||
| 42 | let bank = if !is_dual_bank() || (start_address - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32 { | ||
| 38 | pac::FLASH.bank(0) | 43 | pac::FLASH.bank(0) |
| 39 | } else { | 44 | } else { |
| 40 | pac::FLASH.bank(1) | 45 | pac::FLASH.bank(1) |
| 41 | }; | 46 | }; |
| 42 | |||
| 43 | bank.cr().write(|w| { | 47 | bank.cr().write(|w| { |
| 44 | w.set_pg(true); | 48 | w.set_pg(true); |
| 45 | w.set_psize(2); // 32 bits at once | 49 | w.set_psize(2); // 32 bits at once |
| 46 | }); | 50 | }); |
| 47 | |||
| 48 | cortex_m::asm::isb(); | 51 | cortex_m::asm::isb(); |
| 49 | cortex_m::asm::dsb(); | 52 | cortex_m::asm::dsb(); |
| 50 | core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); | 53 | fence(Ordering::SeqCst); |
| 51 | 54 | ||
| 52 | let ret = { | 55 | let mut res = None; |
| 53 | let mut ret: Result<(), Error> = Ok(()); | 56 | let mut address = start_address; |
| 54 | let mut address = first_address; | 57 | for val in buf.chunks(4) { |
| 55 | 'outer: for chunk in buf.chunks(WRITE_SIZE) { | 58 | write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); |
| 56 | let vals = chunk.chunks_exact(size_of::<u32>()); | 59 | address += val.len() as u32; |
| 57 | assert!(vals.remainder().is_empty()); | 60 | |
| 58 | for val in vals { | 61 | res = Some(blocking_wait_ready(bank)); |
| 59 | write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); | 62 | bank.sr().modify(|w| { |
| 60 | address += val.len() as u32; | 63 | if w.eop() { |
| 61 | 64 | w.set_eop(true); | |
| 62 | ret = blocking_wait_ready(bank); | ||
| 63 | bank.sr().modify(|w| { | ||
| 64 | if w.eop() { | ||
| 65 | w.set_eop(true); | ||
| 66 | } | ||
| 67 | }); | ||
| 68 | if ret.is_err() { | ||
| 69 | break 'outer; | ||
| 70 | } | ||
| 71 | } | 65 | } |
| 66 | }); | ||
| 67 | if res.unwrap().is_err() { | ||
| 68 | break; | ||
| 72 | } | 69 | } |
| 73 | ret | 70 | } |
| 74 | }; | ||
| 75 | 71 | ||
| 76 | bank.cr().write(|w| w.set_pg(false)); | 72 | bank.cr().write(|w| w.set_pg(false)); |
| 77 | 73 | ||
| 78 | cortex_m::asm::isb(); | 74 | cortex_m::asm::isb(); |
| 79 | cortex_m::asm::dsb(); | 75 | cortex_m::asm::dsb(); |
| 80 | core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); | 76 | fence(Ordering::SeqCst); |
| 81 | 77 | ||
| 82 | ret | 78 | res.unwrap() |
| 83 | } | 79 | } |
| 84 | 80 | ||
| 85 | pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { | 81 | pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { |
| 86 | let start_sector = (from - super::FLASH_BASE as u32) / ERASE_SIZE as u32; | 82 | start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0 |
| 87 | let end_sector = (to - super::FLASH_BASE as u32) / ERASE_SIZE as u32; | 83 | } |
| 88 | 84 | ||
| 85 | pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { | ||
| 86 | let start_sector = (start_address - super::FLASH_BASE as u32) / ERASE_SIZE as u32; | ||
| 87 | let end_sector = (end_address - super::FLASH_BASE as u32) / ERASE_SIZE as u32; | ||
| 89 | for sector in start_sector..end_sector { | 88 | for sector in start_sector..end_sector { |
| 90 | let bank = if sector >= 8 { 1 } else { 0 }; | 89 | let bank = if sector >= 8 { 1 } else { 0 }; |
| 91 | let ret = erase_sector(pac::FLASH.bank(bank), (sector % 8) as u8); | 90 | let ret = erase_sector(pac::FLASH.bank(bank), (sector % 8) as u8); |
| @@ -93,7 +92,6 @@ pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { | |||
| 93 | return ret; | 92 | return ret; |
| 94 | } | 93 | } |
| 95 | } | 94 | } |
| 96 | |||
| 97 | Ok(()) | 95 | Ok(()) |
| 98 | } | 96 | } |
| 99 | 97 | ||
| @@ -157,7 +155,7 @@ unsafe fn bank_clear_all_err(bank: pac::flash::Bank) { | |||
| 157 | }); | 155 | }); |
| 158 | } | 156 | } |
| 159 | 157 | ||
| 160 | pub(crate) unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { | 158 | unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { |
| 161 | loop { | 159 | loop { |
| 162 | let sr = bank.sr().read(); | 160 | let sr = bank.sr().read(); |
| 163 | 161 | ||
diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index 9ab732b8b..57989625c 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs | |||
| @@ -1,14 +1,12 @@ | |||
| 1 | use core::convert::TryInto; | ||
| 2 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 3 | 2 | ||
| 4 | use super::FlashRegion; | 3 | use atomic_polyfill::{fence, Ordering}; |
| 4 | |||
| 5 | use super::{FlashRegion, WRITE_SIZE}; | ||
| 5 | use crate::flash::Error; | 6 | use crate::flash::Error; |
| 6 | use crate::pac; | 7 | use crate::pac; |
| 7 | 8 | ||
| 8 | const WRITE_SIZE: usize = super::BANK1::WRITE_SIZE; | ||
| 9 | const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; | 9 | const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; |
| 10 | pub(crate) const MAX_WRITE_SIZE: usize = WRITE_SIZE; | ||
| 11 | pub(crate) const MAX_ERASE_SIZE: usize = ERASE_SIZE; | ||
| 12 | 10 | ||
| 13 | pub(crate) unsafe fn lock() { | 11 | pub(crate) unsafe fn lock() { |
| 14 | #[cfg(any(flash_wl, flash_wb, flash_l4))] | 12 | #[cfg(any(flash_wl, flash_wb, flash_l4))] |
| @@ -39,35 +37,37 @@ pub(crate) unsafe fn unlock() { | |||
| 39 | } | 37 | } |
| 40 | } | 38 | } |
| 41 | 39 | ||
| 42 | pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { | 40 | pub(crate) unsafe fn begin_write() { |
| 41 | assert_eq!(0, WRITE_SIZE % 4); | ||
| 42 | |||
| 43 | #[cfg(any(flash_wl, flash_wb, flash_l4))] | 43 | #[cfg(any(flash_wl, flash_wb, flash_l4))] |
| 44 | pac::FLASH.cr().write(|w| w.set_pg(true)); | 44 | pac::FLASH.cr().write(|w| w.set_pg(true)); |
| 45 | } | ||
| 45 | 46 | ||
| 46 | let ret = { | 47 | pub(crate) unsafe fn end_write() { |
| 47 | let mut ret: Result<(), Error> = Ok(()); | ||
| 48 | let mut address = first_address; | ||
| 49 | for chunk in buf.chunks(WRITE_SIZE) { | ||
| 50 | for val in chunk.chunks(4) { | ||
| 51 | write_volatile(address as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); | ||
| 52 | address += val.len() as u32; | ||
| 53 | } | ||
| 54 | |||
| 55 | ret = blocking_wait_ready(); | ||
| 56 | if ret.is_err() { | ||
| 57 | break; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | ret | ||
| 61 | }; | ||
| 62 | |||
| 63 | #[cfg(any(flash_wl, flash_wb, flash_l4))] | 48 | #[cfg(any(flash_wl, flash_wb, flash_l4))] |
| 64 | pac::FLASH.cr().write(|w| w.set_pg(false)); | 49 | pac::FLASH.cr().write(|w| w.set_pg(false)); |
| 50 | } | ||
| 51 | |||
| 52 | pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { | ||
| 53 | let mut address = start_address; | ||
| 54 | for val in buf.chunks(4) { | ||
| 55 | write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); | ||
| 56 | address += val.len() as u32; | ||
| 57 | |||
| 58 | // prevents parallelism errors | ||
| 59 | fence(Ordering::SeqCst); | ||
| 60 | } | ||
| 61 | |||
| 62 | blocking_wait_ready() | ||
| 63 | } | ||
| 65 | 64 | ||
| 66 | ret | 65 | pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { |
| 66 | start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0 | ||
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { | 69 | pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { |
| 70 | for page in (from_address..to_address).step_by(ERASE_SIZE) { | 70 | for page in (start_address..end_address).step_by(ERASE_SIZE) { |
| 71 | #[cfg(any(flash_l0, flash_l1))] | 71 | #[cfg(any(flash_l0, flash_l1))] |
| 72 | { | 72 | { |
| 73 | pac::FLASH.pecr().modify(|w| { | 73 | pac::FLASH.pecr().modify(|w| { |
| @@ -155,7 +155,7 @@ pub(crate) unsafe fn clear_all_err() { | |||
| 155 | }); | 155 | }); |
| 156 | } | 156 | } |
| 157 | 157 | ||
| 158 | pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { | 158 | unsafe fn blocking_wait_ready() -> Result<(), Error> { |
| 159 | loop { | 159 | loop { |
| 160 | let sr = pac::FLASH.sr().read(); | 160 | let sr = pac::FLASH.sr().read(); |
| 161 | 161 | ||
diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index b6cecfdb3..c704909ac 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs | |||
| @@ -1,8 +1,10 @@ | |||
| 1 | use embassy_hal_common::{into_ref, PeripheralRef}; | 1 | use embassy_hal_common::{into_ref, PeripheralRef}; |
| 2 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 3 | use embassy_sync::mutex::{Mutex, MutexGuard}; | ||
| 2 | use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; | 4 | use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; |
| 3 | 5 | ||
| 4 | pub use crate::_generated::flash_regions::*; | 6 | pub use crate::_generated::flash_regions::*; |
| 5 | pub use crate::pac::{FLASH_BASE, FLASH_SIZE}; | 7 | pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; |
| 6 | use crate::peripherals::FLASH; | 8 | use crate::peripherals::FLASH; |
| 7 | use crate::Peripheral; | 9 | use crate::Peripheral; |
| 8 | 10 | ||
| @@ -17,6 +19,8 @@ pub struct Flash<'d> { | |||
| 17 | _inner: PeripheralRef<'d, FLASH>, | 19 | _inner: PeripheralRef<'d, FLASH>, |
| 18 | } | 20 | } |
| 19 | 21 | ||
| 22 | static REGION_LOCK: Mutex<CriticalSectionRawMutex, ()> = Mutex::new(()); | ||
| 23 | |||
| 20 | impl<'d> Flash<'d> { | 24 | impl<'d> Flash<'d> { |
| 21 | pub fn new(p: impl Peripheral<P = FLASH> + 'd) -> Self { | 25 | pub fn new(p: impl Peripheral<P = FLASH> + 'd) -> Self { |
| 22 | into_ref!(p); | 26 | into_ref!(p); |
| @@ -33,7 +37,6 @@ impl<'d> Flash<'d> { | |||
| 33 | } | 37 | } |
| 34 | 38 | ||
| 35 | let first_address = FLASH_BASE as u32 + offset; | 39 | let first_address = FLASH_BASE as u32 + offset; |
| 36 | |||
| 37 | let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; | 40 | let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; |
| 38 | bytes.copy_from_slice(flash_data); | 41 | bytes.copy_from_slice(flash_data); |
| 39 | Ok(()) | 42 | Ok(()) |
| @@ -43,39 +46,56 @@ impl<'d> Flash<'d> { | |||
| 43 | if offset as usize + buf.len() > FLASH_SIZE { | 46 | if offset as usize + buf.len() > FLASH_SIZE { |
| 44 | return Err(Error::Size); | 47 | return Err(Error::Size); |
| 45 | } | 48 | } |
| 46 | if offset as usize % family::MAX_WRITE_SIZE != 0 || buf.len() as usize % family::MAX_WRITE_SIZE != 0 { | 49 | if offset as usize % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { |
| 47 | return Err(Error::Unaligned); | 50 | return Err(Error::Unaligned); |
| 48 | } | 51 | } |
| 49 | 52 | ||
| 50 | let first_address = FLASH_BASE as u32 + offset; | 53 | let start_address = FLASH_BASE as u32 + offset; |
| 51 | trace!("Writing {} bytes at 0x{:x}", buf.len(), first_address); | 54 | trace!("Writing {} bytes at 0x{:x}", buf.len(), start_address); |
| 55 | |||
| 56 | // No need to take lock here as we only have one mut flash reference. | ||
| 52 | 57 | ||
| 53 | unsafe { | 58 | unsafe { |
| 54 | family::clear_all_err(); | 59 | family::clear_all_err(); |
| 55 | |||
| 56 | family::unlock(); | 60 | family::unlock(); |
| 57 | let res = family::blocking_write(first_address, buf); | 61 | let res = Flash::blocking_write_all(start_address, buf); |
| 58 | family::lock(); | 62 | family::lock(); |
| 59 | res | 63 | res |
| 60 | } | 64 | } |
| 61 | } | 65 | } |
| 62 | 66 | ||
| 67 | unsafe fn blocking_write_all(start_address: u32, buf: &[u8]) -> Result<(), Error> { | ||
| 68 | family::begin_write(); | ||
| 69 | let mut address = start_address; | ||
| 70 | for chunk in buf.chunks(WRITE_SIZE) { | ||
| 71 | let res = unsafe { family::blocking_write(address, chunk.try_into().unwrap()) }; | ||
| 72 | if res.is_err() { | ||
| 73 | family::end_write(); | ||
| 74 | return res; | ||
| 75 | } | ||
| 76 | address += WRITE_SIZE as u32; | ||
| 77 | } | ||
| 78 | |||
| 79 | family::end_write(); | ||
| 80 | Ok(()) | ||
| 81 | } | ||
| 82 | |||
| 63 | pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { | 83 | pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { |
| 64 | if to < from || to as usize > FLASH_SIZE { | 84 | if to < from || to as usize > FLASH_SIZE { |
| 65 | return Err(Error::Size); | 85 | return Err(Error::Size); |
| 66 | } | 86 | } |
| 67 | if (from as usize % family::MAX_ERASE_SIZE) != 0 || (to as usize % family::MAX_ERASE_SIZE) != 0 { | 87 | |
| 88 | let start_address = FLASH_BASE as u32 + from; | ||
| 89 | let end_address = FLASH_BASE as u32 + to; | ||
| 90 | if !family::is_eraseable_range(start_address, end_address) { | ||
| 68 | return Err(Error::Unaligned); | 91 | return Err(Error::Unaligned); |
| 69 | } | 92 | } |
| 70 | 93 | trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); | |
| 71 | let from_address = FLASH_BASE as u32 + from; | ||
| 72 | let to_address = FLASH_BASE as u32 + to; | ||
| 73 | 94 | ||
| 74 | unsafe { | 95 | unsafe { |
| 75 | family::clear_all_err(); | 96 | family::clear_all_err(); |
| 76 | |||
| 77 | family::unlock(); | 97 | family::unlock(); |
| 78 | let res = family::blocking_erase(from_address, to_address); | 98 | let res = family::blocking_erase(start_address, end_address); |
| 79 | family::lock(); | 99 | family::lock(); |
| 80 | res | 100 | res |
| 81 | } | 101 | } |
| @@ -101,7 +121,6 @@ pub trait FlashRegion { | |||
| 101 | } | 121 | } |
| 102 | 122 | ||
| 103 | let first_address = Self::BASE as u32 + offset; | 123 | let first_address = Self::BASE as u32 + offset; |
| 104 | |||
| 105 | let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; | 124 | let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; |
| 106 | bytes.copy_from_slice(flash_data); | 125 | bytes.copy_from_slice(flash_data); |
| 107 | Ok(()) | 126 | Ok(()) |
| @@ -115,17 +134,19 @@ pub trait FlashRegion { | |||
| 115 | return Err(Error::Unaligned); | 134 | return Err(Error::Unaligned); |
| 116 | } | 135 | } |
| 117 | 136 | ||
| 118 | let first_address = Self::BASE as u32 + offset; | 137 | let start_address = Self::BASE as u32 + offset; |
| 119 | trace!("Writing {} bytes from 0x{:x}", buf.len(), first_address); | 138 | trace!("Writing {} bytes from 0x{:x}", buf.len(), start_address); |
| 120 | 139 | ||
| 121 | critical_section::with(|_| unsafe { | 140 | // Protect agains simultaneous write/erase to multiple regions. |
| 122 | family::clear_all_err(); | 141 | let _guard = take_lock_spin(); |
| 123 | 142 | ||
| 143 | unsafe { | ||
| 144 | family::clear_all_err(); | ||
| 124 | family::unlock(); | 145 | family::unlock(); |
| 125 | let res = family::blocking_write(first_address, buf); | 146 | let res = Flash::blocking_write_all(start_address, buf); |
| 126 | family::lock(); | 147 | family::lock(); |
| 127 | res | 148 | res |
| 128 | }) | 149 | } |
| 129 | } | 150 | } |
| 130 | 151 | ||
| 131 | fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { | 152 | fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { |
| @@ -136,18 +157,28 @@ pub trait FlashRegion { | |||
| 136 | return Err(Error::Unaligned); | 157 | return Err(Error::Unaligned); |
| 137 | } | 158 | } |
| 138 | 159 | ||
| 139 | let from_address = Self::BASE as u32 + from; | 160 | let start_address = Self::BASE as u32 + from; |
| 140 | let to_address = Self::BASE as u32 + to; | 161 | let end_address = Self::BASE as u32 + to; |
| 141 | trace!("Erasing from 0x{:x} to 0x{:x}", from_address, to_address); | 162 | trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); |
| 142 | 163 | ||
| 143 | critical_section::with(|_| unsafe { | 164 | // Protect agains simultaneous write/erase to multiple regions. |
| 144 | family::clear_all_err(); | 165 | let _guard = take_lock_spin(); |
| 145 | 166 | ||
| 167 | unsafe { | ||
| 168 | family::clear_all_err(); | ||
| 146 | family::unlock(); | 169 | family::unlock(); |
| 147 | let res = family::blocking_erase(from_address, to_address); | 170 | let res = family::blocking_erase(start_address, end_address); |
| 148 | family::lock(); | 171 | family::lock(); |
| 149 | res | 172 | res |
| 150 | }) | 173 | } |
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { | ||
| 178 | loop { | ||
| 179 | if let Ok(guard) = REGION_LOCK.try_lock() { | ||
| 180 | return guard; | ||
| 181 | } | ||
| 151 | } | 182 | } |
| 152 | } | 183 | } |
| 153 | 184 | ||
