flash: init abstraction

This commit is contained in:
occheung 2020-12-18 17:37:56 +08:00
parent d6fdc36c78
commit 68d58857cf

View File

@ -1,64 +1,408 @@
use heapless::{String, consts::*};
use smoltcp as net;
use embedded_nal as nal;
use core::str::FromStr;
//! Flash memory, with major reference from STM32F7xx_HAL crate,
//! as well as @Astro 's work in STM32F4xx_HAL
unsafe fn read_record_length(addr: u32) -> u32 {
core::ptr::read(addr as *const u32)
use stm32h7xx_hal::pac::FLASH;
/// Base address of flash memory on AXIM interface.
const FLASH_BASE: *mut u8 = 0x800_0000 as *mut u8;
/// The last valid flash address in STM32H743
const MAX_FLASH_ADDRESS: *mut u8 = 0x81F_FFFF as *mut u8;
/// Flash programming error.
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
Busy,
Locked,
WriteProtection,
ProgrammingSequence,
Strobe,
Inconsistency,
Operation,
ErrorCorrectionCode,
ReadProtection,
ReadSecure,
WriteError
}
unsafe fn read_byte(addr: u32) -> u8 {
core::ptr::read(addr as *const u8)
/// Embedded flash memory.
pub struct Flash {
registers: FLASH,
}
fn read_flash_str(addr: &mut u32) -> (String<U32>, String<U64>) {
let mut key: String<U32> = String::new();
let mut string: String<U64> = String::new();
let record_length = unsafe {
read_record_length(*addr)
};
if record_length == 0xFFFFFFFF {
return (key, string);
impl Flash {
/// Creates a new Flash instance.
pub fn new(flash: FLASH) -> Self {
let mut flash = Self { registers: flash };
// Lock both banks initially
flash.lock();
flash
}
let mut key_string_div = false;
for f_addr in (*addr)+4..((*addr)+record_length) {
if key_string_div {
string.push(unsafe {read_byte(f_addr)} as char).unwrap();
} else {
let c = unsafe {read_byte(f_addr)};
if c == 0 {
key_string_div = true;
} else {
key.push(c as char).unwrap();
}
}
}
*addr += record_length;
(key, string)
/// Unlocks the FLASH_CR1/2 register.
pub fn unlock(&mut self) {
// Note: Unlocking an unlocked bank will cause HardFault
// Unlock bank 1 if needed.
if self.bank1_is_locked() {
self.registers.bank1_mut().keyr.write(|w| unsafe {
w.keyr().bits(0x45670123)
});
self.registers.bank1_mut().keyr.write(|w| unsafe {
w.keyr().bits(0xCDEF89AB)
});
}
pub fn read_flash(addr: &mut u32)
-> (net::wire::IpCidr, net::wire::EthernetAddress, nal::Ipv4Addr, String<U64>)
{
let mut cidr = net::wire::IpCidr::Ipv6(
net::wire::Ipv6Cidr::SOLICITED_NODE_PREFIX,
// Unlock bank 2 if needed.
if self.bank2_is_locked() {
self.registers.bank2_mut().keyr.write(|w| unsafe {
w.keyr().bits(0x45670123)
});
self.registers.bank2_mut().keyr.write(|w| unsafe {
w.keyr().bits(0xCDEF89AB)
});
}
}
/// Unlocks the FLASH_OPTCR register
pub fn unlock_optcr(&mut self) {
if self.optcr_is_locked() {
self.registers.optkeyr_mut().write(|w| unsafe {
w.optkeyr().bits(0x08192A3B)
});
self.registers.optkeyr_mut().write(|w| unsafe {
w.optkeyr().bits(0x4C5D6E7F)
});
}
}
/// Locks the FLASH_CR1/2 register.
pub fn lock(&mut self) {
self.registers.bank1_mut().cr.modify(|_, w| w.lock().set_bit());
self.registers.bank2_mut().cr.modify(|_, w| w.lock().set_bit());
}
/// Lock the FLASH_OPTCR register
pub fn lock_optcr(&mut self) {
self.registers.optcr_mut().modify(|_, w| w.optlock().set_bit());
}
// More literal methods to get bank status
fn bank1_is_locked(&self) -> bool {
self.registers.bank1_mut().cr.read().lock().bit_is_set()
}
fn bank2_is_locked(&self) -> bool {
self.registers.bank2_mut().cr.read().lock().bit_is_set()
}
fn optcr_is_locked(&self) -> bool {
self.registers.optcr().read().optlock().bit_is_set()
}
pub fn is_locked(&self) -> bool {
self.bank1_is_locked() || self.bank2_is_locked()
}
/// Returns `true` if a write/erase operation is in progress.
fn is_busy(&self) -> bool {
let (sr1, sr2) = (
self.registers.bank1_mut().sr.read(),
self.registers.bank2_mut().sr.read()
);
let mut eth = net::wire::EthernetAddress::BROADCAST;
let mut ip = nal::Ipv4Addr::unspecified();
let mut name = String::new();
loop {
let (key, string) = read_flash_str(addr);
if key.len() == 0 {
return (cidr, eth, ip, name);
sr1.bsy().bit_is_set() || sr2.bsy().bit_is_set()
}
match key.as_str() {
"CIDR" => cidr = net::wire::IpCidr::from_str(&string).unwrap(),
"MAC" => eth = net::wire::EthernetAddress::from_str(&string).unwrap(),
"BrokerIP" => ip = nal::Ipv4Addr::from_str(&string).unwrap(),
"Name" => name = string,
_ => {},
/// Returns `true` if a write/erase operation is in a command queue buffer.
fn is_queuing(&self) -> bool {
let (sr1, sr2) = (
self.registers.bank1_mut().sr.read(),
self.registers.bank2_mut().sr.read()
);
sr1.qw().bit_is_set() || sr2.qw().bit_is_set()
}
/// Returns `true` if write buffer is not empty in bank 1
fn bank1_is_buffering(&self) -> bool {
let sr1 = self.registers.bank1_mut().sr.read();
sr1.wbne().bit_is_set()
}
/// Returns `true` if write buffer is not empty in bank 2
fn bank2_is_buffering(&self) -> bool {
let sr2 = self.registers.bank2_mut().sr.read();
sr2.wbne().bit_is_set()
}
/// Erase a sector.
pub fn erase_sector(&mut self, bank_number: u8, sector_number: u8) -> Result<(), Error> {
// Assert parameters validity
assert!(bank_number == 1 || bank_number == 2);
assert!(sector_number < 8);
// 1. Check & clear error flags
self.clear_errors();
// 2. Unlock FLASH_CR1/2 register if necessary
self.unlock();
// 3. Set SER1/2 & SNB1/2 bits in FLASH_CR1/2 register
// 4. Set START1/2 bit in FLASH_CR1/2 register
match bank_number {
1 => {
self.registers.bank1_mut().cr.modify(|_, w| unsafe {
w.ser()
.set_bit()
.snb()
.bits(sector_number)
});
self.registers.bank1_mut().cr.modify(|_, w| {
w.start().set_bit()
});
},
2 => {
self.registers.bank2_mut().cr.modify(|_, w| unsafe {
w.ser()
.set_bit()
.snb()
.bits(sector_number)
});
self.registers.bank2_mut().cr.modify(|_, w| {
w.start().set_bit()
});
},
_ => unreachable!()
}
// Lock the flash CR again
self.lock();
Ok(())
}
/// Erase a bank.
pub fn erase_bank(&mut self, bank_number: u8) -> Result<(), Error> {
// Assert parameters validity
assert!(bank_number == 1 || bank_number == 2);
// 1. Check & clear error flags
self.clear_errors();
// 2. Unlock FLASH_CR1/2 register if necessary
self.unlock();
// 3 & 4. Set BER1/2 bit and START1/2 bit in FLASH_CR1/2 register
// Wait until the corresponding QW1/2 bit is cleared.
// Note: By setting BER1/2 bit alongside START1/2,
// mass erase is invoked since it supersedes sector erase.
match bank_number {
1 => {
self.registers.bank1_mut().cr.modify(|_, w| {
w.ber()
.set_bit()
.start()
.set_bit()
});
while self.registers.bank1_mut().sr.read().qw().bit_is_set() {}
},
2 => {
self.registers.bank2_mut().cr.modify(|_, w| {
w.ber()
.set_bit()
.start()
.set_bit()
});
while self.registers.bank2_mut().sr.read().qw().bit_is_set() {}
},
_ => unreachable!()
}
// Lock the flash CR again
self.lock();
Ok(())
}
/// Mass erases of the flash memory.
pub fn mass_erase(&mut self) -> Result<(), Error> {
// 1. Check & clear error flags
self.clear_errors();
// 2. Unlock Flash_CR1&2, FLASH_OPTCR registers
self.unlock();
self.unlock_optcr();
// 3. Set MER in FLASH_OPTCR to 1, wait until both QW to clear
self.registers.optcr_mut().modify(|_, w| {
w.mer().set_bit()
});
while self.is_queuing() {}
// Lock the flash CR and OPTCR again
self.lock();
self.lock_optcr();
Ok(())
}
/// Program flash words (32-bytes).
/// Flashing incomplete flash word is "tolerated", but you have been warned..
pub fn program<'a, 'b>(
&'a mut self,
start_offset: usize,
data: &'b [u8],
) -> Result<(), Error> {
if (start_offset % 32 != 0) || (data.len() % 32 != 0) {
log::warn!("Warning: This flash operation might not be supported...");
log::warn!("Consider force writing the data in buffer...");
}
self.clear_errors();
// Invoke single writes per 32-bytes row
let mut current_address = start_offset;
let mut remaining_data = data;
while remaining_data.len() != 0 {
let single_write_size = 32 - (current_address % 32);
// Determine the index that split the coming row and the remaining bytes.
let splitting_index = core::cmp::min(
single_write_size,
remaining_data.len()
);
let single_row_data = &remaining_data[..splitting_index];
// 1. Unlock FLASH_CR1/2 register if necessary
self.unlock();
// 2. Set PG bit in FLASH_CR1/2
self.registers.bank1_mut().cr.modify(|_, w| {
w.pg().set_bit()
});
self.registers.bank2_mut().cr.modify(|_, w| {
w.pg().set_bit()
});
// 3. Check Protection
// There should not be any data protection anyway...
// 4. Write data byte by byte
for (index, byte) in single_row_data.iter().enumerate() {
while self.is_busy() {}
match self.check_errors() {
Ok(_) => {
let address: *mut u8 = unsafe {
FLASH_BASE.add(current_address + index)
};
if address > MAX_FLASH_ADDRESS {
self.registers.bank1_mut().cr.modify(|_, w| w.pg().clear_bit());
self.registers.bank2_mut().cr.modify(|_, w| w.pg().clear_bit());
return Err(Error::WriteError);
} else {
unsafe {
core::ptr::write_volatile(address, *byte);
}
}
},
Err(error) => {
self.registers.bank1_mut().cr.modify(|_, w| w.pg().clear_bit());
self.registers.bank2_mut().cr.modify(|_, w| w.pg().clear_bit());
return Err(error);
}
}
}
// 5. Wait till the command queue of the device to be empty
while self.is_queuing() {}
// Modify remaining data and the address for next 32-bytes row.
remaining_data = &remaining_data[splitting_index..];
current_address += single_row_data.len();
}
// Reset PG1/2
self.registers.bank1_mut().cr.modify(|_, w| w.pg().clear_bit());
self.registers.bank2_mut().cr.modify(|_, w| w.pg().clear_bit());
// Lock FLASH_CR1/2 register
self.lock();
Ok(())
}
/// Force empty the bytes buffer for flash programming
/// Warning: It can invalidate the whole flash due to invalid CRC.
pub fn force_write<'a>(
&'a mut self
) -> Result<(), Error> {
if self.bank1_is_buffering() {
self.registers.bank1_mut().cr.modify(
|_, w| w.fw().set_bit()
);
}
if self.bank2_is_buffering() {
self.registers.bank2_mut().cr.modify(
|_, w| w.fw().set_bit()
);
}
Ok(())
}
/// Read a slice from flash memory
pub fn read(&self, start_offset: usize, len: usize) -> &'static [u8] {
let address = unsafe {
FLASH_BASE.add(start_offset)
};
unsafe { core::slice::from_raw_parts(address, len) }
}
/// Releases the flash peripheral.
pub fn free(self) -> FLASH {
self.registers
}
/// Checks the error flags.
fn check_errors(&self) -> Result<(), Error> {
let (sr1, sr2) = (
self.registers.bank1_mut().sr.read(),
self.registers.bank2_mut().sr.read()
);
if sr1.wrperr().bit_is_set() || sr2.wrperr().bit_is_set() {
Err(Error::WriteProtection)
} else if sr1.pgserr().bit_is_set() || sr2.pgserr().bit_is_set() {
Err(Error::ProgrammingSequence)
} else if sr1.strberr().bit_is_set() || sr2.strberr().bit_is_set() {
Err(Error::Strobe)
} else if sr1.incerr().bit_is_set() || sr2.incerr().bit_is_set() {
Err(Error::Inconsistency)
} else if sr1.operr().bit_is_set() || sr2.operr().bit_is_set() {
Err(Error::Operation)
} else if sr1.sneccerr1().bit_is_set() || sr1.dbeccerr().bit_is_set()
|| sr2.sneccerr1().bit_is_set() || sr2.dbeccerr().bit_is_set() {
Err(Error::ErrorCorrectionCode)
} else if sr1.rdperr().bit_is_set() || sr2.rdperr().bit_is_set() {
Err(Error::ReadProtection)
} else if sr1.rdserr().bit_is_set() || sr2.rdserr().bit_is_set() {
Err(Error::ReadSecure)
} else {
Ok(())
}
}
/// Clears all error flags.
fn clear_errors(&mut self) {
self.registers.bank1_mut().ccr.write(|w| {
w.clr_wrperr()
.set_bit()
.clr_pgserr()
.set_bit()
.clr_strberr()
.set_bit()
.clr_incerr()
.set_bit()
.clr_operr()
.set_bit()
.clr_rdperr()
.set_bit()
.clr_rdserr()
.set_bit()
.clr_sneccerr()
.set_bit()
.clr_dbeccerr()
.set_bit()
});
self.registers.bank2_mut().ccr.write(|w| {
w.clr_wrperr()
.set_bit()
.clr_pgserr()
.set_bit()
.clr_strberr()
.set_bit()
.clr_incerr()
.set_bit()
.clr_operr()
.set_bit()
.clr_rdperr()
.set_bit()
.clr_rdserr()
.set_bit()
.clr_sneccerr()
.set_bit()
.clr_dbeccerr()
.set_bit()
});
}
}