diff --git a/CHANGELOG.md b/CHANGELOG.md index 5052bc63..7fc6aef6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,12 +23,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - `Pwm` struct with `split` method and implementation of embedded-hal::Pwm (similar to f1xx-hal) [#425] - VSCode setting file - Add CAN1 PB8/PB9 and SPI3 MOSI PC1 pin mappings for F446 [#421] +- Add embedded-storage traits for flash [#429] [#421]: https://github.com/stm32-rs/stm32f4xx-hal/pull/421 [#422]: https://github.com/stm32-rs/stm32f4xx-hal/pull/422 [#423]: https://github.com/stm32-rs/stm32f4xx-hal/pull/423 [#428]: https://github.com/stm32-rs/stm32f4xx-hal/pull/428 [#425]: https://github.com/stm32-rs/stm32f4xx-hal/pull/425 +[#429]: https://github.com/stm32-rs/stm32f4xx-hal/pull/429 ### Changed diff --git a/Cargo.toml b/Cargo.toml index 3784be88..c80e2b5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ fugit = "0.3.3" fugit-timer = "0.1.3" rtic-monotonic = { version = "1.0", optional = true } bitflags = "1.3.2" +embedded-storage = "0.2" [dependencies.stm32_i2s_v12x] version = "0.2.0" diff --git a/src/flash.rs b/src/flash.rs index 726f217f..1686edd3 100644 --- a/src/flash.rs +++ b/src/flash.rs @@ -1,3 +1,5 @@ +use embedded_storage::nor_flash::{MultiwriteNorFlash, NorFlash, ReadNorFlash}; + use crate::pac::FLASH; use crate::signature::FlashSize; use core::{ptr, slice}; @@ -46,6 +48,10 @@ pub trait FlashExt { /// Unlock flash for erasing/programming until this method's /// result is dropped fn unlocked(&mut self) -> UnlockedFlash; + // Returns true if flash is in dual bank organization + fn dual_bank(&self) -> bool; + /// Returns flash memory sector of a given offset. Returns none if offset is out of range. + fn sector(&self, offset: usize) -> Option; } impl FlashExt for FLASH { @@ -61,11 +67,114 @@ impl FlashExt for FLASH { unlock(self); UnlockedFlash { flash: self } } + + fn dual_bank(&self) -> bool { + match self.len() / 1024 { + // 1 MB devices depend on configuration + 1024 => { + if cfg!(any( + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f469", + feature = "stm32f479", + )) { + // DB1M bit is not present in all SVDs + // self.optcr.read().db1m().bit_is_set() + self.optcr.read().bits() & (1 << 30) != 0 + } else { + false + } + } + // 2 MB devices are always dual bank + 2048 => true, + // All other devices are single bank + _ => false, + } + } + + fn sector(&self, offset: usize) -> Option { + flash_sectors(self.len(), self.dual_bank()).find(|s| s.contains(offset)) + } } const PSIZE_X8: u8 = 0b00; +/// Read-only flash +/// +/// # Examples +/// +/// ``` +/// use stm32f4xx_hal::pac::Peripherals; +/// use stm32f4xx_hal::flash::LockedFlash; +/// use embedded_storage::nor_flash::ReadNorFlash; +/// +/// let dp = Peripherals::take().unwrap(); +/// let mut flash = LockedFlash::new(dp.FLASH); +/// println!("Flash capacity: {}", ReadNorFlash::capacity(&flash)); +/// +/// let mut buf = [0u8; 64]; +/// ReadNorFlash::read(&mut flash, 0x0, &mut buf).unwrap(); +/// println!("First 64 bytes of flash memory: {:?}", buf); +/// ``` +pub struct LockedFlash { + flash: FLASH, +} + +impl LockedFlash { + pub fn new(flash: FLASH) -> Self { + Self { flash } + } +} + +impl FlashExt for LockedFlash { + fn address(&self) -> usize { + self.flash.address() + } + + fn len(&self) -> usize { + self.flash.len() + } + + fn unlocked(&mut self) -> UnlockedFlash { + self.flash.unlocked() + } + + fn dual_bank(&self) -> bool { + self.flash.dual_bank() + } + + fn sector(&self, offset: usize) -> Option { + self.flash.sector(offset) + } +} + /// Result of `FlashExt::unlocked()` +/// +/// # Examples +/// +/// ``` +/// use stm32f4xx_hal::pac::Peripherals; +/// use stm32f4xx_hal::flash::{FlashExt, LockedFlash, UnlockedFlash}; +/// use embedded_storage::nor_flash::NorFlash; +/// +/// let dp = Peripherals::take().unwrap(); +/// let mut flash = LockedFlash::new(dp.FLASH); +/// +/// // Unlock flash for writing +/// let mut unlocked_flash = flash.unlocked(); +/// +/// // Erase the second 128 KB sector. +/// NorFlash::erase(&mut unlocked_flash, 128 * 1024, 256 * 1024).unwrap(); +/// +/// // Write some data at the start of the second 128 KB sector. +/// let buf = [0u8; 64]; +/// NorFlash::write(&mut unlocked_flash, 128 * 1024, &buf).unwrap(); +/// +/// // Lock flash by dropping +/// drop(unlocked_flash); +/// ``` pub struct UnlockedFlash<'a> { flash: &'a mut FLASH, } @@ -166,3 +275,145 @@ fn unlock(flash: &FLASH) { fn lock(flash: &FLASH) { flash.cr.modify(|_, w| w.lock().set_bit()); } + +/// Flash memory sector +pub struct FlashSector { + /// Sector number + pub number: u8, + /// Offset from base memory address + pub offset: usize, + /// Sector size in bytes + pub size: usize, +} + +impl FlashSector { + /// Returns true if given offset belongs to this sector + pub fn contains(&self, offset: usize) -> bool { + self.offset <= offset && offset < self.offset + self.size + } +} + +/// Iterator of flash memory sectors in a single bank. +/// Yields a size sequence of [16, 16, 16, 64, 128, 128, ..] +pub struct FlashSectorIterator { + index: u8, + start_sector: u8, + start_offset: usize, + end_offset: usize, +} + +impl FlashSectorIterator { + fn new(start_sector: u8, start_offset: usize, end_offset: usize) -> Self { + Self { + index: 0, + start_sector, + start_offset, + end_offset, + } + } +} + +impl Iterator for FlashSectorIterator { + type Item = FlashSector; + + fn next(&mut self) -> Option { + if self.start_offset >= self.end_offset { + None + } else { + // First 4 sectors are 16 KB, then one 64 KB and the rest are 128 KB + let size = match self.index { + 0..=3 => 16 * 1024, + 4 => 64 * 1024, + _ => 128 * 1024, + }; + + let sector = FlashSector { + number: self.start_sector + self.index, + offset: self.start_offset, + size, + }; + + self.index += 1; + self.start_offset += size; + + Some(sector) + } + } +} + +/// Returns iterator of flash memory sectors for single and dual bank flash. +/// Sectors are returned in continuous memory order, while sector numbers can have spaces between banks. +pub fn flash_sectors(flash_size: usize, dual_bank: bool) -> impl Iterator { + if dual_bank { + // Second memory bank always starts from sector 12 + FlashSectorIterator::new(0, 0, flash_size / 2).chain(FlashSectorIterator::new( + 12, + flash_size / 2, + flash_size, + )) + } else { + // Chain an empty iterator to match types + FlashSectorIterator::new(0, 0, flash_size).chain(FlashSectorIterator::new(0, 0, 0)) + } +} + +impl ReadNorFlash for LockedFlash { + type Error = Error; + + const READ_SIZE: usize = 1; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + let offset = offset as usize; + Ok(bytes.copy_from_slice(&self.flash.read()[offset..offset + bytes.len()])) + } + + fn capacity(&self) -> usize { + self.flash.len() + } +} + +impl<'a> ReadNorFlash for UnlockedFlash<'a> { + type Error = Error; + + const READ_SIZE: usize = 1; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + let offset = offset as usize; + Ok(bytes.copy_from_slice(&self.flash.read()[offset..offset + bytes.len()])) + } + + fn capacity(&self) -> usize { + self.flash.len() + } +} + +impl<'a> NorFlash for UnlockedFlash<'a> { + const WRITE_SIZE: usize = 1; + + // Use largest sector size of 128 KB. All smaller sectors will be erased together. + const ERASE_SIZE: usize = 128 * 1024; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + let mut current = from as usize; + + for sector in flash_sectors(self.flash.len(), self.flash.dual_bank()) { + if sector.contains(current) { + UnlockedFlash::erase(self, sector.number)?; + current += sector.size; + } + + if current >= to as usize { + break; + } + } + + Ok(()) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.program(offset as usize, bytes.iter()) + } +} + +// STM32F4 supports multiple writes +impl<'a> MultiwriteNorFlash for UnlockedFlash<'a> {}