Skip to content

Commit

Permalink
Merge pull request #41 from mettz/master
Browse files Browse the repository at this point in the history
Improved flash support
  • Loading branch information
andresv authored Feb 22, 2021
2 parents 8d6aad5 + f1de556 commit 32d1d4c
Show file tree
Hide file tree
Showing 5 changed files with 343 additions and 0 deletions.
44 changes: 44 additions & 0 deletions examples/flash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#![deny(warnings)]
#![deny(unsafe_code)]
#![no_main]
#![no_std]

extern crate cortex_m;
extern crate cortex_m_rt as rt;
extern crate panic_halt;
extern crate stm32g0xx_hal as hal;

use core::convert::TryInto;
use cortex_m_semihosting::hprintln;
use hal::flash::*;
use hal::stm32;
use rt::entry;

#[entry]
fn main() -> ! {
let dp = stm32::Peripherals::take().expect("cannot take peripherals");
let flash = dp.FLASH;

match flash.unlock() {
Ok(mut unlocked) => {
let page = FlashPage(10);
unlocked.erase_page(page).expect("cannot erase Page10");

let addr = page.to_address();
unlocked
.write(addr, &0xCAFE_BABE_FACE_B00Cu64.to_le_bytes())
.expect("cannot write to Page10");

let mut buffer = [0; 8];
unlocked.read(addr, &mut buffer);
hprintln!(
"{:02X?}",
u64::from_le_bytes((&buffer[0..8]).try_into().expect("never fails"))
)
.ok();
}
Err(_) => hprintln!("Cannot unlock flash").unwrap(),
}

loop {}
}
242 changes: 242 additions & 0 deletions src/flash/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
mod traits;

use crate::stm32::FLASH;
use core::convert::TryInto;
use core::mem;
use cortex_m::interrupt;
pub use traits::{Error, FlashPage, Read, Result, WriteErase};

/// The first address of flash memory
pub const FLASH_START: usize = 0x0800_0000;
pub const FLASH_END: usize = 0x0801_FFFF;

/// The size of a Flash memory page, in bytes
pub const PAGE_SIZE: u32 = 2048;
/// How many Flash memory pages there are
pub const NUM_PAGES: u32 = 64;

const FLASH_KEY1: u32 = 0x4567_0123;
const FLASH_KEY2: u32 = 0xCDEF_89AB;

impl FlashPage {
/// This gives the starting address of a flash page in physical address
pub const fn to_address(&self) -> usize {
FLASH_START + self.0 * PAGE_SIZE as usize
}
}

pub trait FlashExt {
/// Unlocks Flash memory for erasure and writing
fn unlock(self) -> core::result::Result<UnlockedFlash, FLASH>;
}

impl FlashExt for FLASH {
fn unlock(self) -> core::result::Result<UnlockedFlash, FLASH> {
// Wait, while the memory interface is busy.
while self.sr.read().bsy().bit_is_set() {}

// Unlock flash
self.keyr.write(|w| unsafe { w.keyr().bits(FLASH_KEY1) });
self.keyr.write(|w| unsafe { w.keyr().bits(FLASH_KEY2) });

// Verify success
if self.cr.read().lock().bit_is_clear() {
Ok(UnlockedFlash { f: self })
} else {
Err(self)
}
}
}

/// Handle for an unlocked flash on which operations can be performed
pub struct UnlockedFlash {
f: FLASH,
}

impl UnlockedFlash {
/// Consumes the unlocked flash instance returning the locked one
pub fn lock(self) -> FLASH {
self.f.cr.modify(|_, w| w.lock().set_bit());
self.f
}
}

impl Read for UnlockedFlash {
type NativeType = u8;

fn read_native(&self, address: usize, array: &mut [Self::NativeType]) {
let mut address = address as *const Self::NativeType;

for data in array {
unsafe {
*data = core::ptr::read(address);
address = address.add(1);
}
}
}

fn read(&self, address: usize, buf: &mut [u8]) {
self.read_native(address, buf);
}
}

