use core::ptr;
use crate::common::Constrain;
use crate::power::{self, Power};
use crate::stm32l0x1::{flash, FLASH};
use crate::time::Hertz;
use fhal::flash::{Locking, WriteErase};
mod private {
pub trait Sealed {}
impl Sealed for crate::power::VCoreRange1 {}
impl Sealed for crate::power::VCoreRange2 {}
impl Sealed for crate::power::VCoreRange3 {}
}
impl Constrain<Flash> for FLASH {
fn constrain(self) -> Flash {
Flash {
acr: ACR(()),
sr: SR(()),
pecr: PECR(()),
pekeyr: PEKEYR(()),
prgkeyr: PRGKEYR(()),
}
}
}
#[derive(Debug)]
pub enum FlashStatus {
Busy,
Done,
Ready,
HVOngoing,
}
#[derive(Debug)]
pub enum FlashError {
Locked,
WriteProtect,
Alignment,
Size,
ReadProtection,
NotZero,
FetchAbort,
Unknown,
}
impl FlashError {
pub fn clear(&self, sr: &mut SR) {
match self {
FlashError::WriteProtect => sr.inner().modify(|_, w| w.wrperr().set_bit()),
FlashError::Alignment => sr.inner().modify(|_, w| w.pgaerr().set_bit()),
FlashError::Size => sr.inner().modify(|_, w| w.sizerr().set_bit()),
FlashError::ReadProtection => sr.inner().modify(|_, w| w.rderr().set_bit()),
FlashError::NotZero => sr.inner().modify(|_, w| w.notzeroerr().set_bit()),
FlashError::FetchAbort => sr.inner().modify(|_, w| w.fwwerr().set_bit()),
_ => {}
};
}
}
pub struct Flash {
acr: ACR,
sr: SR,
pecr: PECR,
pekeyr: PEKEYR,
prgkeyr: PRGKEYR,
}
impl Flash {
pub fn set_latency<VDD, VCORE, RTC>(&mut self, sysclk: Hertz, _pwr: &Power<VDD, VCORE, RTC>)
where
VCORE: Latency,
{
unsafe {
VCORE::latency(sysclk).set(&mut self.acr);
}
}
pub fn get_latency(&mut self) -> FlashLatency {
match self.acr.acr().read().latency().bit() {
true => FlashLatency::_1_Clk,
false => FlashLatency::_0_Clk,
}
}
pub unsafe fn program_word_immediate(&mut self, address: usize, value: u32) {
ptr::write_volatile(address as *mut u32, value);
}
}
pub trait Latency: private::Sealed {
fn latency(f: Hertz) -> FlashLatency;
}
impl Latency for power::VCoreRange1 {
fn latency(f: Hertz) -> FlashLatency {
if f.0 > 16_000_000 {
FlashLatency::_1_Clk
} else {
FlashLatency::_0_Clk
}
}
}
impl Latency for power::VCoreRange2 {
fn latency(f: Hertz) -> FlashLatency {
if f.0 > 8_000_000 {
FlashLatency::_1_Clk
} else {
FlashLatency::_0_Clk
}
}
}
impl Latency for power::VCoreRange3 {
fn latency(_: Hertz) -> FlashLatency {
FlashLatency::_0_Clk
}
}
#[allow(non_camel_case_types)]
pub enum FlashLatency {
_1_Clk,
_0_Clk,
}
impl FlashLatency {
pub unsafe fn set(&self, acr: &mut ACR) {
match self {
FlashLatency::_1_Clk => acr.flash_latency_1(),
FlashLatency::_0_Clk => acr.flash_latency_0(),
};
}
}
pub struct ACR(());
impl ACR {
pub(crate) fn acr(&mut self) -> &flash::ACR {
unsafe { &(*FLASH::ptr()).acr }
}
pub(crate) fn flash_latency_1(&mut self) -> FlashLatency {
self.acr().modify(|_, w| w.latency().set_bit());
while self.acr().read().latency().bit_is_clear() {}
FlashLatency::_1_Clk
}
pub(crate) fn flash_latency_0(&mut self) -> FlashLatency {
self.acr().modify(|_, w| w.latency().clear_bit());
while self.acr().read().latency().bit_is_set() {}
FlashLatency::_0_Clk
}
}
pub struct SR(());
impl SR {
pub(crate) fn inner(&self) -> &flash::SR {
unsafe { &(*FLASH::ptr()).sr }
}
pub fn is_busy(&self) -> bool {
self.inner().read().bsy().bit_is_set()
}
}
pub struct PECR(());
impl PECR {
fn inner() -> &'static flash::PECR {
unsafe { &(*FLASH::ptr()).pecr }
}
pub fn is_pecr_locked(&self) -> bool {
Self::inner().read().pelock().bit_is_set()
}
pub fn is_prgmem_locked(&self) -> bool {
Self::inner().read().prglock().bit_is_set()
}
pub fn lock(&mut self) {
Self::inner().modify(|_, w| w.pelock().set_bit());
}
pub fn enable_erase(&mut self) -> Result<(), FlashError> {
if self.is_pecr_locked() {
Err(FlashError::Locked)
} else {
Self::inner().modify(|_, w| w.erase().set_bit().prog().set_bit());
Ok(())
}
}
pub fn disable_erase(&mut self) -> Result<(), FlashError> {
if self.is_pecr_locked() {
Err(FlashError::Locked)
} else {
Self::inner().modify(|_, w| w.erase().clear_bit().prog().clear_bit());
Ok(())
}
}
}
pub struct PEKEYR(());
impl PEKEYR {
pub(crate) fn inner(&mut self) -> &flash::PEKEYR {
unsafe { &(*FLASH::ptr()).pekeyr }
}
}
pub struct PRGKEYR(());
impl PRGKEYR {
pub(crate) fn inner(&mut self) -> &flash::PRGKEYR {
unsafe { &(*FLASH::ptr()).prgkeyr }
}
}
const PEKEY1: u32 = 0x89ABCDEF;
const PEKEY2: u32 = 0x02030405;
const PRGKEY1: u32 = 0x8C9DAEBF;
const PRGKEY2: u32 = 0x13141516;
impl Locking for Flash {
type Error = FlashError;
fn is_locked(&self) -> bool {
self.pecr.is_prgmem_locked()
}
fn lock(&mut self) {
self.pecr.lock();
}
fn unlock(&mut self) {
if self.sr.is_busy() {
panic!("cannot lock! flash is busy");
}
if self.pecr.is_pecr_locked() {
self.pekeyr.inner().write(|w| w.bits(PEKEY1));
self.pekeyr.inner().write(|w| w.bits(PEKEY2));
if self.pecr.is_prgmem_locked() {
self.prgkeyr.inner().write(|w| w.bits(PRGKEY1));
self.prgkeyr.inner().write(|w| w.bits(PRGKEY2));
}
}
}
}
impl WriteErase for Flash
where
Flash: Locking,
{
type Error = FlashError;
type Status = FlashStatus;
fn status(&self) -> Result<Self::Status, Self::Error> {
if self.sr.is_busy() {
Ok(FlashStatus::Busy)
} else if self.sr.inner().read().eop().bit_is_set() {
Ok(FlashStatus::Done)
} else if self.sr.inner().read().ready().bit_is_set() {
Ok(FlashStatus::Ready)
} else if self.sr.inner().read().endhv().bit_is_clear() {
Ok(FlashStatus::HVOngoing)
} else if self.sr.inner().read().wrperr().bit_is_set() {
Err(FlashError::WriteProtect)
} else if self.sr.inner().read().pgaerr().bit_is_set() {
Err(FlashError::Alignment)
} else if self.sr.inner().read().sizerr().bit_is_set() {
Err(FlashError::Size)
} else if self.sr.inner().read().rderr().bit_is_set() {
Err(FlashError::ReadProtection)
} else if self.sr.inner().read().notzeroerr().bit_is_set() {
Err(FlashError::NotZero)
} else if self.sr.inner().read().fwwerr().bit_is_set() {
Err(FlashError::FetchAbort)
} else {
Err(FlashError::Unknown)
}
}
fn erase_page(&mut self, address: usize) -> Result<(), FlashError> {
self.pecr.enable_erase()?;
unsafe {
ptr::write_volatile(address as *mut u32, 0);
}
while self.sr.is_busy() {}
let result = match self.status() {
Ok(_) => Ok(()),
Err(e) => Err(e),
};
self.pecr.disable_erase()?;
result
}
fn program_word(&mut self, address: usize, value: u32) -> Result<(), FlashError> {
self.erase_page(address)?;
unsafe { self.program_word_immediate(address, value) };
while self.sr.is_busy() {}
match self.status() {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
}
}