From c378fb14d8c0975e670db82ae5a2a0ababf8a172 Mon Sep 17 00:00:00 2001 From: Mathias Koch Date: Fri, 18 Sep 2020 10:10:43 +0200 Subject: [PATCH 1/4] Add storage ReadWrite trait, and helper structs, traits and iterators --- src/lib.rs | 1 + src/storage.rs | 146 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 src/storage.rs diff --git a/src/lib.rs b/src/lib.rs index a5f2c1883..094c1621e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -697,6 +697,7 @@ pub mod pwm; pub mod qei; pub mod rng; pub mod serial; +pub mod storage; pub mod spi; pub mod timer; pub mod watchdog; diff --git a/src/storage.rs b/src/storage.rs new file mode 100644 index 000000000..9a9d88dc8 --- /dev/null +++ b/src/storage.rs @@ -0,0 +1,146 @@ +//! Storage traits to allow on and off board storage deivces 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/ + +use core::ops::{Add, BitOr, Sub}; +use nb; + +/// Trait to check if two entities are bitwise subset of another. +pub trait BitSubset { + /// Check that every '1' bit is a '1' on the right hand side. + fn is_subset_of(&self, rhs: &Self) -> bool; +} + +/// Blanket implementation of [`BitSubset`] for all arrays of a type implementing [`BitOr`] +impl> BitSubset for [T] { + fn is_subset_of(&self, rhs: &Self) -> bool { + if self.len() > rhs.len() { + false + } else { + self.iter().zip(rhs.iter()).all(|(a, b)| (*a | *b) == *b) + } + } +} + +/// 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: Address) -> Self::Output { + Address(self.0 + rhs.0) + } +} + +impl Sub
for Address { + type Output = Self; + + fn sub(self, rhs: Address) -> Self::Output { + Address(self.0 - rhs.0) + } +} + +/// 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; +} + +/// Iterator producing block-region pairs, where each memory block maps to each +/// region. +pub struct OverlapIterator<'a, R, I> +where + R: Region, + I: Iterator, +{ + memory: &'a [u8], + regions: I, + base_address: Address, +} + +/// Trait allowing us to automatically add an `overlaps` function to all iterators over [`Region`] +pub trait IterableByOverlaps<'a, R, I> +where + R: Region, + 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; +} + +impl<'a, R, I> Iterator for OverlapIterator<'a, R, I> +where + R: Region, + I: Iterator, +{ + type Item = (&'a [u8], R, Address); + + 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 + Address(*index as u32))) + .take_while(|index| region.contains(self.base_address + 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 + Address(start as u32), + )); + } + } + None + } +} + +/// Blanket implementation for all types implementing [`Iterator`] over [`Regions`] +impl<'a, R, I> IterableByOverlaps<'a, R, I> for I +where + R: Region, + I: Iterator, +{ + fn overlaps(self, memory: &'a [u8], base_address: Address) -> OverlapIterator { + OverlapIterator { + memory, + regions: self, + base_address, + } + } +} + +/// Storage trait +pub trait ReadWrite { + /// 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 + /// (`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. + /// + /// (start_addr, end_addr) + fn range(&self) -> (Address, Address); + + /// Erase the full storage range, clearing all data withing `range()`. + fn try_erase(&mut self) -> nb::Result<(), Self::Error>; +} From e101dbc76241a8906b91d02743c8cc46f2e8189e Mon Sep 17 00:00:00 2001 From: Mathias Koch Date: Fri, 18 Sep 2020 10:19:47 +0200 Subject: [PATCH 2/4] Fix formatting --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 094c1621e..abef0425e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -697,8 +697,8 @@ pub mod pwm; pub mod qei; pub mod rng; pub mod serial; -pub mod storage; pub mod spi; +pub mod storage; pub mod timer; pub mod watchdog; From 5e34df6d55a4642cfc49b6e10b4504e799aa7ea1 Mon Sep 17 00:00:00 2001 From: Mathias Koch Date: Fri, 9 Oct 2020 11:39:35 +0200 Subject: [PATCH 3/4] Correct typo in docs --- src/storage.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/storage.rs b/src/storage.rs index 9a9d88dc8..012949d9e 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -129,7 +129,7 @@ pub trait ReadWrite { /// Read a slice of data from the storage peripheral, starting the read /// operation at the given address, and reading until end address - /// (`range().1`) or buffer length, whichever comes first. + /// (`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 @@ -141,6 +141,6 @@ pub trait ReadWrite { /// (start_addr, end_addr) fn range(&self) -> (Address, Address); - /// Erase the full storage range, clearing all data withing `range()`. + /// Erase the full storage range, clearing all data within `self.range()`. fn try_erase(&mut self) -> nb::Result<(), Self::Error>; } From 9f3b9555e6765a7987542e375db733c4e482c92a Mon Sep 17 00:00:00 2001 From: Mathias Koch Date: Thu, 15 Oct 2020 08:26:32 +0200 Subject: [PATCH 4/4] Change try_erase to take in a range --- src/storage.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/storage.rs b/src/storage.rs index 012949d9e..a94fa2943 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -141,6 +141,9 @@ pub trait ReadWrite { /// (start_addr, end_addr) fn range(&self) -> (Address, Address); - /// Erase the full storage range, clearing all data within `self.range()`. - fn try_erase(&mut self) -> nb::Result<(), Self::Error>; + /// Erase the given storage range, clearing all data within `[from..to]`. + /// + /// 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>; }