diff options
| author | Rasmus Melchior Jacobsen <[email protected]> | 2023-05-26 21:40:12 +0200 |
|---|---|---|
| committer | Rasmus Melchior Jacobsen <[email protected]> | 2023-05-26 21:40:12 +0200 |
| commit | 62e799da09e144c9bd2bc3935011913e62c86d16 (patch) | |
| tree | ab10c8372782bece3a48d43dd9daf00a58a6939c /embassy-embedded-hal/src/flash/concat_flash.rs | |
| parent | a8b426d0fe86e6fc3d1813765946cc82e774c3d3 (diff) | |
Create flash partition for shared flash access
Diffstat (limited to 'embassy-embedded-hal/src/flash/concat_flash.rs')
| -rw-r--r-- | embassy-embedded-hal/src/flash/concat_flash.rs | 228 |
1 files changed, 228 insertions, 0 deletions
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 @@ | |||
| 1 | use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, ReadNorFlash}; | ||
| 2 | #[cfg(feature = "nightly")] | ||
| 3 | use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; | ||
| 4 | |||
| 5 | /// Convenience helper for concatenating two consecutive flashes into one. | ||
| 6 | /// This is especially useful if used with "flash regions", where one may | ||
| 7 | /// want to concatenate multiple regions into one larger region. | ||
| 8 | pub struct ConcatFlash<First, Second>(First, Second); | ||
| 9 | |||
| 10 | impl<First, Second> ConcatFlash<First, Second> { | ||
| 11 | /// Create a new flash that concatenates two consecutive flashes. | ||
| 12 | pub fn new(first: First, second: Second) -> Self { | ||
| 13 | Self(first, second) | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | const fn get_read_size(first_read_size: usize, second_read_size: usize) -> usize { | ||
| 18 | if first_read_size != second_read_size { | ||
| 19 | panic!("The read size for the concatenated flashes must be the same"); | ||
| 20 | } | ||
| 21 | first_read_size | ||
| 22 | } | ||
| 23 | |||
| 24 | const fn get_write_size(first_write_size: usize, second_write_size: usize) -> usize { | ||
| 25 | if first_write_size != second_write_size { | ||
| 26 | panic!("The write size for the concatenated flashes must be the same"); | ||
| 27 | } | ||
| 28 | first_write_size | ||
| 29 | } | ||
| 30 | |||
| 31 | const fn get_max_erase_size(first_erase_size: usize, second_erase_size: usize) -> usize { | ||
| 32 | let max_erase_size = if first_erase_size > second_erase_size { | ||
| 33 | first_erase_size | ||
| 34 | } else { | ||
| 35 | second_erase_size | ||
| 36 | }; | ||
| 37 | if max_erase_size % first_erase_size != 0 || max_erase_size % second_erase_size != 0 { | ||
| 38 | panic!("The erase sizes for the concatenated flashes must have have a gcd equal to the max erase size"); | ||
| 39 | } | ||
| 40 | max_erase_size | ||
| 41 | } | ||
| 42 | |||
| 43 | impl<First, Second, E> ErrorType for ConcatFlash<First, Second> | ||
| 44 | where | ||
| 45 | First: ErrorType<Error = E>, | ||
| 46 | Second: ErrorType<Error = E>, | ||
| 47 | E: NorFlashError, | ||
| 48 | { | ||
| 49 | type Error = E; | ||
| 50 | } | ||
| 51 | |||
| 52 | impl<First, Second, E> ReadNorFlash for ConcatFlash<First, Second> | ||
| 53 | where | ||
| 54 | First: ReadNorFlash<Error = E>, | ||
| 55 | Second: ReadNorFlash<Error = E>, | ||
| 56 | E: NorFlashError, | ||
| 57 | { | ||
| 58 | const READ_SIZE: usize = get_read_size(First::READ_SIZE, Second::READ_SIZE); | ||
| 59 | |||
| 60 | fn read(&mut self, mut offset: u32, mut bytes: &mut [u8]) -> Result<(), E> { | ||
| 61 | if offset < self.0.capacity() as u32 { | ||
| 62 | let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len()); | ||
| 63 | self.0.read(offset, &mut bytes[..len])?; | ||
| 64 | offset += len as u32; | ||
| 65 | bytes = &mut bytes[len..]; | ||
| 66 | } | ||
| 67 | |||
| 68 | if !bytes.is_empty() { | ||
| 69 | self.1.read(offset - self.0.capacity() as u32, bytes)?; | ||
| 70 | } | ||
| 71 | |||
| 72 | Ok(()) | ||
| 73 | } | ||
| 74 | |||
| 75 | fn capacity(&self) -> usize { | ||
| 76 | self.0.capacity() + self.1.capacity() | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | impl<First, Second, E> NorFlash for ConcatFlash<First, Second> | ||
| 81 | where | ||
| 82 | First: NorFlash<Error = E>, | ||
| 83 | Second: NorFlash<Error = E>, | ||
| 84 | E: NorFlashError, | ||
| 85 | { | ||
| 86 | const WRITE_SIZE: usize = get_write_size(First::WRITE_SIZE, Second::WRITE_SIZE); | ||
| 87 | const ERASE_SIZE: usize = get_max_erase_size(First::ERASE_SIZE, Second::ERASE_SIZE); | ||
| 88 | |||
| 89 | fn write(&mut self, mut offset: u32, mut bytes: &[u8]) -> Result<(), E> { | ||
| 90 | if offset < self.0.capacity() as u32 { | ||
| 91 | let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len()); | ||
| 92 | self.0.write(offset, &bytes[..len])?; | ||
| 93 | offset += len as u32; | ||
| 94 | bytes = &bytes[len..]; | ||
| 95 | } | ||
| 96 | |||
| 97 | if !bytes.is_empty() { | ||
| 98 | self.1.write(offset - self.0.capacity() as u32, bytes)?; | ||
| 99 | } | ||
| 100 | |||
| 101 | Ok(()) | ||
| 102 | } | ||
| 103 | |||
| 104 | fn erase(&mut self, mut from: u32, to: u32) -> Result<(), E> { | ||
| 105 | if from < self.0.capacity() as u32 { | ||
| 106 | let to = core::cmp::min(self.0.capacity() as u32, to); | ||
| 107 | self.0.erase(from, to)?; | ||
| 108 | from = self.0.capacity() as u32; | ||
| 109 | } | ||
| 110 | |||
| 111 | if from < to { | ||
| 112 | self.1 | ||
| 113 | .erase(from - self.0.capacity() as u32, to - self.0.capacity() as u32)?; | ||
| 114 | } | ||
| 115 | |||
| 116 | Ok(()) | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | #[cfg(feature = "nightly")] | ||
| 121 | impl<First, Second, E> AsyncReadNorFlash for ConcatFlash<First, Second> | ||
| 122 | where | ||
| 123 | First: AsyncReadNorFlash<Error = E>, | ||
| 124 | Second: AsyncReadNorFlash<Error = E>, | ||
| 125 | E: NorFlashError, | ||
| 126 | { | ||
| 127 | const READ_SIZE: usize = get_read_size(First::READ_SIZE, Second::READ_SIZE); | ||
| 128 | |||
| 129 | async fn read(&mut self, mut offset: u32, mut bytes: &mut [u8]) -> Result<(), E> { | ||
| 130 | if offset < self.0.capacity() as u32 { | ||
| 131 | let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len()); | ||
| 132 | self.0.read(offset, &mut bytes[..len]).await?; | ||
| 133 | offset += len as u32; | ||
| 134 | bytes = &mut bytes[len..]; | ||
| 135 | } | ||
| 136 | |||
| 137 | if !bytes.is_empty() { | ||
| 138 | self.1.read(offset - self.0.capacity() as u32, bytes).await?; | ||
| 139 | } | ||
| 140 | |||
| 141 | Ok(()) | ||
| 142 | } | ||
| 143 | |||
| 144 | fn capacity(&self) -> usize { | ||
| 145 | self.0.capacity() + self.1.capacity() | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | #[cfg(feature = "nightly")] | ||
| 150 | impl<First, Second, E> AsyncNorFlash for ConcatFlash<First, Second> | ||
| 151 | where | ||
| 152 | First: AsyncNorFlash<Error = E>, | ||
| 153 | Second: AsyncNorFlash<Error = E>, | ||
| 154 | E: NorFlashError, | ||
| 155 | { | ||
| 156 | const WRITE_SIZE: usize = get_write_size(First::WRITE_SIZE, Second::WRITE_SIZE); | ||
| 157 | const ERASE_SIZE: usize = get_max_erase_size(First::ERASE_SIZE, Second::ERASE_SIZE); | ||
| 158 | |||
| 159 | async fn write(&mut self, mut offset: u32, mut bytes: &[u8]) -> Result<(), E> { | ||
| 160 | if offset < self.0.capacity() as u32 { | ||
| 161 | let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len()); | ||
| 162 | self.0.write(offset, &bytes[..len]).await?; | ||
| 163 | offset += len as u32; | ||
| 164 | bytes = &bytes[len..]; | ||
| 165 | } | ||
| 166 | |||
| 167 | if !bytes.is_empty() { | ||
| 168 | self.1.write(offset - self.0.capacity() as u32, bytes).await?; | ||
| 169 | } | ||
| 170 | |||
| 171 | Ok(()) | ||
| 172 | } | ||
| 173 | |||
| 174 | async fn erase(&mut self, mut from: u32, to: u32) -> Result<(), E> { | ||
| 175 | if from < self.0.capacity() as u32 { | ||
| 176 | let to = core::cmp::min(self.0.capacity() as u32, to); | ||
| 177 | self.0.erase(from, to).await?; | ||
| 178 | from = self.0.capacity() as u32; | ||
| 179 | } | ||
| 180 | |||
| 181 | if from < to { | ||
| 182 | self.1 | ||
| 183 | .erase(from - self.0.capacity() as u32, to - self.0.capacity() as u32) | ||
| 184 | .await?; | ||
| 185 | } | ||
| 186 | |||
| 187 | Ok(()) | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | #[cfg(test)] | ||
| 192 | mod tests { | ||
| 193 | use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; | ||
| 194 | |||
| 195 | use super::ConcatFlash; | ||
| 196 | use crate::flash::mem_flash::MemFlash; | ||
| 197 | |||
| 198 | #[test] | ||
| 199 | fn can_write_and_read_across_flashes() { | ||
| 200 | let first = MemFlash::<64, 16, 4>::default(); | ||
| 201 | let second = MemFlash::<64, 64, 4>::default(); | ||
| 202 | let mut f = ConcatFlash::new(first, second); | ||
| 203 | |||
| 204 | f.write(60, &[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]).unwrap(); | ||
| 205 | |||
| 206 | assert_eq!(&[0x11, 0x22, 0x33, 0x44], &f.0.mem[60..]); | ||
| 207 | assert_eq!(&[0x55, 0x66, 0x77, 0x88], &f.1.mem[0..4]); | ||
| 208 | |||
| 209 | let mut read_buf = [0; 8]; | ||
| 210 | f.read(60, &mut read_buf).unwrap(); | ||
| 211 | |||
| 212 | assert_eq!(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88], &read_buf); | ||
| 213 | } | ||
| 214 | |||
| 215 | #[test] | ||
| 216 | fn can_erase_across_flashes() { | ||
| 217 | let first = MemFlash::<128, 16, 4>::new(0x00); | ||
| 218 | let second = MemFlash::<128, 64, 4>::new(0x00); | ||
| 219 | let mut f = ConcatFlash::new(first, second); | ||
| 220 | |||
| 221 | f.erase(64, 192).unwrap(); | ||
| 222 | |||
| 223 | assert_eq!(&[0x00; 64], &f.0.mem[0..64]); | ||
| 224 | assert_eq!(&[0xff; 64], &f.0.mem[64..128]); | ||
| 225 | assert_eq!(&[0xff; 64], &f.1.mem[0..64]); | ||
| 226 | assert_eq!(&[0x00; 64], &f.1.mem[64..128]); | ||
| 227 | } | ||
| 228 | } | ||
