forked from M-Labs/humpback-dds
flash: init abstraction
This commit is contained in:
parent
d6fdc36c78
commit
68d58857cf
442
src/flash.rs
442
src/flash.rs
@ -1,64 +1,408 @@
|
|||||||
use heapless::{String, consts::*};
|
//! Flash memory, with major reference from STM32F7xx_HAL crate,
|
||||||
use smoltcp as net;
|
//! as well as @Astro 's work in STM32F4xx_HAL
|
||||||
use embedded_nal as nal;
|
|
||||||
use core::str::FromStr;
|
|
||||||
|
|
||||||
unsafe fn read_record_length(addr: u32) -> u32 {
|
use stm32h7xx_hal::pac::FLASH;
|
||||||
core::ptr::read(addr as *const u32)
|
|
||||||
|
/// 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 {
|
/// Embedded flash memory.
|
||||||
core::ptr::read(addr as *const u8)
|
pub struct Flash {
|
||||||
|
registers: FLASH,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_flash_str(addr: &mut u32) -> (String<U32>, String<U64>) {
|
impl Flash {
|
||||||
let mut key: String<U32> = String::new();
|
/// Creates a new Flash instance.
|
||||||
let mut string: String<U64> = String::new();
|
pub fn new(flash: FLASH) -> Self {
|
||||||
|
let mut flash = Self { registers: flash };
|
||||||
let record_length = unsafe {
|
// Lock both banks initially
|
||||||
read_record_length(*addr)
|
flash.lock();
|
||||||
};
|
flash
|
||||||
if record_length == 0xFFFFFFFF {
|
|
||||||
return (key, string);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut key_string_div = false;
|
/// Unlocks the FLASH_CR1/2 register.
|
||||||
for f_addr in (*addr)+4..((*addr)+record_length) {
|
pub fn unlock(&mut self) {
|
||||||
if key_string_div {
|
// Note: Unlocking an unlocked bank will cause HardFault
|
||||||
string.push(unsafe {read_byte(f_addr)} as char).unwrap();
|
|
||||||
} else {
|
// Unlock bank 1 if needed.
|
||||||
let c = unsafe {read_byte(f_addr)};
|
if self.bank1_is_locked() {
|
||||||
if c == 0 {
|
self.registers.bank1_mut().keyr.write(|w| unsafe {
|
||||||
key_string_div = true;
|
w.keyr().bits(0x45670123)
|
||||||
} else {
|
});
|
||||||
key.push(c as char).unwrap();
|
self.registers.bank1_mut().keyr.write(|w| unsafe {
|
||||||
|
w.keyr().bits(0xCDEF89AB)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
);
|
||||||
|
sr1.bsy().bit_is_set() || sr2.bsy().bit_is_set()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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(())
|
||||||
}
|
}
|
||||||
*addr += record_length;
|
|
||||||
(key, string)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_flash(addr: &mut u32)
|
/// Force empty the bytes buffer for flash programming
|
||||||
-> (net::wire::IpCidr, net::wire::EthernetAddress, nal::Ipv4Addr, String<U64>)
|
/// Warning: It can invalidate the whole flash due to invalid CRC.
|
||||||
{
|
pub fn force_write<'a>(
|
||||||
let mut cidr = net::wire::IpCidr::Ipv6(
|
&'a mut self
|
||||||
net::wire::Ipv6Cidr::SOLICITED_NODE_PREFIX,
|
) -> Result<(), Error> {
|
||||||
);
|
if self.bank1_is_buffering() {
|
||||||
let mut eth = net::wire::EthernetAddress::BROADCAST;
|
self.registers.bank1_mut().cr.modify(
|
||||||
let mut ip = nal::Ipv4Addr::unspecified();
|
|_, w| w.fw().set_bit()
|
||||||
let mut name = String::new();
|
);
|
||||||
loop {
|
|
||||||
let (key, string) = read_flash_str(addr);
|
|
||||||
if key.len() == 0 {
|
|
||||||
return (cidr, eth, ip, name);
|
|
||||||
}
|
}
|
||||||
match key.as_str() {
|
if self.bank2_is_buffering() {
|
||||||
"CIDR" => cidr = net::wire::IpCidr::from_str(&string).unwrap(),
|
self.registers.bank2_mut().cr.modify(
|
||||||
"MAC" => eth = net::wire::EthernetAddress::from_str(&string).unwrap(),
|
|_, w| w.fw().set_bit()
|
||||||
"BrokerIP" => ip = nal::Ipv4Addr::from_str(&string).unwrap(),
|
);
|
||||||
"Name" => name = string,
|
}
|
||||||
_ => {},
|
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()
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user