Skip to content

Commit 6613316

Browse files
Merge #429
429: Implement embedded-storage traits r=burrbull a=chemicstry This PR implements [embedded-storage](https://github.com/rust-embedded-community/embedded-storage) traits for flash. One major headache with F4 series is dual-bank flash and non-uniform sector sizes, which required quite a bit of code to abstract away. I went through all of the F4 reference manuals and believe that `flash_sectors(...)` function should be correct for all variants. AFAIK, there is no single source about flash layout for all the chips. Moreover, ST [lists](https://www.st.com/en/microcontrollers-microprocessors/stm32f4-series.html#products) F429 and F469 with 512 KB of flash as dual-bank, but there is no information about it in the reference manual. I believe this could be an error in the website and only 1 MB chips have dual-bank capabilities. The PAC crate is also missing `DB1M` field for some of the dual-bank capable chips, so for now I hardcoded the bit position in `OPTCR` register. The writable `NorFlash` trait was implemented for the largest sector size of 128 KB, because `embedded-storage` does not intend to supprot non-uniform sectors (see [comment](rust-embedded-community/embedded-storage#23 (comment))). Smaller sectors are erased together in the larger 128 KB group. There was a suggestion to add different types for the smaller sector ranges, but I'm not sure if that is useful? Co-authored-by: chemicstry <[email protected]>
2 parents 41d8dd9 + c519799 commit 6613316

File tree

3 files changed

+254
-0
lines changed

3 files changed

+254
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2323
- `Pwm` struct with `split` method and implementation of embedded-hal::Pwm (similar to f1xx-hal) [#425]
2424
- VSCode setting file
2525
- Add CAN1 PB8/PB9 and SPI3 MOSI PC1 pin mappings for F446 [#421]
26+
- Add embedded-storage traits for flash [#429]
2627

2728
[#421]: https://github.com/stm32-rs/stm32f4xx-hal/pull/421
2829
[#422]: https://github.com/stm32-rs/stm32f4xx-hal/pull/422
2930
[#423]: https://github.com/stm32-rs/stm32f4xx-hal/pull/423
3031
[#428]: https://github.com/stm32-rs/stm32f4xx-hal/pull/428
3132
[#425]: https://github.com/stm32-rs/stm32f4xx-hal/pull/425
33+
[#429]: https://github.com/stm32-rs/stm32f4xx-hal/pull/429
3234

3335
### Changed
3436

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ fugit = "0.3.3"
4545
fugit-timer = "0.1.3"
4646
rtic-monotonic = { version = "1.0", optional = true }
4747
bitflags = "1.3.2"
48+
embedded-storage = "0.2"
4849

4950
[dependencies.stm32_i2s_v12x]
5051
version = "0.2.0"

src/flash.rs

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use embedded_storage::nor_flash::{MultiwriteNorFlash, NorFlash, ReadNorFlash};
2+
13
use crate::pac::FLASH;
24
use crate::signature::FlashSize;
35
use core::{ptr, slice};
@@ -46,6 +48,10 @@ pub trait FlashExt {
4648
/// Unlock flash for erasing/programming until this method's
4749
/// result is dropped
4850
fn unlocked(&mut self) -> UnlockedFlash;
51+
// Returns true if flash is in dual bank organization
52+
fn dual_bank(&self) -> bool;
53+
/// Returns flash memory sector of a given offset. Returns none if offset is out of range.
54+
fn sector(&self, offset: usize) -> Option<FlashSector>;
4955
}
5056

5157
impl FlashExt for FLASH {
@@ -61,11 +67,114 @@ impl FlashExt for FLASH {
6167
unlock(self);
6268
UnlockedFlash { flash: self }
6369
}
70+
71+
fn dual_bank(&self) -> bool {
72+
match self.len() / 1024 {
73+
// 1 MB devices depend on configuration
74+
1024 => {
75+
if cfg!(any(
76+
feature = "stm32f427",
77+
feature = "stm32f429",
78+
feature = "stm32f437",
79+
feature = "stm32f439",
80+
feature = "stm32f469",
81+
feature = "stm32f479",
82+
)) {
83+
// DB1M bit is not present in all SVDs
84+
// self.optcr.read().db1m().bit_is_set()
85+
self.optcr.read().bits() & (1 << 30) != 0
86+
} else {
87+
false
88+
}
89+
}
90+
// 2 MB devices are always dual bank
91+
2048 => true,
92+
// All other devices are single bank
93+
_ => false,
94+
}
95+
}
96+
97+
fn sector(&self, offset: usize) -> Option<FlashSector> {
98+
flash_sectors(self.len(), self.dual_bank()).find(|s| s.contains(offset))
99+
}
64100
}
65101

66102
const PSIZE_X8: u8 = 0b00;
67103

104+
/// Read-only flash
105+
///
106+
/// # Examples
107+
///
108+
/// ```
109+
/// use stm32f4xx_hal::pac::Peripherals;
110+
/// use stm32f4xx_hal::flash::LockedFlash;
111+
/// use embedded_storage::nor_flash::ReadNorFlash;
112+
///
113+
/// let dp = Peripherals::take().unwrap();
114+
/// let mut flash = LockedFlash::new(dp.FLASH);
115+
/// println!("Flash capacity: {}", ReadNorFlash::capacity(&flash));
116+
///
117+
/// let mut buf = [0u8; 64];
118+
/// ReadNorFlash::read(&mut flash, 0x0, &mut buf).unwrap();
119+
/// println!("First 64 bytes of flash memory: {:?}", buf);
120+
/// ```
121+
pub struct LockedFlash {
122+
flash: FLASH,
123+
}
124+
125+
impl LockedFlash {
126+
pub fn new(flash: FLASH) -> Self {
127+
Self { flash }
128+
}
129+
}
130+
131+
impl FlashExt for LockedFlash {
132+
fn address(&self) -> usize {
133+
self.flash.address()
134+
}
135+
136+
fn len(&self) -> usize {
137+
self.flash.len()
138+
}
139+
140+
fn unlocked(&mut self) -> UnlockedFlash {
141+
self.flash.unlocked()
142+
}
143+
144+
fn dual_bank(&self) -> bool {
145+
self.flash.dual_bank()
146+
}
147+
148+
fn sector(&self, offset: usize) -> Option<FlashSector> {
149+
self.flash.sector(offset)
150+
}
151+
}
152+
68153
/// Result of `FlashExt::unlocked()`
154+
///
155+
/// # Examples
156+
///
157+
/// ```
158+
/// use stm32f4xx_hal::pac::Peripherals;
159+
/// use stm32f4xx_hal::flash::{FlashExt, LockedFlash, UnlockedFlash};
160+
/// use embedded_storage::nor_flash::NorFlash;
161+
///
162+
/// let dp = Peripherals::take().unwrap();
163+
/// let mut flash = LockedFlash::new(dp.FLASH);
164+
///
165+
/// // Unlock flash for writing
166+
/// let mut unlocked_flash = flash.unlocked();
167+
///
168+
/// // Erase the second 128 KB sector.
169+
/// NorFlash::erase(&mut unlocked_flash, 128 * 1024, 256 * 1024).unwrap();
170+
///
171+
/// // Write some data at the start of the second 128 KB sector.
172+
/// let buf = [0u8; 64];
173+
/// NorFlash::write(&mut unlocked_flash, 128 * 1024, &buf).unwrap();
174+
///
175+
/// // Lock flash by dropping
176+
/// drop(unlocked_flash);
177+
/// ```
69178
pub struct UnlockedFlash<'a> {
70179
flash: &'a mut FLASH,
71180
}
@@ -166,3 +275,145 @@ fn unlock(flash: &FLASH) {
166275
fn lock(flash: &FLASH) {
167276
flash.cr.modify(|_, w| w.lock().set_bit());
168277
}
278+
279+
/// Flash memory sector
280+
pub struct FlashSector {
281+
/// Sector number
282+
pub number: u8,
283+
/// Offset from base memory address
284+
pub offset: usize,
285+
/// Sector size in bytes
286+
pub size: usize,
287+
}
288+
289+
impl FlashSector {
290+
/// Returns true if given offset belongs to this sector
291+
pub fn contains(&self, offset: usize) -> bool {
292+
self.offset <= offset && offset < self.offset + self.size
293+
}
294+
}
295+
296+
/// Iterator of flash memory sectors in a single bank.
297+
/// Yields a size sequence of [16, 16, 16, 64, 128, 128, ..]
298+
pub struct FlashSectorIterator {
299+
index: u8,
300+
start_sector: u8,
301+
start_offset: usize,
302+
end_offset: usize,
303+
}
304+
305+
impl FlashSectorIterator {
306+
fn new(start_sector: u8, start_offset: usize, end_offset: usize) -> Self {
307+
Self {
308+
index: 0,
309+
start_sector,
310+
start_offset,
311+
end_offset,
312+
}
313+
}
314+
}
315+
316+
impl Iterator for FlashSectorIterator {
317+
type Item = FlashSector;
318+
319+
fn next(&mut self) -> Option<Self::Item> {
320+
if self.start_offset >= self.end_offset {
321+
None
322+
} else {
323+
// First 4 sectors are 16 KB, then one 64 KB and the rest are 128 KB
324+
let size = match self.index {
325+
0..=3 => 16 * 1024,
326+
4 => 64 * 1024,
327+
_ => 128 * 1024,
328+
};
329+
330+
let sector = FlashSector {
331+
number: self.start_sector + self.index,
332+
offset: self.start_offset,
333+
size,
334+
};
335+
336+
self.index += 1;
337+
self.start_offset += size;
338+
339+
Some(sector)
340+
}
341+
}
342+
}
343+
344+
/// Returns iterator of flash memory sectors for single and dual bank flash.
345+
/// Sectors are returned in continuous memory order, while sector numbers can have spaces between banks.
346+
pub fn flash_sectors(flash_size: usize, dual_bank: bool) -> impl Iterator<Item = FlashSector> {
347+
if dual_bank {
348+
// Second memory bank always starts from sector 12
349+
FlashSectorIterator::new(0, 0, flash_size / 2).chain(FlashSectorIterator::new(
350+
12,
351+
flash_size / 2,
352+
flash_size,
353+
))
354+
} else {
355+
// Chain an empty iterator to match types
356+
FlashSectorIterator::new(0, 0, flash_size).chain(FlashSectorIterator::new(0, 0, 0))
357+
}
358+
}
359+
360+
impl ReadNorFlash for LockedFlash {
361+
type Error = Error;
362+
363+
const READ_SIZE: usize = 1;
364+
365+
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
366+
let offset = offset as usize;
367+
Ok(bytes.copy_from_slice(&self.flash.read()[offset..offset + bytes.len()]))
368+
}
369+
370+
fn capacity(&self) -> usize {
371+
self.flash.len()
372+
}
373+
}
374+
375+
impl<'a> ReadNorFlash for UnlockedFlash<'a> {
376+
type Error = Error;
377+
378+
const READ_SIZE: usize = 1;
379+
380+
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
381+
let offset = offset as usize;
382+
Ok(bytes.copy_from_slice(&self.flash.read()[offset..offset + bytes.len()]))
383+
}
384+
385+
fn capacity(&self) -> usize {
386+
self.flash.len()
387+
}
388+
}
389+
390+
impl<'a> NorFlash for UnlockedFlash<'a> {
391+
const WRITE_SIZE: usize = 1;
392+
393+
// Use largest sector size of 128 KB. All smaller sectors will be erased together.
394+
const ERASE_SIZE: usize = 128 * 1024;
395+
396+
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
397+
let mut current = from as usize;
398+
399+
for sector in flash_sectors(self.flash.len(), self.flash.dual_bank()) {
400+
if sector.contains(current) {
401+
UnlockedFlash::erase(self, sector.number)?;
402+
current += sector.size;
403+
}
404+
405+
if current >= to as usize {
406+
break;
407+
}
408+
}
409+
410+
Ok(())
411+
}
412+
413+
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
414+
self.program(offset as usize, bytes.iter())
415+
}
416+
}
417+
418+
// STM32F4 supports multiple writes
419+
impl<'a> MultiwriteNorFlash for UnlockedFlash<'a> {}

0 commit comments

Comments
 (0)