diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 21adb5dd..440de85e 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -47,6 +47,7 @@ embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true} embedded-storage = "0.3.0" +embedded-storage-async = { version = "0.4.0", optional = true } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } @@ -68,6 +69,7 @@ cfg-if = "1.0.0" embedded-io = { version = "0.4.0", features = ["async"], optional = true } chrono = { version = "^0.4", default-features = false, optional = true} bit_field = "0.10.2" +paste = "1.0.12" [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } @@ -98,7 +100,7 @@ time-driver-tim12 = ["_time-driver"] time-driver-tim15 = ["_time-driver"] # Enable nightly-only features -nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"] +nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"] # Reexport stm32-metapac at `embassy_stm32::pac`. # This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs new file mode 100644 index 00000000..44e23d9c --- /dev/null +++ b/embassy-stm32/src/flash/asynch.rs @@ -0,0 +1,129 @@ +use atomic_polyfill::{fence, Ordering}; +use embassy_hal_common::drop::OnDrop; + +use super::{ + ensure_sector_aligned, family, get_sector, Error, Flash, FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, + REGION_ACCESS, WRITE_SIZE, +}; + +impl<'d> Flash<'d> { + pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + unsafe { write_chunked(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes).await } + } + + pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + unsafe { erase_sectored(FLASH_BASE as u32, from, to).await } + } +} + +impl embedded_storage_async::nor_flash::NorFlash for Flash<'_> { + const WRITE_SIZE: usize = WRITE_SIZE; + const ERASE_SIZE: usize = MAX_ERASE_SIZE; + + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes).await + } + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to).await + } +} + +pub(super) async unsafe fn write_chunked(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Result<(), Error> { + if offset + bytes.len() as u32 > size { + return Err(Error::Size); + } + if offset % WRITE_SIZE as u32 != 0 || bytes.len() % WRITE_SIZE != 0 { + return Err(Error::Unaligned); + } + + let mut address = base + offset; + trace!("Writing {} bytes at 0x{:x}", bytes.len(), address); + + for chunk in bytes.chunks(WRITE_SIZE) { + family::clear_all_err(); + fence(Ordering::SeqCst); + family::unlock(); + fence(Ordering::SeqCst); + family::enable_write(); + fence(Ordering::SeqCst); + + let _on_drop = OnDrop::new(|| { + family::disable_write(); + fence(Ordering::SeqCst); + family::lock(); + }); + + family::write(address, chunk.try_into().unwrap()).await?; + address += WRITE_SIZE as u32; + } + Ok(()) +} + +pub(super) async unsafe fn erase_sectored(base: u32, from: u32, to: u32) -> Result<(), Error> { + let start_address = base + from; + let end_address = base + to; + let regions = family::get_flash_regions(); + + ensure_sector_aligned(start_address, end_address, regions)?; + + trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); + + let mut address = start_address; + while address < end_address { + let sector = get_sector(address, regions); + trace!("Erasing sector: {:?}", sector); + + family::clear_all_err(); + fence(Ordering::SeqCst); + family::unlock(); + fence(Ordering::SeqCst); + + let _on_drop = OnDrop::new(|| family::lock()); + + family::erase_sector(§or).await?; + address += sector.size; + } + Ok(()) +} + +foreach_flash_region! { + ($type_name:ident, $write_size:literal, $erase_size:literal) => { + impl crate::_generated::flash_regions::$type_name<'_> { + pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + let _guard = REGION_ACCESS.lock().await; + unsafe { write_chunked(self.0.base, self.0.size, offset, bytes).await } + } + + pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + let _guard = REGION_ACCESS.lock().await; + unsafe { erase_sectored(self.0.base, from, to).await } + } + } + + impl embedded_storage_async::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_> { + const READ_SIZE: usize = READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes) + } + + fn capacity(&self) -> usize { + self.0.size as usize + } + } + + impl embedded_storage_async::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name<'_> { + const WRITE_SIZE: usize = $write_size; + const ERASE_SIZE: usize = $erase_size; + + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes).await + } + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to).await + } + } + }; +} diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 432c8a43..990104a3 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -1,44 +1,55 @@ use atomic_polyfill::{fence, Ordering}; +use embassy_cortex_m::interrupt::InterruptExt; +use embassy_futures::block_on; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::mutex::Mutex; +use stm32_metapac::FLASH_BASE; -use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, WRITE_SIZE}; -use crate::flash::FlashBank; +use super::{ + ensure_sector_aligned, family, get_sector, Error, FlashLayout, FlashRegion, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, + WRITE_SIZE, +}; +use crate::peripherals::FLASH; use crate::Peripheral; pub struct Flash<'d> { - inner: PeripheralRef<'d, crate::peripherals::FLASH>, + pub(crate) inner: PeripheralRef<'d, FLASH>, } +pub(crate) static REGION_ACCESS: Mutex = Mutex::new(()); + impl<'d> Flash<'d> { - pub fn new(p: impl Peripheral

