Skip to content

Instantly share code, notes, and snippets.

@eflukx
Created July 28, 2022 07:35
Show Gist options
  • Save eflukx/8d85fad415b8fdc4d86dc4f382d8de7b to your computer and use it in GitHub Desktop.
Save eflukx/8d85fad415b8fdc4d86dc4f382d8de7b to your computer and use it in GitHub Desktop.
use core::cell::RefCell;
use bsp::hal;
use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
use hal::{nvmc::Nvmc, pac::NVMC};
use tickv::{error_codes::ErrorCode, flash_controller::FlashController, TicKV};
pub const FLASH_PAGE_SIZE: usize = 4096;
pub struct NvmcFlash {
nvmc: RefCell<Nvmc<NVMC>>,
}
impl<'a> NvmcFlash {
pub fn tickv(nvmc: Nvmc<NVMC>, read_buffer: &'a mut [u8; FLASH_PAGE_SIZE], num_pages: usize) -> TicKV<NvmcFlash, FLASH_PAGE_SIZE> {
let ctrl = Self { nvmc: RefCell::new(nvmc) };
TicKV::new(ctrl, read_buffer, FLASH_PAGE_SIZE * num_pages)
}
fn region_to_address(region_number: usize, offset: usize) -> u32 {
(region_number * FLASH_PAGE_SIZE + offset) as u32
}
fn unaligned_write(&self, address: usize, buf: &[u8]) -> Result<(), ErrorCode> {
let len = buf.len();
let aligned_addr = address & !0b11; // chop off 2LSB's
let address_offset = address & 0b11; // And save what we chopped off
let flash_len = (((len + address_offset) >> 2) + 1) << 2; // align4 + 1 word
defmt::trace!(
"Write unaligned address {}({}) -> {}({})",
address,
len,
aligned_addr,
flash_len
);
let mut nvmc = self.nvmc.try_borrow_mut().map_err(|_| ErrorCode::WriteFail)?;
let mut merge_buffer = [0u8; FLASH_PAGE_SIZE];
let temp_buffer = &mut merge_buffer[0..flash_len];
// Just pad the data out with all 1's, so we don't need to actually _read_ the data
// from flash first. (Writing 0xff's will change nothing to flash memory as one can
// only write 0's to flash and set all 1's on a erase operation)
for i in temp_buffer.as_mut() {
*i = 0xff
}
// overwrite with the (unaligned) data we actually want to write.
for i in 0..len {
temp_buffer[i + address_offset] = buf[i]
}
nvmc.write(aligned_addr as u32, &merge_buffer[0..flash_len])
.map_err(|_| ErrorCode::WriteFail)
}
fn is_page_erased(page: &[u8; FLASH_PAGE_SIZE as usize]) -> bool {
page.iter().all(|&x| x == 0xff)
}
}
impl FlashController<FLASH_PAGE_SIZE> for NvmcFlash {
fn read_region(&self, region_number: usize, offset: usize, buf: &mut [u8; FLASH_PAGE_SIZE]) -> Result<(), ErrorCode> {
let mut nvmc = self.nvmc.try_borrow_mut().map_err(|_| ErrorCode::WriteFail)?;
nvmc.read(Self::region_to_address(region_number, offset), buf)
.map_err(|_| ErrorCode::ReadFail)
}
fn write(&self, address: usize, buf: &[u8]) -> Result<(), ErrorCode> {
let len = buf.len();
if len > FLASH_PAGE_SIZE {
return Err(ErrorCode::WriteFail);
}
// Check if we're word (`Nvmc::WRITE_SIZE`) aligned
if address % core::mem::size_of::<u32>() == 0 && len % core::mem::size_of::<u32>() == 0 {
let mut nvmc = self.nvmc.try_borrow_mut().map_err(|_| ErrorCode::WriteFail)?;
defmt::trace!("write offset {}, address {}, len {}", address, address, buf.len());
nvmc.write(address as u32, buf).map_err(|e| {
defmt::error!("write error: {:?}", defmt::Debug2Format(&e));
ErrorCode::EraseFail
})
} else {
self.unaligned_write(address, buf)
}
}
fn erase_region(&self, region_number: usize) -> Result<(), ErrorCode> {
let mut nvmc = self.nvmc.try_borrow_mut().map_err(|_| ErrorCode::WriteFail)?;
let from = Self::region_to_address(region_number, 0);
let to = from + FLASH_PAGE_SIZE as u32;
let mut page = [0; FLASH_PAGE_SIZE as usize];
nvmc.read(from, &mut page).unwrap();
if Self::is_page_erased(&page) {
return Ok(());
}
defmt::trace!("erase_region: {}, {}-{}", region_number, from, to);
nvmc.erase(from, to).map_err(|e| {
defmt::error!("erase_region error: {:?}", defmt::Debug2Format(&e));
ErrorCode::EraseFail
})?;
if nvmc.read(from, &mut page).is_ok() && Self::is_page_erased(&page) {
defmt::trace!("The page was correctly erased.");
Ok(())
} else {
defmt::trace!("Error: The page was not correctly erased.");
Err(ErrorCode::EraseFail)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment