Skip to content

Implement embedded-storage traits #429

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
251 changes: 251 additions & 0 deletions src/flash.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use embedded_storage::nor_flash::{MultiwriteNorFlash, NorFlash, ReadNorFlash};

use crate::pac::FLASH;
use crate::signature::FlashSize;
use core::{ptr, slice};
Expand Down Expand Up @@ -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<FlashSector>;
}

impl FlashExt for FLASH {
Expand All @@ -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<FlashSector> {
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<FlashSector> {
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,
}
Expand Down Expand Up @@ -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<Self::Item> {
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<Item = FlashSector> {
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> {}