+ 'd) -> Self { - into_ref!(p); + pub fn new(p: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { + into_ref!(p, irq); + + irq.set_handler(family::on_interrupt); + irq.unpend(); + irq.enable(); + Self { inner: p } } pub fn into_regions(self) -> FlashLayout<'d> { family::set_default_layout(); - FlashLayout::new(self.release()) + FlashLayout::new(self.inner) } - pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - blocking_read(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) + pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + read_blocking(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) } - pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { blocking_write_chunked(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) } + pub fn write_blocking(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + unsafe { write_chunked_blocking(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) } } - pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { blocking_erase_sectored(FLASH_BASE as u32, from, to) } - } - - pub(crate) fn release(self) -> PeripheralRef<'d, crate::peripherals::FLASH> { - unsafe { self.inner.clone_unchecked() } + pub fn erase_blocking(&mut self, from: u32, to: u32) -> Result<(), Error> { + unsafe { erase_sectored_blocking(FLASH_BASE as u32, from, to) } } } -pub(super) fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { +pub(super) fn read_blocking(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { if offset + bytes.len() as u32 > size { return Err(Error::Size); } @@ -49,7 +60,7 @@ pub(super) fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) Ok(()) } -pub(super) unsafe fn blocking_write_chunked(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Result<(), Error> { +pub(super) unsafe fn write_chunked_blocking(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Result<(), Error> { if offset + bytes.len() as u32 > size { return Err(Error::Size); } @@ -61,44 +72,31 @@ pub(super) unsafe fn blocking_write_chunked(base: u32, size: u32, offset: u32, b trace!("Writing {} bytes at 0x{:x}", bytes.len(), address); for chunk in bytes.chunks(WRITE_SIZE) { - critical_section::with(|_| { - family::clear_all_err(); - fence(Ordering::SeqCst); - family::unlock(); - fence(Ordering::SeqCst); - family::begin_write(); - fence(Ordering::SeqCst); + family::clear_all_err(); + fence(Ordering::SeqCst); + family::unlock(); + fence(Ordering::SeqCst); + family::enable_blocking_write(); + fence(Ordering::SeqCst); - let _on_drop = OnDrop::new(|| { - family::end_write(); - fence(Ordering::SeqCst); - family::lock(); - }); + let _on_drop = OnDrop::new(|| { + family::disable_blocking_write(); + fence(Ordering::SeqCst); + family::lock(); + }); - family::blocking_write(address, chunk.try_into().unwrap()) - })?; + family::write_blocking(address, chunk.try_into().unwrap())?; address += WRITE_SIZE as u32; } Ok(()) } -pub(super) unsafe fn blocking_erase_sectored(base: u32, from: u32, to: u32) -> Result<(), Error> { +pub(super) unsafe fn erase_sectored_blocking(base: u32, from: u32, to: u32) -> Result<(), Error> { let start_address = base + from; let end_address = base + to; let regions = family::get_flash_regions(); - // Test if the address range is aligned at sector base addresses - let mut address = start_address; - while address < end_address { - let sector = get_sector(address, regions); - if sector.start != address { - return Err(Error::Unaligned); - } - address += sector.size; - } - if address != end_address { - return Err(Error::Unaligned); - } + ensure_sector_aligned(start_address, end_address, regions)?; trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); @@ -107,71 +105,28 @@ pub(super) unsafe fn blocking_erase_sectored(base: u32, from: u32, to: u32) -> R let sector = get_sector(address, regions); trace!("Erasing sector: {:?}", sector); - critical_section::with(|_| { - family::clear_all_err(); - fence(Ordering::SeqCst); - family::unlock(); - fence(Ordering::SeqCst); + family::clear_all_err(); + fence(Ordering::SeqCst); + family::unlock(); + fence(Ordering::SeqCst); - let _on_drop = OnDrop::new(|| { - family::lock(); - }); + let _on_drop = OnDrop::new(|| family::lock()); - family::blocking_erase_sector(§or) - })?; + family::erase_sector_blocking(§or)?; address += sector.size; } Ok(()) } -pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector { - let mut current_bank = FlashBank::Bank1; - let mut bank_offset = 0; - for region in regions { - if region.bank != current_bank { - current_bank = region.bank; - bank_offset = 0; - } - - if address < region.end() { - let index_in_region = (address - region.base) / region.erase_size; - return FlashSector { - bank: region.bank, - index_in_bank: bank_offset + index_in_region as u8, - start: region.base + index_in_region * region.erase_size, - size: region.erase_size, - }; - } - - bank_offset += region.sectors(); - } - - panic!("Flash sector not found"); -} - -impl FlashRegion { - pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - blocking_read(self.base, self.size, offset, bytes) - } - - pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { blocking_write_chunked(self.base, self.size, offset, bytes) } - } - - pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { blocking_erase_sectored(self.base, from, to) } - } -} - impl embedded_storage::nor_flash::ErrorType for Flash<'_> { type Error = Error; } impl embedded_storage::nor_flash::ReadNorFlash for Flash<'_> { - const READ_SIZE: usize = 1; + const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(offset, bytes) + self.read(offset, bytes) } fn capacity(&self) -> usize { @@ -184,27 +139,112 @@ impl embedded_storage::nor_flash::NorFlash for Flash<'_> { const ERASE_SIZE: usize = MAX_ERASE_SIZE; fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(offset, bytes) + self.write_blocking(offset, bytes) } fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.blocking_erase(from, to) + self.erase_blocking(from, to) + } +} + +#[cfg(feature = "nightly")] +impl embedded_storage_async::nor_flash::ReadNorFlash for Flash<'_> { + const READ_SIZE: usize = READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes) + } + + fn capacity(&self) -> usize { + FLASH_SIZE + } +} + +pub struct BlockingFlashRegion<'d, const WRITE_SIZE: u32, const ERASE_SIZE: u32>( + &'static FlashRegion, + PeripheralRef<'d, FLASH>, +); + +impl BlockingFlashRegion<'_, WRITE_SIZE, ERASE_SIZE> { + pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + read_blocking(self.0.base, self.0.size, offset, bytes) + } + + pub fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + let _guard = block_on(REGION_ACCESS.lock()); + unsafe { write_chunked_blocking(self.0.base, self.0.size, offset, bytes) } + } + + pub fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + let _guard = block_on(REGION_ACCESS.lock()); + unsafe { erase_sectored_blocking(self.0.base, from, to) } + } +} + +impl embedded_storage::nor_flash::ErrorType + for BlockingFlashRegion<'_, WRITE_SIZE, ERASE_SIZE> +{ + type Error = Error; +} + +impl embedded_storage::nor_flash::ReadNorFlash + for BlockingFlashRegion<'_, WRITE_SIZE, ERASE_SIZE> +{ + const READ_SIZE: usize = READ_SIZE; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes) + } + + fn capacity(&self) -> usize { + self.0.size as usize + } +} + +impl embedded_storage::nor_flash::NorFlash + for BlockingFlashRegion<'_, WRITE_SIZE, ERASE_SIZE> +{ + const WRITE_SIZE: usize = WRITE_SIZE as usize; + const ERASE_SIZE: usize = ERASE_SIZE as usize; + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes) + } + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to) } } foreach_flash_region! { ($type_name:ident, $write_size:literal, $erase_size:literal) => { - impl crate::_generated::flash_regions::$type_name<'_> { - pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - blocking_read(self.0.base, self.0.size, offset, bytes) + paste::paste! { + pub type []<'d> = BlockingFlashRegion<'d, $write_size, $erase_size>; + } + + impl<'d> crate::_generated::flash_regions::$type_name<'d> { + /// Make this flash region work in a blocking context. + /// + /// SAFETY + /// + /// This function is unsafe as incorect usage of parallel blocking operations + /// on multiple regions may cause a deadlock because each region requires mutual access to the flash. + pub unsafe fn into_blocking(self) -> BlockingFlashRegion<'d, $write_size, $erase_size> { + BlockingFlashRegion(self.0, self.1) } - pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { blocking_write_chunked(self.0.base, self.0.size, offset, bytes) } + pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + read_blocking(self.0.base, self.0.size, offset, bytes) } - pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { blocking_erase_sectored(self.0.base, from, to) } + pub fn try_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + let _guard = REGION_ACCESS.try_lock().map_err(|_| Error::TryLockError)?; + unsafe { write_chunked_blocking(self.0.base, self.0.size, offset, bytes) } + } + + pub fn try_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + let _guard = REGION_ACCESS.try_lock().map_err(|_| Error::TryLockError)?; + unsafe { erase_sectored_blocking(self.0.base, from, to) } } } @@ -213,28 +253,15 @@ foreach_flash_region! { } impl embedded_storage::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_> { - const READ_SIZE: usize = 1; + const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(offset, bytes) + self.read(offset, bytes) } fn capacity(&self) -> usize { self.0.size as usize } } - - impl embedded_storage::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name<'_> { - const WRITE_SIZE: usize = $write_size; - const ERASE_SIZE: usize = $erase_size; - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(offset, bytes) - } - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.blocking_erase(from, to) - } - } }; } diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index c6441ef1..ecf3a698 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs @@ -13,6 +13,10 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + unimplemented!(); +} + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -22,17 +26,17 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB)); } -pub(crate) unsafe fn begin_write() { +pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 2); pac::FLASH.cr().write(|w| w.set_pg(true)); } -pub(crate) unsafe fn end_write() { +pub(crate) unsafe fn disable_blocking_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> { +pub(crate) unsafe fn write_blocking(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())); @@ -42,10 +46,10 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) fence(Ordering::SeqCst); } - blocking_wait_ready() + wait_ready_blocking() } -pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { pac::FLASH.cr().modify(|w| { w.set_per(true); }); @@ -56,7 +60,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E w.set_strt(true); }); - let mut ret: Result<(), Error> = blocking_wait_ready(); + let mut ret: Result<(), Error> = wait_ready_blocking(); if !pac::FLASH.sr().read().eop() { trace!("FLASH: EOP not set"); @@ -88,7 +92,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn wait_ready_blocking() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 4c817220..fd778f2b 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -13,6 +13,10 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + unimplemented!(); +} + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -22,17 +26,17 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB)); } -pub(crate) unsafe fn begin_write() { +pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 2); pac::FLASH.cr().write(|w| w.set_pg(true)); } -pub(crate) unsafe fn end_write() { +pub(crate) unsafe fn disable_blocking_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> { +pub(crate) unsafe fn write_blocking(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())); @@ -42,10 +46,10 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) fence(Ordering::SeqCst); } - blocking_wait_ready() + wait_ready_blocking() } -pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { pac::FLASH.cr().modify(|w| { w.set_per(true); }); @@ -56,7 +60,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E w.set_strt(true); }); - let mut ret: Result<(), Error> = blocking_wait_ready(); + let mut ret: Result<(), Error> = wait_ready_blocking(); if !pac::FLASH.sr().read().eop() { trace!("FLASH: EOP not set"); @@ -88,7 +92,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn wait_ready_blocking() -> 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 7f1c5f67..3c8f81eb 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -2,6 +2,8 @@ use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; +use embassy_sync::waitqueue::AtomicWaker; + use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; @@ -13,8 +15,8 @@ mod alt_regions { use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION}; use crate::flash::{ - blocking_erase_sectored, blocking_read, blocking_write_chunked, Bank1Region1, Bank1Region2, Error, Flash, - FlashBank, FlashRegion, + asynch, common, Bank1Region1, Bank1Region2, BlockingFlashRegion, Error, Flash, FlashBank, FlashRegion, + READ_SIZE, REGION_ACCESS, }; use crate::peripherals::FLASH; @@ -53,6 +55,15 @@ mod alt_regions { pub struct AltBank2Region2<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); pub struct AltBank2Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); + pub type BlockingAltBank1Region3<'d> = + BlockingFlashRegion<'d, { ALT_BANK1_REGION3.write_size }, { ALT_BANK1_REGION3.erase_size }>; + pub type BlockingAltBank2Region1<'d> = + BlockingFlashRegion<'d, { ALT_BANK2_REGION1.write_size }, { ALT_BANK2_REGION1.erase_size }>; + pub type BlockingAltBank2Region2<'d> = + BlockingFlashRegion<'d, { ALT_BANK2_REGION2.write_size }, { ALT_BANK2_REGION2.erase_size }>; + pub type BlockingAltBank2Region3<'d> = + BlockingFlashRegion<'d, { ALT_BANK2_REGION3.write_size }, { ALT_BANK2_REGION3.erase_size }>; + pub struct AltFlashLayout<'d> { pub bank1_region1: Bank1Region1<'d>, pub bank1_region2: Bank1Region2<'d>, @@ -69,7 +80,7 @@ mod alt_regions { // SAFETY: We never expose the cloned peripheral references, and their instance is not public. // Also, all blocking flash region operations are protected with a cs. - let p = self.release(); + let p = self.inner; AltFlashLayout { bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }), bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }), @@ -85,16 +96,30 @@ mod alt_regions { macro_rules! foreach_altflash_region { ($type_name:ident, $region:ident) => { impl $type_name<'_> { - pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - blocking_read(self.0.base, self.0.size, offset, bytes) + pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + common::read_blocking(self.0.base, self.0.size, offset, bytes) } - pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { blocking_write_chunked(self.0.base, self.0.size, offset, bytes) } + #[cfg(all(feature = "nightly"))] + pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + let _guard = REGION_ACCESS.lock().await; + unsafe { asynch::write_chunked(self.0.base, self.0.size, offset, bytes).await } } - pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { blocking_erase_sectored(self.0.base, from, to) } + #[cfg(all(feature = "nightly"))] + pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + let _guard = REGION_ACCESS.lock().await; + unsafe { asynch::erase_sectored(self.0.base, from, to).await } + } + + pub fn try_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + let _guard = REGION_ACCESS.try_lock().map_err(|_| Error::TryLockError)?; + unsafe { common::write_chunked_blocking(self.0.base, self.0.size, offset, bytes) } + } + + pub fn try_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + let _guard = REGION_ACCESS.try_lock().map_err(|_| Error::TryLockError)?; + unsafe { common::erase_sectored_blocking(self.0.base, from, to) } } } @@ -103,10 +128,10 @@ mod alt_regions { } impl embedded_storage::nor_flash::ReadNorFlash for $type_name<'_> { - const READ_SIZE: usize = 1; + const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(offset, bytes) + self.read(offset, bytes) } fn capacity(&self) -> usize { @@ -114,16 +139,28 @@ mod alt_regions { } } - impl embedded_storage::nor_flash::NorFlash for $type_name<'_> { + impl embedded_storage_async::nor_flash::ReadNorFlash for $type_name<'_> { + const READ_SIZE: usize = READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes) + } + + fn capacity(&self) -> usize { + self.0.size as usize + } + } + + impl embedded_storage_async::nor_flash::NorFlash for $type_name<'_> { const WRITE_SIZE: usize = $region.write_size as usize; const ERASE_SIZE: usize = $region.erase_size as usize; - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(offset, bytes) + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes).await } - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.blocking_erase(from, to) + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to).await } } }; @@ -138,6 +175,9 @@ mod alt_regions { #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] pub use alt_regions::*; +#[cfg(feature = "nightly")] +static WAKER: AtomicWaker = AtomicWaker::new(); + #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] pub fn set_default_layout() { unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(false)) }; @@ -160,6 +200,16 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + // Clear IRQ flags + pac::FLASH.sr().write(|w| { + w.set_operr(true); + w.set_eop(true); + }); + + WAKER.wake(); +} + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -169,7 +219,28 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); } -pub(crate) unsafe fn begin_write() { +#[cfg(feature = "nightly")] +pub(crate) unsafe fn enable_write() { + assert_eq!(0, WRITE_SIZE % 4); + + pac::FLASH.cr().write(|w| { + w.set_pg(true); + w.set_psize(pac::flash::vals::Psize::PSIZE32); + w.set_eopie(true); + w.set_errie(true); + }); +} + +#[cfg(feature = "nightly")] +pub(crate) unsafe fn disable_write() { + pac::FLASH.cr().write(|w| { + w.set_pg(false); + w.set_eopie(false); + w.set_errie(false); + }); +} + +pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 4); pac::FLASH.cr().write(|w| { @@ -178,11 +249,22 @@ pub(crate) unsafe fn begin_write() { }); } -pub(crate) unsafe fn end_write() { +pub(crate) unsafe fn disable_blocking_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> { +#[cfg(feature = "nightly")] +pub(crate) async unsafe fn write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + write_start(start_address, buf); + wait_ready().await +} + +pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + write_start(start_address, buf); + wait_ready_blocking() +} + +unsafe fn write_start(start_address: u32, buf: &[u8; WRITE_SIZE]) { 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())); @@ -191,11 +273,32 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) // prevents parallelism errors fence(Ordering::SeqCst); } - - blocking_wait_ready() } -pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { +pub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Error> { + let snb = ((sector.bank as u8) << 4) + sector.index_in_bank; + + pac::FLASH.cr().modify(|w| { + w.set_ser(true); + w.set_snb(snb); + w.set_eopie(true); + w.set_errie(true); + }); + + pac::FLASH.cr().modify(|w| { + w.set_strt(true); + }); + + let ret: Result<(), Error> = wait_ready().await; + pac::FLASH.cr().modify(|w| { + w.set_eopie(false); + w.set_errie(false); + }); + clear_all_err(); + ret +} + +pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { let snb = ((sector.bank as u8) << 4) + sector.index_in_bank; pac::FLASH.cr().modify(|w| { @@ -207,10 +310,8 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E w.set_strt(true); }); - let ret: Result<(), Error> = blocking_wait_ready(); - + let ret: Result<(), Error> = wait_ready_blocking(); clear_all_err(); - ret } @@ -220,11 +321,39 @@ pub(crate) unsafe fn clear_all_err() { w.set_pgperr(true); w.set_pgaerr(true); w.set_wrperr(true); - w.set_eop(true); }); } -unsafe fn blocking_wait_ready() -> Result<(), Error> { +#[cfg(feature = "nightly")] +pub(crate) async unsafe fn wait_ready() -> Result<(), Error> { + use core::task::Poll; + + use futures::future::poll_fn; + + poll_fn(|cx| { + WAKER.register(cx.waker()); + + let sr = pac::FLASH.sr().read(); + if !sr.bsy() { + Poll::Ready(if sr.pgserr() { + Err(Error::Seq) + } else if sr.pgperr() { + Err(Error::Parallelism) + } else if sr.pgaerr() { + Err(Error::Unaligned) + } else if sr.wrperr() { + Err(Error::Protected) + } else { + Ok(()) + }) + } else { + return Poll::Pending; + } + }) + .await +} + +unsafe fn wait_ready_blocking() -> 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 ac2834a8..a0593b14 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -12,6 +12,10 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + unimplemented!(); +} + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -21,7 +25,7 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); } -pub(crate) unsafe fn begin_write() { +pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 4); pac::FLASH.cr().write(|w| { @@ -30,11 +34,11 @@ pub(crate) unsafe fn begin_write() { }); } -pub(crate) unsafe fn end_write() { +pub(crate) unsafe fn disable_blocking_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> { +pub(crate) unsafe fn write_blocking(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())); @@ -44,10 +48,10 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) fence(Ordering::SeqCst); } - blocking_wait_ready() + wait_ready_blocking() } -pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { pac::FLASH.cr().modify(|w| { w.set_ser(true); w.set_snb(sector.index_in_bank) @@ -57,12 +61,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E w.set_strt(true); }); - let ret: Result<(), Error> = blocking_wait_ready(); - + let ret: Result<(), Error> = wait_ready_blocking(); pac::FLASH.cr().modify(|w| w.set_ser(false)); - clear_all_err(); - ret } @@ -86,7 +87,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn wait_ready_blocking() -> 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 1b163106..865f1328 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -17,6 +17,10 @@ pub fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + unimplemented!(); +} + pub(crate) unsafe fn lock() { pac::FLASH.bank(0).cr().modify(|w| w.set_lock(true)); if is_dual_bank() { @@ -33,13 +37,13 @@ pub(crate) unsafe fn unlock() { } } -pub(crate) unsafe fn begin_write() { +pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 4); } -pub(crate) unsafe fn end_write() {} +pub(crate) unsafe fn disable_blocking_write() {} -pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn write_blocking(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 start_address < BANK1_REGION.end() { pac::FLASH.bank(0) @@ -60,7 +64,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); address += val.len() as u32; - res = Some(blocking_wait_ready(bank)); + res = Some(wait_ready_blocking(bank)); bank.sr().modify(|w| { if w.eop() { w.set_eop(true); @@ -80,7 +84,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) res.unwrap() } -pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { let bank = pac::FLASH.bank(sector.bank as usize); bank.cr().modify(|w| { w.set_ser(true); @@ -91,12 +95,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E w.set_start(true); }); - let ret: Result<(), Error> = blocking_wait_ready(bank); - + let ret: Result<(), Error> = wait_ready_blocking(bank); bank.cr().modify(|w| w.set_ser(false)); - bank_clear_all_err(bank); - ret } @@ -141,7 +142,7 @@ unsafe fn bank_clear_all_err(bank: pac::flash::Bank) { }); } -unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { +unsafe fn wait_ready_blocking(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 c94f6190..f8a0dac4 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -12,6 +12,10 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + unimplemented!(); +} + pub(crate) unsafe fn lock() { #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().modify(|w| w.set_lock(true)); @@ -41,19 +45,19 @@ pub(crate) unsafe fn unlock() { } } -pub(crate) unsafe fn begin_write() { +pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 4); #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().write(|w| w.set_pg(true)); } -pub(crate) unsafe fn end_write() { +pub(crate) unsafe fn disable_blocking_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> { +pub(crate) unsafe fn write_blocking(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())); @@ -63,10 +67,10 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) fence(Ordering::SeqCst); } - blocking_wait_ready() + wait_ready_blocking() } -pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { #[cfg(any(flash_l0, flash_l1))] { pac::FLASH.pecr().modify(|w| { @@ -96,7 +100,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E }); } - let ret: Result<(), Error> = blocking_wait_ready(); + let ret: Result<(), Error> = wait_ready_blocking(); #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().modify(|w| w.set_per(false)); @@ -108,7 +112,6 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E }); clear_all_err(); - ret } @@ -150,7 +153,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn wait_ready_blocking() -> 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 b93270ae..e781f1b8 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -1,5 +1,7 @@ use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; +#[cfg(all(feature = "nightly", flash_f4))] +pub mod asynch; #[cfg(flash)] mod common; @@ -10,6 +12,8 @@ pub use crate::_generated::flash_regions::*; pub use crate::_generated::MAX_ERASE_SIZE; pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; +pub const READ_SIZE: usize = 1; + #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct FlashRegion { @@ -76,6 +80,7 @@ pub enum Error { Protected, Unaligned, Parallelism, + TryLockError, } impl NorFlashError for Error { @@ -87,3 +92,47 @@ impl NorFlashError for Error { } } } + +pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector { + let mut current_bank = FlashBank::Bank1; + let mut bank_offset = 0; + for region in regions { + if region.bank != current_bank { + current_bank = region.bank; + bank_offset = 0; + } + + if address < region.end() { + let index_in_region = (address - region.base) / region.erase_size; + return FlashSector { + bank: region.bank, + index_in_bank: bank_offset + index_in_region as u8, + start: region.base + index_in_region * region.erase_size, + size: region.erase_size, + }; + } + + bank_offset += region.sectors(); + } + + panic!("Flash sector not found"); +} + +pub(crate) fn ensure_sector_aligned( + start_address: u32, + end_address: u32, + regions: &[&FlashRegion], +) -> Result<(), Error> { + let mut address = start_address; + while address < end_address { + let sector = get_sector(address, regions); + if sector.start != address { + return Err(Error::Unaligned); + } + address += sector.size; + } + if address != end_address { + return Err(Error::Unaligned); + } + Ok(()) +} diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs index 55603465..e21b0b24 100644 --- a/embassy-stm32/src/flash/other.rs +++ b/embassy-stm32/src/flash/other.rs @@ -8,22 +8,26 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + unimplemented!(); +} + pub(crate) unsafe fn lock() { unimplemented!(); } pub(crate) unsafe fn unlock() { unimplemented!(); } -pub(crate) unsafe fn begin_write() { +pub(crate) unsafe fn enable_blocking_write() { unimplemented!(); } -pub(crate) unsafe fn end_write() { +pub(crate) unsafe fn disable_blocking_write() { unimplemented!(); } -pub(crate) unsafe fn blocking_write(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn write_blocking(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { unimplemented!(); } -pub(crate) unsafe fn blocking_erase_sector(_sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn erase_sector_blocking(_sector: &FlashSector) -> Result<(), Error> { unimplemented!(); } pub(crate) unsafe fn clear_all_err() {