1297: feat(stm32): Support multiple flash regions r=Dirbaio a=rmja

This depends on https://github.com/embassy-rs/stm32-data/pull/176

This is a general overhaul of the flash module to support multiple erase sizes.
Overall this PR contains:
* Move complex sector computation to embassy-hal-common to allow for tests
* Implement `FlashRegion` trait for each available flash region
* Add Flash::into_regions() to get each region.
* Implement embedded-storage traits for each region to support different erase sizes
* Split family write operations into begin/do/end
* Protection against simultaneous writes/erases for each split region is done through a global mutex

Co-authored-by: Rasmus Melchior Jacobsen <rmja@laesoe.org>
Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
This commit is contained in:
bors[bot] 2023-04-05 11:12:40 +00:00 committed by GitHub
commit eed2b12325
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 881 additions and 439 deletions

1
ci.sh
View File

@ -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,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,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,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,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,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 \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits \

View File

@ -60,7 +60,7 @@ sdio-host = "0.5.0"
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true }
critical-section = "1.1" critical-section = "1.1"
atomic-polyfill = "1.0.1" atomic-polyfill = "1.0.1"
stm32-metapac = { version = "2", features = ["rt"] } stm32-metapac = "3"
vcell = "0.1.3" vcell = "0.1.3"
bxcan = "0.7.0" bxcan = "0.7.0"
nb = "1.0.0" nb = "1.0.0"
@ -69,12 +69,16 @@ seq-macro = "0.3.0"
cfg-if = "1.0.0" cfg-if = "1.0.0"
embedded-io = { version = "0.4.0", features = ["async"], optional = true } embedded-io = { version = "0.4.0", features = ["async"], optional = true }
[dev-dependencies]
critical-section = { version = "1.1", features = ["std"] }
[build-dependencies] [build-dependencies]
proc-macro2 = "1.0.36" proc-macro2 = "1.0.36"
quote = "1.0.15" quote = "1.0.15"
stm32-metapac = { version = "2", default-features = false, features = ["metadata"]} stm32-metapac = { version = "3", default-features = false, features = ["metadata"]}
[features] [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"] 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"] memory-x = ["stm32-metapac/memory-x"]
subghz = [] subghz = []

View File

@ -3,9 +3,9 @@ use std::fmt::Write as _;
use std::path::PathBuf; use std::path::PathBuf;
use std::{env, fs}; use std::{env, fs};
use proc_macro2::TokenStream; use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use stm32_metapac::metadata::METADATA; use stm32_metapac::metadata::{MemoryRegionKind, METADATA};
fn main() { fn main() {
let chip_name = match env::vars() 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<TokenStream>, (Vec<TokenStream>, Vec<Ident>)) = 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. // Generate DMA IRQs.
@ -578,11 +666,25 @@ fn main() {
// ======== // ========
// Write foreach_foo! macrotables // Write foreach_foo! macrotables
let mut flash_regions_table: Vec<Vec<String>> = Vec::new();
let mut interrupts_table: Vec<Vec<String>> = Vec::new(); let mut interrupts_table: Vec<Vec<String>> = Vec::new();
let mut peripherals_table: Vec<Vec<String>> = Vec::new(); let mut peripherals_table: Vec<Vec<String>> = Vec::new();
let mut pins_table: Vec<Vec<String>> = Vec::new(); let mut pins_table: Vec<Vec<String>> = Vec::new();
let mut dma_channels_table: Vec<Vec<String>> = Vec::new(); let mut dma_channels_table: Vec<Vec<String>> = 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_base = METADATA.peripherals.iter().find(|p| p.name == "GPIOA").unwrap().address as u32;
let gpio_stride = 0x400; let gpio_stride = 0x400;
@ -679,6 +781,7 @@ fn main() {
let mut m = String::new(); 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_interrupt", &interrupts_table);
make_table(&mut m, "foreach_peripheral", &peripherals_table); make_table(&mut m, "foreach_peripheral", &peripherals_table);
make_table(&mut m, "foreach_pin", &pins_table); make_table(&mut m, "foreach_pin", &pins_table);
@ -831,3 +934,19 @@ macro_rules! {} {{
) )
.unwrap(); .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("_", "")
}

View File

@ -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<P = crate::peripherals::FLASH> + '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(&sector)
})?;
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)
}
}
};
}

View File

