use cortex_m::interrupt;
use crate::{
pac::{self, flash::acr::LATENCY_A},
rcc::{Enable, Rcc, Reset},
};
pub const FLASH_START: usize = 0x0800_0000;
pub const PAGE_SIZE: usize = 128;
#[cfg(feature = "eeprom-256")]
pub const EEPROM_SIZE: usize = 256;
#[cfg(feature = "eeprom-1024")]
pub const EEPROM_SIZE: usize = 1024;
#[cfg(feature = "eeprom-3072")]
pub const EEPROM_SIZE: usize = 3072;
#[cfg(feature = "eeprom-2048")]
pub const EEPROM_SIZE: usize = 2048;
#[cfg(feature = "eeprom-6144")]
pub const EEPROM_SIZE: usize = 6144;
#[cfg(feature = "eeprom-512")]
pub const EEPROM_SIZE: usize = 512;
#[cfg(feature = "eeprom-128")]
pub const EEPROM_SIZE: usize = 128;
pub const EEPROM_START_BANK1: usize = 0x0808_0000;
pub const EEPROM_START_BANK2: usize = 0x0808_0C00;
pub struct FLASH {
flash: pac::FLASH,
flash_end: usize,
eeprom_start: usize,
eeprom_end: usize,
}
impl FLASH {
pub fn new(flash: pac::FLASH, rcc: &mut Rcc) -> Self {
let flash_size_in_kb = flash_size_in_kb();
let flash_end = FLASH_START + flash_size_in_kb * 1024;
let eeprom_start = if flash_size_in_kb == 64 && EEPROM_SIZE == 3072 {
EEPROM_START_BANK2
} else {
EEPROM_START_BANK1
};
let eeprom_end = eeprom_start + EEPROM_SIZE;
pac::FLASH::enable(rcc);
pac::FLASH::reset(rcc);
Self {
flash,
flash_end,
eeprom_start,
eeprom_end,
}
}
pub fn set_wait_states(&mut self, wait_states: LATENCY_A) {
self.flash
.acr
.modify(|_, w| w.latency().variant(wait_states));
}
pub fn erase_flash_page(&mut self, address: *mut u32) -> Result {
self.unlock(|self_| {
let memory = self_.verify_address(address);
if !memory.is_flash() {
panic!("Address does not point to Flash memory");
}
if address as u32 & 0x7f != 0 {
panic!("Address is not aligned to page boundary");
}
while self_.flash.sr.read().bsy().is_active() {}
self_.flash.pecr.modify(|_, w| {
w.erase().set_bit();
w.prog().set_bit();
w
});
unsafe { address.write_volatile(0) }
while self_.flash.sr.read().bsy().is_active() {}
self_.check_errors()
})
}
pub fn write_word(&mut self, address: *mut u32, word: u32) -> Result {
self.unlock(|self_| {
self_.verify_address(address);
while self_.flash.sr.read().bsy().is_active() {}
unsafe { address.write_volatile(word) }
while self_.flash.sr.read().bsy().is_active() {}
self_.check_errors()
})
}
pub fn write_byte(&mut self, address: *mut u8, byte: u8) -> Result {
self.unlock(|self_| {
let memory = self_.verify_address(address);
if !memory.is_eeprom() {
panic!("Address does not point to EEPROM memory");
}
while self_.flash.sr.read().bsy().is_active() {}
unsafe { address.write_volatile(byte) }
while self_.flash.sr.read().bsy().is_active() {}
self_.check_errors()
})
}
pub fn write_flash_half_page(&mut self, address: *mut u32, words: &[u32]) -> Result {
self.unlock(|self_| {
let memory = self_.verify_address(address);
if !memory.is_flash() {
panic!("Address does not point to Flash memory");
}
if address as usize & 0x3f != 0 {
panic!("Address is not aligned to half-page boundary");
}
if words.len() != 16 {
panic!("`words` is not exactly a half-page of memory");
}
while self_.flash.sr.read().bsy().is_active() {}
self_.flash.pecr.modify(|_, w| {
w.fprg().set_bit();
w.prog().set_bit();
w
});
interrupt::free(|_| {
unsafe {
write_half_page(address, words.as_ptr());
}
});
while self_.flash.sr.read().bsy().is_active() {}
self_.check_errors()
})
}
fn unlock(&mut self, f: impl FnOnce(&mut Self) -> Result) -> Result {
self.flash.pekeyr.write(|w| w.pekeyr().bits(0x89ABCDEF));
self.flash.pekeyr.write(|w| w.pekeyr().bits(0x02030405));
self.flash.prgkeyr.write(|w| w.prgkeyr().bits(0x8C9DAEBF));
self.flash.prgkeyr.write(|w| w.prgkeyr().bits(0x13141516));
self.flash.optkeyr.write(|w| w.optkeyr().bits(0xFBEAD9C8));
self.flash.optkeyr.write(|w| w.optkeyr().bits(0x24252627));
let result = f(self);
self.flash.pecr.reset();
result
}
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,
_ if self.eeprom_start <= address && address < self.eeprom_end => Memory::Eeprom,
_ => Memory::Other,
};
if memory.is_other() {
panic!("Address is neither in Flash memory nor EEPROM");
}
memory
}
pub fn check_errors(&self) -> Result {
let sr = self.flash.sr.read();
if sr.fwwerr().bit_is_set() {
self.flash.sr.write(|w| w.fwwerr().set_bit());
return Err(Error::AbortedByFetch);
}
if sr.notzeroerr().bit_is_set() {
self.flash.sr.write(|w| w.notzeroerr().set_bit());
return Err(Error::NotErased);
}
if sr.rderr().bit_is_set() {
self.flash.sr.write(|w| w.rderr().set_bit());
return Err(Error::ReadProtection);
}
if sr.optverr().bit_is_set() {
self.flash.sr.write(|w| w.optverr().set_bit());
return Err(Error::ConfigMismatch);
}
if sr.sizerr().bit_is_set() {
self.flash.sr.write(|w| w.sizerr().set_bit());
return Err(Error::InvalidSize);
}
if sr.pgaerr().bit_is_set() {
self.flash.sr.write(|w| w.pgaerr().set_bit());
return Err(Error::InvalidAlignment);
}
if sr.wrperr().bit_is_set() {
self.flash.sr.write(|w| w.wrperr().set_bit());
return Err(Error::WriteProtection);
}
Ok(())
}
}
pub fn flash_size_in_kb() -> usize {
unsafe { (0x1FF8_007C as *const u16).read() as usize }
}
extern "C" {
fn write_half_page(address: *mut u32, words: *const u32);
}
#[derive(Copy, Clone, Eq, PartialEq)]
enum Memory {
Flash,
Eeprom,
Other,
}
impl Memory {
fn is_flash(&self) -> bool {
*self == Memory::Flash
}
fn is_eeprom(&self) -> bool {
*self == Memory::Eeprom
}
fn is_other(&self) -> bool {
*self == Memory::Other
}
}
type Result = core::result::Result<(), Error>;
#[derive(Debug)]
pub enum Error {
AbortedByFetch,
NotErased,
ReadProtection,
ConfigMismatch,
InvalidSize,
InvalidAlignment,
WriteProtection,
}