From e0b6bcb13bb3c5b419ace922b8f9955a0c620d35 Mon Sep 17 00:00:00 2001 From: Filip Brozovic Date: Tue, 18 Nov 2025 14:01:07 +0100 Subject: stm32: async flash erase/write for h7 --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/flash/h7.rs | 190 +++++++++++++++++++++++++++++++++-------- embassy-stm32/src/flash/mod.rs | 4 +- 3 files changed, 158 insertions(+), 37 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 8e3e802a4..ed523debd 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -66,6 +66,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - feat: add flash support for c0 family ([#4874](https://github.com/embassy-rs/embassy/pull/4874)) - fix: fixing channel numbers on vbat and vddcore for adc on adc - adc: adding disable to vbat +- feat: stm32/flash: add async support for h7 family ## 0.4.0 - 2025-08-26 diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 8a43cce3f..b342f4a83 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -1,10 +1,31 @@ use core::ptr::write_volatile; use core::sync::atomic::{Ordering, fence}; +use embassy_sync::waitqueue::AtomicWaker; +use pac::flash::regs::Sr; + use super::{BANK1_REGION, FLASH_REGIONS, FlashSector, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +static WAKER: AtomicWaker = AtomicWaker::new(); + +pub(crate) unsafe fn on_interrupt() { + // Clear IRQ flags + pac::FLASH.bank(0).ccr().write(|w| { + w.set_clr_eop(true); + w.set_clr_operr(true); + }); + if is_dual_bank() { + pac::FLASH.bank(1).ccr().write(|w| { + w.set_clr_eop(true); + w.set_clr_operr(true); + }); + } + + WAKER.wake(); +} + const fn is_dual_bank() -> bool { FLASH_REGIONS.len() >= 2 } @@ -29,12 +50,68 @@ pub(crate) unsafe fn unlock() { } } +pub(crate) unsafe fn enable_write() { + enable_blocking_write(); +} + +pub(crate) unsafe fn disable_write() { + disable_blocking_write(); +} + pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 4); } pub(crate) unsafe fn disable_blocking_write() {} +pub(crate) async unsafe fn 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); + #[cfg(flash_h7)] + w.set_psize(2); // 32 bits at once + w.set_eopie(true); + w.set_operrie(true); + }); + cortex_m::asm::isb(); + cortex_m::asm::dsb(); + fence(Ordering::SeqCst); + + 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(unwrap!(val.try_into()))); + address += val.len() as u32; + + res = Some(wait_ready(bank).await); + bank.sr().modify(|w| { + if w.eop() { + w.set_eop(true); + } + }); + if unwrap!(res).is_err() { + break; + } + } + + cortex_m::asm::isb(); + cortex_m::asm::dsb(); + fence(Ordering::SeqCst); + + bank.cr().write(|w| { + w.set_pg(false); + w.set_eopie(false); + w.set_operrie(false); + }); + + unwrap!(res) +} + 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() { @@ -77,6 +154,36 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) unwrap!(res) } +pub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Error> { + let bank = pac::FLASH.bank(sector.bank as usize); + bank.cr().modify(|w| { + w.set_ser(true); + #[cfg(flash_h7)] + w.set_snb(sector.index_in_bank); + #[cfg(flash_h7ab)] + w.set_ssn(sector.index_in_bank); + w.set_eopie(true); + w.set_operrie(true); + }); + + bank.cr().modify(|w| { + w.set_start(true); + }); + + cortex_m::asm::isb(); + cortex_m::asm::dsb(); + fence(Ordering::SeqCst); + + let ret: Result<(), Error> = wait_ready(bank).await; + bank.cr().modify(|w| { + w.set_ser(false); + w.set_eopie(false); + w.set_operrie(false); + }); + bank_clear_all_err(bank); + ret +} + pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { let bank = pac::FLASH.bank(sector.bank as usize); bank.cr().modify(|w| { @@ -112,46 +219,59 @@ unsafe fn bank_clear_all_err(bank: pac::flash::Bank) { bank.sr().modify(|_| {}); } +async fn wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { + use core::future::poll_fn; + use core::task::Poll; + + poll_fn(|cx| { + WAKER.register(cx.waker()); + + let sr = bank.sr().read(); + if !sr.bsy() && !sr.qw() { + Poll::Ready(get_result(sr)) + } else { + return Poll::Pending; + } + }) + .await +} + unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { loop { let sr = bank.sr().read(); if !sr.bsy() && !sr.qw() { - if sr.wrperr() { - return Err(Error::Protected); - } - if sr.pgserr() { - error!("pgserr"); - return Err(Error::Seq); - } - if sr.incerr() { - // writing to a different address when programming 256 bit word was not finished - error!("incerr"); - return Err(Error::Seq); - } - if sr.crcrderr() { - error!("crcrderr"); - return Err(Error::Seq); - } - if sr.operr() { - return Err(Error::Prog); - } - if sr.sneccerr1() { - // single ECC error - return Err(Error::Prog); - } - if sr.dbeccerr() { - // double ECC error - return Err(Error::Prog); - } - if sr.rdperr() { - return Err(Error::Protected); - } - if sr.rdserr() { - return Err(Error::Protected); - } - - return Ok(()); + return get_result(sr); } } } + +fn get_result(sr: Sr) -> Result<(), Error> { + if sr.wrperr() { + Err(Error::Protected) + } else if sr.pgserr() { + error!("pgserr"); + Err(Error::Seq) + } else if sr.incerr() { + // writing to a different address when programming 256 bit word was not finished + error!("incerr"); + Err(Error::Seq) + } else if sr.crcrderr() { + error!("crcrderr"); + Err(Error::Seq) + } else if sr.operr() { + Err(Error::Prog) + } else if sr.sneccerr1() { + // single ECC error + Err(Error::Prog) + } else if sr.dbeccerr() { + // double ECC error + Err(Error::Prog) + } else if sr.rdperr() { + Err(Error::Protected) + } else if sr.rdserr() { + Err(Error::Protected) + } else { + Ok(()) + } +} diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 39cd9b3a9..6211a37b7 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -1,14 +1,14 @@ //! Flash memory (FLASH) use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; -#[cfg(flash_f4)] +#[cfg(any(flash_f4, flash_h7, flash_h7ab))] mod asynch; #[cfg(flash)] mod common; #[cfg(eeprom)] mod eeprom; -#[cfg(flash_f4)] +#[cfg(any(flash_f4, flash_h7, flash_h7ab))] pub use asynch::InterruptHandler; #[cfg(flash)] pub use common::*; -- cgit From 8f6f6ae8e90610ddd76df001e618075524299d2b Mon Sep 17 00:00:00 2001 From: Filip Brozovic Date: Tue, 18 Nov 2025 14:05:34 +0100 Subject: stm32: add async flash example for h7 --- examples/stm32h7/src/bin/flash_async.rs | 84 +++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 examples/stm32h7/src/bin/flash_async.rs diff --git a/examples/stm32h7/src/bin/flash_async.rs b/examples/stm32h7/src/bin/flash_async.rs new file mode 100644 index 000000000..96d1936f3 --- /dev/null +++ b/examples/stm32h7/src/bin/flash_async.rs @@ -0,0 +1,84 @@ +#![no_std] +#![no_main] + +use defmt::{info, unwrap}; +use embassy_executor::Spawner; +use embassy_stm32::flash::{Flash, InterruptHandler}; +use embassy_stm32::gpio::{AnyPin, Level, Output, Speed}; +use embassy_stm32::{Peri, bind_interrupts}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + FLASH => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello Flash!"); + + let mut f = Flash::new(p.FLASH, Irqs); + + // Led should blink uninterrupted during ~2sec erase operation + spawner.spawn(blinky(p.PB14.into()).unwrap()); + + // Test on bank 2 in order not to stall CPU. + test_flash(&mut f, 1024 * 1024, 128 * 1024).await; +} + +#[embassy_executor::task] +async fn blinky(p: Peri<'static, AnyPin>) { + let mut led = Output::new(p, Level::High, Speed::Low); + + loop { + info!("high"); + led.set_high(); + Timer::after_millis(300).await; + + info!("low"); + led.set_low(); + Timer::after_millis(300).await; + } +} + +async fn test_flash<'a>(f: &mut Flash<'a>, offset: u32, size: u32) { + info!("Testing offset: {=u32:#X}, size: {=u32:#X}", offset, size); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.blocking_read(offset, &mut buf)); + info!("Read: {=[u8]:x}", buf); + + info!("Erasing..."); + unwrap!(f.erase(offset, offset + size).await); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.blocking_read(offset, &mut buf)); + info!("Read after erase: {=[u8]:x}", buf); + + info!("Writing..."); + unwrap!( + f.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, 30, 31, 32 + ] + ) + .await + ); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.blocking_read(offset, &mut buf)); + info!("Read: {=[u8]:x}", buf); + assert_eq!( + &buf[..], + &[ + 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, + 30, 31, 32 + ] + ); +} -- cgit