From 9e6c94f2b10fa5e0a74cfe0d1c425417c47717fa Mon Sep 17 00:00:00 2001 From: chemicstry Date: Fri, 15 Jul 2022 03:02:36 +0300 Subject: [PATCH] Async F4 flash driver --- embassy-stm32/src/flash/f4.rs | 133 +++++++++++++++++++++++- embassy-stm32/src/flash/mod.rs | 85 +++++++++------ examples/stm32f4/Cargo.toml | 1 + examples/stm32f4/src/bin/flash_async.rs | 83 +++++++++++++++ 4 files changed, 268 insertions(+), 34 deletions(-) create mode 100644 examples/stm32f4/src/bin/flash_async.rs diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index b8327ce4..692c8897 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -1,11 +1,14 @@ use core::convert::TryInto; use core::ptr::write_volatile; +use core::task::Poll; use atomic_polyfill::{fence, Ordering}; +use embassy::waitqueue::AtomicWaker; +use futures::future::poll_fn; use super::{ERASE_SIZE, FLASH_BASE, FLASH_SIZE}; use crate::flash::Error; -use crate::pac; +use crate::{interrupt, pac}; const SECOND_BANK_SECTOR_START: u32 = 12; @@ -110,14 +113,14 @@ pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { while addr < to { let sector = get_sector(addr, dual_bank); - erase_sector(sector.index)?; + blocking_erase_sector(sector.index)?; addr += sector.size; } Ok(()) } -unsafe fn erase_sector(sector: u8) -> Result<(), Error> { +unsafe fn blocking_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); @@ -145,7 +148,6 @@ pub(crate) unsafe fn clear_all_err() { w.set_pgperr(true); w.set_pgaerr(true); w.set_wrperr(true); - w.set_eop(true); }); } @@ -174,3 +176,126 @@ pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { } } } + +pub(crate) async unsafe fn write(offset: u32, buf: &[u8]) -> Result<(), Error> { + pac::FLASH.cr().write(|w| { + w.set_pg(true); + w.set_psize(pac::flash::vals::Psize::PSIZE32); + w.set_eopie(true); + w.set_errie(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; + + // prevents parallelism errors + fence(Ordering::SeqCst); + } + + ret = wait_ready().await; + + if ret.is_err() { + break; + } + } + ret + }; + + pac::FLASH.cr().write(|w| { + w.set_pg(false); + w.set_eopie(false); + w.set_errie(false); +}); + + ret +} + +pub(crate) async unsafe fn 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).await?; + addr += sector.size; + } + + Ok(()) +} + +async unsafe fn erase_sector(sector: u8) -> Result<(), Error> { + let bank = sector / SECOND_BANK_SECTOR_START as u8; + let snb = (bank << 4) + (sector % SECOND_BANK_SECTOR_START as u8); + + trace!("Erasing sector: {}", sector); + + pac::FLASH.cr().modify(|w| { + w.set_ser(true); + w.set_snb(snb); + w.set_eopie(true); + w.set_errie(true); + }); + + pac::FLASH.cr().modify(|w| { + w.set_strt(true); + }); + + let ret: Result<(), Error> = wait_ready().await; + + pac::FLASH.cr().modify(|w| { + w.set_eopie(false); + w.set_errie(false); + }); + + clear_all_err(); + + ret +} + +static WAKER: AtomicWaker = AtomicWaker::new(); + +pub(crate) async unsafe fn wait_ready() -> Result<(), Error> { + poll_fn(|cx| { + WAKER.register(cx.waker()); + + let sr = pac::FLASH.sr().read(); + + if !sr.bsy() { + Poll::Ready(if sr.pgserr() { + Err(Error::Seq) + } else if sr.pgperr() { + Err(Error::Parallelism) + } else if sr.pgaerr() { + Err(Error::Unaligned) + } else if sr.wrperr() { + Err(Error::Protected) + } else { + Ok(()) + }) + } else { + return Poll::Pending; + } + }) + .await +} + +pub(crate) unsafe fn init() { + use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; + crate::interrupt::FLASH::steal().enable(); +} + +#[interrupt] +unsafe fn FLASH() { + // Clear IRQ flags + pac::FLASH.sr().write(|w| { + w.set_operr(true); + w.set_eop(true); + }); + + WAKER.wake(); +} diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 2047f70e..26ae0862 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -23,6 +23,9 @@ pub struct Flash<'d> { impl<'d> Flash<'d> { pub fn new(p: impl Unborrow) -> Self { unborrow!(p); + + unsafe { family::init(); } + Self { _inner: p, _phantom: PhantomData, @@ -143,45 +146,67 @@ impl<'d> NorFlash for Flash<'d> { } } -/* -cfg_if::cfg_if! { - if #[cfg(feature = "nightly")] - { - use embedded_storage_async::nor_flash::{AsyncNorFlash, AsyncReadNorFlash}; - use core::future::Future; +#[cfg(all(feature = "nightly", flash_f4))] +mod asynch { + use core::future::Future; - impl<'d> AsyncNorFlash for Flash<'d> { - const WRITE_SIZE: usize = ::WRITE_SIZE; - const ERASE_SIZE: usize = ::ERASE_SIZE; + use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; + use embedded_storage_async::nor_flash::{AsyncNorFlash, AsyncReadNorFlash}; - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - fn write<'a>(&'a mut self, offset: u32, data: &'a [u8]) -> Self::WriteFuture<'a> { - async move { - todo!() + use super::{family, Error, Flash, ERASE_SIZE, FLASH_BASE, FLASH_END, FLASH_SIZE, WRITE_SIZE}; + + impl<'d> AsyncReadNorFlash for Flash<'d> { + const READ_SIZE: usize = ::READ_SIZE; + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; + + fn read<'a>(&'a mut self, offset: u32, bytes: &'a mut [u8]) -> Self::ReadFuture<'a> { + async move { self.blocking_read(offset, bytes) } + } + + fn capacity(&self) -> usize { + FLASH_SIZE + } + } + + impl<'d> AsyncNorFlash for Flash<'d> { + const WRITE_SIZE: usize = ::WRITE_SIZE; + const ERASE_SIZE: usize = ::ERASE_SIZE; + + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; + + fn write<'a>(&'a mut self, offset: u32, data: &'a [u8]) -> Self::WriteFuture<'a> { + async move { + let addr = FLASH_BASE as u32 + offset; + if addr as usize + data.len() > FLASH_END { + return Err(Error::Size); } - } - - type EraseFuture<'a> = impl Future> + 'a where Self: 'a; - fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> { - async move { - todo!() + if addr as usize % WRITE_SIZE != 0 || data.len() as usize % WRITE_SIZE != 0 { + return Err(Error::Unaligned); } + trace!("Writing {} bytes at 0x{:x}", data.len(), addr); + + self.clear_all_err(); + + unsafe { family::write(addr, data).await } } } - impl<'d> AsyncReadNorFlash for Flash<'d> { - const READ_SIZE: usize = ::READ_SIZE; - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - fn read<'a>(&'a mut self, address: u32, data: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { - todo!() - } - } + type EraseFuture<'a> = impl Future> + 'a where Self: 'a; - fn capacity(&self) -> usize { - FLASH_SIZE + fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> { + async move { + 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::erase(from, to).await } } } } } -*/ diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 100c0e60..03179fee 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -20,6 +20,7 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa heapless = { version = "0.7.5", default-features = false } nb = "1.0.0" embedded-storage = "0.3.0" +embedded-storage-async = "0.3.0" usb-device = "0.2" usbd-serial = "0.1.1" diff --git a/examples/stm32f4/src/bin/flash_async.rs b/examples/stm32f4/src/bin/flash_async.rs new file mode 100644 index 00000000..b5c1f1ad --- /dev/null +++ b/examples/stm32f4/src/bin/flash_async.rs @@ -0,0 +1,83 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{info, unwrap}; +use embassy::executor::Spawner; +use embassy::time::{Duration, Timer}; +use embassy_stm32::flash::Flash; +use embassy_stm32::Peripherals; +use embassy_stm32::gpio::{AnyPin, Level, Speed, Output, Pin}; +use embedded_storage_async::nor_flash::{AsyncReadNorFlash, AsyncNorFlash}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy::task] +async fn blinky(p: AnyPin) { + let mut led = Output::new(p, Level::High, Speed::Low); + + loop { + info!("high"); + led.set_high(); + Timer::after(Duration::from_millis(300)).await; + + info!("low"); + led.set_low(); + Timer::after(Duration::from_millis(300)).await; + } +} + +#[embassy::main] +async fn main(spawner: Spawner, p: Peripherals) { + info!("Hello Flash!"); + + let mut f = Flash::unlock(p.FLASH); + + spawner.spawn(blinky(p.PB7.degrade())).unwrap(); + + // Sector 5 + // test_flash(&mut f, 128 * 1024, 128 * 1024).await; + + // Sectors 11..=16, across banks (128K, 16K, 16K, 16K, 16K, 64K) + // test_flash(&mut f, (1024 - 128) * 1024, 256 * 1024).await; + + // Sectors 23, last in bank 2 + test_flash(&mut f, (2048 - 128) * 1024, 128 * 1024).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.read(offset, &mut buf).await); + info!("Read: {=[u8]:x}", buf); + + info!("Erasing..."); + unwrap!(f.erase(offset, offset + size).await); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.read(offset, &mut buf).await); + 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.read(offset, &mut buf).await); + 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 + ] + ); +}