Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,4 @@ keywords = ["storage"]
categories = ["embedded", "hardware-support", "no-std"]

[dependencies]
nb = "1"
no-std-net = "0.4"
heapless = "^0.5"
heapless = "^0.5"
20 changes: 12 additions & 8 deletions src/iter.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Address, Region};
use crate::Region;

/// Iterator producing block-region pairs, where each memory block maps to each
/// region.
Expand All @@ -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`]
Expand All @@ -19,25 +19,29 @@ where
I: Iterator<Item = R>,
{
/// 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<R, I>;
fn overlaps(self, memory: &'a [u8], base_address: u32) -> OverlapIterator<R, I>;
}

impl<'a, R, I> Iterator for OverlapIterator<'a, R, I>
where
R: Region,
I: Iterator<Item = R>,
{
type Item = (&'a [u8], R, Address);
type Item = (&'a [u8], R, u32);

fn next(&mut self) -> Option<Self::Item> {
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
Expand All @@ -50,7 +54,7 @@ where
R: Region,
I: Iterator<Item = R>,
{
fn overlaps(self, memory: &'a [u8], base_address: Address) -> OverlapIterator<R, I> {
fn overlaps(self, memory: &'a [u8], base_address: u32) -> OverlapIterator<R, I> {
OverlapIterator {
memory,
regions: self,
Expand Down
160 changes: 18 additions & 142 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<usize> for Address {
type Output = Self;

fn add(self, rhs: usize) -> Self::Output {
Address(self.0 + rhs as u32)
}
}

impl Add<isize> for Address {
type Output = Self;

fn add(self, rhs: isize) -> Self::Output {
Address((self.0 as isize + rhs) as u32)
}
}
impl Sub<usize> for Address {
type Output = Self;

fn sub(self, rhs: usize) -> Self::Output {
Address(self.0 - rhs as u32)
}
}

impl Sub<isize> for Address {
type Output = Self;

fn sub(self, rhs: isize) -> Self::Output {
Address((self.0 as isize - rhs) as u32)
}
}

impl Sub<Address> 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you fix the tab usage clippy warning here?

/// `self.capacity()`.
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<usize, U5>;
}

/// Blanket implementation for all types implementing [`NorFlashRegion`]
impl<T: NorFlashRegion> 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<Self::Region, U4>;
}

/// 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<T: NorFlash + UniformNorFlash> 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<usize, U5> {
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>;
}
Loading