You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have written a Flash struct that implements the Read, Write and Read traits for the Raspberry Pico's flash memory. I am trying to format the volume using fatfs::format_volume with the following options:
let options = fatfs::FormatVolumeOptions::new().bytes_per_sector(4096).bytes_per_cluster(4096);
and after a while, my write function returns an error that "The cursor is not aligned to SECTOR_SIZE" (that error type is mine). Furthermore, it seems like this specific error occurs after the library zeroes a bunch of memory at the beginning of the flash, when it tries to write the buffer [255, 255] to the address 4097, just 1 byte off from the start of the 2nd sector.
Additional info
I am not doing any kind of buffering, everything is flushed immediately
I am using C bindings to the RP2040 ROM functions described in section 2.8.3.1.3, page 136 of the RP2040 datasheet
I have enabled the alloc feature, but that doesn't seem to change the program's behaviour
Another issue I have found is that the size of the buffer data to write isn't a multiple of SECTOR_SIZE. Is this intended behavior?
Code
use alloc::format;use core::{cmp, slice};// custom error typesusecrate::error;use error::{Error,ErrorKind,FlashErrorKind};use fatfs::{self,SeekFrom};// ROM functionsuse rp_pico::hal::rom_data::{
connect_internal_flash, flash_enter_cmd_xip, flash_exit_xip, flash_flush_cache,
flash_range_erase, flash_range_program,};// SECTOR_SIZE is already a multiple of PAGE_SIZE (256), so we use that insteadpubconstSECTOR_SIZE:usize = 4096;pubconstSECTOR_ERASE:u8 = 0x20;pubconstXIP_BASE:usize = 0x1000_0000;pubstructFlash<'a>{pubslice:&'a [u8],puboffset:usize,}impl<'a>Flash<'a>{/// Returns `None` if offset isn't aligned to a sectorpubfnnew(offset:usize,len:usize) -> Option<Self>{let slice = unsafe{ slice::from_raw_parts((XIP_BASE + offset)as*constu8, len)};let flash = Flash{ slice,offset:0};if flash.is_aligned()
&& slice.len() % SECTOR_SIZE == 0
&& slice.as_ptr()asusize >= XIP_BASE{Some(flash)}else{None}}pubfnlen(&self) -> usize{self.slice.len()}fnis_aligned(&self) -> bool{// Below we are not using that pointer to read/write anything, so this is actually safeunsafe{self.slice.as_ptr().add(self.offset)}.align_offset(SECTOR_SIZE) == 0}fnoffset_from_xip_base(&self) -> usize{self.slice.as_ptr()asusize - XIP_BASE}}/// Copy the contents of `src` to `dest` and return how many bytes were read////// Note: unlike the `copy_from_slice` function, this doesn't panic;/// instead, this will cap at the when an end-of-slice is reached for either `src` or `dst`fnslice_copy<T>(src:&[T],dest:&mut[T]) -> usizewhereT:Copy,{let src_len = src.len();let dest_len = dest.len();let size_cap = cmp::min(src_len, dest_len);// Perform copy operation
dest[..size_cap].copy_from_slice(&src[..size_cap]);return size_cap;}#[derive(Debug)]pubstructIoError(Error);implFrom<Error>forIoError{fnfrom(value:Error) -> Self{Self(value)}}impl fatfs::IoBaseforFlash<'_>{typeError = IoError;}impl fatfs::IoErrorforIoError{fnis_interrupted(&self) -> bool{self.0.kind() == ErrorKind::Interrupted}fnnew_unexpected_eof_error() -> Self{Self(Error::new(ErrorKind::UnexpectedEof,"Encountered unexpected EOF while doing FS operations",))}fnnew_write_zero_error() -> Self{Self(Error::new(ErrorKind::WriteZero,"A FS operation could not be completed because a call to .write() returned Ok(0)",))}}impl fatfs::ReadforFlash<'_>{// `io::Result` always returns `Ok`, so you can safely unwrap thisfnread(&mutself,buf:&mut[u8]) -> Result<usize,Self::Error>{
defmt::debug!("Read {} bytes from offset {}", buf.len(),self.offset);ifself.offset >= self.slice.len(){unreachable!()}else{let bytes_read = slice_copy(&self.slice[self.offset..], buf);self.offset += bytes_read;Ok(bytes_read)}}}impl fatfs::SeekforFlash<'_>{fnseek(&mutself,pos: fatfs::SeekFrom) -> Result<u64,Self::Error>{
defmt::debug!("current pos: {}, seekfrom: {:?}",self.offset,
defmt::Debug2Format(&pos));match pos {SeekFrom::Start(offset) => {let offset:usize = offset.try_into().map_err(|_err| {Error::new(ErrorKind::TryFromIntError,"New offset must fit into a usize",)})?;if offset < self.slice.len(){self.offset = offset;}else{returnErr(Error::new(ErrorKind::FlashRelated(FlashErrorKind::MovedPast),"Moved past end of managed flash",).into());}}SeekFrom::Current(offset) => {let offset:i64 =
offset
.checked_add_unsigned(self.offsetasu64).ok_or(Error::new(ErrorKind::CheckedOverflow,"Numeric overflow between addition of i64 and usize",))?;match u64::try_from(offset){Ok(offset) => self.offset = self.seek(SeekFrom::Start(offset))? asusize,Err(_err) => {returnErr(Error::new(ErrorKind::FlashRelated(FlashErrorKind::MovedBefore),"Moved before start of managed flash",).into())}};}SeekFrom::End(offset) => {let offset:i64 =
offset
.checked_add_unsigned(self.slice.len()asu64).ok_or(Error::new(ErrorKind::CheckedOverflow,"Numeric overflow between addition of i64 and usize",))?
- 1;match u64::try_from(offset){Ok(offset) => self.offset = self.seek(SeekFrom::Start(offset))? asusize,Err(_err) => {returnErr(Error::new(ErrorKind::FlashRelated(FlashErrorKind::MovedBefore),"Moved before start of managed flash",).into())}};}}
defmt::debug!("new pos: {}",self.offset);Ok(self.offsetasu64)}}impl fatfs::WriteforFlash<'_>{// Please note that if writing the entire buffer would exceed the limits// of the managed flash space, everything UP TO that point will be written#[inline(never)]#[link_section = ".data.ram_func"]fnwrite(&mutself,buf:&[u8]) -> Result<usize,Self::Error>{
defmt::debug!("{}: {:?}",self.offset, buf);if !self.is_aligned(){Err(Error::new(ErrorKind::FlashRelated(FlashErrorKind::Unaligned),"The cursor is not aligned to SECTOR_SIZE",).into())}// the issue with the buf len not being a multiple of SECTOR_SIZE/*else if buf.len() % SECTOR_SIZE != 0 { Err(Error::new( ErrorKind::FlashRelated(FlashErrorKind::InvalidBufferSize), format!( "The length of the input buffer ({}) isn't a multiple of SECTOR SIZE ({})", buf.len(), SECTOR_SIZE ), ) .into()) } */else{// Prevent flash memory overflow by taking a slice of the input bufferlet init_buf_slice = &buf[..cmp::min(self.slice.len() - self.offset, buf.len())];// the issue with the buf len not being a multiple of SECTOR_SIZE// Zero out remaining bytes so that our buffer is divisible by SECTOR_SIZElet bytes_needed =
init_buf_slice.len().next_multiple_of(SECTOR_SIZE) - init_buf_slice.len();letmut filler_vec: heapless::Vec<u8,SECTOR_SIZE> = heapless::Vec::new();
filler_vec.resize(bytes_needed,0).unwrap();let buf_vec = [init_buf_slice, filler_vec.as_slice()].concat();let buf_slice = buf_vec.as_slice();// Variation of what is described in https://github.com/rp-rs/rp-hal/issues/257unsafe{
cortex_m::interrupt::free(|_cs| {connect_internal_flash();flash_exit_xip();// Erase the data in that regionflash_range_erase(self.offset_from_xip_base()asu32,
buf_slice.len(),SECTOR_SIZEasu32,0xD8,);// Reprogram the data in that regionflash_range_program(self.offset_from_xip_base()asu32,
buf_slice.as_ptr(),
buf_slice.len(),);flash_flush_cache();flash_enter_cmd_xip();});}self.offset += buf_slice.len();Ok(buf.len())}}// Anything written to the flash is flushed immediatelyfnflush(&mutself) -> Result<(),Self::Error>{Ok(())}}
The text was updated successfully, but these errors were encountered:
This is intended behavior. Implementation assumes IO has some kind of buffering and allows reading/writing arbitrary number of bytes at unaligned addresses.
In your case it probably makes sense to add a buffer that has size of exactly one sector to your Flash struct and use it in Read and Write trait implementations. You can take https://github.com/rafalh/rust-fscommon/blob/master/src/buf_stream.rs as example although it doesn't seem to care about address alignment.
I have written a
Flash
struct that implements theRead
,Write
andRead
traits for the Raspberry Pico's flash memory. I am trying to format the volume usingfatfs::format_volume
with the following options:and after a while, my
write
function returns an error that "The cursor is not aligned to SECTOR_SIZE" (that error type is mine). Furthermore, it seems like this specific error occurs after the library zeroes a bunch of memory at the beginning of the flash, when it tries to write the buffer[255, 255]
to the address4097
, just 1 byte off from the start of the 2nd sector.Additional info
alloc
feature, but that doesn't seem to change the program's behaviourCode
The text was updated successfully, but these errors were encountered: