Skip to content

Commit

Permalink
Rework NorFlash & Storage traits, and add RmwNorFlashStorage wrapper …
Browse files Browse the repository at this point in the history
…between them
  • Loading branch information
MathiasKoch committed Mar 3, 2021
1 parent 52a1352 commit 7667a56
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 149 deletions.
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,4 @@ 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
152 changes: 13 additions & 139 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<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 {
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<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>;
/// 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<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.
///
/// (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()
}
/// The capacity of the storage peripheral in bytes.
fn capacity(&self) -> u32;
}
Loading

0 comments on commit 7667a56

Please sign in to comment.