Created
July 28, 2022 07:35
-
-
Save eflukx/8d85fad415b8fdc4d86dc4f382d8de7b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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