Skip to content

Commit

Permalink
Merge pull request #140 from stm32-rs/flash-eeprom
Browse files Browse the repository at this point in the history
Flash: Support writing single bytes to EEPROM
  • Loading branch information
hannobraun committed Jan 6, 2021
2 parents ecf5cc4 + 89cfe99 commit 3fc4f66
Showing 1 changed file with 80 additions and 26 deletions.
106 changes: 80 additions & 26 deletions src/flash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,37 @@ use crate::{
};

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

/// The size of a Flash memory page, in bytes
pub const PAGE_SIZE: u32 = 128;
pub const PAGE_SIZE: usize = 128;

// EEPROM sizes in bytes, generated with cube-parse
#[cfg(feature = "eeprom-256")]
pub const EEPROM_SIZE: u32 = 256;
pub const EEPROM_SIZE: usize = 256;
#[cfg(feature = "eeprom-1024")]
pub const EEPROM_SIZE: u32 = 1024;
pub const EEPROM_SIZE: usize = 1024;
#[cfg(feature = "eeprom-3072")]
pub const EEPROM_SIZE: u32 = 3072;
pub const EEPROM_SIZE: usize = 3072;
#[cfg(feature = "eeprom-2048")]
pub const EEPROM_SIZE: u32 = 2048;
pub const EEPROM_SIZE: usize = 2048;
#[cfg(feature = "eeprom-6144")]
pub const EEPROM_SIZE: u32 = 6144;
pub const EEPROM_SIZE: usize = 6144;
#[cfg(feature = "eeprom-512")]
pub const EEPROM_SIZE: u32 = 512;
pub const EEPROM_SIZE: usize = 512;
#[cfg(feature = "eeprom-128")]
pub const EEPROM_SIZE: u32 = 128;
pub const EEPROM_SIZE: usize = 128;

// EEPROM start addresses
const EEPROM_START_BANK1: u32 = 0x0808_0000;
const EEPROM_START_BANK2: u32 = 0x0808_0C00;
pub const EEPROM_START_BANK1: usize = 0x0808_0000;
pub const EEPROM_START_BANK2: usize = 0x0808_0C00;

/// Entry point to the non-volatile memory (NVM) API
pub struct FLASH {
flash: pac::FLASH,
flash_end: u32,
eeprom_start: u32,
eeprom_end: u32,
flash_end: usize,
eeprom_start: usize,
eeprom_end: usize,
}

impl FLASH {
Expand Down Expand Up @@ -181,6 +181,48 @@ impl FLASH {
})
}

/// Writes a single byte to EEPROM
///
/// Please note that any access to Flash or EEPROM on the same memory bank
/// will be stalled until this operation completes.
///
/// # Constant Time Writes
///
/// Note that the write operation does not complete in constant time. If
/// all bits of the current value in EEPROM are set to 0, the new value is
/// written directly in `Tprog` (3.2 ms on the STM32L0x1). Otherwise, an
/// erase operation is executed first, resulting in a total duration of
/// 2x`Tprog` (6.4 ms on the STM32L0x1).
///
/// If constant time writes are important, you could set the FIX bit to
/// force the memory interface to always execute an erase before writing
/// new data. However, this is not currently supported in the HAL.
///
/// # Panics
///
/// Panics, if `address` does not point to EEPROM.
pub fn write_byte(&mut self, address: *mut u8, byte: u8) -> Result {
self.unlock(|self_| {
// Verify that the address points to EEPROM
let memory = self_.verify_address(address);
if !memory.is_eeprom() {
panic!("Address does not point to EEPROM memory");
}

// Wait, while the memory interface is busy.
while self_.flash.sr.read().bsy().is_active() {}

// Write memory
// Safe, as we know that this points to flash or EEPROM.
unsafe { address.write_volatile(byte) }

// Wait for operation to complete
while self_.flash.sr.read().bsy().is_active() {}

self_.check_errors()
})
}

/// Writes a half-page (16 words) of Flash memory
///
/// The memory written to must have been erased before, otherwise this
Expand All @@ -199,7 +241,7 @@ impl FLASH {
if !memory.is_flash() {
panic!("Address does not point to Flash memory");
}
if address as u32 & 0x3f != 0 {
if address as usize & 0x3f != 0 {
panic!("Address is not aligned to half-page boundary");
}
if words.len() != 16 {
Expand Down Expand Up @@ -236,16 +278,29 @@ impl FLASH {

self_.check_errors()

// No need to reset PECR flags, that's done by `unlock`.
// No need to manually reset PECR flags, that's done by `unlock`.
})
}

/// Unlock everything that needs unlocking:
///
/// - FLASH_PECR lock (PELOCK)
/// - Program memory lock (PRGLOCK)
/// - Option bytes lock (OPTLOCK)
///
/// Then, once unlocked, run the provided function.
///
/// References:
///
/// - STM32L0x1 reference manual (RM0377), section 3.3.4 (Writing/erasing the NVM)
fn unlock(&mut self, f: impl FnOnce(&mut Self) -> Result) -> Result {
// Unlock everything that needs unlocking
// FLASH_PECR lock
self.flash.pekeyr.write(|w| w.pekeyr().bits(0x89ABCDEF));
self.flash.pekeyr.write(|w| w.pekeyr().bits(0x02030405));
// Program memory lock
self.flash.prgkeyr.write(|w| w.prgkeyr().bits(0x8C9DAEBF));
self.flash.prgkeyr.write(|w| w.prgkeyr().bits(0x13141516));
// Option bytes lock
self.flash.optkeyr.write(|w| w.optkeyr().bits(0xFBEAD9C8));
self.flash.optkeyr.write(|w| w.optkeyr().bits(0x24252627));

Expand All @@ -257,8 +312,8 @@ impl FLASH {
result
}

fn verify_address(&self, address: *mut u32) -> Memory {
let address = address as u32;
fn verify_address<T>(&self, address: *mut T) -> Memory {
let address = address as usize;

let memory = match address {
_ if FLASH_START <= address && address < self.flash_end => Memory::Flash,
Expand All @@ -273,6 +328,7 @@ impl FLASH {
memory
}

/// Check for errors.
pub fn check_errors(&self) -> Result {
let sr = self.flash.sr.read();

Expand Down Expand Up @@ -332,10 +388,10 @@ impl FLASH {
// - STM32L0x1 reference manual, section 28.1.1
// - STM32L0x2 reference manual, section 33.1.1
// - STM32L0x3 reference manual, section 34.1.1
pub fn flash_size_in_kb() -> u32 {
pub fn flash_size_in_kb() -> usize {
// This is safe, as we're reading from a valid address (as per the
// reference manual) which is aligned to 16 bits.
unsafe { (0x1FF8_007C as *const u16).read() as u32 }
unsafe { (0x1FF8_007C as *const u16).read() as usize }
}

extern "C" {
Expand Down Expand Up @@ -371,11 +427,9 @@ impl Memory {
*self == Memory::Flash
}

// The following method is not currently used, but I left it here, in case
// new methods need this functionality later.
// fn is_eeprom(&self) -> bool {
// *self == Memory::Eeprom
// }
fn is_eeprom(&self) -> bool {
*self == Memory::Eeprom
}

fn is_other(&self) -> bool {
*self == Memory::Other
Expand Down

0 comments on commit 3fc4f66

Please sign in to comment.