From 734c38eb9c6385dfb51e3934211f140c623fefc8 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 11 Jul 2022 03:57:46 +0300 Subject: [PATCH] Add F4 flash driver --- embassy-stm32/src/flash/f4.rs | 175 ++++++++++++++++++++++++++++++ embassy-stm32/src/flash/mod.rs | 1 + embassy-stm32/src/lib.rs | 4 +- examples/stm32f4/Cargo.toml | 1 + examples/stm32f4/src/bin/flash.rs | 57 ++++++++++ 5 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 embassy-stm32/src/flash/f4.rs create mode 100644 examples/stm32f4/src/bin/flash.rs diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs new file mode 100644 index 00000000..0afa9e82 --- /dev/null +++ b/embassy-stm32/src/flash/f4.rs @@ -0,0 +1,175 @@ +use core::convert::TryInto; +use core::ptr::write_volatile; + +use atomic_polyfill::{fence, Ordering}; + +use super::{ERASE_SIZE, FLASH_BASE, FLASH_SIZE}; +use crate::flash::Error; +use crate::pac; + +// Only available on some devices +const SECOND_BANK_OFFSET: usize = FLASH_SIZE / 2; +const SECOND_BANK_SECTOR_START: u32 = 12; + +unsafe fn is_dual_bank() -> bool { + match FLASH_SIZE / 1024 { + // 1 MB devices depend on configuration + 1024 => { + if cfg!(any( + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f469", + feature = "stm32f479", + )) { + pac::FLASH.optcr().read().db1m() + } else { + false + } + } + // 2 MB devices are always dual bank + 2048 => true, + // All other devices are single bank + _ => false, + } +} + +pub(crate) unsafe fn lock() { + pac::FLASH.cr().modify(|w| w.set_lock(true)); +} + +pub(crate) unsafe fn unlock() { + pac::FLASH.keyr().write(|w| w.set_key(0x4567_0123)); + pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); +} + +pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { + 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 +} + +unsafe fn get_sector(addr: u32) -> u8 { + let offset = addr - FLASH_BASE as u32; + + let sector = if is_dual_bank() { + let bank = offset / SECOND_BANK_OFFSET as u32; + let offset_in_bank = offset % SECOND_BANK_OFFSET as u32; + + let sector_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) + }; + + if bank == 1 { + SECOND_BANK_SECTOR_START + sector_in_bank + } else { + sector_in_bank + } + } else { + if offset >= ERASE_SIZE as u32 / 2 { + 4 + offset / ERASE_SIZE as u32 + } else { + offset / (ERASE_SIZE as u32 / 8) + } + }; + + sector as u8 +} + +pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { + let start_sector = get_sector(from); + let end_sector = get_sector(to); + + for sector in start_sector..end_sector { + let ret = erase_sector(sector as u8); + if ret.is_err() { + return ret; + } + } + + Ok(()) +} + +unsafe fn erase_sector(sector: u8) -> Result<(), Error> { + let bank = (sector >= SECOND_BANK_SECTOR_START) as u8; + let snb = (bank << 4) + (sector % SECOND_BANK_SECTOR_START as u8); + + pac::FLASH.cr().modify(|w| { + w.set_ser(true); + w.set_snb(snb) + }); + + pac::FLASH.cr().modify(|w| { + w.set_strt(true); + }); + + let ret: Result<(), Error> = blocking_wait_ready(); + + clear_all_err(); + + ret +} + +pub(crate) unsafe fn clear_all_err() { + pac::FLASH.sr().write(|w| { + w.set_pgserr(true); + w.set_pgperr(true); + w.set_pgaerr(true); + w.set_wrperr(true); + w.set_eop(true); + }); +} + +pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { + loop { + let sr = pac::FLASH.sr().read(); + + if !sr.bsy() { + if sr.pgserr() { + return Err(Error::Seq); + } + + if sr.pgperr() { + return Err(Error::Parallelism); + } + + if sr.pgaerr() { + return Err(Error::Unaligned); + } + + if sr.wrperr() { + return Err(Error::Protected); + } + + return Ok(()); + } + } +} diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 31ca243a..59ca59f6 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -10,6 +10,7 @@ const FLASH_END: usize = FLASH_BASE + FLASH_SIZE; #[cfg_attr(any(flash_wl, flash_wb, flash_l0, flash_l1, flash_l4), path = "l.rs")] #[cfg_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")] mod family; diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 7be0c77e..8b816858 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -39,7 +39,9 @@ pub mod i2c; #[cfg(crc)] pub mod crc; -#[cfg(any(flash_l0, flash_l1, flash_wl, flash_wb, flash_l4, flash_f3, flash_f7, flash_h7))] +#[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(rng)] diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index de33ffad..100c0e60 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -19,6 +19,7 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } nb = "1.0.0" +embedded-storage = "0.3.0" usb-device = "0.2" usbd-serial = "0.1.1" diff --git a/examples/stm32f4/src/bin/flash.rs b/examples/stm32f4/src/bin/flash.rs new file mode 100644 index 00000000..b531d6f1 --- /dev/null +++ b/examples/stm32f4/src/bin/flash.rs @@ -0,0 +1,57 @@ +#![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 embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy::main] +async fn main(_spawner: Spawner, p: Peripherals) { + info!("Hello Flash!"); + + const ADDR: u32 = 0x10_0000; + + // wait a bit before accessing the flash + Timer::after(Duration::from_millis(300)).await; + + let mut f = Flash::unlock(p.FLASH); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.read(ADDR, &mut buf)); + info!("Read: {=[u8]:x}", buf); + + info!("Erasing..."); + unwrap!(f.erase(ADDR, ADDR + 128 * 1024)); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.read(ADDR, &mut buf)); + info!("Read after erase: {=[u8]:x}", buf); + + info!("Writing..."); + unwrap!(f.write( + ADDR, + &[ + 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 + ] + )); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.read(ADDR, &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 + ] + ); +}