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:
		
							
								
								
									
										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,58 +20,55 @@ pub(crate) unsafe fn unlock() {
 | 
			
		||||
    pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> {
 | 
			
		||||
pub(crate) unsafe fn begin_write() {
 | 
			
		||||
    assert_eq!(0, WRITE_SIZE % 2);
 | 
			
		||||
 | 
			
		||||
    pac::FLASH.cr().write(|w| w.set_pg(true));
 | 
			
		||||
 | 
			
		||||
    let ret = {
 | 
			
		||||
        let mut ret: Result<(), Error> = Ok(());
 | 
			
		||||
        let mut offset = offset;
 | 
			
		||||
        for chunk in buf.chunks(2) {
 | 
			
		||||
            write_volatile(offset as *mut u16, u16::from_le_bytes(chunk[0..2].try_into().unwrap()));
 | 
			
		||||
            offset += chunk.len() as u32;
 | 
			
		||||
 | 
			
		||||
            ret = blocking_wait_ready();
 | 
			
		||||
            if ret.is_err() {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        ret
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    pac::FLASH.cr().write(|w| w.set_pg(false));
 | 
			
		||||
 | 
			
		||||
    ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
 | 
			
		||||
    for page in (from..to).step_by(super::ERASE_SIZE) {
 | 
			
		||||
        pac::FLASH.cr().modify(|w| {
 | 
			
		||||
            w.set_per(true);
 | 
			
		||||
        });
 | 
			
		||||
pub(crate) unsafe fn end_write() {
 | 
			
		||||
    pac::FLASH.cr().write(|w| w.set_pg(false));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
        pac::FLASH.ar().write(|w| w.set_far(page));
 | 
			
		||||
pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
 | 
			
		||||
    let mut address = start_address;
 | 
			
		||||
    for chunk in buf.chunks(2) {
 | 
			
		||||
        write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap()));
 | 
			
		||||
        address += chunk.len() as u32;
 | 
			
		||||
 | 
			
		||||
        pac::FLASH.cr().modify(|w| {
 | 
			
		||||
            w.set_strt(true);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        let mut ret: Result<(), Error> = blocking_wait_ready();
 | 
			
		||||
 | 
			
		||||
        if !pac::FLASH.sr().read().eop() {
 | 
			
		||||
            trace!("FLASH: EOP not set");
 | 
			
		||||
            ret = Err(Error::Prog);
 | 
			
		||||
        } else {
 | 
			
		||||
            pac::FLASH.sr().write(|w| w.set_eop(true));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pac::FLASH.cr().modify(|w| w.set_per(false));
 | 
			
		||||
 | 
			
		||||
        clear_all_err();
 | 
			
		||||
        if ret.is_err() {
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
        // prevents parallelism errors
 | 
			
		||||
        fence(Ordering::SeqCst);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    blocking_wait_ready()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
 | 
			
		||||
    pac::FLASH.cr().modify(|w| {
 | 
			
		||||
        w.set_per(true);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    pac::FLASH.ar().write(|w| w.set_far(sector.start));
 | 
			
		||||
 | 
			
		||||
    pac::FLASH.cr().modify(|w| {
 | 
			
		||||
        w.set_strt(true);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let mut ret: Result<(), Error> = blocking_wait_ready();
 | 
			
		||||
 | 
			
		||||
    if !pac::FLASH.sr().read().eop() {
 | 
			
		||||
        trace!("FLASH: EOP not set");
 | 
			
		||||
        ret = Err(Error::Prog);
 | 
			
		||||
    } else {
 | 
			
		||||
        pac::FLASH.sr().write(|w| w.set_eop(true));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pac::FLASH.cr().modify(|w| w.set_per(false));
 | 
			
		||||
 | 
			
		||||
    clear_all_err();
 | 
			
		||||
    if ret.is_err() {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -82,7 +86,7 @@ pub(crate) unsafe fn clear_all_err() {
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> {
 | 
			
		||||
unsafe fn blocking_wait_ready() -> Result<(), Error> {
 | 
			
		||||
    loop {
 | 
			
		||||
        let sr = pac::FLASH.sr().read();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,27 +2,108 @@ use core::convert::TryInto;
 | 
			
		||||
use core::ptr::write_volatile;
 | 
			
		||||
use core::sync::atomic::{fence, Ordering};
 | 
			
		||||
 | 
			
		||||
use super::{ERASE_SIZE, FLASH_BASE, FLASH_SIZE};
 | 
			
		||||
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
 | 
			
		||||
use crate::flash::Error;
 | 
			
		||||
use crate::pac;
 | 
			
		||||
 | 
			
		||||
const SECOND_BANK_SECTOR_START: u32 = 12;
 | 
			
		||||
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))]
 | 
			
		||||
mod alt_regions {
 | 
			
		||||
    use embassy_hal_common::PeripheralRef;
 | 
			
		||||
    use stm32_metapac::FLASH_SIZE;
 | 
			
		||||
 | 
			
		||||
unsafe fn is_dual_bank() -> bool {
 | 
			
		||||
    match FLASH_SIZE / 1024 {
 | 
			
		||||
        // 1 MB devices depend on configuration
 | 
			
		||||
        1024 => {
 | 
			
		||||
            if cfg!(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)) {
 | 
			
		||||
                pac::FLASH.optcr().read().db1m()
 | 
			
		||||
            } else {
 | 
			
		||||
                false
 | 
			
		||||
    use crate::_generated::flash_regions::{BANK1_REGION1, BANK1_REGION2, BANK1_REGION3};
 | 
			
		||||
    use crate::flash::{Bank1Region1, Bank1Region2, Flash, FlashBank, FlashRegion};
 | 
			
		||||
    use crate::peripherals::FLASH;
 | 
			
		||||
 | 
			
		||||
    pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion {
 | 
			
		||||
        size: 3 * BANK1_REGION3.erase_size,
 | 
			
		||||
        ..BANK1_REGION3
 | 
			
		||||
    };
 | 
			
		||||
    pub const ALT_BANK2_REGION1: FlashRegion = FlashRegion {
 | 
			
		||||
        bank: FlashBank::Bank2,
 | 
			
		||||
        base: BANK1_REGION1.base + FLASH_SIZE as u32 / 2,
 | 
			
		||||
        ..BANK1_REGION1
 | 
			
		||||
    };
 | 
			
		||||
    pub const ALT_BANK2_REGION2: FlashRegion = FlashRegion {
 | 
			
		||||
        bank: FlashBank::Bank2,
 | 
			
		||||
        base: BANK1_REGION2.base + FLASH_SIZE as u32 / 2,
 | 
			
		||||
        ..BANK1_REGION2
 | 
			
		||||
    };
 | 
			
		||||
    pub const ALT_BANK2_REGION3: FlashRegion = FlashRegion {
 | 
			
		||||
        bank: FlashBank::Bank2,
 | 
			
		||||
        base: BANK1_REGION3.base + FLASH_SIZE as u32 / 2,
 | 
			
		||||
        size: 3 * BANK1_REGION3.erase_size,
 | 
			
		||||
        ..BANK1_REGION3
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    pub const ALT_FLASH_REGIONS: [&FlashRegion; 6] = [
 | 
			
		||||
        &BANK1_REGION1,
 | 
			
		||||
        &BANK1_REGION2,
 | 
			
		||||
        &ALT_BANK1_REGION3,
 | 
			
		||||
        &ALT_BANK2_REGION1,
 | 
			
		||||
        &ALT_BANK2_REGION2,
 | 
			
		||||
        &ALT_BANK2_REGION3,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    pub type AltBank1Region1<'d> = Bank1Region1<'d>;
 | 
			
		||||
    pub type AltBank1Region2<'d> = Bank1Region2<'d>;
 | 
			
		||||
    pub struct AltBank1Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>);
 | 
			
		||||
    pub struct AltBank2Region1<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>);
 | 
			
		||||
    pub struct AltBank2Region2<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>);
 | 
			
		||||
    pub struct AltBank2Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>);
 | 
			
		||||
 | 
			
		||||
    pub struct AltFlashLayout<'d> {
 | 
			
		||||
        pub bank1_region1: AltBank1Region1<'d>,
 | 
			
		||||
        pub bank1_region2: AltBank1Region2<'d>,
 | 
			
		||||
        pub bank1_region3: AltBank1Region3<'d>,
 | 
			
		||||
        pub bank2_region1: AltBank2Region1<'d>,
 | 
			
		||||
        pub bank2_region2: AltBank2Region2<'d>,
 | 
			
		||||
        pub bank2_region3: AltBank2Region3<'d>,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl<'d> Flash<'d> {
 | 
			
		||||
        pub fn into_alt_regions(self) -> AltFlashLayout<'d> {
 | 
			
		||||
            unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(true)) };
 | 
			
		||||
 | 
			
		||||
            // SAFETY: We never expose the cloned peripheral references, and their instance is not public.
 | 
			
		||||
            // Also, all flash region operations are protected with a cs.
 | 
			
		||||
            let mut p = self.release();
 | 
			
		||||
            AltFlashLayout {
 | 
			
		||||
                bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }),
 | 
			
		||||
                bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }),
 | 
			
		||||
                bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3, unsafe { p.clone_unchecked() }),
 | 
			
		||||
                bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }),
 | 
			
		||||
                bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }),
 | 
			
		||||
                bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // 2 MB devices are always dual bank
 | 
			
		||||
        2048 => true,
 | 
			
		||||
        // All other devices are single bank
 | 
			
		||||
        _ => false,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl Drop for AltFlashLayout<'_> {
 | 
			
		||||
        fn drop(&mut self) {
 | 
			
		||||
            unsafe {
 | 
			
		||||
                super::lock();
 | 
			
		||||
                crate::pac::FLASH.optcr().modify(|r| r.set_db1m(false))
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))]
 | 
			
		||||
pub use alt_regions::{AltFlashLayout, ALT_FLASH_REGIONS};
 | 
			
		||||
 | 
			
		||||
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))]
 | 
			
		||||
pub fn get_flash_regions() -> &'static [&'static FlashRegion] {
 | 
			
		||||
    if unsafe { pac::FLASH.optcr().read().db1m() } {
 | 
			
		||||
        &ALT_FLASH_REGIONS
 | 
			
		||||
    } else {
 | 
			
		||||
        &FLASH_REGIONS
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)))]
 | 
			
		||||
pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
 | 
			
		||||
    &FLASH_REGIONS
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn lock() {
 | 
			
		||||
@@ -34,93 +115,34 @@ pub(crate) unsafe fn unlock() {
 | 
			
		||||
    pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> {
 | 
			
		||||
pub(crate) unsafe fn begin_write() {
 | 
			
		||||
    assert_eq!(0, WRITE_SIZE % 4);
 | 
			
		||||
 | 
			
		||||
    pac::FLASH.cr().write(|w| {
 | 
			
		||||
        w.set_pg(true);
 | 
			
		||||
        w.set_psize(pac::flash::vals::Psize::PSIZE32);
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    let ret = {
 | 
			
		||||
        let mut ret: Result<(), Error> = Ok(());
 | 
			
		||||
        let mut offset = offset;
 | 
			
		||||
        for chunk in buf.chunks(super::WRITE_SIZE) {
 | 
			
		||||
            for val in chunk.chunks(4) {
 | 
			
		||||
                write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap()));
 | 
			
		||||
                offset += val.len() as u32;
 | 
			
		||||
 | 
			
		||||
                // prevents parallelism errors
 | 
			
		||||
                fence(Ordering::SeqCst);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ret = blocking_wait_ready();
 | 
			
		||||
            if ret.is_err() {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        ret
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn end_write() {
 | 
			
		||||
    pac::FLASH.cr().write(|w| w.set_pg(false));
 | 
			
		||||
 | 
			
		||||
    ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct FlashSector {
 | 
			
		||||
    index: u8,
 | 
			
		||||
    size: u32,
 | 
			
		||||
}
 | 
			
		||||
pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
 | 
			
		||||
    let mut address = start_address;
 | 
			
		||||
    for val in buf.chunks(4) {
 | 
			
		||||
        write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
 | 
			
		||||
        address += val.len() as u32;
 | 
			
		||||
 | 
			
		||||
fn get_sector(addr: u32, dual_bank: bool) -> FlashSector {
 | 
			
		||||
    let offset = addr - FLASH_BASE as u32;
 | 
			
		||||
 | 
			
		||||
    let bank_size = match dual_bank {
 | 
			
		||||
        true => FLASH_SIZE / 2,
 | 
			
		||||
        false => FLASH_SIZE,
 | 
			
		||||
    } as u32;
 | 
			
		||||
 | 
			
		||||
    let bank = offset / bank_size;
 | 
			
		||||
    let offset_in_bank = offset % bank_size;
 | 
			
		||||
 | 
			
		||||
    let index_in_bank = if offset_in_bank >= ERASE_SIZE as u32 / 2 {
 | 
			
		||||
        4 + offset_in_bank / ERASE_SIZE as u32
 | 
			
		||||
    } else {
 | 
			
		||||
        offset_in_bank / (ERASE_SIZE as u32 / 8)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // First 4 sectors are 16KB, then one 64KB, and rest are 128KB
 | 
			
		||||
    let size = match index_in_bank {
 | 
			
		||||
        0..=3 => 16 * 1024,
 | 
			
		||||
        4 => 64 * 1024,
 | 
			
		||||
        _ => 128 * 1024,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let index = if bank == 1 {
 | 
			
		||||
        SECOND_BANK_SECTOR_START + index_in_bank
 | 
			
		||||
    } else {
 | 
			
		||||
        index_in_bank
 | 
			
		||||
    } as u8;
 | 
			
		||||
 | 
			
		||||
    FlashSector { index, size }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
 | 
			
		||||
    let mut addr = from;
 | 
			
		||||
    let dual_bank = is_dual_bank();
 | 
			
		||||
 | 
			
		||||
    while addr < to {
 | 
			
		||||
        let sector = get_sector(addr, dual_bank);
 | 
			
		||||
        erase_sector(sector.index)?;
 | 
			
		||||
        addr += sector.size;
 | 
			
		||||
        // prevents parallelism errors
 | 
			
		||||
        fence(Ordering::SeqCst);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
    blocking_wait_ready()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe fn erase_sector(sector: u8) -> Result<(), Error> {
 | 
			
		||||
    let bank = sector / SECOND_BANK_SECTOR_START as u8;
 | 
			
		||||
    let snb = (bank << 4) + (sector % SECOND_BANK_SECTOR_START as u8);
 | 
			
		||||
 | 
			
		||||
    trace!("Erasing sector: {}", sector);
 | 
			
		||||
pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
 | 
			
		||||
    let snb = ((sector.bank as u8) << 4) + sector.index_in_bank;
 | 
			
		||||
 | 
			
		||||
    pac::FLASH.cr().modify(|w| {
 | 
			
		||||
        w.set_ser(true);
 | 
			
		||||
@@ -148,7 +170,7 @@ pub(crate) unsafe fn clear_all_err() {
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> {
 | 
			
		||||
unsafe fn blocking_wait_ready() -> Result<(), Error> {
 | 
			
		||||
    loop {
 | 
			
		||||
        let sr = pac::FLASH.sr().read();
 | 
			
		||||
 | 
			
		||||
@@ -173,3 +195,80 @@ pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::flash::{get_sector, FlashBank};
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[cfg(stm32f429)]
 | 
			
		||||
    fn can_get_sector_single_bank() {
 | 
			
		||||
        const SMALL_SECTOR_SIZE: u32 = 16 * 1024;
 | 
			
		||||
        const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024;
 | 
			
		||||
        const LARGE_SECTOR_SIZE: u32 = 128 * 1024;
 | 
			
		||||
 | 
			
		||||
        let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32| {
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                FlashSector {
 | 
			
		||||
                    bank: FlashBank::Bank1,
 | 
			
		||||
                    index_in_bank,
 | 
			
		||||
                    start,
 | 
			
		||||
                    size
 | 
			
		||||
                },
 | 
			
		||||
                get_sector(address, &FLASH_REGIONS)
 | 
			
		||||
            )
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000);
 | 
			
		||||
        assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF);
 | 
			
		||||
        assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000);
 | 
			
		||||
        assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF);
 | 
			
		||||
 | 
			
		||||
        assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000);
 | 
			
		||||
        assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF);
 | 
			
		||||
 | 
			
		||||
        assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000);
 | 
			
		||||
        assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF);
 | 
			
		||||
        assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000);
 | 
			
		||||
        assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF);
 | 
			
		||||
 | 
			
		||||
        let assert_sector = |bank: FlashBank, index_in_bank: u8, start: u32, size: u32, address: u32| {
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                FlashSector {
 | 
			
		||||
                    bank,
 | 
			
		||||
                    index_in_bank,
 | 
			
		||||
                    start,
 | 
			
		||||
                    size
 | 
			
		||||
                },
 | 
			
		||||
                get_sector(address, &ALT_FLASH_REGIONS)
 | 
			
		||||
            )
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        assert_sector(FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000);
 | 
			
		||||
        assert_sector(FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF);
 | 
			
		||||
        assert_sector(FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000);
 | 
			
		||||
        assert_sector(FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF);
 | 
			
		||||
 | 
			
		||||
        assert_sector(FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000);
 | 
			
		||||
        assert_sector(FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF);
 | 
			
		||||
 | 
			
		||||
        assert_sector(FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000);
 | 
			
		||||
        assert_sector(FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF);
 | 
			
		||||
        assert_sector(FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000);
 | 
			
		||||
        assert_sector(FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF);
 | 
			
		||||
 | 
			
		||||
        assert_sector(FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000);
 | 
			
		||||
        assert_sector(FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF);
 | 
			
		||||
        assert_sector(FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000);
 | 
			
		||||
        assert_sector(FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF);
 | 
			
		||||
 | 
			
		||||
        assert_sector(FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000);
 | 
			
		||||
        assert_sector(FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF);
 | 
			
		||||
 | 
			
		||||
        assert_sector(FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000);
 | 
			
		||||
        assert_sector(FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF);
 | 
			
		||||
        assert_sector(FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000);
 | 
			
		||||
        assert_sector(FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,9 +2,14 @@ use core::convert::TryInto;
 | 
			
		||||
use core::ptr::write_volatile;
 | 
			
		||||
use core::sync::atomic::{fence, Ordering};
 | 
			
		||||
 | 
			
		||||
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
 | 
			
		||||
use crate::flash::Error;
 | 
			
		||||
use crate::pac;
 | 
			
		||||
 | 
			
		||||
pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
 | 
			
		||||
    &FLASH_REGIONS
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn lock() {
 | 
			
		||||
    pac::FLASH.cr().modify(|w| w.set_lock(true));
 | 
			
		||||
}
 | 
			
		||||
@@ -14,64 +19,36 @@ pub(crate) unsafe fn unlock() {
 | 
			
		||||
    pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> {
 | 
			
		||||
pub(crate) unsafe fn begin_write() {
 | 
			
		||||
    assert_eq!(0, WRITE_SIZE % 4);
 | 
			
		||||
 | 
			
		||||
    pac::FLASH.cr().write(|w| {
 | 
			
		||||
        w.set_pg(true);
 | 
			
		||||
        w.set_psize(pac::flash::vals::Psize::PSIZE32);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let ret = {
 | 
			
		||||
        let mut ret: Result<(), Error> = Ok(());
 | 
			
		||||
        let mut offset = offset;
 | 
			
		||||
        for chunk in buf.chunks(super::WRITE_SIZE) {
 | 
			
		||||
            for val in chunk.chunks(4) {
 | 
			
		||||
                write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap()));
 | 
			
		||||
                offset += val.len() as u32;
 | 
			
		||||
 | 
			
		||||
                // prevents parallelism errors
 | 
			
		||||
                fence(Ordering::SeqCst);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ret = blocking_wait_ready();
 | 
			
		||||
            if ret.is_err() {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        ret
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    pac::FLASH.cr().write(|w| w.set_pg(false));
 | 
			
		||||
 | 
			
		||||
    ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
 | 
			
		||||
    let start_sector = if from >= (super::FLASH_BASE + super::ERASE_SIZE / 2) as u32 {
 | 
			
		||||
        4 + (from - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32
 | 
			
		||||
    } else {
 | 
			
		||||
        (from - super::FLASH_BASE as u32) / (super::ERASE_SIZE as u32 / 8)
 | 
			
		||||
    };
 | 
			
		||||
pub(crate) unsafe fn end_write() {
 | 
			
		||||
    pac::FLASH.cr().write(|w| w.set_pg(false));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    let end_sector = if to >= (super::FLASH_BASE + super::ERASE_SIZE / 2) as u32 {
 | 
			
		||||
        4 + (to - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32
 | 
			
		||||
    } else {
 | 
			
		||||
        (to - super::FLASH_BASE as u32) / (super::ERASE_SIZE as u32 / 8)
 | 
			
		||||
    };
 | 
			
		||||
pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
 | 
			
		||||
    let mut address = start_address;
 | 
			
		||||
    for val in buf.chunks(4) {
 | 
			
		||||
        write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
 | 
			
		||||
        address += val.len() as u32;
 | 
			
		||||
 | 
			
		||||
    for sector in start_sector..end_sector {
 | 
			
		||||
        let ret = erase_sector(sector as u8);
 | 
			
		||||
        if ret.is_err() {
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
        // prevents parallelism errors
 | 
			
		||||
        fence(Ordering::SeqCst);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
    blocking_wait_ready()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe fn erase_sector(sector: u8) -> Result<(), Error> {
 | 
			
		||||
pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
 | 
			
		||||
    pac::FLASH.cr().modify(|w| {
 | 
			
		||||
        w.set_ser(true);
 | 
			
		||||
        w.set_snb(sector)
 | 
			
		||||
        w.set_snb(sector.index_in_bank)
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    pac::FLASH.cr().modify(|w| {
 | 
			
		||||
@@ -107,7 +84,7 @@ pub(crate) unsafe fn clear_all_err() {
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> {
 | 
			
		||||
unsafe fn blocking_wait_ready() -> Result<(), Error> {
 | 
			
		||||
    loop {
 | 
			
		||||
        let sr = pac::FLASH.sr().read();
 | 
			
		||||
 | 
			
		||||
@@ -132,3 +109,75 @@ pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::flash::{get_sector, FlashBank};
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[cfg(stm32f732)]
 | 
			
		||||
    fn can_get_sector() {
 | 
			
		||||
        const SMALL_SECTOR_SIZE: u32 = 16 * 1024;
 | 
			
		||||
        const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024;
 | 
			
		||||
        const LARGE_SECTOR_SIZE: u32 = 128 * 1024;
 | 
			
		||||
 | 
			
		||||
        let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32| {
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                FlashSector {
 | 
			
		||||
                    bank: FlashBank::Bank1,
 | 
			
		||||
                    index_in_bank,
 | 
			
		||||
                    start,
 | 
			
		||||
                    size
 | 
			
		||||
                },
 | 
			
		||||
                get_sector(address, &FLASH_REGIONS)
 | 
			
		||||
            )
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000);
 | 
			
		||||
        assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF);
 | 
			
		||||
        assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000);
 | 
			
		||||
        assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF);
 | 
			
		||||
 | 
			
		||||
        assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000);
 | 
			
		||||
        assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF);
 | 
			
		||||
 | 
			
		||||
        assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000);
 | 
			
		||||
        assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF);
 | 
			
		||||
        assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000);
 | 
			
		||||
        assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[cfg(stm32f769)]
 | 
			
		||||
    fn can_get_sector() {
 | 
			
		||||
        const SMALL_SECTOR_SIZE: u32 = 32 * 1024;
 | 
			
		||||
        const MEDIUM_SECTOR_SIZE: u32 = 128 * 1024;
 | 
			
		||||
        const LARGE_SECTOR_SIZE: u32 = 256 * 1024;
 | 
			
		||||
 | 
			
		||||
        let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32| {
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                FlashSector {
 | 
			
		||||
                    bank: FlashBank::Bank1,
 | 
			
		||||
                    index_in_bank,
 | 
			
		||||
                    start,
 | 
			
		||||
                    size
 | 
			
		||||
                },
 | 
			
		||||
                get_sector(address, &FLASH_REGIONS)
 | 
			
		||||
            )
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000);
 | 
			
		||||
        assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_7FFF);
 | 
			
		||||
        assert_sector(3, 0x0801_8000, SMALL_SECTOR_SIZE, 0x0801_8000);
 | 
			
		||||
        assert_sector(3, 0x0801_8000, SMALL_SECTOR_SIZE, 0x0801_FFFF);
 | 
			
		||||
 | 
			
		||||
        assert_sector(4, 0x0802_0000, MEDIUM_SECTOR_SIZE, 0x0802_0000);
 | 
			
		||||
        assert_sector(4, 0x0802_0000, MEDIUM_SECTOR_SIZE, 0x0803_FFFF);
 | 
			
		||||
 | 
			
		||||
        assert_sector(5, 0x0804_0000, LARGE_SECTOR_SIZE, 0x0804_0000);
 | 
			
		||||
        assert_sector(5, 0x0804_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF);
 | 
			
		||||
        assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080C_0000);
 | 
			
		||||
        assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,18 @@
 | 
			
		||||
use core::convert::TryInto;
 | 
			
		||||
use core::ptr::write_volatile;
 | 
			
		||||
 | 
			
		||||
use atomic_polyfill::{fence, Ordering};
 | 
			
		||||
 | 
			
		||||
use super::{FlashRegion, FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE};
 | 
			
		||||
use crate::flash::Error;
 | 
			
		||||
use crate::pac;
 | 
			
		||||
 | 
			
		||||
const SECOND_BANK_OFFSET: usize = 0x0010_0000;
 | 
			
		||||
 | 
			
		||||
const fn is_dual_bank() -> bool {
 | 
			
		||||
    super::FLASH_SIZE / 2 > super::ERASE_SIZE
 | 
			
		||||
    FLASH_REGIONS.len() == 2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn get_flash_regions() -> &'static [&'static FlashRegion] {
 | 
			
		||||
    &FLASH_REGIONS
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn lock() {
 | 
			
		||||
@@ -20,90 +25,64 @@ pub(crate) unsafe fn lock() {
 | 
			
		||||
pub(crate) unsafe fn unlock() {
 | 
			
		||||
    pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0x4567_0123));
 | 
			
		||||
    pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0xCDEF_89AB));
 | 
			
		||||
 | 
			
		||||
    if is_dual_bank() {
 | 
			
		||||
        pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0x4567_0123));
 | 
			
		||||
        pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0xCDEF_89AB));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> {
 | 
			
		||||
    let bank = if !is_dual_bank() || (offset - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32 {
 | 
			
		||||
pub(crate) unsafe fn begin_write() {
 | 
			
		||||
    assert_eq!(0, WRITE_SIZE % 4);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn end_write() {}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
 | 
			
		||||
    // We cannot have the write setup sequence in begin_write as it depends on the address
 | 
			
		||||
    let bank = if start_address < BANK1_REGION.end() {
 | 
			
		||||
        pac::FLASH.bank(0)
 | 
			
		||||
    } else {
 | 
			
		||||
        pac::FLASH.bank(1)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    bank.cr().write(|w| {
 | 
			
		||||
        w.set_pg(true);
 | 
			
		||||
        w.set_psize(2); // 32 bits at once
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    cortex_m::asm::isb();
 | 
			
		||||
    cortex_m::asm::dsb();
 | 
			
		||||
    core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
 | 
			
		||||
    fence(Ordering::SeqCst);
 | 
			
		||||
 | 
			
		||||
    let ret = {
 | 
			
		||||
        let mut ret: Result<(), Error> = Ok(());
 | 
			
		||||
        let mut offset = offset;
 | 
			
		||||
        'outer: for chunk in buf.chunks(super::WRITE_SIZE) {
 | 
			
		||||
            for val in chunk.chunks(4) {
 | 
			
		||||
                trace!("Writing at {:x}", offset);
 | 
			
		||||
                write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap()));
 | 
			
		||||
                offset += val.len() as u32;
 | 
			
		||||
    let mut res = None;
 | 
			
		||||
    let mut address = start_address;
 | 
			
		||||
    for val in buf.chunks(4) {
 | 
			
		||||
        write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
 | 
			
		||||
        address += val.len() as u32;
 | 
			
		||||
 | 
			
		||||
                ret = blocking_wait_ready(bank);
 | 
			
		||||
                bank.sr().modify(|w| {
 | 
			
		||||
                    if w.eop() {
 | 
			
		||||
                        w.set_eop(true);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                if ret.is_err() {
 | 
			
		||||
                    break 'outer;
 | 
			
		||||
                }
 | 
			
		||||
        res = Some(blocking_wait_ready(bank));
 | 
			
		||||
        bank.sr().modify(|w| {
 | 
			
		||||
            if w.eop() {
 | 
			
		||||
                w.set_eop(true);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        if res.unwrap().is_err() {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        ret
 | 
			
		||||
    };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bank.cr().write(|w| w.set_pg(false));
 | 
			
		||||
 | 
			
		||||
    cortex_m::asm::isb();
 | 
			
		||||
    cortex_m::asm::dsb();
 | 
			
		||||
    core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
 | 
			
		||||
    fence(Ordering::SeqCst);
 | 
			
		||||
 | 
			
		||||
    ret
 | 
			
		||||
    res.unwrap()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
 | 
			
		||||
    let from = from - super::FLASH_BASE as u32;
 | 
			
		||||
    let to = to - super::FLASH_BASE as u32;
 | 
			
		||||
 | 
			
		||||
    let (start, end) = if to <= super::FLASH_SIZE as u32 {
 | 
			
		||||
        let start_sector = from / super::ERASE_SIZE as u32;
 | 
			
		||||
        let end_sector = to / super::ERASE_SIZE as u32;
 | 
			
		||||
        (start_sector, end_sector)
 | 
			
		||||
    } else {
 | 
			
		||||
        error!("Attempting to write outside of defined sectors {:x} {:x}", from, to);
 | 
			
		||||
        return Err(Error::Unaligned);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    trace!("Erasing sectors from {} to {}", start, end);
 | 
			
		||||
    for sector in start..end {
 | 
			
		||||
        let bank = if sector >= 8 { 1 } else { 0 };
 | 
			
		||||
        let ret = erase_sector(pac::FLASH.bank(bank), (sector % 8) as u8);
 | 
			
		||||
        if ret.is_err() {
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe fn erase_sector(bank: pac::flash::Bank, sector: u8) -> Result<(), Error> {
 | 
			
		||||
pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
 | 
			
		||||
    let bank = pac::FLASH.bank(sector.bank as usize);
 | 
			
		||||
    bank.cr().modify(|w| {
 | 
			
		||||
        w.set_ser(true);
 | 
			
		||||
        w.set_snb(sector)
 | 
			
		||||
        w.set_snb(sector.index_in_bank)
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    bank.cr().modify(|w| {
 | 
			
		||||
@@ -160,7 +139,7 @@ unsafe fn bank_clear_all_err(bank: pac::flash::Bank) {
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> {
 | 
			
		||||
unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> {
 | 
			
		||||
    loop {
 | 
			
		||||
        let sr = bank.sr().read();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,15 @@
 | 
			
		||||
use core::convert::TryInto;
 | 
			
		||||
use core::ptr::write_volatile;
 | 
			
		||||
 | 
			
		||||
use atomic_polyfill::{fence, Ordering};
 | 
			
		||||
 | 
			
		||||
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
 | 
			
		||||
use crate::flash::Error;
 | 
			
		||||
use crate::pac;
 | 
			
		||||
 | 
			
		||||
pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
 | 
			
		||||
    &FLASH_REGIONS
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn lock() {
 | 
			
		||||
    #[cfg(any(flash_wl, flash_wb, flash_l4))]
 | 
			
		||||
    pac::FLASH.cr().modify(|w| w.set_lock(true));
 | 
			
		||||
@@ -33,82 +39,75 @@ pub(crate) unsafe fn unlock() {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> {
 | 
			
		||||
pub(crate) unsafe fn begin_write() {
 | 
			
		||||
    assert_eq!(0, WRITE_SIZE % 4);
 | 
			
		||||
 | 
			
		||||
    #[cfg(any(flash_wl, flash_wb, flash_l4))]
 | 
			
		||||
    pac::FLASH.cr().write(|w| w.set_pg(true));
 | 
			
		||||
 | 
			
		||||
    let ret = {
 | 
			
		||||
        let mut ret: Result<(), Error> = Ok(());
 | 
			
		||||
        let mut offset = offset;
 | 
			
		||||
        for chunk in buf.chunks(super::WRITE_SIZE) {
 | 
			
		||||
            for val in chunk.chunks(4) {
 | 
			
		||||
                write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap()));
 | 
			
		||||
                offset += val.len() as u32;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ret = blocking_wait_ready();
 | 
			
		||||
            if ret.is_err() {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        ret
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    #[cfg(any(flash_wl, flash_wb, flash_l4))]
 | 
			
		||||
    pac::FLASH.cr().write(|w| w.set_pg(false));
 | 
			
		||||
 | 
			
		||||
    ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
 | 
			
		||||
    for page in (from..to).step_by(super::ERASE_SIZE) {
 | 
			
		||||
        #[cfg(any(flash_l0, flash_l1))]
 | 
			
		||||
        {
 | 
			
		||||
            pac::FLASH.pecr().modify(|w| {
 | 
			
		||||
                w.set_erase(true);
 | 
			
		||||
                w.set_prog(true);
 | 
			
		||||
            });
 | 
			
		||||
pub(crate) unsafe fn end_write() {
 | 
			
		||||
    #[cfg(any(flash_wl, flash_wb, flash_l4))]
 | 
			
		||||
    pac::FLASH.cr().write(|w| w.set_pg(false));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
            write_volatile(page as *mut u32, 0xFFFFFFFF);
 | 
			
		||||
        }
 | 
			
		||||
pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
 | 
			
		||||
    let mut address = start_address;
 | 
			
		||||
    for val in buf.chunks(4) {
 | 
			
		||||
        write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
 | 
			
		||||
        address += val.len() as u32;
 | 
			
		||||
 | 
			
		||||
        #[cfg(any(flash_wl, flash_wb, flash_l4))]
 | 
			
		||||
        {
 | 
			
		||||
            let idx = (page - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32;
 | 
			
		||||
 | 
			
		||||
            #[cfg(flash_l4)]
 | 
			
		||||
            let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) };
 | 
			
		||||
 | 
			
		||||
            pac::FLASH.cr().modify(|w| {
 | 
			
		||||
                w.set_per(true);
 | 
			
		||||
                w.set_pnb(idx as u8);
 | 
			
		||||
                #[cfg(any(flash_wl, flash_wb))]
 | 
			
		||||
                w.set_strt(true);
 | 
			
		||||
                #[cfg(any(flash_l4))]
 | 
			
		||||
                w.set_start(true);
 | 
			
		||||
                #[cfg(any(flash_l4))]
 | 
			
		||||
                w.set_bker(bank);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let ret: Result<(), Error> = blocking_wait_ready();
 | 
			
		||||
 | 
			
		||||
        #[cfg(any(flash_wl, flash_wb, flash_l4))]
 | 
			
		||||
        pac::FLASH.cr().modify(|w| w.set_per(false));
 | 
			
		||||
 | 
			
		||||
        #[cfg(any(flash_l0, flash_l1))]
 | 
			
		||||
        pac::FLASH.pecr().modify(|w| {
 | 
			
		||||
            w.set_erase(false);
 | 
			
		||||
            w.set_prog(false);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        clear_all_err();
 | 
			
		||||
        if ret.is_err() {
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
        // prevents parallelism errors
 | 
			
		||||
        fence(Ordering::SeqCst);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
    blocking_wait_ready()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
 | 
			
		||||
    #[cfg(any(flash_l0, flash_l1))]
 | 
			
		||||
    {
 | 
			
		||||
        pac::FLASH.pecr().modify(|w| {
 | 
			
		||||
            w.set_erase(true);
 | 
			
		||||
            w.set_prog(true);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        write_volatile(sector.start as *mut u32, 0xFFFFFFFF);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[cfg(any(flash_wl, flash_wb, flash_l4))]
 | 
			
		||||
    {
 | 
			
		||||
        let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32;
 | 
			
		||||
 | 
			
		||||
        #[cfg(flash_l4)]
 | 
			
		||||
        let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) };
 | 
			
		||||
 | 
			
		||||
        pac::FLASH.cr().modify(|w| {
 | 
			
		||||
            w.set_per(true);
 | 
			
		||||
            w.set_pnb(idx as u8);
 | 
			
		||||
            #[cfg(any(flash_wl, flash_wb))]
 | 
			
		||||
            w.set_strt(true);
 | 
			
		||||
            #[cfg(any(flash_l4))]
 | 
			
		||||
            w.set_start(true);
 | 
			
		||||
            #[cfg(any(flash_l4))]
 | 
			
		||||
            w.set_bker(bank);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let ret: Result<(), Error> = blocking_wait_ready();
 | 
			
		||||
 | 
			
		||||
    #[cfg(any(flash_wl, flash_wb, flash_l4))]
 | 
			
		||||
    pac::FLASH.cr().modify(|w| w.set_per(false));
 | 
			
		||||
 | 
			
		||||
    #[cfg(any(flash_l0, flash_l1))]
 | 
			
		||||
    pac::FLASH.pecr().modify(|w| {
 | 
			
		||||
        w.set_erase(false);
 | 
			
		||||
        w.set_prog(false);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    clear_all_err();
 | 
			
		||||
 | 
			
		||||
    ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn clear_all_err() {
 | 
			
		||||
@@ -149,7 +148,7 @@ pub(crate) unsafe fn clear_all_err() {
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> {
 | 
			
		||||
unsafe fn blocking_wait_ready() -> Result<(), Error> {
 | 
			
		||||
    loop {
 | 
			
		||||
        let sr = pac::FLASH.sr().read();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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];
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user