diff --git a/ci.sh b/ci.sh index d86c9352..b9dddad3 100755 --- a/ci.sh +++ b/ci.sh @@ -49,6 +49,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f413vh,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits,embedded-sdmmc \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f730i8,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits \ diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 14ec3d70..504caacb 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -60,7 +60,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = { version = "2", features = ["rt"] } +stm32-metapac = "3" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -69,12 +69,16 @@ seq-macro = "0.3.0" cfg-if = "1.0.0" embedded-io = { version = "0.4.0", features = ["async"], optional = true } +[dev-dependencies] +critical-section = { version = "1.1", features = ["std"] } + [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "2", default-features = false, features = ["metadata"]} +stm32-metapac = { version = "3", default-features = false, features = ["metadata"]} [features] +default = ["stm32-metapac/rt"] defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] memory-x = ["stm32-metapac/memory-x"] subghz = [] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 481fec67..61aceed9 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -3,9 +3,9 @@ use std::fmt::Write as _; use std::path::PathBuf; use std::{env, fs}; -use proc_macro2::TokenStream; +use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; -use stm32_metapac::metadata::METADATA; +use stm32_metapac::metadata::{MemoryRegionKind, METADATA}; fn main() { let chip_name = match env::vars() @@ -106,6 +106,94 @@ fn main() { } }); + // ======== + // Generate FLASH regions + let mut flash_regions = TokenStream::new(); + let flash_memory_regions: Vec<_> = METADATA + .memory + .iter() + .filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some()) + .collect(); + for region in flash_memory_regions.iter() { + let region_name = format_ident!("{}", get_flash_region_name(region.name)); + let bank_variant = format_ident!( + "{}", + if region.name.starts_with("BANK_1") { + "Bank1" + } else if region.name.starts_with("BANK_2") { + "Bank2" + } else if region.name == "OTP" { + "Otp" + } else { + continue; + } + ); + let base = region.address; + let size = region.size; + let settings = region.settings.as_ref().unwrap(); + let erase_size = settings.erase_size; + let write_size = settings.write_size; + let erase_value = settings.erase_value; + + flash_regions.extend(quote! { + pub const #region_name: crate::flash::FlashRegion = crate::flash::FlashRegion { + bank: crate::flash::FlashBank::#bank_variant, + base: #base, + size: #size, + erase_size: #erase_size, + write_size: #write_size, + erase_value: #erase_value, + }; + }); + + let region_type = format_ident!("{}", get_flash_region_type_name(region.name)); + flash_regions.extend(quote! { + #[cfg(flash)] + pub struct #region_type<'d>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>,); + }); + } + + let (fields, (inits, region_names)): (Vec, (Vec, Vec)) = flash_memory_regions + .iter() + .map(|f| { + let region_name = get_flash_region_name(f.name); + let field_name = format_ident!("{}", region_name.to_lowercase()); + let field_type = format_ident!("{}", get_flash_region_type_name(f.name)); + let field = quote! { + pub #field_name: #field_type<'d> + }; + let region_name = format_ident!("{}", region_name); + let init = quote! { + #field_name: #field_type(&#region_name, unsafe { p.clone_unchecked()}) + }; + + (field, (init, region_name)) + }) + .unzip(); + + let regions_len = flash_memory_regions.len(); + flash_regions.extend(quote! { + #[cfg(flash)] + pub struct FlashLayout<'d> { + #(#fields),* + } + + #[cfg(flash)] + impl<'d> FlashLayout<'d> { + pub(crate) fn new(mut p: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { + Self { + #(#inits),* + } + } + } + + pub const FLASH_REGIONS: [&crate::flash::FlashRegion; #regions_len] = [ + #(&#region_names),* + ]; + }); + + g.extend(quote! { pub mod flash_regions { #flash_regions } }); + // ======== // Generate DMA IRQs. @@ -578,11 +666,25 @@ fn main() { // ======== // Write foreach_foo! macrotables + let mut flash_regions_table: Vec> = Vec::new(); let mut interrupts_table: Vec> = Vec::new(); let mut peripherals_table: Vec> = Vec::new(); let mut pins_table: Vec> = Vec::new(); let mut dma_channels_table: Vec> = Vec::new(); + for m in METADATA + .memory + .iter() + .filter(|m| m.kind == MemoryRegionKind::Flash && m.settings.is_some()) + { + let settings = m.settings.as_ref().unwrap(); + let mut row = Vec::new(); + row.push(get_flash_region_type_name(m.name)); + row.push(settings.write_size.to_string()); + row.push(settings.erase_size.to_string()); + flash_regions_table.push(row); + } + let gpio_base = METADATA.peripherals.iter().find(|p| p.name == "GPIOA").unwrap().address as u32; let gpio_stride = 0x400; @@ -679,6 +781,7 @@ fn main() { let mut m = String::new(); + make_table(&mut m, "foreach_flash_region", &flash_regions_table); make_table(&mut m, "foreach_interrupt", &interrupts_table); make_table(&mut m, "foreach_peripheral", &peripherals_table); make_table(&mut m, "foreach_pin", &pins_table); @@ -831,3 +934,19 @@ macro_rules! {} {{ ) .unwrap(); } + +fn get_flash_region_name(name: &str) -> String { + let name = name.replace("BANK_", "BANK").replace("REGION_", "REGION"); + if name.contains("REGION") { + name + } else { + name + "_REGION" + } +} + +fn get_flash_region_type_name(name: &str) -> String { + get_flash_region_name(name) + .replace("BANK", "Bank") + .replace("REGION", "Region") + .replace("_", "") +} diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs new file mode 100644 index 00000000..8235d6f0 --- /dev/null +++ b/embassy-stm32/src/flash/common.rs @@ -0,0 +1,211 @@ +use atomic_polyfill::{fence, Ordering}; +use embassy_hal_common::drop::OnDrop; +use embassy_hal_common::{into_ref, PeripheralRef}; + +use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; +use crate::flash::FlashBank; +use crate::Peripheral; + +pub struct Flash<'d> { + inner: PeripheralRef<'d, crate::peripherals::FLASH>, +} + +impl<'d> Flash<'d> { + pub fn new(p: impl Peripheral

