diff --git a/Cargo.toml b/Cargo.toml index 54f0f3e..62ed979 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,3 @@ documentation = "https://docs.rs/embedded-storage" readme = "README.md" keywords = ["storage"] categories = ["embedded", "hardware-support", "no-std"] - -[dependencies] -nb = "1" -no-std-net = "0.4" -heapless = "^0.5" 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..683d1d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,169 +2,45 @@ //! //! 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 { +/// Transparent read only storage trait +pub trait ReadStorage { /// 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>; - - /// 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. + /// operation at the given address offset, and reading `bytes.len()` bytes. /// - /// (start_addr, end_addr) - fn range(&self) -> (Address, Address); + /// This should throw an error in case `bytes.len()` will be larger than + /// `self.capacity() - offset`. + fn try_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error>; - /// Erase the given storage range, clearing all data within `[from..to]`. - fn try_erase(&mut self, from: Address, to: Address) -> nb::Result<(), Self::Error>; + /// The capacity of the storage peripheral in bytes. + fn capacity(&self) -> usize; } -/// NOR flash region trait. -pub trait NorFlashRegion { - /// The range of possible addresses within the region. - /// - /// (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>; - +/// Transparent read/write storage trait +pub trait Storage: ReadStorage { /// 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. + /// operation at the given address offset (between 0 and `self.capacity()`). /// - /// (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() - } + /// **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. + fn try_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error>; } diff --git a/src/nor_flash.rs b/src/nor_flash.rs new file mode 100644 index 0000000..5f09149 --- /dev/null +++ b/src/nor_flash.rs @@ -0,0 +1,54 @@ +/// Read only NOR flash trait. +pub trait ReadNorFlash { + /// An enumeration of storage errors + type Error; + + /// The minumum number of bytes the storage peripheral can read + const READ_SIZE: usize; + + /// Read a slice of data from the storage peripheral, starting the read + /// operation at the given address offset, 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, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error>; + + /// The capacity of the peripheral in bytes. + fn capacity(&self) -> usize; +} + +/// NOR flash trait. +pub trait NorFlash: ReadNorFlash { + /// The minumum number of bytes the storage peripheral can write + const WRITE_SIZE: usize; + + /// The minumum number of bytes the storage peripheral can erase + const ERASE_SIZE: usize; + + /// 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 + /// 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>; + + /// If power is lost during write, the contents of the written words are undefined, + /// but the rest of the page is guaranteed to be unchanged. + /// It is not allowed to write to the same word twice. + /// `offset` and `bytes.len()` must both be multiples of `WRITE_SIZE`. + fn try_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error>; +} + +/// 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 {}