Merge #1297
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:
commit
eed2b12325
1
ci.sh
1
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 \
|
||||
|
@ -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 = []
|
||||
|
@ -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<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.
|
||||
|
||||
@ -578,11 +666,25 @@ fn main() {
|
||||
// ========
|
||||
// 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 peripherals_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();
|
||||
|
||||
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("_", "")
|
||||
}
|
||||
|
211
embassy-stm32/src/flash/common.rs
Normal file
211
embassy-stm32/src/flash/common.rs
Normal 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(§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)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
@ -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,36 +20,35 @@ 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) {
|
||||
pub(crate) unsafe fn end_write() {
|
||||
pac::FLASH.cr().write(|w| w.set_pg(false));
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
|
||||
let mut address = start_address;
|
||||
for chunk in buf.chunks(2) {
|
||||
write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap()));
|
||||
address += chunk.len() as u32;
|
||||
|
||||
// prevents parallelism errors
|
||||
fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
blocking_wait_ready()
|
||||
}
|
||||
|
||||
pub(crate) 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(page));
|
||||
pac::FLASH.ar().write(|w| w.set_far(sector.start));
|
||||
|
||||
pac::FLASH.cr().modify(|w| {
|
||||
w.set_strt(true);
|
||||
@ -63,8 +69,6 @@ pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
|
||||
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();
|
||||
|
||||
|
@ -2,29 +2,110 @@ 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()
|
||||
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() }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
false
|
||||
}
|
||||
}
|
||||
// 2 MB devices are always dual bank
|
||||
2048 => true,
|
||||
// All other devices are single bank
|
||||
_ => false,
|
||||
&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() {
|
||||
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));
|
||||
}
|
||||
|
||||
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;
|
||||
pub(crate) unsafe fn end_write() {
|
||||
pac::FLASH.cr().write(|w| w.set_pg(false));
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
|
||||
let mut address = start_address;
|
||||
for val in buf.chunks(4) {
|
||||
write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
|
||||
address += val.len() as u32;
|
||||
|
||||
// prevents parallelism errors
|
||||
fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
ret = blocking_wait_ready();
|
||||
if ret.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret
|
||||
};
|
||||
|
||||
pac::FLASH.cr().write(|w| w.set_pg(false));
|
||||
|
||||
ret
|
||||
blocking_wait_ready()
|
||||
}
|
||||
|
||||
struct FlashSector {
|
||||
index: u8,
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
pub(crate) unsafe fn end_write() {
|
||||
pac::FLASH.cr().write(|w| w.set_pg(false));
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
|
||||
let mut address = start_address;
|
||||
for val in buf.chunks(4) {
|
||||
write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
|
||||
address += val.len() as u32;
|
||||
|
||||
// prevents parallelism errors
|
||||
fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
ret = blocking_wait_ready();
|
||||
if ret.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret
|
||||
};
|
||||
|
||||
pac::FLASH.cr().write(|w| w.set_pg(false));
|
||||
|
||||
ret
|
||||
blocking_wait_ready()
|
||||
}
|
||||
|
||||
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)
|
||||
};
|
||||
|
||||
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> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
res = Some(blocking_wait_ready(bank));
|
||||
bank.sr().modify(|w| {
|
||||
if w.eop() {
|
||||
w.set_eop(true);
|
||||
}
|
||||
});
|
||||
if ret.is_err() {
|
||||
break 'outer;
|
||||
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();
|
||||
|
||||
|
@ -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,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))]
|
||||
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) {
|
||||
pub(crate) unsafe fn end_write() {
|
||||
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
||||
pac::FLASH.cr().write(|w| w.set_pg(false));
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
|
||||
let mut address = start_address;
|
||||
for val in buf.chunks(4) {
|
||||
write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
|
||||
address += val.len() as u32;
|
||||
|
||||
// prevents parallelism errors
|
||||
fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
blocking_wait_ready()
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
|
||||
#[cfg(any(flash_l0, flash_l1))]
|
||||
{
|
||||
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);
|
||||
});
|
||||
|
||||
write_volatile(page as *mut u32, 0xFFFFFFFF);
|
||||
write_volatile(sector.start as *mut u32, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
#[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)]
|
||||
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();
|
||||
if ret.is_err() {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
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();
|
||||
|
||||
|
@ -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<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() };
|
||||
}
|
||||
}
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
29
embassy-stm32/src/flash/other.rs
Normal file
29
embassy-stm32/src/flash/other.rs
Normal 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!();
|
||||
}
|
@ -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)]
|
||||
|
@ -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<ERASE_SIZE> = 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) }
|
||||
|
@ -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];
|
||||
|
@ -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[..],
|
||||
|
@ -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];
|
||||
|
@ -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];
|
||||
|
@ -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];
|
||||
|
@ -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];
|
||||
|
@ -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];
|
||||
|
Loading…
Reference in New Issue
Block a user