Skip to content

Commit 32d1d4c

Browse files
authored
Merge pull request #41 from mettz/master
Improved flash support
2 parents 8d6aad5 + f1de556 commit 32d1d4c

File tree

5 files changed

+343
-0
lines changed

5 files changed

+343
-0
lines changed

examples/flash.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#![deny(warnings)]
2+
#![deny(unsafe_code)]
3+
#![no_main]
4+
#![no_std]
5+
6+
extern crate cortex_m;
7+
extern crate cortex_m_rt as rt;
8+
extern crate panic_halt;
9+
extern crate stm32g0xx_hal as hal;
10+
11+
use core::convert::TryInto;
12+
use cortex_m_semihosting::hprintln;
13+
use hal::flash::*;
14+
use hal::stm32;
15+
use rt::entry;
16+
17+
#[entry]
18+
fn main() -> ! {
19+
let dp = stm32::Peripherals::take().expect("cannot take peripherals");
20+
let flash = dp.FLASH;
21+
22+
match flash.unlock() {
23+
Ok(mut unlocked) => {
24+
let page = FlashPage(10);
25+
unlocked.erase_page(page).expect("cannot erase Page10");
26+
27+
let addr = page.to_address();
28+
unlocked
29+
.write(addr, &0xCAFE_BABE_FACE_B00Cu64.to_le_bytes())
30+
.expect("cannot write to Page10");
31+
32+
let mut buffer = [0; 8];
33+
unlocked.read(addr, &mut buffer);
34+
hprintln!(
35+
"{:02X?}",
36+
u64::from_le_bytes((&buffer[0..8]).try_into().expect("never fails"))
37+
)
38+
.ok();
39+
}
40+
Err(_) => hprintln!("Cannot unlock flash").unwrap(),
41+
}
42+
43+
loop {}
44+
}

