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 @@ use core::convert::TryInto; -use core::mem::size_of; use core::ptr::write_volatile; -use super::FlashRegion; +use atomic_polyfill::{fence, Ordering}; + +use super::{FlashRegion, BANK1, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1::WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; +const ERASE_SIZE: usize = BANK1::ERASE_SIZE; pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); @@ -18,33 +18,35 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB)); } -pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { +pub(crate) unsafe fn begin_write() { + assert_eq!(0, WRITE_SIZE % 2); + pac::FLASH.cr().write(|w| w.set_pg(true)); - - let ret = { - let mut ret: Result<(), Error> = Ok(()); - let mut address = first_address; - let chunks = buf.chunks_exact(size_of::()); - assert!(chunks.remainder().is_empty()); - for chunk in chunks { - write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap())); - address += chunk.len() as u32; - - ret = blocking_wait_ready(); - if ret.is_err() { - break; - } - } - ret - }; - - pac::FLASH.cr().write(|w| w.set_pg(false)); - - ret } -pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { - for page in (from_address..to_address).step_by(MAX_ERASE_SIZE) { +pub(crate) unsafe fn end_write() { + pac::FLASH.cr().write(|w| w.set_pg(false)); +} + +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + let mut address = start_address; + for chunk in buf.chunks(2) { + write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap())); + address += chunk.len() as u32; + + // prevents parallelism errors + fence(Ordering::SeqCst); + } + + blocking_wait_ready() +} + +pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { + start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0 +} + +pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { + for page in (start_address..end_address).step_by(ERASE_SIZE) { pac::FLASH.cr().modify(|w| { w.set_per(true); }); @@ -71,7 +73,6 @@ pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Resul return ret; } } - Ok(()) } @@ -89,7 +90,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn blocking_wait_ready() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); 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 @@ use core::convert::TryInto; -use core::mem::size_of; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; use embassy_hal_common::stm32::flash::f4::{get_sector, SECOND_BANK_SECTOR_OFFSET}; -use super::{FlashRegion, FLASH_SIZE}; +use super::{FLASH_SIZE, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1_REGION3::WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1_REGION3::ERASE_SIZE; - -unsafe fn is_dual_bank() -> bool { +fn is_dual_bank() -> bool { match FLASH_SIZE / 1024 { // 1 MB devices depend on configuration 1024 => { if cfg!(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)) { - pac::FLASH.optcr().read().db1m() + unsafe { pac::FLASH.optcr().read().db1m() } } else { false } @@ -38,49 +34,53 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); } -pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { +pub(crate) unsafe fn begin_write() { + assert_eq!(0, WRITE_SIZE % 4); + pac::FLASH.cr().write(|w| { w.set_pg(true); w.set_psize(pac::flash::vals::Psize::PSIZE32); }); - - let ret = { - let mut ret: Result<(), Error> = Ok(()); - let mut address = first_address; - for chunk in buf.chunks(MAX_WRITE_SIZE) { - let vals = chunk.chunks_exact(size_of::()); - assert!(vals.remainder().is_empty()); - for val in vals { - write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); - address += val.len() as u32; - - // prevents parallelism errors - fence(Ordering::SeqCst); - } - - ret = blocking_wait_ready(); - if ret.is_err() { - break; - } - } - ret - }; - - pac::FLASH.cr().write(|w| w.set_pg(false)); - - ret } -pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { - let mut addr = from_address; - let dual_bank = is_dual_bank(); +pub(crate) unsafe fn end_write() { + pac::FLASH.cr().write(|w| w.set_pg(false)); +} - while addr < to_address { - let sector = get_sector(addr, dual_bank, FLASH_SIZE as u32); - erase_sector(sector.index)?; - addr += sector.size; +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + let mut address = start_address; + for val in buf.chunks(4) { + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; + + // prevents parallelism errors + fence(Ordering::SeqCst); } + blocking_wait_ready() +} + +pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { + let dual_bank = is_dual_bank(); + let mut address = start_address; + while address < end_address { + let sector = get_sector(address, dual_bank, FLASH_SIZE as u32); + if sector.start != address { + return false; + } + address += sector.size; + } + address == end_address +} + +pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { + let dual_bank = is_dual_bank(); + let mut address = start_address; + while address < end_address { + let sector = get_sector(address, dual_bank, FLASH_SIZE as u32); + erase_sector(sector.index)?; + address += sector.size; + } Ok(()) } @@ -116,7 +116,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn blocking_wait_ready() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); 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 @@ use core::convert::TryInto; -use core::mem::size_of; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; use embassy_hal_common::stm32::flash::f7::get_sector; -use super::FlashRegion; +use super::WRITE_SIZE; use crate::flash::Error; use crate::pac; -pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1_REGION3::WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1_REGION3::ERASE_SIZE; - pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -21,49 +17,51 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); } -pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { +pub(crate) unsafe fn begin_write() { + assert_eq!(0, WRITE_SIZE % 4); + pac::FLASH.cr().write(|w| { w.set_pg(true); w.set_psize(pac::flash::vals::Psize::PSIZE32); }); - - let ret = { - let mut ret: Result<(), Error> = Ok(()); - let mut address = first_address; - for chunk in buf.chunks(MAX_WRITE_SIZE) { - let vals = chunk.chunks_exact(size_of::()); - assert!(vals.remainder().is_empty()); - for val in vals { - write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); - address += val.len() as u32; - - // prevents parallelism errors - fence(Ordering::SeqCst); - } - - ret = blocking_wait_ready(); - if ret.is_err() { - break; - } - } - ret - }; - - pac::FLASH.cr().write(|w| w.set_pg(false)); - - ret } -pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { - let start_sector = get_sector(from_address); - let end_sector = get_sector(to_address); - for sector in start_sector.index..end_sector.index { - let ret = erase_sector(sector as u8); - if ret.is_err() { - return ret; - } +pub(crate) unsafe fn end_write() { + pac::FLASH.cr().write(|w| w.set_pg(false)); +} + +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + let mut address = start_address; + for val in buf.chunks(4) { + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; + + // prevents parallelism errors + fence(Ordering::SeqCst); } + blocking_wait_ready() +} + +pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { + let mut address = start_address; + while address < end_address { + let sector = get_sector(address); + if sector.start != address { + return false; + } + address += sector.size; + } + address == end_address +} + +pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { + let mut address = start_address; + while address < end_address { + let sector = get_sector(address); + erase_sector(sector.index)?; + address += sector.size; + } Ok(()) } @@ -106,7 +104,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn blocking_wait_ready() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); 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 @@ use core::convert::TryInto; -use core::mem::size_of; use core::ptr::write_volatile; -use super::{FlashRegion, FLASH_SIZE}; +use atomic_polyfill::{fence, Ordering}; + +use super::{FlashRegion, FLASH_SIZE, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const WRITE_SIZE: usize = super::BANK1::WRITE_SIZE; const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; -pub(crate) const MAX_WRITE_SIZE: usize = WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = ERASE_SIZE; const SECOND_BANK_OFFSET: usize = 0x0010_0000; const fn is_dual_bank() -> bool { @@ -33,59 +31,60 @@ pub(crate) unsafe fn unlock() { } } -pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { - let bank = if !is_dual_bank() || (first_address - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32 { +pub(crate) unsafe fn begin_write() { + assert_eq!(0, WRITE_SIZE % 4); +} + +pub(crate) unsafe fn end_write() {} + +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + // We cannot have the write setup sequence in begin_write as it depends on the address + let bank = if !is_dual_bank() || (start_address - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32 { pac::FLASH.bank(0) } else { pac::FLASH.bank(1) }; - bank.cr().write(|w| { w.set_pg(true); w.set_psize(2); // 32 bits at once }); - cortex_m::asm::isb(); cortex_m::asm::dsb(); - core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); + fence(Ordering::SeqCst); - let ret = { - let mut ret: Result<(), Error> = Ok(()); - let mut address = first_address; - 'outer: for chunk in buf.chunks(WRITE_SIZE) { - let vals = chunk.chunks_exact(size_of::()); - assert!(vals.remainder().is_empty()); - for val in vals { - write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); - address += val.len() as u32; + let mut res = None; + let mut address = start_address; + for val in buf.chunks(4) { + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; - ret = blocking_wait_ready(bank); - bank.sr().modify(|w| { - if w.eop() { - w.set_eop(true); - } - }); - if ret.is_err() { - break 'outer; - } + res = Some(blocking_wait_ready(bank)); + bank.sr().modify(|w| { + if w.eop() { + w.set_eop(true); } + }); + if res.unwrap().is_err() { + break; } - ret - }; + } bank.cr().write(|w| w.set_pg(false)); cortex_m::asm::isb(); cortex_m::asm::dsb(); - core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); + fence(Ordering::SeqCst); - ret + res.unwrap() } -pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { - let start_sector = (from - super::FLASH_BASE as u32) / ERASE_SIZE as u32; - let end_sector = (to - super::FLASH_BASE as u32) / ERASE_SIZE as u32; +pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { + start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0 +} +pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { + let start_sector = (start_address - super::FLASH_BASE as u32) / ERASE_SIZE as u32; + let end_sector = (end_address - super::FLASH_BASE as u32) / ERASE_SIZE as u32; for sector in start_sector..end_sector { let bank = if sector >= 8 { 1 } else { 0 }; 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> { return ret; } } - Ok(()) } @@ -157,7 +155,7 @@ unsafe fn bank_clear_all_err(bank: pac::flash::Bank) { }); } -pub(crate) unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { +unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { loop { let sr = bank.sr().read(); 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 @@ -use core::convert::TryInto; use core::ptr::write_volatile; -use super::FlashRegion; +use atomic_polyfill::{fence, Ordering}; + +use super::{FlashRegion, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const WRITE_SIZE: usize = super::BANK1::WRITE_SIZE; const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; -pub(crate) const MAX_WRITE_SIZE: usize = WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = ERASE_SIZE; pub(crate) unsafe fn lock() { #[cfg(any(flash_wl, flash_wb, flash_l4))] @@ -39,35 +37,37 @@ pub(crate) unsafe fn unlock() { } } -pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { +pub(crate) unsafe fn begin_write() { + assert_eq!(0, WRITE_SIZE % 4); + #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().write(|w| w.set_pg(true)); - - let ret = { - let mut ret: Result<(), Error> = Ok(()); - let mut address = first_address; - for chunk in buf.chunks(WRITE_SIZE) { - for val in chunk.chunks(4) { - write_volatile(address as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); - address += val.len() as u32; - } - - ret = blocking_wait_ready(); - if ret.is_err() { - break; - } - } - ret - }; - - #[cfg(any(flash_wl, flash_wb, flash_l4))] - pac::FLASH.cr().write(|w| w.set_pg(false)); - - ret } -pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { - for page in (from_address..to_address).step_by(ERASE_SIZE) { +pub(crate) unsafe fn end_write() { + #[cfg(any(flash_wl, flash_wb, flash_l4))] + pac::FLASH.cr().write(|w| w.set_pg(false)); +} + +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + let mut address = start_address; + for val in buf.chunks(4) { + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; + + // prevents parallelism errors + fence(Ordering::SeqCst); + } + + blocking_wait_ready() +} + +pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { + start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0 +} + +pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { + for page in (start_address..end_address).step_by(ERASE_SIZE) { #[cfg(any(flash_l0, flash_l1))] { pac::FLASH.pecr().modify(|w| { @@ -155,7 +155,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn blocking_wait_ready() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); 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 @@ use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::mutex::{Mutex, MutexGuard}; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; pub use crate::_generated::flash_regions::*; -pub use crate::pac::{FLASH_BASE, FLASH_SIZE}; +pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; use crate::peripherals::FLASH; use crate::Peripheral; @@ -17,6 +19,8 @@ pub struct Flash<'d> { _inner: PeripheralRef<'d, FLASH>, } +static REGION_LOCK: Mutex = Mutex::new(()); + impl<'d> Flash<'d> { pub fn new(p: impl Peripheral

+ 'd) -> Self { into_ref!(p); @@ -33,7 +37,6 @@ impl<'d> Flash<'d> { } let first_address = FLASH_BASE as u32 + offset; - let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; bytes.copy_from_slice(flash_data); Ok(()) @@ -43,39 +46,56 @@ impl<'d> Flash<'d> { if offset as usize + buf.len() > FLASH_SIZE { return Err(Error::Size); } - if offset as usize % family::MAX_WRITE_SIZE != 0 || buf.len() as usize % family::MAX_WRITE_SIZE != 0 { + if offset as usize % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { return Err(Error::Unaligned); } - let first_address = FLASH_BASE as u32 + offset; - trace!("Writing {} bytes at 0x{:x}", buf.len(), first_address); + let start_address = FLASH_BASE as u32 + offset; + trace!("Writing {} bytes at 0x{:x}", buf.len(), start_address); + + // No need to take lock here as we only have one mut flash reference. unsafe { family::clear_all_err(); - family::unlock(); - let res = family::blocking_write(first_address, buf); + let res = Flash::blocking_write_all(start_address, buf); family::lock(); res } } + unsafe fn blocking_write_all(start_address: u32, buf: &[u8]) -> Result<(), Error> { + family::begin_write(); + let mut address = start_address; + for chunk in buf.chunks(WRITE_SIZE) { + let res = unsafe { family::blocking_write(address, chunk.try_into().unwrap()) }; + if res.is_err() { + family::end_write(); + return res; + } + address += WRITE_SIZE as u32; + } + + family::end_write(); + Ok(()) + } + pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { if to < from || to as usize > FLASH_SIZE { return Err(Error::Size); } - if (from as usize % family::MAX_ERASE_SIZE) != 0 || (to as usize % family::MAX_ERASE_SIZE) != 0 { + + let start_address = FLASH_BASE as u32 + from; + let end_address = FLASH_BASE as u32 + to; + if !family::is_eraseable_range(start_address, end_address) { return Err(Error::Unaligned); } - - let from_address = FLASH_BASE as u32 + from; - let to_address = FLASH_BASE as u32 + to; + trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); unsafe { family::clear_all_err(); - family::unlock(); - let res = family::blocking_erase(from_address, to_address); + let res = family::blocking_erase(start_address, end_address); family::lock(); res } @@ -101,7 +121,6 @@ pub trait FlashRegion { } let first_address = Self::BASE as u32 + offset; - let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; bytes.copy_from_slice(flash_data); Ok(()) @@ -115,17 +134,19 @@ pub trait FlashRegion { return Err(Error::Unaligned); } - let first_address = Self::BASE as u32 + offset; - trace!("Writing {} bytes from 0x{:x}", buf.len(), first_address); + let start_address = Self::BASE as u32 + offset; + trace!("Writing {} bytes from 0x{:x}", buf.len(), start_address); - critical_section::with(|_| unsafe { + // Protect agains simultaneous write/erase to multiple regions. + let _guard = take_lock_spin(); + + unsafe { family::clear_all_err(); - family::unlock(); - let res = family::blocking_write(first_address, buf); + let res = Flash::blocking_write_all(start_address, buf); family::lock(); res - }) + } } fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { @@ -136,18 +157,28 @@ pub trait FlashRegion { return Err(Error::Unaligned); } - let from_address = Self::BASE as u32 + from; - let to_address = Self::BASE as u32 + to; - trace!("Erasing from 0x{:x} to 0x{:x}", from_address, to_address); + let start_address = Self::BASE as u32 + from; + let end_address = Self::BASE as u32 + to; + trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); - critical_section::with(|_| unsafe { + // Protect agains simultaneous write/erase to multiple regions. + let _guard = take_lock_spin(); + + unsafe { family::clear_all_err(); - family::unlock(); - let res = family::blocking_erase(from_address, to_address); + let res = family::blocking_erase(start_address, end_address); family::lock(); res - }) + } + } +} + +fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { + loop { + if let Ok(guard) = REGION_LOCK.try_lock() { + return guard; + } } }