impl WriteErase for UnlockedFlash {
type NativeType = u64;

fn status(&self) -> Result {
let sr = self.f.sr.read();

if sr.bsy().bit_is_set() {
return Err(Error::Busy);
}

if sr.pgaerr().bit_is_set() || sr.progerr().bit_is_set() || sr.wrperr().bit_is_set() {
return Err(Error::Illegal);
}

Ok(())
}

fn erase_page(&mut self, page: FlashPage) -> Result {
if page.0 >= NUM_PAGES as usize {
return Err(Error::PageOutOfRange);
}

// Wait, while the memory interface is busy.
while self.f.sr.read().bsy().bit_is_set() {}

self.clear_errors();

// We absoluty can't have any access to Flash while preparing the
// erase, or the process will be interrupted. This includes any
// access to the vector table or interrupt handlers that might be
// caused by an interrupt.
interrupt::free(|_| {
self.f.cr.modify(|_, w| unsafe {
w.per().set_bit().pnb().bits(page.0 as u8).strt().set_bit()
});
});

let result = self.wait();
self.f.cr.modify(|_, w| w.per().clear_bit());

result
}

fn write_native(&mut self, address: usize, array: &[Self::NativeType]) -> Result {
// Wait, while the memory interface is busy.
while self.f.sr.read().bsy().bit_is_set() {}

// Enable Flash programming
self.clear_errors();
self.f.cr.modify(|_, w| w.pg().set_bit());

// It is only possible to program a double word (2 x 32-bit data).
let mut address = address as *mut u32;

for &word in array {
// We absoluty can't have any access to Flash while preparing the
// write, or the process will be interrupted. This includes any
// access to the vector table or interrupt handlers that might be
// caused by an interrupt.
interrupt::free(|_| {
// Safe, because we've verified the valididty of `address`.
unsafe {
address.write_volatile(word as u32);
address.offset(1).write_volatile((word >> 32) as u32);

address = address.add(2);
}
});

self.wait()?;

if self.f.sr.read().eop().bit_is_set() {
self.f.sr.modify(|_, w| w.eop().clear_bit());
}
}

self.f.cr.modify(|_, w| w.pg().clear_bit());

Ok(())
}

fn write(&mut self, address: usize, data: &[u8]) -> Result {
let address_offset = address % mem::align_of::<Self::NativeType>();
let unaligned_size = (mem::size_of::<Self::NativeType>() - address_offset)
% mem::size_of::<Self::NativeType>();

if unaligned_size > 0 {
let unaligned_data = &data[..unaligned_size];
// Handle unaligned address data, make it into a native write
let mut data = 0xffff_ffff_ffff_ffffu64;
for b in unaligned_data {
data = (data >> 8) | ((*b as Self::NativeType) << 56);
}

let unaligned_address = address - address_offset;
let native = &[data];
self.write_native(unaligned_address, native)?;
}

// Handle aligned address data
let aligned_data = &data[unaligned_size..];
let mut aligned_address = if unaligned_size > 0 {
address - address_offset + mem::size_of::<Self::NativeType>()
} else {
address
};

let mut chunks = aligned_data.chunks_exact(mem::size_of::<Self::NativeType>());

while let Some(exact_chunk) = chunks.next() {
// Write chunks
let native = &[Self::NativeType::from_ne_bytes(
exact_chunk.try_into().unwrap(),
)];
self.write_native(aligned_address, native)?;
aligned_address += mem::size_of::<Self::NativeType>();
}

let rem = chunks.remainder();

if !rem.is_empty() {
let mut data = 0xffff_ffff_ffff_ffffu64;
// Write remainder
for b in rem.iter().rev() {
data = (data << 8) | *b as Self::NativeType;
}

let native = &[data];
self.write_native(aligned_address, native)?;
}

Ok(())
}
}

impl UnlockedFlash {
fn clear_errors(&mut self) {
self.f.sr.modify(|_, w| {
w.progerr()
.set_bit()
.pgserr()
.set_bit()
.rderr()
.set_bit()
.optverr()
.set_bit()
.sizerr()
.set_bit()
.pgaerr()
.set_bit()
.wrperr()
.set_bit()
});
}

fn wait(&self) -> Result {
while self.f.sr.read().bsy().bit_is_set() {}
self.status()
}
}
55 changes: 55 additions & 0 deletions src/flash/traits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/// Flash page representation where each flash page represents a region of 2048 bytes. The flash
/// controller can only erase on a page basis.
#[derive(Copy, Clone, Debug)]
pub struct FlashPage(pub usize);

/// Flash operation error
#[derive(Copy, Clone, Debug)]
pub enum Error {
/// Flash controller is not done yet
Busy,
/// Error detected (by command execution, or because no command could be executed)
Illegal,
/// Set during read if ECC decoding logic detects correctable or uncorrectable error
EccError,
/// Page number is out of range
PageOutOfRange,
/// (Legal) command failed
Failure,
}

/// A type alias for the result of a Flash operation.
pub type Result = core::result::Result<(), Error>;

pub trait Read {
/// Native type of the flash for reading with the correct alignment of the memory and size
///
/// Can be `u8`, `u16`, `u32`, ..., or any user defined type
type NativeType;

/// Read from the flash memory using the native interface
fn read_native(&self, address: usize, array: &mut [Self::NativeType]);

/// Read a buffer of bytes from memory
fn read(&self, address: usize, buf: &mut [u8]);
}

pub trait WriteErase {
/// Native type of the flash for writing with the correct alignment and size
///
/// Can be `u8`, `u16`, `u32`, ..., or any user defined type
type NativeType;

/// check flash status
fn status(&self) -> Result;

/// Erase specified flash page.
fn erase_page(&mut self, page: FlashPage) -> Result;

/// The smallest possible write, depends on platform
fn write_native(&mut self, address: usize, array: &[Self::NativeType]) -> Result;

/// Read a buffer of bytes to memory, this uses the native writes internally and if it's not
/// the same length and a set of native writes the write will be padded to fill a native write.
fn write(&mut self, address: usize, data: &[u8]) -> Result;
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ pub mod delay;
pub mod dma;
pub mod dmamux;
pub mod exti;
pub mod flash;
pub mod gpio;
pub mod i2c;
pub mod prelude;
Expand Down
1 change: 1 addition & 0 deletions src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub use crate::dma::DmaExt as _;
// pub use crate::dma::ReadDma as _;
// pub use crate::dma::WriteDma as _;
pub use crate::exti::ExtiExt as _;
pub use crate::flash::FlashExt as _;
pub use crate::gpio::GpioExt as _;
pub use crate::i2c::I2cExt as _;
pub use crate::rcc::LSCOExt as _;
Expand Down

0 comments on commit 32d1d4c

Please sign in to comment.