@ -1,9 +1,16 @@
use core::convert::TryInto; use core::convert::TryInto;
use core::ptr::write_volatile; use core::ptr::write_volatile;
use atomic_polyfill::{fence, Ordering};
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
use crate::flash::Error; use crate::flash::Error;
use crate::pac; use crate::pac;
pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
&FLASH_REGIONS
}
pub(crate) unsafe fn lock() { pub(crate) unsafe fn lock() {
pac::FLASH.cr().modify(|w| w.set_lock(true)); pac::FLASH.cr().modify(|w| w.set_lock(true));
} }
@ -13,36 +20,35 @@ pub(crate) unsafe fn unlock() {
pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB)); 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)); 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> { pub(crate) unsafe fn end_write() {
for page in (from..to).step_by(super::ERASE_SIZE) { pac::FLASH.cr().write(|w| w.set_pg(false));
}
pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
let mut address = start_address;
for chunk in buf.chunks(2) {
write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap()));
address += chunk.len() as u32;
// prevents parallelism errors
fence(Ordering::SeqCst);
}
blocking_wait_ready()
}
pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
pac::FLASH.cr().modify(|w| { pac::FLASH.cr().modify(|w| {
w.set_per(true); w.set_per(true);
}); });
pac::FLASH.ar().write(|w| w.set_far(page)); pac::FLASH.ar().write(|w| w.set_far(sector.start));
pac::FLASH.cr().modify(|w| { pac::FLASH.cr().modify(|w| {
w.set_strt(true); w.set_strt(true);
@ -63,8 +69,6 @@ pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
if ret.is_err() { if ret.is_err() {
return ret; return ret;
} }
}
Ok(()) 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 { loop {
let sr = pac::FLASH.sr().read(); let sr = pac::FLASH.sr().read();

View File

@ -2,29 +2,110 @@ use core::convert::TryInto;
use core::ptr::write_volatile; use core::ptr::write_volatile;
use core::sync::atomic::{fence, Ordering}; 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::flash::Error;
use crate::pac; 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 { use crate::_generated::flash_regions::{BANK1_REGION1, BANK1_REGION2, BANK1_REGION3};
match FLASH_SIZE / 1024 { use crate::flash::{Bank1Region1, Bank1Region2, Flash, FlashBank, FlashRegion};
// 1 MB devices depend on configuration use crate::peripherals::FLASH;
1024 => {
if cfg!(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)) { pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion {
pac::FLASH.optcr().read().db1m() 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() }),
}
}
}
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 { } else {
false &FLASH_REGIONS
}
}
// 2 MB devices are always dual bank
2048 => true,
// All other devices are single bank
_ => false,
} }
} }
#[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() { pub(crate) unsafe fn lock() {
pac::FLASH.cr().modify(|w| w.set_lock(true)); pac::FLASH.cr().modify(|w| w.set_lock(true));
} }
@ -34,93 +115,34 @@ pub(crate) unsafe fn unlock() {
pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); 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| { pac::FLASH.cr().write(|w| {
w.set_pg(true); w.set_pg(true);
w.set_psize(pac::flash::vals::Psize::PSIZE32); w.set_psize(pac::flash::vals::Psize::PSIZE32);
}); });
}
let ret = { pub(crate) unsafe fn end_write() {
let mut ret: Result<(), Error> = Ok(()); pac::FLASH.cr().write(|w| w.set_pg(false));
let mut offset = offset; }
for chunk in buf.chunks(super::WRITE_SIZE) {
for val in chunk.chunks(4) { pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); let mut address = start_address;
offset += val.len() as u32; for val in buf.chunks(4) {
write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
address += val.len() as u32;
// prevents parallelism errors // prevents parallelism errors
fence(Ordering::SeqCst); fence(Ordering::SeqCst);
} }
ret = blocking_wait_ready(); blocking_wait_ready()
if ret.is_err() {
break;
}
}
ret
};
pac::FLASH.cr().write(|w| w.set_pg(false));
ret
} }
struct FlashSector { pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
index: u8, let snb = ((sector.bank as u8) << 4) + sector.index_in_bank;
size: 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;
}
Ok(())
}
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);
pac::FLASH.cr().modify(|w| { pac::FLASH.cr().modify(|w| {
w.set_ser(true); 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 { loop {
let sr = pac::FLASH.sr().read(); 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);
}
}

View File

@ -2,9 +2,14 @@ use core::convert::TryInto;
use core::ptr::write_volatile; use core::ptr::write_volatile;
use core::sync::atomic::{fence, Ordering}; use core::sync::atomic::{fence, Ordering};
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
use crate::flash::Error; use crate::flash::Error;
use crate::pac; use crate::pac;
pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
&FLASH_REGIONS
}
pub(crate) unsafe fn lock() { pub(crate) unsafe fn lock() {
pac::FLASH.cr().modify(|w| w.set_lock(true)); 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)); 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| { pac::FLASH.cr().write(|w| {
w.set_pg(true); w.set_pg(true);
w.set_psize(pac::flash::vals::Psize::PSIZE32); w.set_psize(pac::flash::vals::Psize::PSIZE32);
}); });
}
let ret = { pub(crate) unsafe fn end_write() {
let mut ret: Result<(), Error> = Ok(()); pac::FLASH.cr().write(|w| w.set_pg(false));
let mut offset = offset; }
for chunk in buf.chunks(super::WRITE_SIZE) {
for val in chunk.chunks(4) { pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); let mut address = start_address;
offset += val.len() as u32; for val in buf.chunks(4) {
write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
address += val.len() as u32;
// prevents parallelism errors // prevents parallelism errors
fence(Ordering::SeqCst); fence(Ordering::SeqCst);
} }
ret = blocking_wait_ready(); 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> { pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> 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)
};
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)
};
for sector in start_sector..end_sector {
let ret = erase_sector(sector as u8);
if ret.is_err() {
return ret;
}
}
Ok(())
}
unsafe fn erase_sector(sector: u8) -> Result<(), Error> {
pac::FLASH.cr().modify(|w| { pac::FLASH.cr().modify(|w| {
w.set_ser(true); w.set_ser(true);
w.set_snb(sector) w.set_snb(sector.index_in_bank)
}); });
pac::FLASH.cr().modify(|w| { 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 { loop {
let sr = pac::FLASH.sr().read(); 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);
}
}

View File

@ -1,13 +1,18 @@
use core::convert::TryInto; use core::convert::TryInto;
use core::ptr::write_volatile; 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::flash::Error;
use crate::pac; use crate::pac;
const SECOND_BANK_OFFSET: usize = 0x0010_0000;
const fn is_dual_bank() -> bool { 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() { pub(crate) unsafe fn lock() {
@ -20,90 +25,64 @@ pub(crate) unsafe fn lock() {
pub(crate) unsafe fn unlock() { 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(0x4567_0123));
pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0xCDEF_89AB)); pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0xCDEF_89AB));
if is_dual_bank() { 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(0x4567_0123));
pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0xCDEF_89AB)); pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0xCDEF_89AB));
} }
} }
pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { pub(crate) unsafe fn begin_write() {
let bank = if !is_dual_bank() || (offset - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32 { 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) pac::FLASH.bank(0)
} else { } else {
pac::FLASH.bank(1) pac::FLASH.bank(1)
}; };
bank.cr().write(|w| { bank.cr().write(|w| {
w.set_pg(true); w.set_pg(true);
w.set_psize(2); // 32 bits at once w.set_psize(2); // 32 bits at once
}); });
cortex_m::asm::isb(); cortex_m::asm::isb();
cortex_m::asm::dsb(); cortex_m::asm::dsb();
core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); fence(Ordering::SeqCst);
let ret = { let mut res = None;
let mut ret: Result<(), Error> = Ok(()); let mut address = start_address;
let mut offset = offset; for val in buf.chunks(4) {
'outer: for chunk in buf.chunks(super::WRITE_SIZE) { write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
for val in chunk.chunks(4) { address += val.len() as u32;
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;
ret = blocking_wait_ready(bank); res = Some(blocking_wait_ready(bank));
bank.sr().modify(|w| { bank.sr().modify(|w| {
if w.eop() { if w.eop() {
w.set_eop(true); w.set_eop(true);
} }
}); });
if ret.is_err() { if res.unwrap().is_err() {
break 'outer; break;
} }
} }
}
ret
};
bank.cr().write(|w| w.set_pg(false)); bank.cr().write(|w| w.set_pg(false));
cortex_m::asm::isb(); cortex_m::asm::isb();
cortex_m::asm::dsb(); 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> { pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
let from = from - super::FLASH_BASE as u32; let bank = pac::FLASH.bank(sector.bank as usize);
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> {
bank.cr().modify(|w| { bank.cr().modify(|w| {
w.set_ser(true); w.set_ser(true);
w.set_snb(sector) w.set_snb(sector.index_in_bank)
}); });
bank.cr().modify(|w| { 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 { loop {
let sr = bank.sr().read(); let sr = bank.sr().read();

View File

@ -1,9 +1,15 @@
use core::convert::TryInto;
use core::ptr::write_volatile; use core::ptr::write_volatile;
use atomic_polyfill::{fence, Ordering};
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
use crate::flash::Error; use crate::flash::Error;
use crate::pac; use crate::pac;
pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
&FLASH_REGIONS
}
pub(crate) unsafe fn lock() { pub(crate) unsafe fn lock() {
#[cfg(any(flash_wl, flash_wb, flash_l4))] #[cfg(any(flash_wl, flash_wb, flash_l4))]
pac::FLASH.cr().modify(|w| w.set_lock(true)); pac::FLASH.cr().modify(|w| w.set_lock(true));
@ -33,35 +39,32 @@ 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))] #[cfg(any(flash_wl, flash_wb, flash_l4))]
pac::FLASH.cr().write(|w| w.set_pg(true)); 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> { pub(crate) unsafe fn end_write() {
for page in (from..to).step_by(super::ERASE_SIZE) { #[cfg(any(flash_wl, flash_wb, flash_l4))]
pac::FLASH.cr().write(|w| w.set_pg(false));
}
pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
let mut address = start_address;
for val in buf.chunks(4) {
write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
address += val.len() as u32;
// prevents parallelism errors
fence(Ordering::SeqCst);
}
blocking_wait_ready()
}
pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
#[cfg(any(flash_l0, flash_l1))] #[cfg(any(flash_l0, flash_l1))]
{ {
pac::FLASH.pecr().modify(|w| { pac::FLASH.pecr().modify(|w| {
@ -69,12 +72,12 @@ pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
w.set_prog(true); w.set_prog(true);
}); });
write_volatile(page as *mut u32, 0xFFFFFFFF); write_volatile(sector.start as *mut u32, 0xFFFFFFFF);
} }
#[cfg(any(flash_wl, flash_wb, flash_l4))] #[cfg(any(flash_wl, flash_wb, flash_l4))]
{ {
let idx = (page - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32; let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32;
#[cfg(flash_l4)] #[cfg(flash_l4)]
let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) };
@ -103,12 +106,8 @@ pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
}); });
clear_all_err(); clear_all_err();
if ret.is_err() {
return ret;
}
}
Ok(()) ret
} }
pub(crate) unsafe fn clear_all_err() { 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 { loop {
let sr = pac::FLASH.sr().read(); let sr = pac::FLASH.sr().read();

View File

@ -1,89 +1,67 @@
use embassy_hal_common::{into_ref, PeripheralRef}; use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
pub use crate::pac::{ERASE_SIZE, ERASE_VALUE, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; #[cfg(flash)]
use crate::peripherals::FLASH; mod common;
use crate::Peripheral;
const FLASH_END: usize = FLASH_BASE + FLASH_SIZE;
#[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_f3, path = "f3.rs")]
#[cfg_attr(flash_f4, path = "f4.rs")] #[cfg_attr(flash_f4, path = "f4.rs")]
#[cfg_attr(flash_f7, path = "f7.rs")] #[cfg_attr(flash_f7, path = "f7.rs")]
#[cfg_attr(flash_h7, path = "h7.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; mod family;
pub struct Flash<'d> { #[allow(unused_imports)]
_inner: PeripheralRef<'d, FLASH>, pub use family::*;
}
impl<'d> Flash<'d> {
pub fn new(p: impl Peripheral<P = FLASH> + '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() };
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -97,10 +75,6 @@ pub enum Error {
Parallelism, Parallelism,
} }
impl<'d> ErrorType for Flash<'d> {
type Error = Error;
}
impl NorFlashError for Error { impl NorFlashError for Error {
fn kind(&self) -> NorFlashErrorKind { fn kind(&self) -> NorFlashErrorKind {
match self { 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)
}
}

View File

@ -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!();
}

View File

@ -43,9 +43,6 @@ pub mod i2c;
#[cfg(crc)] #[cfg(crc)]
pub mod 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 flash;
pub mod pwm; pub mod pwm;
#[cfg(quadspi)] #[cfg(quadspi)]

View File

@ -5,7 +5,7 @@ use cortex_m_rt::{entry, exception};
#[cfg(feature = "defmt")] #[cfg(feature = "defmt")]
use defmt_rtt as _; use defmt_rtt as _;
use embassy_boot_stm32::*; use embassy_boot_stm32::*;
use embassy_stm32::flash::{Flash, ERASE_SIZE}; use embassy_stm32::flash::Flash;
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
@ -19,9 +19,10 @@ fn main() -> ! {
} }
*/ */
let mut bl: BootLoader<ERASE_SIZE> = BootLoader::default(); let mut bl: BootLoader<2048> = BootLoader::default();
let flash = Flash::new(p.FLASH); 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)); let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash));
core::mem::drop(flash); core::mem::drop(flash);
unsafe { bl.load(start) } unsafe { bl.load(start) }

