forked from M-Labs/humpback-dds
flash: init abstraction
This commit is contained in:
parent
d6fdc36c78
commit
68d58857cf
444
src/flash.rs
444
src/flash.rs
@ -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()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user