aboutsummaryrefslogtreecommitdiff
path: root/embassy-embedded-hal/src/flash.rs
diff options
context:
space:
mode:
authorRasmus Melchior Jacobsen <[email protected]>2023-05-24 14:40:34 +0200
committerRasmus Melchior Jacobsen <[email protected]>2023-05-24 14:40:34 +0200
commite785e1bc22fde8f203048d143ceafdd45ec4d4b6 (patch)
treef7e53d7786d3d0cc82ceb4d5bc9a88ff8c0562f2 /embassy-embedded-hal/src/flash.rs
parent627d7f66efb7ff3dcf627477814c52ba4171bade (diff)
Add ConcatFlash utility
Diffstat (limited to 'embassy-embedded-hal/src/flash.rs')
-rw-r--r--embassy-embedded-hal/src/flash.rs286
1 files changed, 286 insertions, 0 deletions
diff --git a/embassy-embedded-hal/src/flash.rs b/embassy-embedded-hal/src/flash.rs
new file mode 100644
index 000000000..9a6e4bd92
--- /dev/null
+++ b/embassy-embedded-hal/src/flash.rs
@@ -0,0 +1,286 @@
1//! Utilities related to flash.
2
3use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, ReadNorFlash};
4#[cfg(feature = "nightly")]
5use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
6
7/// Convenience helper for concatenating two consecutive flashes into one.
8/// This is especially useful if used with "flash regions", where one may
9/// want to concatenate multiple regions into one larger region.
10pub struct ConcatFlash<First, Second>(First, Second);
11
12impl<First, Second> ConcatFlash<First, Second> {
13 /// Create a new flash that concatenates two consecutive flashes.
14 pub fn new(first: First, second: Second) -> Self {
15 Self(first, second)
16 }
17}
18
19const fn get_read_size(first_read_size: usize, second_read_size: usize) -> usize {
20 if first_read_size != second_read_size {
21 panic!("The read size for the concatenated flashes must be the same");
22 }
23 first_read_size
24}
25
26const fn get_write_size(first_write_size: usize, second_write_size: usize) -> usize {
27 if first_write_size != second_write_size {
28 panic!("The write size for the concatenated flashes must be the same");
29 }
30 first_write_size
31}
32
33const fn get_max_erase_size(first_erase_size: usize, second_erase_size: usize) -> usize {
34 let max_erase_size = if first_erase_size > second_erase_size {
35 first_erase_size
36 } else {
37 second_erase_size
38 };
39 if max_erase_size % first_erase_size != 0 || max_erase_size % second_erase_size != 0 {
40 panic!("The erase sizes for the concatenated flashes must have have a gcd equal to the max erase size");
41 }
42 max_erase_size
43}
44
45impl<First, Second, E> ErrorType for ConcatFlash<First, Second>
46where
47 First: ErrorType<Error = E>,
48 Second: ErrorType<Error = E>,
49 E: NorFlashError,
50{
51 type Error = E;
52}
53
54impl<First, Second, E> ReadNorFlash for ConcatFlash<First, Second>
55where
56 First: ReadNorFlash<Error = E>,
57 Second: ReadNorFlash<Error = E>,
58 E: NorFlashError,
59{
60 const READ_SIZE: usize = get_read_size(First::READ_SIZE, Second::READ_SIZE);
61
62 fn read(&mut self, mut offset: u32, mut bytes: &mut [u8]) -> Result<(), E> {
63 if offset < self.0.capacity() as u32 {
64 let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len());
65 self.0.read(offset, &mut bytes[..len])?;
66 offset += len as u32;
67 bytes = &mut bytes[len..];
68 }
69
70 if !bytes.is_empty() {
71 self.1.read(offset - self.0.capacity() as u32, bytes)?;
72 }
73
74 Ok(())
75 }
76
77 fn capacity(&self) -> usize {
78 self.0.capacity() + self.1.capacity()
79 }
80}
81
82impl<First, Second, E> NorFlash for ConcatFlash<First, Second>
83where
84 First: NorFlash<Error = E>,
85 Second: NorFlash<Error = E>,
86 E: NorFlashError,
87{
88 const WRITE_SIZE: usize = get_write_size(First::WRITE_SIZE, Second::WRITE_SIZE);
89 const ERASE_SIZE: usize = get_max_erase_size(First::ERASE_SIZE, Second::ERASE_SIZE);
90
91 fn write(&mut self, mut offset: u32, mut bytes: &[u8]) -> Result<(), E> {
92 if offset < self.0.capacity() as u32 {
93 let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len());
94 self.0.write(offset, &bytes[..len])?;
95 offset += len as u32;
96 bytes = &bytes[len..];
97 }
98
99 if !bytes.is_empty() {
100 self.1.write(offset - self.0.capacity() as u32, bytes)?;
101 }
102
103 Ok(())
104 }
105
106 fn erase(&mut self, mut from: u32, to: u32) -> Result<(), E> {
107 if from < self.0.capacity() as u32 {
108 let to = core::cmp::min(self.0.capacity() as u32, to);
109 self.0.erase(from, to)?;
110 from = self.0.capacity() as u32;
111 }
112
113 if from < to {
114 self.1
115 .erase(from - self.0.capacity() as u32, to - self.0.capacity() as u32)?;
116 }
117
118 Ok(())
119 }
120}
121
122#[cfg(feature = "nightly")]
123impl<First, Second, E> AsyncReadNorFlash for ConcatFlash<First, Second>
124where
125 First: AsyncReadNorFlash<Error = E>,
126 Second: AsyncReadNorFlash<Error = E>,
127 E: NorFlashError,
128{
129 const READ_SIZE: usize = get_read_size(First::READ_SIZE, Second::READ_SIZE);
130
131 async fn read(&mut self, mut offset: u32, mut bytes: &mut [u8]) -> Result<(), E> {
132 if offset < self.0.capacity() as u32 {
133 let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len());
134 self.0.read(offset, &mut bytes[..len]).await?;
135 offset += len as u32;
136 bytes = &mut bytes[len..];
137 }
138
139 if !bytes.is_empty() {
140 self.1.read(offset - self.0.capacity() as u32, bytes).await?;
141 }
142
143 Ok(())
144 }
145
146 fn capacity(&self) -> usize {
147 self.0.capacity() + self.1.capacity()
148 }
149}
150
151#[cfg(feature = "nightly")]
152impl<First, Second, E> AsyncNorFlash for ConcatFlash<First, Second>
153where
154 First: AsyncNorFlash<Error = E>,
155 Second: AsyncNorFlash<Error = E>,
156 E: NorFlashError,
157{
158 const WRITE_SIZE: usize = get_write_size(First::WRITE_SIZE, Second::WRITE_SIZE);
159 const ERASE_SIZE: usize = get_max_erase_size(First::ERASE_SIZE, Second::ERASE_SIZE);
160
161 async fn write(&mut self, mut offset: u32, mut bytes: &[u8]) -> Result<(), E> {
162 if offset < self.0.capacity() as u32 {
163 let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len());
164 self.0.write(offset, &bytes[..len]).await?;
165 offset += len as u32;
166 bytes = &bytes[len..];
167 }
168
169 if !bytes.is_empty() {
170 self.1.write(offset - self.0.capacity() as u32, bytes).await?;
171 }
172
173 Ok(())
174 }
175
176 async fn erase(&mut self, mut from: u32, to: u32) -> Result<(), E> {
177 if from < self.0.capacity() as u32 {
178 let to = core::cmp::min(self.0.capacity() as u32, to);
179 self.0.erase(from, to).await?;
180 from = self.0.capacity() as u32;
181 }
182
183 if from < to {
184 self.1
185 .erase(from - self.0.capacity() as u32, to - self.0.capacity() as u32)
186 .await?;
187 }
188
189 Ok(())
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196
197 #[test]
198 fn can_write_and_read_across_flashes() {
199 let first = MemFlash::<64, 16, 4>::new();
200 let second = MemFlash::<64, 64, 4>::new();
201 let mut f = ConcatFlash::new(first, second);
202
203 f.write(60, &[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]).unwrap();
204
205 assert_eq!(&[0x11, 0x22, 0x33, 0x44], &f.0 .0[60..]);
206 assert_eq!(&[0x55, 0x66, 0x77, 0x88], &f.1 .0[0..4]);
207
208 let mut read_buf = [0; 8];
209 f.read(60, &mut read_buf).unwrap();
210
211 assert_eq!(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88], &read_buf);
212 }
213
214 #[test]
215 fn can_erase_across_flashes() {
216 let mut first = MemFlash::<128, 16, 4>::new();
217 let mut second = MemFlash::<128, 64, 4>::new();
218 first.0.fill(0x00);
219 second.0.fill(0x00);
220
221 let mut f = ConcatFlash::new(first, second);
222
223 f.erase(64, 192).unwrap();
224
225 assert_eq!(&[0x00; 64], &f.0 .0[0..64]);
226 assert_eq!(&[0xff; 64], &f.0 .0[64..128]);
227 assert_eq!(&[0xff; 64], &f.1 .0[0..64]);
228 assert_eq!(&[0x00; 64], &f.1 .0[64..128]);
229 }
230
231 pub struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize>([u8; SIZE]);
232
233 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> {
234 pub const fn new() -> Self {
235 Self([0xff; SIZE])
236 }
237 }
238
239 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ErrorType
240 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
241 {
242 type Error = core::convert::Infallible;
243 }
244
245 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash
246 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
247 {
248 const READ_SIZE: usize = 1;
249
250 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
251 let len = bytes.len();
252 bytes.copy_from_slice(&self.0[offset as usize..offset as usize + len]);
253 Ok(())
254 }
255
256 fn capacity(&self) -> usize {
257 SIZE
258 }
259 }
260
261 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash
262 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
263 {
264 const WRITE_SIZE: usize = WRITE_SIZE;
265 const ERASE_SIZE: usize = ERASE_SIZE;
266
267 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
268 let from = from as usize;
269 let to = to as usize;
270 assert_eq!(0, from % ERASE_SIZE);
271 assert_eq!(0, to % ERASE_SIZE);
272 self.0[from..to].fill(0xff);
273 Ok(())
274 }
275
276 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
277 let offset = offset as usize;
278 assert_eq!(0, bytes.len() % WRITE_SIZE);
279 assert_eq!(0, offset % WRITE_SIZE);
280 assert!(offset + bytes.len() <= SIZE);
281
282 self.0[offset..offset + bytes.len()].copy_from_slice(bytes);
283 Ok(())
284 }
285 }
286}