View File

@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) {
const ADDR: u32 = 0x26000; const ADDR: u32 = 0x26000;
let mut f = Flash::new(p.FLASH); let mut f = Flash::new(p.FLASH).into_regions().bank1_region;
info!("Reading..."); info!("Reading...");
let mut buf = [0u8; 8]; let mut buf = [0u8; 8];

View File

@ -5,7 +5,6 @@
use defmt::{info, unwrap}; use defmt::{info, unwrap};
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::flash::Flash; use embassy_stm32::flash::Flash;
use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main] #[embassy_executor::main]
@ -13,6 +12,8 @@ async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default()); let p = embassy_stm32::init(Default::default());
info!("Hello Flash!"); 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); let mut f = Flash::new(p.FLASH);
// Sector 5 // Sector 5
@ -30,19 +31,19 @@ fn test_flash(f: &mut Flash, offset: u32, size: u32) {
info!("Reading..."); info!("Reading...");
let mut buf = [0u8; 32]; let mut buf = [0u8; 32];
unwrap!(f.read(offset, &mut buf)); unwrap!(f.blocking_read(offset, &mut buf));
info!("Read: {=[u8]:x}", buf); info!("Read: {=[u8]:x}", buf);
info!("Erasing..."); info!("Erasing...");
unwrap!(f.erase(offset, offset + size)); unwrap!(f.blocking_erase(offset, offset + size));
info!("Reading..."); info!("Reading...");
let mut buf = [0u8; 32]; 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!("Read after erase: {=[u8]:x}", buf);
info!("Writing..."); info!("Writing...");
unwrap!(f.write( unwrap!(f.blocking_write(
offset, 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, 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..."); info!("Reading...");
let mut buf = [0u8; 32]; let mut buf = [0u8; 32];
unwrap!(f.read(offset, &mut buf)); unwrap!(f.blocking_read(offset, &mut buf));
info!("Read: {=[u8]:x}", buf); info!("Read: {=[u8]:x}", buf);
assert_eq!( assert_eq!(
&buf[..], &buf[..],

View File

@ -14,12 +14,12 @@ async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default()); let p = embassy_stm32::init(Default::default());
info!("Hello Flash!"); 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 // wait a bit before accessing the flash
Timer::after(Duration::from_millis(300)).await; 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..."); info!("Reading...");
let mut buf = [0u8; 32]; let mut buf = [0u8; 32];

View File

@ -14,12 +14,12 @@ async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default()); let p = embassy_stm32::init(Default::default());
info!("Hello Flash!"); 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 // wait a bit before accessing the flash
Timer::after(Duration::from_millis(300)).await; 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..."); info!("Reading...");
let mut buf = [0u8; 32]; let mut buf = [0u8; 32];

View File

@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) {
const ADDR: u32 = 0x26000; const ADDR: u32 = 0x26000;
let mut f = Flash::new(p.FLASH); let mut f = Flash::new(p.FLASH).into_regions().bank1_region;
info!("Reading..."); info!("Reading...");
let mut buf = [0u8; 8]; let mut buf = [0u8; 8];

View File

@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) {
const ADDR: u32 = 0x26000; const ADDR: u32 = 0x26000;
let mut f = Flash::new(p.FLASH); let mut f = Flash::new(p.FLASH).into_regions().bank1_region;
info!("Reading..."); info!("Reading...");
let mut buf = [0u8; 8]; let mut buf = [0u8; 8];

View File

@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) {
const ADDR: u32 = 0x36000; const ADDR: u32 = 0x36000;
let mut f = Flash::new(p.FLASH); let mut f = Flash::new(p.FLASH).into_regions().bank1_region;
info!("Reading..."); info!("Reading...");
let mut buf = [0u8; 8]; let mut buf = [0u8; 8];