+ 'd) -> Self { + into_ref!(p); + Self { inner: p } + } + + pub fn into_regions(self) -> FlashLayout<'d> { + FlashLayout::new(self.release()) + } + + 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 blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + unsafe { blocking_write(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(FLASH_BASE as u32, from, to) } + } + + pub(crate) fn release(self) -> PeripheralRef<'d, crate::peripherals::FLASH> { + let mut flash = self; + unsafe { flash.inner.clone_unchecked() } + } +} + +fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + if offset + bytes.len() as u32 > size { + return Err(Error::Size); + } + + let start_address = base + offset; + let flash_data = unsafe { core::slice::from_raw_parts(start_address as *const u8, bytes.len()) }; + bytes.copy_from_slice(flash_data); + Ok(()) +} + +unsafe fn blocking_write(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) { + critical_section::with(|_| { + family::clear_all_err(); + fence(Ordering::SeqCst); + family::unlock(); + fence(Ordering::SeqCst); + family::begin_write(); + fence(Ordering::SeqCst); + + let _on_drop = OnDrop::new(|| { + family::end_write(); + fence(Ordering::SeqCst); + family::lock(); + }); + + family::blocking_write(address, chunk.try_into().unwrap()) + })?; + address += WRITE_SIZE as u32; + } + Ok(()) +} + +unsafe fn blocking_erase(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); + } + + 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); + + critical_section::with(|_| { + family::clear_all_err(); + fence(Ordering::SeqCst); + family::unlock(); + fence(Ordering::SeqCst); + + let _on_drop = OnDrop::new(|| { + family::lock(); + }); + + family::blocking_erase_sector(§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(self.base, self.size, offset, bytes) } + } + + pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + unsafe { blocking_erase(self.base, 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) + } + + pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + unsafe { blocking_write(self.0.base, self.0.size, offset, bytes) } + } + + pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + unsafe { blocking_erase(self.0.base, from, to) } + } + } + + impl embedded_storage::nor_flash::ErrorType for crate::_generated::flash_regions::$type_name<'_> { + type Error = Error; + } + + impl embedded_storage::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_> { + const READ_SIZE: usize = 1; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_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/f3.rs b/embassy-stm32/src/flash/f3.rs index 1cb08ee1..10a09c42 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -1,9 +1,16 @@ use core::convert::TryInto; use core::ptr::write_volatile; +use atomic_polyfill::{fence, Ordering}; + +use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -13,58 +20,55 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB)); } -pub(crate) unsafe fn blocking_write(offset: 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 offset = offset; - for chunk in buf.chunks(2) { - write_volatile(offset as *mut u16, u16::from_le_bytes(chunk[0..2].try_into().unwrap())); - offset += 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: u32, to: u32) -> Result<(), Error> { - for page in (from..to).step_by(super::ERASE_SIZE) { - pac::FLASH.cr().modify(|w| { - w.set_per(true); - }); +pub(crate) unsafe fn end_write() { + pac::FLASH.cr().write(|w| w.set_pg(false)); +} - pac::FLASH.ar().write(|w| w.set_far(page)); +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; - pac::FLASH.cr().modify(|w| { - w.set_strt(true); - }); - - let mut ret: Result<(), Error> = blocking_wait_ready(); - - if !pac::FLASH.sr().read().eop() { - trace!("FLASH: EOP not set"); - ret = Err(Error::Prog); - } else { - pac::FLASH.sr().write(|w| w.set_eop(true)); - } - - pac::FLASH.cr().modify(|w| w.set_per(false)); - - clear_all_err(); - if ret.is_err() { - return ret; - } + // prevents parallelism errors + fence(Ordering::SeqCst); } + blocking_wait_ready() +} + +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + pac::FLASH.cr().modify(|w| { + w.set_per(true); + }); + + pac::FLASH.ar().write(|w| w.set_far(sector.start)); + + pac::FLASH.cr().modify(|w| { + w.set_strt(true); + }); + + let mut ret: Result<(), Error> = blocking_wait_ready(); + + if !pac::FLASH.sr().read().eop() { + trace!("FLASH: EOP not set"); + ret = Err(Error::Prog); + } else { + pac::FLASH.sr().write(|w| w.set_eop(true)); + } + + pac::FLASH.cr().modify(|w| w.set_per(false)); + + clear_all_err(); + if ret.is_err() { + return ret; + } Ok(()) } @@ -82,7 +86,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 9e23a8ad..2ce9df69 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -2,27 +2,108 @@ use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use super::{ERASE_SIZE, FLASH_BASE, FLASH_SIZE}; +use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const SECOND_BANK_SECTOR_START: u32 = 12; +#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] +mod alt_regions { + use embassy_hal_common::PeripheralRef; + use stm32_metapac::FLASH_SIZE; -unsafe 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() - } else { - false + use crate::_generated::flash_regions::{BANK1_REGION1, BANK1_REGION2, BANK1_REGION3}; + use crate::flash::{Bank1Region1, Bank1Region2, Flash, FlashBank, FlashRegion}; + use crate::peripherals::FLASH; + + pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion { + size: 3 * BANK1_REGION3.erase_size, + ..BANK1_REGION3 + }; + pub const ALT_BANK2_REGION1: FlashRegion = FlashRegion { + bank: FlashBank::Bank2, + base: BANK1_REGION1.base + FLASH_SIZE as u32 / 2, + ..BANK1_REGION1 + }; + pub const ALT_BANK2_REGION2: FlashRegion = FlashRegion { + bank: FlashBank::Bank2, + base: BANK1_REGION2.base + FLASH_SIZE as u32 / 2, + ..BANK1_REGION2 + }; + pub const ALT_BANK2_REGION3: FlashRegion = FlashRegion { + bank: FlashBank::Bank2, + base: BANK1_REGION3.base + FLASH_SIZE as u32 / 2, + size: 3 * BANK1_REGION3.erase_size, + ..BANK1_REGION3 + }; + + pub const ALT_FLASH_REGIONS: [&FlashRegion; 6] = [ + &BANK1_REGION1, + &BANK1_REGION2, + &ALT_BANK1_REGION3, + &ALT_BANK2_REGION1, + &ALT_BANK2_REGION2, + &ALT_BANK2_REGION3, + ]; + + pub type AltBank1Region1<'d> = Bank1Region1<'d>; + pub type AltBank1Region2<'d> = Bank1Region2<'d>; + pub struct AltBank1Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); + pub struct AltBank2Region1<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); + pub struct AltBank2Region2<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); + pub struct AltBank2Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); + + pub struct AltFlashLayout<'d> { + pub bank1_region1: AltBank1Region1<'d>, + pub bank1_region2: AltBank1Region2<'d>, + pub bank1_region3: AltBank1Region3<'d>, + pub bank2_region1: AltBank2Region1<'d>, + pub bank2_region2: AltBank2Region2<'d>, + pub bank2_region3: AltBank2Region3<'d>, + } + + impl<'d> Flash<'d> { + pub fn into_alt_regions(self) -> AltFlashLayout<'d> { + unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(true)) }; + + // SAFETY: We never expose the cloned peripheral references, and their instance is not public. + // Also, all flash region operations are protected with a cs. + let mut p = self.release(); + AltFlashLayout { + bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }), + bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }), + bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3, unsafe { p.clone_unchecked() }), + bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }), + bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }), + bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }), } } - // 2 MB devices are always dual bank - 2048 => true, - // All other devices are single bank - _ => false, } + + impl Drop for AltFlashLayout<'_> { + fn drop(&mut self) { + unsafe { + super::lock(); + crate::pac::FLASH.optcr().modify(|r| r.set_db1m(false)) + }; + } + } +} + +#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] +pub use alt_regions::{AltFlashLayout, ALT_FLASH_REGIONS}; + +#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] +pub fn get_flash_regions() -> &'static [&'static FlashRegion] { + if unsafe { pac::FLASH.optcr().read().db1m() } { + &ALT_FLASH_REGIONS + } else { + &FLASH_REGIONS + } +} + +#[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)))] +pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS } pub(crate) unsafe fn lock() { @@ -34,93 +115,34 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); } -pub(crate) unsafe fn blocking_write(offset: 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 offset = offset; - for chunk in buf.chunks(super::WRITE_SIZE) { - for val in chunk.chunks(4) { - write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); - offset += val.len() as u32; - - // prevents parallelism errors - fence(Ordering::SeqCst); - } - - ret = blocking_wait_ready(); - if ret.is_err() { - break; - } - } - ret - }; - +pub(crate) unsafe fn end_write() { pac::FLASH.cr().write(|w| w.set_pg(false)); - - ret } -struct FlashSector { - index: u8, - size: u32, -} +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; -fn get_sector(addr: u32, dual_bank: bool) -> FlashSector { - let offset = addr - FLASH_BASE as u32; - - let bank_size = match dual_bank { - true => FLASH_SIZE / 2, - false => FLASH_SIZE, - } as u32; - - let bank = offset / bank_size; - let offset_in_bank = offset % bank_size; - - let index_in_bank = if offset_in_bank >= ERASE_SIZE as u32 / 2 { - 4 + offset_in_bank / ERASE_SIZE as u32 - } else { - offset_in_bank / (ERASE_SIZE as u32 / 8) - }; - - // First 4 sectors are 16KB, then one 64KB, and rest are 128KB - let size = match index_in_bank { - 0..=3 => 16 * 1024, - 4 => 64 * 1024, - _ => 128 * 1024, - }; - - let index = if bank == 1 { - SECOND_BANK_SECTOR_START + index_in_bank - } else { - index_in_bank - } as u8; - - FlashSector { index, size } -} - -pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { - let mut addr = from; - let dual_bank = is_dual_bank(); - - while addr < to { - let sector = get_sector(addr, dual_bank); - erase_sector(sector.index)?; - addr += sector.size; + // prevents parallelism errors + fence(Ordering::SeqCst); } - Ok(()) + blocking_wait_ready() } -unsafe fn erase_sector(sector: u8) -> Result<(), Error> { - let bank = sector / SECOND_BANK_SECTOR_START as u8; - let snb = (bank << 4) + (sector % SECOND_BANK_SECTOR_START as u8); - - trace!("Erasing sector: {}", sector); +pub(crate) unsafe fn blocking_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); @@ -148,7 +170,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(); @@ -173,3 +195,80 @@ pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::flash::{get_sector, FlashBank}; + + #[test] + #[cfg(stm32f429)] + fn can_get_sector_single_bank() { + const SMALL_SECTOR_SIZE: u32 = 16 * 1024; + const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; + const LARGE_SECTOR_SIZE: u32 = 128 * 1024; + + let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32| { + assert_eq!( + FlashSector { + bank: FlashBank::Bank1, + index_in_bank, + start, + size + }, + get_sector(address, &FLASH_REGIONS) + ) + }; + + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); + + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); + assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); + + let assert_sector = |bank: FlashBank, index_in_bank: u8, start: u32, size: u32, address: u32| { + assert_eq!( + FlashSector { + bank, + index_in_bank, + start, + size + }, + get_sector(address, &ALT_FLASH_REGIONS) + ) + }; + + assert_sector(FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); + + assert_sector(FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + + assert_sector(FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000); + assert_sector(FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); + + assert_sector(FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000); + assert_sector(FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF); + assert_sector(FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000); + assert_sector(FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF); + + assert_sector(FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000); + assert_sector(FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); + + assert_sector(FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000); + assert_sector(FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF); + assert_sector(FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); + assert_sector(FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); + } +} diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index dd0d8439..6427d5a0 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -2,9 +2,14 @@ use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; +use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -14,64 +19,36 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); } -pub(crate) unsafe fn blocking_write(offset: 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 offset = offset; - for chunk in buf.chunks(super::WRITE_SIZE) { - for val in chunk.chunks(4) { - write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); - offset += 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: u32, to: u32) -> Result<(), Error> { - let start_sector = if from >= (super::FLASH_BASE + super::ERASE_SIZE / 2) as u32 { - 4 + (from - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32 - } else { - (from - super::FLASH_BASE as u32) / (super::ERASE_SIZE as u32 / 8) - }; +pub(crate) unsafe fn end_write() { + pac::FLASH.cr().write(|w| w.set_pg(false)); +} - let end_sector = if to >= (super::FLASH_BASE + super::ERASE_SIZE / 2) as u32 { - 4 + (to - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32 - } else { - (to - super::FLASH_BASE as u32) / (super::ERASE_SIZE as u32 / 8) - }; +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; - for sector in start_sector..end_sector { - let ret = erase_sector(sector as u8); - if ret.is_err() { - return ret; - } + // prevents parallelism errors + fence(Ordering::SeqCst); } - Ok(()) + blocking_wait_ready() } -unsafe fn erase_sector(sector: u8) -> Result<(), Error> { +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { pac::FLASH.cr().modify(|w| { w.set_ser(true); - w.set_snb(sector) + w.set_snb(sector.index_in_bank) }); pac::FLASH.cr().modify(|w| { @@ -107,7 +84,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(); @@ -132,3 +109,75 @@ pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::flash::{get_sector, FlashBank}; + + #[test] + #[cfg(stm32f732)] + fn can_get_sector() { + const SMALL_SECTOR_SIZE: u32 = 16 * 1024; + const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; + const LARGE_SECTOR_SIZE: u32 = 128 * 1024; + + let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32| { + assert_eq!( + FlashSector { + bank: FlashBank::Bank1, + index_in_bank, + start, + size + }, + get_sector(address, &FLASH_REGIONS) + ) + }; + + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); + + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000); + assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); + } + + #[test] + #[cfg(stm32f769)] + fn can_get_sector() { + const SMALL_SECTOR_SIZE: u32 = 32 * 1024; + const MEDIUM_SECTOR_SIZE: u32 = 128 * 1024; + const LARGE_SECTOR_SIZE: u32 = 256 * 1024; + + let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32| { + assert_eq!( + FlashSector { + bank: FlashBank::Bank1, + index_in_bank, + start, + size + }, + get_sector(address, &FLASH_REGIONS) + ) + }; + + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_7FFF); + assert_sector(3, 0x0801_8000, SMALL_SECTOR_SIZE, 0x0801_8000); + assert_sector(3, 0x0801_8000, SMALL_SECTOR_SIZE, 0x0801_FFFF); + + assert_sector(4, 0x0802_0000, MEDIUM_SECTOR_SIZE, 0x0802_0000); + assert_sector(4, 0x0802_0000, MEDIUM_SECTOR_SIZE, 0x0803_FFFF); + + assert_sector(5, 0x0804_0000, LARGE_SECTOR_SIZE, 0x0804_0000); + assert_sector(5, 0x0804_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); + assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080C_0000); + assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); + } +} diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 7de95ac1..4f38d50c 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -1,13 +1,18 @@ use core::convert::TryInto; use core::ptr::write_volatile; +use atomic_polyfill::{fence, Ordering}; + +use super::{FlashRegion, FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const SECOND_BANK_OFFSET: usize = 0x0010_0000; - const fn is_dual_bank() -> bool { - super::FLASH_SIZE / 2 > super::ERASE_SIZE + FLASH_REGIONS.len() == 2 +} + +pub fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS } pub(crate) unsafe fn lock() { @@ -20,90 +25,64 @@ pub(crate) unsafe fn lock() { pub(crate) unsafe fn unlock() { pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0x4567_0123)); pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0xCDEF_89AB)); - if is_dual_bank() { pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0x4567_0123)); pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0xCDEF_89AB)); } } -pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { - let bank = if !is_dual_bank() || (offset - 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 start_address < BANK1_REGION.end() { 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 offset = offset; - 'outer: for chunk in buf.chunks(super::WRITE_SIZE) { - for val in chunk.chunks(4) { - trace!("Writing at {:x}", offset); - write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); - offset += 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 from = from - super::FLASH_BASE as u32; - let to = to - super::FLASH_BASE as u32; - - let (start, end) = if to <= super::FLASH_SIZE as u32 { - let start_sector = from / super::ERASE_SIZE as u32; - let end_sector = to / super::ERASE_SIZE as u32; - (start_sector, end_sector) - } else { - error!("Attempting to write outside of defined sectors {:x} {:x}", from, to); - return Err(Error::Unaligned); - }; - - trace!("Erasing sectors from {} to {}", start, end); - for sector in start..end { - let bank = if sector >= 8 { 1 } else { 0 }; - let ret = erase_sector(pac::FLASH.bank(bank), (sector % 8) as u8); - if ret.is_err() { - return ret; - } - } - - Ok(()) -} - -unsafe fn erase_sector(bank: pac::flash::Bank, sector: u8) -> Result<(), Error> { +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + let bank = pac::FLASH.bank(sector.bank as usize); bank.cr().modify(|w| { w.set_ser(true); - w.set_snb(sector) + w.set_snb(sector.index_in_bank) }); bank.cr().modify(|w| { @@ -160,7 +139,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 5048a331..7d9cc6ea 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -1,9 +1,15 @@ -use core::convert::TryInto; use core::ptr::write_volatile; +use atomic_polyfill::{fence, Ordering}; + +use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} + pub(crate) unsafe fn lock() { #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().modify(|w| w.set_lock(true)); @@ -33,82 +39,75 @@ pub(crate) unsafe fn unlock() { } } -pub(crate) unsafe fn blocking_write(offset: 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 offset = offset; - for chunk in buf.chunks(super::WRITE_SIZE) { - for val in chunk.chunks(4) { - write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); - offset += 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: u32, to: u32) -> Result<(), Error> { - for page in (from..to).step_by(super::ERASE_SIZE) { - #[cfg(any(flash_l0, flash_l1))] - { - pac::FLASH.pecr().modify(|w| { - w.set_erase(true); - w.set_prog(true); - }); +pub(crate) unsafe fn end_write() { + #[cfg(any(flash_wl, flash_wb, flash_l4))] + pac::FLASH.cr().write(|w| w.set_pg(false)); +} - write_volatile(page as *mut u32, 0xFFFFFFFF); - } +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; - #[cfg(any(flash_wl, flash_wb, flash_l4))] - { - let idx = (page - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32; - - #[cfg(flash_l4)] - let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; - - pac::FLASH.cr().modify(|w| { - w.set_per(true); - w.set_pnb(idx as u8); - #[cfg(any(flash_wl, flash_wb))] - w.set_strt(true); - #[cfg(any(flash_l4))] - w.set_start(true); - #[cfg(any(flash_l4))] - w.set_bker(bank); - }); - } - - let ret: Result<(), Error> = blocking_wait_ready(); - - #[cfg(any(flash_wl, flash_wb, flash_l4))] - pac::FLASH.cr().modify(|w| w.set_per(false)); - - #[cfg(any(flash_l0, flash_l1))] - pac::FLASH.pecr().modify(|w| { - w.set_erase(false); - w.set_prog(false); - }); - - clear_all_err(); - if ret.is_err() { - return ret; - } + // prevents parallelism errors + fence(Ordering::SeqCst); } - Ok(()) + blocking_wait_ready() +} + +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + #[cfg(any(flash_l0, flash_l1))] + { + pac::FLASH.pecr().modify(|w| { + w.set_erase(true); + w.set_prog(true); + }); + + write_volatile(sector.start as *mut u32, 0xFFFFFFFF); + } + + #[cfg(any(flash_wl, flash_wb, flash_l4))] + { + let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; + + #[cfg(flash_l4)] + let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; + + pac::FLASH.cr().modify(|w| { + w.set_per(true); + w.set_pnb(idx as u8); + #[cfg(any(flash_wl, flash_wb))] + w.set_strt(true); + #[cfg(any(flash_l4))] + w.set_start(true); + #[cfg(any(flash_l4))] + w.set_bker(bank); + }); + } + + let ret: Result<(), Error> = blocking_wait_ready(); + + #[cfg(any(flash_wl, flash_wb, flash_l4))] + pac::FLASH.cr().modify(|w| w.set_per(false)); + + #[cfg(any(flash_l0, flash_l1))] + pac::FLASH.pecr().modify(|w| { + w.set_erase(false); + w.set_prog(false); + }); + + clear_all_err(); + + ret } pub(crate) unsafe fn clear_all_err() { @@ -149,7 +148,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 b7166a43..231ff1f9 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -1,89 +1,67 @@ -use embassy_hal_common::{into_ref, PeripheralRef}; -use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; +use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; -pub use crate::pac::{ERASE_SIZE, ERASE_VALUE, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; -use crate::peripherals::FLASH; -use crate::Peripheral; -const FLASH_END: usize = FLASH_BASE + FLASH_SIZE; +#[cfg(flash)] +mod common; -#[cfg_attr(any(flash_wl, flash_wb, flash_l0, flash_l1, flash_l4), path = "l.rs")] +#[cfg(flash)] +pub use common::*; + +pub use crate::_generated::flash_regions::*; +pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; + +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct FlashRegion { + pub bank: FlashBank, + pub base: u32, + pub size: u32, + pub erase_size: u32, + pub write_size: u32, + pub erase_value: u8, +} + +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct FlashSector { + pub bank: FlashBank, + pub index_in_bank: u8, + pub start: u32, + pub size: u32, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum FlashBank { + Bank1 = 0, + Bank2 = 1, + Otp, +} + +impl FlashRegion { + pub const fn end(&self) -> u32 { + self.base + self.size + } + + pub const fn sectors(&self) -> u8 { + (self.size / self.erase_size) as u8 + } +} + +#[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_wl, flash_wb), path = "l.rs")] #[cfg_attr(flash_f3, path = "f3.rs")] #[cfg_attr(flash_f4, path = "f4.rs")] #[cfg_attr(flash_f7, path = "f7.rs")] #[cfg_attr(flash_h7, path = "h7.rs")] +#[cfg_attr( + not(any( + flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f3, flash_f4, flash_f7, flash_h7 + )), + path = "other.rs" +)] mod family; -pub struct Flash<'d> { - _inner: PeripheralRef<'d, FLASH>, -} - -impl<'d> Flash<'d> { - pub fn new(p: impl Peripheral

+ 'd) -> Self { - into_ref!(p); - Self { _inner: p } - } - - pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - let offset = FLASH_BASE as u32 + offset; - if offset as usize >= FLASH_END || offset as usize + bytes.len() > FLASH_END { - return Err(Error::Size); - } - - let flash_data = unsafe { core::slice::from_raw_parts(offset as *const u8, bytes.len()) }; - bytes.copy_from_slice(flash_data); - Ok(()) - } - - pub fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { - let offset = FLASH_BASE as u32 + offset; - if offset as usize + buf.len() > FLASH_END { - return Err(Error::Size); - } - if offset as usize % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { - return Err(Error::Unaligned); - } - trace!("Writing {} bytes at 0x{:x}", buf.len(), offset); - - self.clear_all_err(); - - unsafe { - family::unlock(); - let res = family::blocking_write(offset, buf); - family::lock(); - res - } - } - - pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let from = FLASH_BASE as u32 + from; - let to = FLASH_BASE as u32 + to; - if to < from || to as usize > FLASH_END { - return Err(Error::Size); - } - if (from as usize % ERASE_SIZE) != 0 || (to as usize % ERASE_SIZE) != 0 { - return Err(Error::Unaligned); - } - - self.clear_all_err(); - - unsafe { - family::unlock(); - let res = family::blocking_erase(from, to); - family::lock(); - res - } - } - - fn clear_all_err(&mut self) { - unsafe { family::clear_all_err() }; - } -} - -impl Drop for Flash<'_> { - fn drop(&mut self) { - unsafe { family::lock() }; - } -} +#[allow(unused_imports)] +pub use family::*; #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -97,10 +75,6 @@ pub enum Error { Parallelism, } -impl<'d> ErrorType for Flash<'d> { - type Error = Error; -} - impl NorFlashError for Error { fn kind(&self) -> NorFlashErrorKind { match self { @@ -110,28 +84,3 @@ impl NorFlashError for Error { } } } - -impl<'d> ReadNorFlash for Flash<'d> { - const READ_SIZE: usize = WRITE_SIZE; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(offset, bytes) - } - - fn capacity(&self) -> usize { - FLASH_SIZE - } -} - -impl<'d> NorFlash for Flash<'d> { - const WRITE_SIZE: usize = WRITE_SIZE; - const ERASE_SIZE: usize = ERASE_SIZE; - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.blocking_erase(from, to) - } - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(offset, bytes) - } -} diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs new file mode 100644 index 00000000..c151cb82 --- /dev/null +++ b/embassy-stm32/src/flash/other.rs @@ -0,0 +1,29 @@ +#![allow(unused)] + +use super::{Error, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; + +pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} + +pub(crate) unsafe fn lock() { + unimplemented!(); +} +pub(crate) unsafe fn unlock() { + unimplemented!(); +} +pub(crate) unsafe fn begin_write() { + unimplemented!(); +} +pub(crate) unsafe fn end_write() { + unimplemented!(); +} +pub(crate) unsafe fn blocking_write(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + unimplemented!(); +} +pub(crate) unsafe fn blocking_erase_sector(_sector: &FlashSector) -> Result<(), Error> { + unimplemented!(); +} +pub(crate) unsafe fn clear_all_err() { + unimplemented!(); +} diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 8dc4df2d..3f2d078f 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -43,9 +43,6 @@ pub mod i2c; #[cfg(crc)] pub mod crc; -#[cfg(any( - flash_l0, flash_l1, flash_wl, flash_wb, flash_l4, flash_f3, flash_f4, flash_f7, flash_h7 -))] pub mod flash; pub mod pwm; #[cfg(quadspi)] diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index b8027d19..49c21920 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -5,7 +5,7 @@ use cortex_m_rt::{entry, exception}; #[cfg(feature = "defmt")] use defmt_rtt as _; use embassy_boot_stm32::*; -use embassy_stm32::flash::{Flash, ERASE_SIZE}; +use embassy_stm32::flash::Flash; #[entry] fn main() -> ! { @@ -19,9 +19,10 @@ fn main() -> ! { } */ - let mut bl: BootLoader = BootLoader::default(); + let mut bl: BootLoader<2048> = BootLoader::default(); let flash = Flash::new(p.FLASH); - let mut flash = BootFlash::new(flash); + let layout = flash.into_regions(); + let mut flash = BootFlash::new(layout.bank1_region); let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); core::mem::drop(flash); unsafe { bl.load(start) } diff --git a/examples/stm32f3/src/bin/flash.rs b/examples/stm32f3/src/bin/flash.rs index baa7484d..e40ad4fc 100644 --- a/examples/stm32f3/src/bin/flash.rs +++ b/examples/stm32f3/src/bin/flash.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH); + let mut f = Flash::new(p.FLASH).into_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32f4/src/bin/flash.rs b/examples/stm32f4/src/bin/flash.rs index 7ea068a4..bd3a7c95 100644 --- a/examples/stm32f4/src/bin/flash.rs +++ b/examples/stm32f4/src/bin/flash.rs @@ -5,7 +5,6 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; use embassy_stm32::flash::Flash; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -13,6 +12,8 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello Flash!"); + // Once can also call `into_regions()` to get access to NorFlash implementations + // for each of the unique characteristics. let mut f = Flash::new(p.FLASH); // Sector 5 @@ -30,19 +31,19 @@ fn test_flash(f: &mut Flash, offset: u32, size: u32) { info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(offset, &mut buf)); + unwrap!(f.blocking_read(offset, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase(offset, offset + size)); + unwrap!(f.blocking_erase(offset, offset + size)); info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(offset, &mut buf)); + unwrap!(f.blocking_read(offset, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write( + unwrap!(f.blocking_write( offset, &[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, @@ -52,7 +53,7 @@ fn test_flash(f: &mut Flash, offset: u32, size: u32) { info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(offset, &mut buf)); + unwrap!(f.blocking_read(offset, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!( &buf[..], diff --git a/examples/stm32f7/src/bin/flash.rs b/examples/stm32f7/src/bin/flash.rs index 4a7bca1f..aabfe855 100644 --- a/examples/stm32f7/src/bin/flash.rs +++ b/examples/stm32f7/src/bin/flash.rs @@ -14,12 +14,12 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello Flash!"); - const ADDR: u32 = 0x8_0000; + const ADDR: u32 = 0x8_0000; // This is the offset into the third region, the absolute address is 4x32K + 128K + 0x8_0000. // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = Flash::new(p.FLASH); + let mut f = Flash::new(p.FLASH).into_regions().bank1_region3; info!("Reading..."); let mut buf = [0u8; 32]; diff --git a/examples/stm32h7/src/bin/flash.rs b/examples/stm32h7/src/bin/flash.rs index ee86bdbf..7ee9838c 100644 --- a/examples/stm32h7/src/bin/flash.rs +++ b/examples/stm32h7/src/bin/flash.rs @@ -14,12 +14,12 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello Flash!"); - const ADDR: u32 = 0x08_0000; + const ADDR: u32 = 0; // This is the offset into bank 2, the absolute address is 0x8_0000 // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = Flash::new(p.FLASH); + let mut f = Flash::new(p.FLASH).into_regions().bank2_region; info!("Reading..."); let mut buf = [0u8; 32]; diff --git a/examples/stm32l0/src/bin/flash.rs b/examples/stm32l0/src/bin/flash.rs index ffe4fb10..33742502 100644 --- a/examples/stm32l0/src/bin/flash.rs +++ b/examples/stm32l0/src/bin/flash.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH); + let mut f = Flash::new(p.FLASH).into_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32l1/src/bin/flash.rs b/examples/stm32l1/src/bin/flash.rs index 476ed51a..38feb0d7 100644 --- a/examples/stm32l1/src/bin/flash.rs +++ b/examples/stm32l1/src/bin/flash.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH); + let mut f = Flash::new(p.FLASH).into_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs index 2a888062..e6bc2865 100644 --- a/examples/stm32wl/src/bin/flash.rs +++ b/examples/stm32wl/src/bin/flash.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x36000; - let mut f = Flash::new(p.FLASH); + let mut f = Flash::new(p.FLASH).into_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8];