From 62e799da09e144c9bd2bc3935011913e62c86d16 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 26 May 2023 21:40:12 +0200 Subject: Create flash partition for shared flash access --- embassy-embedded-hal/Cargo.toml | 1 + embassy-embedded-hal/src/adapter/yielding_async.rs | 62 +---- embassy-embedded-hal/src/flash.rs | 286 --------------------- embassy-embedded-hal/src/flash/concat_flash.rs | 228 ++++++++++++++++ embassy-embedded-hal/src/flash/mem_flash.rs | 127 +++++++++ embassy-embedded-hal/src/flash/mod.rs | 9 + embassy-embedded-hal/src/flash/partition.rs | 150 +++++++++++ 7 files changed, 522 insertions(+), 341 deletions(-) delete mode 100644 embassy-embedded-hal/src/flash.rs create mode 100644 embassy-embedded-hal/src/flash/concat_flash.rs create mode 100644 embassy-embedded-hal/src/flash/mem_flash.rs create mode 100644 embassy-embedded-hal/src/flash/mod.rs create mode 100644 embassy-embedded-hal/src/flash/partition.rs (limited to 'embassy-embedded-hal') diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index ad2f14568..35c70bb63 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -31,4 +31,5 @@ nb = "1.0.0" defmt = { version = "0.3", optional = true } [dev-dependencies] +critical-section = { version = "1.1.1", features = ["std"] } futures-test = "0.3.17" diff --git a/embassy-embedded-hal/src/adapter/yielding_async.rs b/embassy-embedded-hal/src/adapter/yielding_async.rs index 96d5cca8e..f51e4076f 100644 --- a/embassy-embedded-hal/src/adapter/yielding_async.rs +++ b/embassy-embedded-hal/src/adapter/yielding_async.rs @@ -167,66 +167,18 @@ mod tests { use embedded_storage_async::nor_flash::NorFlash; use super::*; - - extern crate std; - - #[derive(Default)] - struct FakeFlash(Vec<(u32, u32)>); - - impl embedded_storage::nor_flash::ErrorType for FakeFlash { - type Error = std::convert::Infallible; - } - - impl embedded_storage_async::nor_flash::ReadNorFlash for FakeFlash { - const READ_SIZE: usize = 1; - - async fn read(&mut self, _offset: u32, _bytes: &mut [u8]) -> Result<(), Self::Error> { - unimplemented!() - } - - fn capacity(&self) -> usize { - unimplemented!() - } - } - - impl embedded_storage_async::nor_flash::NorFlash for FakeFlash { - const WRITE_SIZE: usize = 4; - const ERASE_SIZE: usize = 128; - - async fn write(&mut self, _offset: u32, _bytes: &[u8]) -> Result<(), Self::Error> { - unimplemented!() - } - - async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.0.push((from, to)); - Ok(()) - } - } + use crate::flash::mem_flash::MemFlash; #[futures_test::test] async fn can_erase() { - let fake = FakeFlash::default(); - let mut yielding = YieldingAsync::new(fake); + let flash = MemFlash::<1024, 128, 4>::new(0x00); + let mut yielding = YieldingAsync::new(flash); yielding.erase(0, 256).await.unwrap(); - let fake = yielding.wrapped; - assert_eq!(2, fake.0.len()); - assert_eq!((0, 128), fake.0[0]); - assert_eq!((128, 256), fake.0[1]); - } - - #[futures_test::test] - async fn can_erase_wrong_erase_size() { - let fake = FakeFlash::default(); - let mut yielding = YieldingAsync::new(fake); - - yielding.erase(0, 257).await.unwrap(); - - let fake = yielding.wrapped; - assert_eq!(3, fake.0.len()); - assert_eq!((0, 128), fake.0[0]); - assert_eq!((128, 256), fake.0[1]); - assert_eq!((256, 257), fake.0[2]); + let flash = yielding.wrapped; + assert_eq!(2, flash.erases.len()); + assert_eq!((0, 128), flash.erases[0]); + assert_eq!((128, 256), flash.erases[1]); } } diff --git a/embassy-embedded-hal/src/flash.rs b/embassy-embedded-hal/src/flash.rs deleted file mode 100644 index 9a6e4bd92..000000000 --- a/embassy-embedded-hal/src/flash.rs +++ /dev/null @@ -1,286 +0,0 @@ -//! Utilities related to flash. - -use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, ReadNorFlash}; -#[cfg(feature = "nightly")] -use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; - -/// Convenience helper for concatenating two consecutive flashes into one. -/// This is especially useful if used with "flash regions", where one may -/// want to concatenate multiple regions into one larger region. -pub struct ConcatFlash(First, Second); - -impl ConcatFlash { - /// Create a new flash that concatenates two consecutive flashes. - pub fn new(first: First, second: Second) -> Self { - Self(first, second) - } -} - -const fn get_read_size(first_read_size: usize, second_read_size: usize) -> usize { - if first_read_size != second_read_size { - panic!("The read size for the concatenated flashes must be the same"); - } - first_read_size -} - -const fn get_write_size(first_write_size: usize, second_write_size: usize) -> usize { - if first_write_size != second_write_size { - panic!("The write size for the concatenated flashes must be the same"); - } - first_write_size -} - -const fn get_max_erase_size(first_erase_size: usize, second_erase_size: usize) -> usize { - let max_erase_size = if first_erase_size > second_erase_size { - first_erase_size - } else { - second_erase_size - }; - if max_erase_size % first_erase_size != 0 || max_erase_size % second_erase_size != 0 { - panic!("The erase sizes for the concatenated flashes must have have a gcd equal to the max erase size"); - } - max_erase_size -} - -impl ErrorType for ConcatFlash -where - First: ErrorType, - Second: ErrorType, - E: NorFlashError, -{ - type Error = E; -} - -impl ReadNorFlash for ConcatFlash -where - First: ReadNorFlash, - Second: ReadNorFlash, - E: NorFlashError, -{ - const READ_SIZE: usize = get_read_size(First::READ_SIZE, Second::READ_SIZE); - - fn read(&mut self, mut offset: u32, mut bytes: &mut [u8]) -> Result<(), E> { - if offset < self.0.capacity() as u32 { - let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len()); - self.0.read(offset, &mut bytes[..len])?; - offset += len as u32; - bytes = &mut bytes[len..]; - } - - if !bytes.is_empty() { - self.1.read(offset - self.0.capacity() as u32, bytes)?; - } - - Ok(()) - } - - fn capacity(&self) -> usize { - self.0.capacity() + self.1.capacity() - } -} - -impl NorFlash for ConcatFlash -where - First: NorFlash, - Second: NorFlash, - E: NorFlashError, -{ - const WRITE_SIZE: usize = get_write_size(First::WRITE_SIZE, Second::WRITE_SIZE); - const ERASE_SIZE: usize = get_max_erase_size(First::ERASE_SIZE, Second::ERASE_SIZE); - - fn write(&mut self, mut offset: u32, mut bytes: &[u8]) -> Result<(), E> { - if offset < self.0.capacity() as u32 { - let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len()); - self.0.write(offset, &bytes[..len])?; - offset += len as u32; - bytes = &bytes[len..]; - } - - if !bytes.is_empty() { - self.1.write(offset - self.0.capacity() as u32, bytes)?; - } - - Ok(()) - } - - fn erase(&mut self, mut from: u32, to: u32) -> Result<(), E> { - if from < self.0.capacity() as u32 { - let to = core::cmp::min(self.0.capacity() as u32, to); - self.0.erase(from, to)?; - from = self.0.capacity() as u32; - } - - if from < to { - self.1 - .erase(from - self.0.capacity() as u32, to - self.0.capacity() as u32)?; - } - - Ok(()) - } -} - -#[cfg(feature = "nightly")] -impl AsyncReadNorFlash for ConcatFlash -where - First: AsyncReadNorFlash, - Second: AsyncReadNorFlash, - E: NorFlashError, -{ - const READ_SIZE: usize = get_read_size(First::READ_SIZE, Second::READ_SIZE); - - async fn read(&mut self, mut offset: u32, mut bytes: &mut [u8]) -> Result<(), E> { - if offset < self.0.capacity() as u32 { - let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len()); - self.0.read(offset, &mut bytes[..len]).await?; - offset += len as u32; - bytes = &mut bytes[len..]; - } - - if !bytes.is_empty() { - self.1.read(offset - self.0.capacity() as u32, bytes).await?; - } - - Ok(()) - } - - fn capacity(&self) -> usize { - self.0.capacity() + self.1.capacity() - } -} - -#[cfg(feature = "nightly")] -impl AsyncNorFlash for ConcatFlash -where - First: AsyncNorFlash, - Second: AsyncNorFlash, - E: NorFlashError, -{ - const WRITE_SIZE: usize = get_write_size(First::WRITE_SIZE, Second::WRITE_SIZE); - const ERASE_SIZE: usize = get_max_erase_size(First::ERASE_SIZE, Second::ERASE_SIZE); - - async fn write(&mut self, mut offset: u32, mut bytes: &[u8]) -> Result<(), E> { - if offset < self.0.capacity() as u32 { - let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len()); - self.0.write(offset, &bytes[..len]).await?; - offset += len as u32; - bytes = &bytes[len..]; - } - - if !bytes.is_empty() { - self.1.write(offset - self.0.capacity() as u32, bytes).await?; - } - - Ok(()) - } - - async fn erase(&mut self, mut from: u32, to: u32) -> Result<(), E> { - if from < self.0.capacity() as u32 { - let to = core::cmp::min(self.0.capacity() as u32, to); - self.0.erase(from, to).await?; - from = self.0.capacity() as u32; - } - - if from < to { - self.1 - .erase(from - self.0.capacity() as u32, to - self.0.capacity() as u32) - .await?; - } - - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn can_write_and_read_across_flashes() { - let first = MemFlash::<64, 16, 4>::new(); - let second = MemFlash::<64, 64, 4>::new(); - let mut f = ConcatFlash::new(first, second); - - f.write(60, &[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]).unwrap(); - - assert_eq!(&[0x11, 0x22, 0x33, 0x44], &f.0 .0[60..]); - assert_eq!(&[0x55, 0x66, 0x77, 0x88], &f.1 .0[0..4]); - - let mut read_buf = [0; 8]; - f.read(60, &mut read_buf).unwrap(); - - assert_eq!(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88], &read_buf); - } - - #[test] - fn can_erase_across_flashes() { - let mut first = MemFlash::<128, 16, 4>::new(); - let mut second = MemFlash::<128, 64, 4>::new(); - first.0.fill(0x00); - second.0.fill(0x00); - - let mut f = ConcatFlash::new(first, second); - - f.erase(64, 192).unwrap(); - - assert_eq!(&[0x00; 64], &f.0 .0[0..64]); - assert_eq!(&[0xff; 64], &f.0 .0[64..128]); - assert_eq!(&[0xff; 64], &f.1 .0[0..64]); - assert_eq!(&[0x00; 64], &f.1 .0[64..128]); - } - - pub struct MemFlash([u8; SIZE]); - - impl MemFlash { - pub const fn new() -> Self { - Self([0xff; SIZE]) - } - } - - impl ErrorType - for MemFlash - { - type Error = core::convert::Infallible; - } - - impl ReadNorFlash - for MemFlash - { - const READ_SIZE: usize = 1; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - let len = bytes.len(); - bytes.copy_from_slice(&self.0[offset as usize..offset as usize + len]); - Ok(()) - } - - fn capacity(&self) -> usize { - SIZE - } - } - - impl NorFlash - for MemFlash - { - const WRITE_SIZE: usize = WRITE_SIZE; - const ERASE_SIZE: usize = ERASE_SIZE; - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - let from = from as usize; - let to = to as usize; - assert_eq!(0, from % ERASE_SIZE); - assert_eq!(0, to % ERASE_SIZE); - self.0[from..to].fill(0xff); - Ok(()) - } - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - let offset = offset as usize; - assert_eq!(0, bytes.len() % WRITE_SIZE); - assert_eq!(0, offset % WRITE_SIZE); - assert!(offset + bytes.len() <= SIZE); - - self.0[offset..offset + bytes.len()].copy_from_slice(bytes); - Ok(()) - } - } -} diff --git a/embassy-embedded-hal/src/flash/concat_flash.rs b/embassy-embedded-hal/src/flash/concat_flash.rs new file mode 100644 index 000000000..1ea84269c --- /dev/null +++ b/embassy-embedded-hal/src/flash/concat_flash.rs @@ -0,0 +1,228 @@ +use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, ReadNorFlash}; +#[cfg(feature = "nightly")] +use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; + +/// Convenience helper for concatenating two consecutive flashes into one. +/// This is especially useful if used with "flash regions", where one may +/// want to concatenate multiple regions into one larger region. +pub struct ConcatFlash(First, Second); + +impl ConcatFlash { + /// Create a new flash that concatenates two consecutive flashes. + pub fn new(first: First, second: Second) -> Self { + Self(first, second) + } +} + +const fn get_read_size(first_read_size: usize, second_read_size: usize) -> usize { + if first_read_size != second_read_size { + panic!("The read size for the concatenated flashes must be the same"); + } + first_read_size +} + +const fn get_write_size(first_write_size: usize, second_write_size: usize) -> usize { + if first_write_size != second_write_size { + panic!("The write size for the concatenated flashes must be the same"); + } + first_write_size +} + +const fn get_max_erase_size(first_erase_size: usize, second_erase_size: usize) -> usize { + let max_erase_size = if first_erase_size > second_erase_size { + first_erase_size + } else { + second_erase_size + }; + if max_erase_size % first_erase_size != 0 || max_erase_size % second_erase_size != 0 { + panic!("The erase sizes for the concatenated flashes must have have a gcd equal to the max erase size"); + } + max_erase_size +} + +impl ErrorType for ConcatFlash +where + First: ErrorType, + Second: ErrorType, + E: NorFlashError, +{ + type Error = E; +} + +impl ReadNorFlash for ConcatFlash +where + First: ReadNorFlash, + Second: ReadNorFlash, + E: NorFlashError, +{ + const READ_SIZE: usize = get_read_size(First::READ_SIZE, Second::READ_SIZE); + + fn read(&mut self, mut offset: u32, mut bytes: &mut [u8]) -> Result<(), E> { + if offset < self.0.capacity() as u32 { + let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len()); + self.0.read(offset, &mut bytes[..len])?; + offset += len as u32; + bytes = &mut bytes[len..]; + } + + if !bytes.is_empty() { + self.1.read(offset - self.0.capacity() as u32, bytes)?; + } + + Ok(()) + } + + fn capacity(&self) -> usize { + self.0.capacity() + self.1.capacity() + } +} + +impl NorFlash for ConcatFlash +where + First: NorFlash, + Second: NorFlash, + E: NorFlashError, +{ + const WRITE_SIZE: usize = get_write_size(First::WRITE_SIZE, Second::WRITE_SIZE); + const ERASE_SIZE: usize = get_max_erase_size(First::ERASE_SIZE, Second::ERASE_SIZE); + + fn write(&mut self, mut offset: u32, mut bytes: &[u8]) -> Result<(), E> { + if offset < self.0.capacity() as u32 { + let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len()); + self.0.write(offset, &bytes[..len])?; + offset += len as u32; + bytes = &bytes[len..]; + } + + if !bytes.is_empty() { + self.1.write(offset - self.0.capacity() as u32, bytes)?; + } + + Ok(()) + } + + fn erase(&mut self, mut from: u32, to: u32) -> Result<(), E> { + if from < self.0.capacity() as u32 { + let to = core::cmp::min(self.0.capacity() as u32, to); + self.0.erase(from, to)?; + from = self.0.capacity() as u32; + } + + if from < to { + self.1 + .erase(from - self.0.capacity() as u32, to - self.0.capacity() as u32)?; + } + + Ok(()) + } +} + +#[cfg(feature = "nightly")] +impl AsyncReadNorFlash for ConcatFlash +where + First: AsyncReadNorFlash, + Second: AsyncReadNorFlash, + E: NorFlashError, +{ + const READ_SIZE: usize = get_read_size(First::READ_SIZE, Second::READ_SIZE); + + async fn read(&mut self, mut offset: u32, mut bytes: &mut [u8]) -> Result<(), E> { + if offset < self.0.capacity() as u32 { + let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len()); + self.0.read(offset, &mut bytes[..len]).await?; + offset += len as u32; + bytes = &mut bytes[len..]; + } + + if !bytes.is_empty() { + self.1.read(offset - self.0.capacity() as u32, bytes).await?; + } + + Ok(()) + } + + fn capacity(&self) -> usize { + self.0.capacity() + self.1.capacity() + } +} + +#[cfg(feature = "nightly")] +impl AsyncNorFlash for ConcatFlash +where + First: AsyncNorFlash, + Second: AsyncNorFlash, + E: NorFlashError, +{ + const WRITE_SIZE: usize = get_write_size(First::WRITE_SIZE, Second::WRITE_SIZE); + const ERASE_SIZE: usize = get_max_erase_size(First::ERASE_SIZE, Second::ERASE_SIZE); + + async fn write(&mut self, mut offset: u32, mut bytes: &[u8]) -> Result<(), E> { + if offset < self.0.capacity() as u32 { + let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len()); + self.0.write(offset, &bytes[..len]).await?; + offset += len as u32; + bytes = &bytes[len..]; + } + + if !bytes.is_empty() { + self.1.write(offset - self.0.capacity() as u32, bytes).await?; + } + + Ok(()) + } + + async fn erase(&mut self, mut from: u32, to: u32) -> Result<(), E> { + if from < self.0.capacity() as u32 { + let to = core::cmp::min(self.0.capacity() as u32, to); + self.0.erase(from, to).await?; + from = self.0.capacity() as u32; + } + + if from < to { + self.1 + .erase(from - self.0.capacity() as u32, to - self.0.capacity() as u32) + .await?; + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; + + use super::ConcatFlash; + use crate::flash::mem_flash::MemFlash; + + #[test] + fn can_write_and_read_across_flashes() { + let first = MemFlash::<64, 16, 4>::default(); + let second = MemFlash::<64, 64, 4>::default(); + let mut f = ConcatFlash::new(first, second); + + f.write(60, &[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]).unwrap(); + + assert_eq!(&[0x11, 0x22, 0x33, 0x44], &f.0.mem[60..]); + assert_eq!(&[0x55, 0x66, 0x77, 0x88], &f.1.mem[0..4]); + + let mut read_buf = [0; 8]; + f.read(60, &mut read_buf).unwrap(); + + assert_eq!(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88], &read_buf); + } + + #[test] + fn can_erase_across_flashes() { + let first = MemFlash::<128, 16, 4>::new(0x00); + let second = MemFlash::<128, 64, 4>::new(0x00); + let mut f = ConcatFlash::new(first, second); + + f.erase(64, 192).unwrap(); + + assert_eq!(&[0x00; 64], &f.0.mem[0..64]); + assert_eq!(&[0xff; 64], &f.0.mem[64..128]); + assert_eq!(&[0xff; 64], &f.1.mem[0..64]); + assert_eq!(&[0x00; 64], &f.1.mem[64..128]); + } +} diff --git a/embassy-embedded-hal/src/flash/mem_flash.rs b/embassy-embedded-hal/src/flash/mem_flash.rs new file mode 100644 index 000000000..4e10627df --- /dev/null +++ b/embassy-embedded-hal/src/flash/mem_flash.rs @@ -0,0 +1,127 @@ +use alloc::vec::Vec; + +use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; +use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; + +extern crate alloc; + +pub(crate) struct MemFlash { + pub mem: [u8; SIZE], + pub writes: Vec<(u32, usize)>, + pub erases: Vec<(u32, u32)>, +} + +impl MemFlash { + #[allow(unused)] + pub const fn new(fill: u8) -> Self { + Self { + mem: [fill; SIZE], + writes: Vec::new(), + erases: Vec::new(), + } + } + + fn read(&mut self, offset: u32, bytes: &mut [u8]) { + let len = bytes.len(); + bytes.copy_from_slice(&self.mem[offset as usize..offset as usize + len]); + } + + fn write(&mut self, offset: u32, bytes: &[u8]) { + self.writes.push((offset, bytes.len())); + let offset = offset as usize; + assert_eq!(0, bytes.len() % WRITE_SIZE); + assert_eq!(0, offset % WRITE_SIZE); + assert!(offset + bytes.len() <= SIZE); + + self.mem[offset..offset + bytes.len()].copy_from_slice(bytes); + } + + fn erase(&mut self, from: u32, to: u32) { + self.erases.push((from, to)); + let from = from as usize; + let to = to as usize; + assert_eq!(0, from % ERASE_SIZE); + assert_eq!(0, to % ERASE_SIZE); + self.mem[from..to].fill(0xff); + } +} + +impl Default + for MemFlash +{ + fn default() -> Self { + Self::new(0xff) + } +} + +impl ErrorType + for MemFlash +{ + type Error = core::convert::Infallible; +} + +impl ReadNorFlash + for MemFlash +{ + const READ_SIZE: usize = 1; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes); + Ok(()) + } + + fn capacity(&self) -> usize { + SIZE + } +} + +impl NorFlash + for MemFlash +{ + const WRITE_SIZE: usize = WRITE_SIZE; + const ERASE_SIZE: usize = ERASE_SIZE; + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes); + Ok(()) + } + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to); + Ok(()) + } +} + +#[cfg(feature = "nightly")] +impl AsyncReadNorFlash + for MemFlash +{ + const READ_SIZE: usize = 1; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes); + Ok(()) + } + + fn capacity(&self) -> usize { + SIZE + } +} + +#[cfg(feature = "nightly")] +impl AsyncNorFlash + for MemFlash +{ + const WRITE_SIZE: usize = WRITE_SIZE; + const ERASE_SIZE: usize = ERASE_SIZE; + + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes); + Ok(()) + } + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to); + Ok(()) + } +} diff --git a/embassy-embedded-hal/src/flash/mod.rs b/embassy-embedded-hal/src/flash/mod.rs new file mode 100644 index 000000000..c80dd6aac --- /dev/null +++ b/embassy-embedded-hal/src/flash/mod.rs @@ -0,0 +1,9 @@ +//! Utilities related to flash. + +mod concat_flash; +#[cfg(test)] +pub(crate) mod mem_flash; +mod partition; + +pub use concat_flash::ConcatFlash; +pub use partition::Partition; diff --git a/embassy-embedded-hal/src/flash/partition.rs b/embassy-embedded-hal/src/flash/partition.rs new file mode 100644 index 000000000..084425e95 --- /dev/null +++ b/embassy-embedded-hal/src/flash/partition.rs @@ -0,0 +1,150 @@ +use embassy_sync::blocking_mutex::raw::RawMutex; +use embassy_sync::mutex::Mutex; +use embedded_storage::nor_flash::{ErrorType, NorFlashError, NorFlashErrorKind}; +#[cfg(feature = "nightly")] +use embedded_storage_async::nor_flash::{NorFlash, ReadNorFlash}; + +/// A logical partition of an underlying shared flash +/// +/// A partition holds an offset and a size of the flash, +/// and is restricted to operate with that range. +/// There is no guarantee that muliple partitions on the same flash +/// operate on mutually exclusive ranges - such a separation is up to +/// the user to guarantee. +pub struct Partition<'a, M: RawMutex, T> { + flash: &'a Mutex, + offset: u32, + size: u32, +} + +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + Partition, + OutOfBounds, + Flash(T), +} + +impl<'a, M: RawMutex, T> Partition<'a, M, T> { + /// Create a new partition + pub const fn new(flash: &'a Mutex, offset: u32, size: u32) -> Self { + Self { flash, offset, size } + } +} + +impl NorFlashError for Error { + fn kind(&self) -> NorFlashErrorKind { + match self { + Error::Partition => NorFlashErrorKind::Other, + Error::OutOfBounds => NorFlashErrorKind::OutOfBounds, + Error::Flash(f) => f.kind(), + } + } +} + +impl ErrorType for Partition<'_, M, T> { + type Error = Error; +} + +#[cfg(feature = "nightly")] +impl ReadNorFlash for Partition<'_, M, T> { + const READ_SIZE: usize = T::READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + if self.offset % T::READ_SIZE as u32 != 0 || self.size % T::READ_SIZE as u32 != 0 { + return Err(Error::Partition); + } + if offset + bytes.len() as u32 > self.size { + return Err(Error::OutOfBounds); + } + + let mut flash = self.flash.lock().await; + flash.read(self.offset + offset, bytes).await.map_err(Error::Flash) + } + + fn capacity(&self) -> usize { + self.size as usize + } +} + +#[cfg(feature = "nightly")] +impl NorFlash for Partition<'_, M, T> { + const WRITE_SIZE: usize = T::WRITE_SIZE; + const ERASE_SIZE: usize = T::ERASE_SIZE; + + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + if self.offset % T::WRITE_SIZE as u32 != 0 || self.size % T::WRITE_SIZE as u32 != 0 { + return Err(Error::Partition); + } + if offset + bytes.len() as u32 > self.size { + return Err(Error::OutOfBounds); + } + + let mut flash = self.flash.lock().await; + flash.write(self.offset + offset, bytes).await.map_err(Error::Flash) + } + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + if self.offset % T::ERASE_SIZE as u32 != 0 || self.size % T::ERASE_SIZE as u32 != 0 { + return Err(Error::Partition); + } + if to > self.size { + return Err(Error::OutOfBounds); + } + + let mut flash = self.flash.lock().await; + flash + .erase(self.offset + from, self.offset + to) + .await + .map_err(Error::Flash) + } +} + +#[cfg(test)] +mod tests { + use embassy_sync::blocking_mutex::raw::NoopRawMutex; + + use super::*; + use crate::flash::mem_flash::MemFlash; + + #[futures_test::test] + async fn can_read() { + let mut flash = MemFlash::<1024, 128, 4>::default(); + flash.mem[12..20].fill(0xAA); + + let flash = Mutex::::new(flash); + let mut partition = Partition::new(&flash, 8, 12); + + let mut read_buf = [0; 8]; + partition.read(4, &mut read_buf).await.unwrap(); + + assert!(read_buf.iter().position(|&x| x != 0xAA).is_none()); + } + + #[futures_test::test] + async fn can_write() { + let flash = MemFlash::<1024, 128, 4>::default(); + + let flash = Mutex::::new(flash); + let mut partition = Partition::new(&flash, 8, 12); + + let write_buf = [0xAA; 8]; + partition.write(4, &write_buf).await.unwrap(); + + let flash = flash.try_lock().unwrap(); + assert!(flash.mem[12..20].iter().position(|&x| x != 0xAA).is_none()); + } + + #[futures_test::test] + async fn can_erase() { + let flash = MemFlash::<1024, 128, 4>::new(0x00); + + let flash = Mutex::::new(flash); + let mut partition = Partition::new(&flash, 128, 256); + + partition.erase(0, 128).await.unwrap(); + + let flash = flash.try_lock().unwrap(); + assert!(flash.mem[128..256].iter().position(|&x| x != 0xFF).is_none()); + } +} -- cgit From e495473fc341e1403a004901e0d0a575f33350e0 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 26 May 2023 22:07:23 +0200 Subject: Remove runtime offset and size assertions --- embassy-embedded-hal/src/flash/mem_flash.rs | 1 + embassy-embedded-hal/src/flash/mod.rs | 2 ++ embassy-embedded-hal/src/flash/partition.rs | 35 +++++++++++++---------------- 3 files changed, 18 insertions(+), 20 deletions(-) (limited to 'embassy-embedded-hal') diff --git a/embassy-embedded-hal/src/flash/mem_flash.rs b/embassy-embedded-hal/src/flash/mem_flash.rs index 4e10627df..afb0d1a15 100644 --- a/embassy-embedded-hal/src/flash/mem_flash.rs +++ b/embassy-embedded-hal/src/flash/mem_flash.rs @@ -1,6 +1,7 @@ use alloc::vec::Vec; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; +#[cfg(feature = "nightly")] use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; extern crate alloc; diff --git a/embassy-embedded-hal/src/flash/mod.rs b/embassy-embedded-hal/src/flash/mod.rs index c80dd6aac..0210b198d 100644 --- a/embassy-embedded-hal/src/flash/mod.rs +++ b/embassy-embedded-hal/src/flash/mod.rs @@ -3,7 +3,9 @@ mod concat_flash; #[cfg(test)] pub(crate) mod mem_flash; +#[cfg(feature = "nightly")] mod partition; pub use concat_flash::ConcatFlash; +#[cfg(feature = "nightly")] pub use partition::Partition; diff --git a/embassy-embedded-hal/src/flash/partition.rs b/embassy-embedded-hal/src/flash/partition.rs index 084425e95..66d93c0ea 100644 --- a/embassy-embedded-hal/src/flash/partition.rs +++ b/embassy-embedded-hal/src/flash/partition.rs @@ -1,7 +1,6 @@ use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::mutex::Mutex; use embedded_storage::nor_flash::{ErrorType, NorFlashError, NorFlashErrorKind}; -#[cfg(feature = "nightly")] use embedded_storage_async::nor_flash::{NorFlash, ReadNorFlash}; /// A logical partition of an underlying shared flash @@ -11,7 +10,7 @@ use embedded_storage_async::nor_flash::{NorFlash, ReadNorFlash}; /// There is no guarantee that muliple partitions on the same flash /// operate on mutually exclusive ranges - such a separation is up to /// the user to guarantee. -pub struct Partition<'a, M: RawMutex, T> { +pub struct Partition<'a, M: RawMutex, T: NorFlash> { flash: &'a Mutex, offset: u32, size: u32, @@ -20,14 +19,20 @@ pub struct Partition<'a, M: RawMutex, T> { #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { - Partition, OutOfBounds, Flash(T), } -impl<'a, M: RawMutex, T> Partition<'a, M, T> { +impl<'a, M: RawMutex, T: NorFlash> Partition<'a, M, T> { /// Create a new partition pub const fn new(flash: &'a Mutex, offset: u32, size: u32) -> Self { + if offset % T::READ_SIZE as u32 != 0 || offset % T::WRITE_SIZE as u32 != 0 || offset % T::ERASE_SIZE as u32 != 0 + { + panic!("Partition offset must be a multiple of read, write and erase size"); + } + if size % T::READ_SIZE as u32 != 0 || size % T::WRITE_SIZE as u32 != 0 || size % T::ERASE_SIZE as u32 != 0 { + panic!("Partition size must be a multiple of read, write and erase size"); + } Self { flash, offset, size } } } @@ -35,25 +40,21 @@ impl<'a, M: RawMutex, T> Partition<'a, M, T> { impl NorFlashError for Error { fn kind(&self) -> NorFlashErrorKind { match self { - Error::Partition => NorFlashErrorKind::Other, Error::OutOfBounds => NorFlashErrorKind::OutOfBounds, Error::Flash(f) => f.kind(), } } } -impl ErrorType for Partition<'_, M, T> { +impl ErrorType for Partition<'_, M, T> { type Error = Error; } #[cfg(feature = "nightly")] -impl ReadNorFlash for Partition<'_, M, T> { +impl ReadNorFlash for Partition<'_, M, T> { const READ_SIZE: usize = T::READ_SIZE; async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - if self.offset % T::READ_SIZE as u32 != 0 || self.size % T::READ_SIZE as u32 != 0 { - return Err(Error::Partition); - } if offset + bytes.len() as u32 > self.size { return Err(Error::OutOfBounds); } @@ -73,9 +74,6 @@ impl NorFlash for Partition<'_, M, T> { const ERASE_SIZE: usize = T::ERASE_SIZE; async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - if self.offset % T::WRITE_SIZE as u32 != 0 || self.size % T::WRITE_SIZE as u32 != 0 { - return Err(Error::Partition); - } if offset + bytes.len() as u32 > self.size { return Err(Error::OutOfBounds); } @@ -85,9 +83,6 @@ impl NorFlash for Partition<'_, M, T> { } async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - if self.offset % T::ERASE_SIZE as u32 != 0 || self.size % T::ERASE_SIZE as u32 != 0 { - return Err(Error::Partition); - } if to > self.size { return Err(Error::OutOfBounds); } @@ -110,10 +105,10 @@ mod tests { #[futures_test::test] async fn can_read() { let mut flash = MemFlash::<1024, 128, 4>::default(); - flash.mem[12..20].fill(0xAA); + flash.mem[132..132 + 8].fill(0xAA); let flash = Mutex::::new(flash); - let mut partition = Partition::new(&flash, 8, 12); + let mut partition = Partition::new(&flash, 128, 256); let mut read_buf = [0; 8]; partition.read(4, &mut read_buf).await.unwrap(); @@ -126,13 +121,13 @@ mod tests { let flash = MemFlash::<1024, 128, 4>::default(); let flash = Mutex::::new(flash); - let mut partition = Partition::new(&flash, 8, 12); + let mut partition = Partition::new(&flash, 128, 256); let write_buf = [0xAA; 8]; partition.write(4, &write_buf).await.unwrap(); let flash = flash.try_lock().unwrap(); - assert!(flash.mem[12..20].iter().position(|&x| x != 0xAA).is_none()); + assert!(flash.mem[132..132 + 8].iter().position(|&x| x != 0xAA).is_none()); } #[futures_test::test] -- cgit