Skip to content

Commit eb05ad4

Browse files
committed
Added FLASH support with basic example
1 parent db5b656 commit eb05ad4

File tree

5 files changed

+341
-0
lines changed

5 files changed

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

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)