diff --git a/Cargo.toml b/Cargo.toml index 54f0f3e..947413e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,5 +16,4 @@ categories = ["embedded", "hardware-support", "no-std"] [dependencies] nb = "1" -no-std-net = "0.4" -heapless = "^0.5" +heapless = "^0.5" \ No newline at end of file diff --git a/src/iter.rs b/src/iter.rs index da310a1..3b5c102 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -1,4 +1,4 @@ -use crate::{Address, Region}; +use crate::Region; /// Iterator producing block-region pairs, where each memory block maps to each /// region. @@ -9,7 +9,7 @@ where { memory: &'a [u8], regions: I, - base_address: Address, + base_address: u32, } /// Trait allowing us to automatically add an `overlaps` function to all iterators over [`Region`] @@ -19,7 +19,7 @@ where I: Iterator, { /// Obtain an [`OverlapIterator`] over a subslice of `memory` that overlaps with the region in `self` - fn overlaps(self, memory: &'a [u8], base_address: Address) -> OverlapIterator; + fn overlaps(self, memory: &'a [u8], base_address: u32) -> OverlapIterator; } impl<'a, R, I> Iterator for OverlapIterator<'a, R, I> @@ -27,17 +27,21 @@ where R: Region, I: Iterator, { - type Item = (&'a [u8], R, Address); + type Item = (&'a [u8], R, u32); fn next(&mut self) -> Option { while let Some(region) = self.regions.next() { // TODO: This might be possible to do in a smarter way? let mut block_range = (0..self.memory.len()) - .skip_while(|index| !region.contains(self.base_address + *index)) - .take_while(|index| region.contains(self.base_address + *index)); + .skip_while(|index| !region.contains(self.base_address + *index as u32)) + .take_while(|index| region.contains(self.base_address + *index as u32)); if let Some(start) = block_range.next() { let end = block_range.last().unwrap_or(start) + 1; - return Some((&self.memory[start..end], region, self.base_address + start)); + return Some(( + &self.memory[start..end], + region, + self.base_address + start as u32, + )); } } None @@ -50,7 +54,7 @@ where R: Region, I: Iterator, { - fn overlaps(self, memory: &'a [u8], base_address: Address) -> OverlapIterator { + fn overlaps(self, memory: &'a [u8], base_address: u32) -> OverlapIterator { OverlapIterator { memory, regions: self, diff --git a/src/lib.rs b/src/lib.rs index de13c0f..33c789c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,169 +2,43 @@ //! //! Storage traits to allow on and off board storage devices to read and write //! data. -//! -//! Implementation based on `Cuervo`s great work in -//! https://www.ecorax.net/as-above-so-below-1/ and -//! https://www.ecorax.net/as-above-so-below-2/ #![no_std] #![deny(missing_docs)] #![deny(unsafe_code)] -use core::ops::{Add, Sub}; -use heapless::{consts::*, Vec}; -use nb; - /// Currently contains [`OverlapIterator`] pub mod iter; - -/// An address denotes the read/write address of a single word. -#[derive(Default, Copy, Clone, Debug, PartialOrd, PartialEq, Eq, Ord)] -pub struct Address(pub u32); - -impl Add for Address { - type Output = Self; - - fn add(self, rhs: usize) -> Self::Output { - Address(self.0 + rhs as u32) - } -} - -impl Add for Address { - type Output = Self; - - fn add(self, rhs: isize) -> Self::Output { - Address((self.0 as isize + rhs) as u32) - } -} -impl Sub for Address { - type Output = Self; - - fn sub(self, rhs: usize) -> Self::Output { - Address(self.0 - rhs as u32) - } -} - -impl Sub for Address { - type Output = Self; - - fn sub(self, rhs: isize) -> Self::Output { - Address((self.0 as isize - rhs) as u32) - } -} - -impl Sub
for Address { - type Output = Self; - - fn sub(self, rhs: Address) -> Self::Output { - Address(self.0 - rhs.0) - } -} +/// Technology specific traits for NOR Flashes +pub mod nor_flash; /// A region denotes a contiguous piece of memory between two addresses. pub trait Region { /// Check if `address` is contained in the region of `Self` - fn contains(&self, address: Address) -> bool; + fn contains(&self, address: u32) -> bool; } /// Transparent storage trait -pub trait ReadWriteStorage { +pub trait Storage { /// An enumeration of storage errors type Error; /// Read a slice of data from the storage peripheral, starting the read /// operation at the given address, and reading until end address /// (`self.range().1`) or buffer length, whichever comes first. - fn try_read(&mut self, address: Address, bytes: &mut [u8]) -> nb::Result<(), Self::Error>; + fn try_read(&mut self, address: u32, bytes: &mut [u8]) -> Result<(), Self::Error>; /// Write a slice of data to the storage peripheral, starting the write /// operation at the given address. - fn try_write(&mut self, address: Address, bytes: &[u8]) -> nb::Result<(), Self::Error>; - - /// The range of possible addresses within the peripheral. /// - /// (start_addr, end_addr) - fn range(&self) -> (Address, Address); - - /// Erase the given storage range, clearing all data within `[from..to]`. - fn try_erase(&mut self, from: Address, to: Address) -> nb::Result<(), Self::Error>; -} - -/// NOR flash region trait. -pub trait NorFlashRegion { - /// The range of possible addresses within the region. + /// **NOTE:** + /// This function will automatically erase any pages necessary to write the given data, + /// and might as such do RMW operations at an undesirable performance impact. /// - /// (start_addr, end_addr) - fn range(&self) -> (Address, Address); - /// Maximum number of bytes that can be written at once. - fn page_size(&self) -> usize; - /// List of avalable erase sizes in this region. - /// Should be sorted in ascending order. - /// Currently limited to 5 sizes, but could be increased if necessary. - fn erase_sizes(&self) -> Vec; -} - -/// Blanket implementation for all types implementing [`NorFlashRegion`] -impl Region for T { - fn contains(&self, address: Address) -> bool { - let (start, end) = self.range(); - address.0 >= start.0 && address.0 < end.0 - } -} - -/// NOR flash storage trait -pub trait NorFlash { - /// An enumeration of storage errors - type Error; - /// Region type - type Region: NorFlashRegion; - - /// Read a slice of data from the storage peripheral, starting the read - /// operation at the given address, and reading until end address - /// (`self.range().1`) or buffer length, whichever comes first. - fn try_read(&mut self, address: Address, bytes: &mut [u8]) -> nb::Result<(), Self::Error>; + /// CONSIDERATIONS: + /// - Should the address here be normalized (always start from zero?) + fn try_write(&mut self, address: u32, bytes: &[u8]) -> Result<(), Self::Error>; - /// Write a slice of data to the storage peripheral, starting the write - /// operation at the given address. - /// - /// Since this is done on a NOR flash all bytes are anded with the current - /// content in the flash. This means no 0s can to turned into 1s this way. - fn try_write(&mut self, address: Address, bytes: &[u8]) -> nb::Result<(), Self::Error>; - - /// Erase the given storage range, clearing all data within `[from..to]`. - /// The given range will contain all 1s afterwards. - /// - /// This should return an error if the range is not aligned to a proper - /// erase resolution - fn try_erase(&mut self, from: Address, to: Address) -> nb::Result<(), Self::Error>; - - /// Get all distinct memory reagions. These must not overlap, but can be disjoint. - /// Most chips will return a single region, but some chips have regions with - /// different erase sizes. - /// Currently limited to 4 regions, but could be increased if necessary - fn regions(&self) -> Vec; -} - -/// Marker trait for NOR flashes with uniform erase and page sizes across the whole -/// address range -pub trait UniformNorFlash {} - -/// Blanket implementation for all types implementing [`NorFlash`] and [`UniformNorFlash`] -impl NorFlashRegion for T { - /// The range of possible addresses within the peripheral. - /// - /// (start_addr, end_addr) - fn range(&self) -> (Address, Address) { - self.regions()[0].range() - } - /// Maximum number of bytes that can be written at once. - fn page_size(&self) -> usize { - self.regions()[0].page_size() - } - /// List of avalable erase sizes in this region. - /// Should be sorted in ascending order. - /// Currently limited to 5 sizes, but could be increased if necessary. - fn erase_sizes(&self) -> Vec { - self.regions()[0].erase_sizes() - } + /// The capacity of the storage peripheral in bytes. + fn capacity(&self) -> u32; } diff --git a/src/nor_flash.rs b/src/nor_flash.rs new file mode 100644 index 0000000..4082846 --- /dev/null +++ b/src/nor_flash.rs @@ -0,0 +1,182 @@ +use crate::{iter::IterableByOverlaps, Region, Storage}; + +/// NOR flash trait. +pub trait NorFlash { + /// An enumeration of storage errors + type Error; + + /// Read a slice of data from the storage peripheral, starting the read + /// operation at the given address, and reading `bytes.len()` bytes. + /// + /// This should throw an error in case `bytes.len()` will be larger than + /// the peripheral end address. + fn try_read(&mut self, address: u32, bytes: &mut [u8]) -> Result<(), Self::Error>; + + /// Erase the given storage range, clearing all data within `[from..to]`. + /// The given range will contain all 1s afterwards. + /// + /// This should return an error if the range is not aligned to a proper + /// erase resolution + /// Erases page at addr, sets it all to 0xFF + /// If power is lost during erase, contents of the page are undefined. + /// `from` and `to` must both be multiples of `erase_size()` and `from` <= `to`. + fn try_erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error>; + + /// Writes data to addr, bitwise ANDing if there's already data written at that location, + /// If power is lost during write, the contents of the written words are undefined. + /// The rest of the page is guaranteed to be unchanged. + /// It is not allowed to write to the same word twice. + /// `address` and `bytes.len()` must both be multiples of `write_size()` and properly aligned. + fn try_write(&mut self, address: u32, bytes: &[u8]) -> Result<(), Self::Error>; + + /// The erase granularity of the storage peripheral + fn erase_size(&self) -> u32; + + /// The minumum write size of the storage peripheral + fn write_size(&self) -> u32; + + /// The capacity of the peripheral in bytes. + fn capacity(&self) -> u32; +} + +/// Marker trait for NorFlash relaxing the restrictions on `write`. +/// +/// Writes to the same word twice are now allowed. The result is the logical AND of the +/// previous data and the written data. That is, it is only possible to change 1 bits to 0 bits. +/// +/// If power is lost during write: +/// - Bits that were 1 on flash and are written to 1 are guaranteed to stay as 1 +/// - Bits that were 1 on flash and are written to 0 are undefined +/// - Bits that were 0 on flash are guaranteed to stay as 0 +/// - Rest of the bits in the page are guaranteed to be unchanged +pub trait MultiwriteNorFlash: NorFlash {} + +/// +pub struct RmwNorFlashStorage(S); + +// FIXME: Not sure how to do this correctly? Ideally we could have `const fn erase_size()` or some const generic? +const MAX_PAGE_SIZE: usize = 2048; + +impl RmwNorFlashStorage { + /// Instantiate a new generic `Storage` from a `NorFlash` peripheral + pub fn new(nor_flash: S) -> Self { + Self(nor_flash) + } +} + +struct Page { + pub start: u32, + pub size: u32, +} + +impl Page { + fn new(index: u32, size: u32) -> Self { + Self { + start: index * size, + size, + } + } + + /// The end address of the page + const fn end(&self) -> u32 { + self.start + self.size as u32 + } +} + +impl Region for Page { + fn contains(&self, address: u32) -> bool { + (self.start <= address) && (self.end() > address) + } +} + +impl Storage for RmwNorFlashStorage { + type Error = S::Error; + + fn try_read(&mut self, address: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + // Nothing special to be done for reads + self.0.try_read(address, bytes) + } + + fn try_write(&mut self, address: u32, bytes: &[u8]) -> Result<(), Self::Error> { + // Perform read/modify/write operations on the byte slice. + let erase_size = self.0.erase_size(); + let last_page = (self.0.capacity() / erase_size) - 1; + + // `data` is the part of `bytes` contained within `page`, + // and `addr` in the address offset of `page` + any offset into the page as requested by `address` + for (data, page, addr) in (0..last_page) + .map(move |i| Page::new(i, erase_size)) + .overlaps(bytes, address) + { + let merge_buffer = &mut [0u8; MAX_PAGE_SIZE][0..erase_size as usize]; + let offset_into_page = addr.saturating_sub(page.start) as usize; + + self.try_read(page.start, merge_buffer)?; + + // If we cannot write multiple times to the same page, we will have to erase it + self.0.try_erase(page.start, page.end())?; + merge_buffer + .iter_mut() + .skip(offset_into_page) + .zip(data) + .for_each(|(byte, input)| *byte = *input); + self.0.try_write(page.start, merge_buffer)?; + } + Ok(()) + } + + fn capacity(&self) -> u32 { + self.0.capacity() + } +} + +// FIXME: Requires specialization to take advantage of MultiwriteNorFlash? +// impl Storage for RmwNorFlashStorage { +// type Error = S::Error; + +// fn try_read(&mut self, address: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { +// // Nothing special to be done for reads +// self.0.try_read(address, bytes) +// } + +// fn try_write(&mut self, address: u32, bytes: &[u8]) -> Result<(), Self::Error> { +// // Perform read/modify/write operations on the byte slice. +// let erase_size = self.0.erase_size(); +// let last_page = (self.0.capacity() / erase_size) - 1; + +// // `data` is the part of `bytes` contained within `page`, +// // and `addr` in the address offset of `page` + any offset into the page as requested by `address` +// for (data, page, addr) in (0..last_page) +// .map(move |i| Page::new(i, erase_size)) +// .overlaps(bytes, address) +// { +// let merge_buffer = &mut [0u8; MAX_PAGE_SIZE][0..erase_size as usize]; +// let offset_into_page = addr.saturating_sub(page.start) as usize; + +// self.try_read(page.start, merge_buffer)?; + +// let rhs = &merge_buffer[offset_into_page..]; +// let is_subset = +// data.len() < rhs.len() && data.iter().zip(rhs.iter()).all(|(a, b)| (*a | *b) == *b); + +// // Check if we can write the data block directly, under the limitations imposed by NorFlash: +// // - We can only change 1's to 0's +// if is_subset { +// self.0.try_write(addr, data)?; +// } else { +// self.0.try_erase(page.start, page.end())?; +// merge_buffer +// .iter_mut() +// .skip(offset_into_page) +// .zip(data) +// .for_each(|(byte, input)| *byte = *input); +// self.0.try_write(page.start, merge_buffer)?; +// } +// } +// Ok(()) +// } + +// fn capacity(&self) -> u32 { +// self.0.capacity() +// } +// }