src/flash/mod.rs

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
mod traits;
2+
3+
use crate::stm32::FLASH;
4+
use core::convert::TryInto;
5+
use core::mem;
6+
use cortex_m::interrupt;
7+
pub use traits::{Error, FlashPage, Read, Result, WriteErase};
8+
9+
/// The first address of flash memory
10+
pub const FLASH_START: usize = 0x0800_0000;
11+
pub const FLASH_END: usize = 0x0801_FFFF;
12+
13+
/// The size of a Flash memory page, in bytes
14+
pub const PAGE_SIZE: u32 = 2048;
15+
/// How many Flash memory pages there are
16+
pub const NUM_PAGES: u32 = 64;
17+
18+
const FLASH_KEY1: u32 = 0x4567_0123;
19+
const FLASH_KEY2: u32 = 0xCDEF_89AB;
20+
21+
impl FlashPage {
22+
/// This gives the starting address of a flash page in physical address
23+
pub const fn to_address(&self) -> usize {
24+
FLASH_START + self.0 * PAGE_SIZE as usize
25+
}
26+
}
27+
28+
pub trait FlashExt {
29+
/// Unlocks Flash memory for erasure and writing
30+
fn unlock(self) -> core::result::Result<UnlockedFlash, FLASH>;
31+
}
32+
33+
impl FlashExt for FLASH {
34+
fn unlock(self) -> core::result::Result<UnlockedFlash, FLASH> {
35+
// Wait, while the memory interface is busy.
36+
while self.sr.read().bsy().bit_is_set() {}
37+
38+
// Unlock flash
39+
self.keyr.write(|w| unsafe { w.keyr().bits(FLASH_KEY1) });
40+
self.keyr.write(|w| unsafe { w.keyr().bits(FLASH_KEY2) });
41+
42+
// Verify success
43+
if self.cr.read().lock().bit_is_clear() {
44+
Ok(UnlockedFlash { f: self })
45+
} else {
46+
Err(self)
47+
}
48+
}
49+
}
50+
51+
/// Handle for an unlocked flash on which operations can be performed
52+
pub struct UnlockedFlash {
53+
f: FLASH,
54+
}
55+
56+
impl UnlockedFlash {
57+
/// Consumes the unlocked flash instance returning the locked one
58+
pub fn lock(self) -> FLASH {
59+
self.f.cr.modify(|_, w| w.lock().set_bit());
60+
self.f
61+
}
62+
}
63+
64+
impl Read for UnlockedFlash {
65+
type NativeType = u8;
66+
67+
fn read_native(&self, address: usize, array: &mut [Self::NativeType]) {
68+
let mut address = address as *const Self::NativeType;
69+
70+
for data in array {
71+
unsafe {
72+
*data = core::ptr::read(address);
73+
address = address.add(1);
74+
}
75+
}
76+
}
77+
78+
fn read(&self, address: usize, buf: &mut [u8]) {
79+
self.read_native(address, buf);
80+
}
81+
}
82+
83+
impl WriteErase for UnlockedFlash {
84+
type NativeType = u64;
85+
86+
fn status(&self) -> Result {
87+
let sr = self.f.sr.read();
88+
89+
if sr.bsy().bit_is_set() {
90+
return Err(Error::Busy);
91+
}
92+
93+
if sr.pgaerr().bit_is_set() || sr.progerr().bit_is_set() || sr.wrperr().bit_is_set() {
94+
return Err(Error::Illegal);
95+
}
96+
97+
Ok(())
98+
}
99+
100+
fn erase_page(&mut self, page: FlashPage) -> Result {
101+
if page.0 >= NUM_PAGES as usize {
102+
return Err(Error::PageOutOfRange);
103+
}
104+
105+
// Wait, while the memory interface is busy.
106+
while self.f.sr.read().bsy().bit_is_set() {}
107+
108+
self.clear_errors();
109+
110+
// We absoluty can't have any access to Flash while preparing the
111+
// erase, or the process will be interrupted. This includes any
112+
// access to the vector table or interrupt handlers that might be
113+
// caused by an interrupt.
114+
interrupt::free(|_| {
115+
self.f.cr.modify(|_, w| unsafe {
116+
w.per().set_bit().pnb().bits(page.0 as u8).strt().set_bit()
117+
});
118+
});
119+
120+
let result = self.wait();
121+
self.f.cr.modify(|_, w| w.per().clear_bit());
122+
123+
result
124+
}
125+
126+
fn write_native(&mut self, address: usize, array: &[Self::NativeType]) -> Result {
127+
// Wait, while the memory interface is busy.
128+
while self.f.sr.read().bsy().bit_is_set() {}
129+
130+
// Enable Flash programming
131+
self.clear_errors();
132+
self.f.cr.modify(|_, w| w.pg().set_bit());
133+
134+
// It is only possible to program a double word (2 x 32-bit data).
135+
let mut address = address as *mut u32;
136+
137+
for &word in array {
138+
// We absoluty can't have any access to Flash while preparing the
139+
// write, or the process will be interrupted. This includes any
140+
// access to the vector table or interrupt handlers that might be
141+
// caused by an interrupt.
142+
interrupt::free(|_| {
143+
// Safe, because we've verified the valididty of `address`.
144+
unsafe {
145+
address.write_volatile(word as u32);
146+
address.offset(1).write_volatile((word >> 32) as u32);
147+
148+
address = address.add(2);
149+
}
150+
});
151+
152+
self.wait()?;
153+
154+
if self.f.sr.read().eop().bit_is_set() {
155+
self.f.sr.modify(|_, w| w.eop().clear_bit());
156+
}
157+
}
158+
159+
self.f.cr.modify(|_, w| w.pg().clear_bit());
160+
161+
Ok(())
162+
}
163+
164+
fn write(&mut self, address: usize, data: &[u8]) -> Result {
165+
let address_offset = address % mem::align_of::<Self::NativeType>();
166+
let unaligned_size = (mem::size_of::<Self::NativeType>() - address_offset)
167+
% mem::size_of::<Self::NativeType>();
168+
169+
if unaligned_size > 0 {
170+
let unaligned_data = &data[..unaligned_size];
171+
// Handle unaligned address data, make it into a native write
172+
let mut data = 0xffff_ffff_ffff_ffffu64;
173+
for b in unaligned_data {
174+
data = (data >> 8) | ((*b as Self::NativeType) << 56);
175+
}
176+
177+
let unaligned_address = address - address_offset;
178+
let native = &[data];
179+
self.write_native(unaligned_address, native)?;
180+
}
181+
182+
// Handle aligned address data
183+
let aligned_data = &data[unaligned_size..];
184+
let mut aligned_address = if unaligned_size > 0 {
185+
address - address_offset + mem::size_of::<Self::NativeType>()
186+
} else {
187+
address
188+
};
189+
190+
let mut chunks = aligned_data.chunks_exact(mem::size_of::<Self::NativeType>());
191+
192+
while let Some(exact_chunk) = chunks.next() {
193+
// Write chunks
194+
let native = &[Self::NativeType::from_ne_bytes(
195+
exact_chunk.try_into().unwrap(),
196+
)];
197+
self.write_native(aligned_address, native)?;
198+
aligned_address += mem::size_of::<Self::NativeType>();
199+
}
200+
201+
let rem = chunks.remainder();
202+
203+
if !rem.is_empty() {
204+
let mut data = 0xffff_ffff_ffff_ffffu64;
205+
// Write remainder
206+
for b in rem.iter().rev() {
207+
data = (data << 8) | *b as Self::NativeType;
208+
}
209+
210+
let native = &[data];
211+
self.write_native(aligned_address, native)?;
212+
}
213+
214+
Ok(())
215+
}
216+
}
217+
218+
impl UnlockedFlash {
219+
fn clear_errors(&mut self) {
220+
self.f.sr.modify(|_, w| {
221+
w.progerr()
222+
.set_bit()
223+
.pgserr()
224+
.set_bit()
225+
.rderr()
226+
.set_bit()
227+
.optverr()
228+
.set_bit()
229+
.sizerr()
230+
.set_bit()
231+
.pgaerr()
232+
.set_bit()
233+
.wrperr()
234+
.set_bit()
235+
});
236+
}
237+
238+
fn wait(&self) -> Result {
239+
while self.f.sr.read().bsy().bit_is_set() {}
240+
self.status()
241+
}
242+
}

src/flash/traits.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/// Flash page representation where each flash page represents a region of 2048 bytes. The flash
2+
/// controller can only erase on a page basis.
3+
#[derive(Copy, Clone, Debug)]
4+
pub struct FlashPage(pub usize);
5+
6+
/// Flash operation error
7+
#[derive(Copy, Clone, Debug)]
8+
pub enum Error {
9+
/// Flash controller is not done yet
10+
Busy,
11+
/// Error detected (by command execution, or because no command could be executed)
12+
Illegal,
13+
/// Set during read if ECC decoding logic detects correctable or uncorrectable error
14+
EccError,
15+
/// Page number is out of range
16+
PageOutOfRange,
17+
/// (Legal) command failed
18+
Failure,
19+
}
20+
21+
/// A type alias for the result of a Flash operation.
22+
pub type Result = core::result::Result<(), Error>;
23+
24+
pub trait Read {
25+
/// Native type of the flash for reading with the correct alignment of the memory and size
26+
///
27+
/// Can be `u8`, `u16`, `u32`, ..., or any user defined type
28+
type NativeType;
29+
30+
/// Read from the flash memory using the native interface
31+
fn read_native(&self, address: usize, array: &mut [Self::NativeType]);
32+
33+
/// Read a buffer of bytes from memory
34+
fn read(&self, address: usize, buf: &mut [u8]);
35+
}
36+
37+
pub trait WriteErase {
38+
/// Native type of the flash for writing with the correct alignment and size
39+
///
40+
/// Can be `u8`, `u16`, `u32`, ..., or any user defined type
41+
type NativeType;
42+
43+
/// check flash status
44+
fn status(&self) -> Result;
45+
46+
/// Erase specified flash page.
47+
fn erase_page(&mut self, page: FlashPage) -> Result;
48+
49+
/// The smallest possible write, depends on platform
50+
fn write_native(&mut self, address: usize, array: &[Self::NativeType]) -> Result;
51+
52+
/// Read a buffer of bytes to memory, this uses the native writes internally and if it's not
53+
/// the same length and a set of native writes the write will be padded to fill a native write.
54+
fn write(&mut self, address: usize, data: &[u8]) -> Result;
55+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ pub mod delay;
5353
pub mod dma;
5454
pub mod dmamux;
5555
pub mod exti;
56+
pub mod flash;
5657
pub mod gpio;
5758
pub mod i2c;
5859
pub mod prelude;

src/prelude.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub use crate::dma::DmaExt as _;
1818
// pub use crate::dma::ReadDma as _;
1919
// pub use crate::dma::WriteDma as _;
2020
pub use crate::exti::ExtiExt as _;
21+
pub use crate::flash::FlashExt as _;
2122
pub use crate::gpio::GpioExt as _;
2223
pub use crate::i2c::I2cExt as _;
2324
pub use crate::rcc::LSCOExt as _;

0 commit comments

Comments
 (0)