1 | use crate::{iter::IterableByOverlaps, ReadStorage, Region, Storage}; |
2 | |
3 | /// NOR flash errors. |
4 | /// |
5 | /// NOR flash implementations must use an error type implementing this trait. This permits generic |
6 | /// code to extract a generic error kind. |
7 | pub trait NorFlashError: core::fmt::Debug { |
8 | /// Convert a specific NOR flash error into a generic error kind. |
9 | fn kind(&self) -> NorFlashErrorKind; |
10 | } |
11 | |
12 | impl NorFlashError for core::convert::Infallible { |
13 | fn kind(&self) -> NorFlashErrorKind { |
14 | match *self {} |
15 | } |
16 | } |
17 | |
18 | /// A trait that NorFlash implementations can use to share an error type. |
19 | pub trait ErrorType { |
20 | /// Errors returned by this NOR flash. |
21 | type Error: NorFlashError; |
22 | } |
23 | |
24 | /// NOR flash error kinds. |
25 | /// |
26 | /// NOR flash implementations must map their error to those generic error kinds through the |
27 | /// [`NorFlashError`] trait. |
28 | #[derive (Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] |
29 | #[non_exhaustive ] |
30 | pub enum NorFlashErrorKind { |
31 | /// The arguments are not properly aligned. |
32 | NotAligned, |
33 | |
34 | /// The arguments are out of bounds. |
35 | OutOfBounds, |
36 | |
37 | /// Error specific to the implementation. |
38 | Other, |
39 | } |
40 | |
41 | impl NorFlashError for NorFlashErrorKind { |
42 | fn kind(&self) -> NorFlashErrorKind { |
43 | *self |
44 | } |
45 | } |
46 | |
47 | impl core::fmt::Display for NorFlashErrorKind { |
48 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
49 | match self { |
50 | Self::NotAligned => write!(f, "Arguments are not properly aligned" ), |
51 | Self::OutOfBounds => write!(f, "Arguments are out of bounds" ), |
52 | Self::Other => write!(f, "An implementation specific error occurred" ), |
53 | } |
54 | } |
55 | } |
56 | |
57 | /// Read only NOR flash trait. |
58 | pub trait ReadNorFlash: ErrorType { |
59 | /// The minumum number of bytes the storage peripheral can read |
60 | const READ_SIZE: usize; |
61 | |
62 | /// Read a slice of data from the storage peripheral, starting the read |
63 | /// operation at the given address offset, and reading `bytes.len()` bytes. |
64 | /// |
65 | /// # Errors |
66 | /// |
67 | /// Returns an error if the arguments are not aligned or out of bounds. The implementation |
68 | /// can use the [`check_read`] helper function. |
69 | fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error>; |
70 | |
71 | /// The capacity of the peripheral in bytes. |
72 | fn capacity(&self) -> usize; |
73 | } |
74 | |
75 | /// Return whether a read operation is within bounds. |
76 | pub fn check_read<T: ReadNorFlash>( |
77 | flash: &T, |
78 | offset: u32, |
79 | length: usize, |
80 | ) -> Result<(), NorFlashErrorKind> { |
81 | check_slice(flash, T::READ_SIZE, offset, length) |
82 | } |
83 | |
84 | /// NOR flash trait. |
85 | pub trait NorFlash: ReadNorFlash { |
86 | /// The minumum number of bytes the storage peripheral can write |
87 | const WRITE_SIZE: usize; |
88 | |
89 | /// The minumum number of bytes the storage peripheral can erase |
90 | const ERASE_SIZE: usize; |
91 | |
92 | /// Erase the given storage range, clearing all data within `[from..to]`. |
93 | /// The given range will contain all 1s afterwards. |
94 | /// |
95 | /// If power is lost during erase, contents of the page are undefined. |
96 | /// |
97 | /// # Errors |
98 | /// |
99 | /// Returns an error if the arguments are not aligned or out of bounds (the case where `to > |
100 | /// from` is considered out of bounds). The implementation can use the [`check_erase`] |
101 | /// helper function. |
102 | fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error>; |
103 | |
104 | /// If power is lost during write, the contents of the written words are undefined, |
105 | /// but the rest of the page is guaranteed to be unchanged. |
106 | /// It is not allowed to write to the same word twice. |
107 | /// |
108 | /// # Errors |
109 | /// |
110 | /// Returns an error if the arguments are not aligned or out of bounds. The implementation |
111 | /// can use the [`check_write`] helper function. |
112 | fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error>; |
113 | } |
114 | |
115 | /// Return whether an erase operation is aligned and within bounds. |
116 | pub fn check_erase<T: NorFlash>(flash: &T, from: u32, to: u32) -> Result<(), NorFlashErrorKind> { |
117 | let (from: usize, to: usize) = (from as usize, to as usize); |
118 | if from > to || to > flash.capacity() { |
119 | return Err(NorFlashErrorKind::OutOfBounds); |
120 | } |
121 | if from % T::ERASE_SIZE != 0 || to % T::ERASE_SIZE != 0 { |
122 | return Err(NorFlashErrorKind::NotAligned); |
123 | } |
124 | Ok(()) |
125 | } |
126 | |
127 | /// Return whether a write operation is aligned and within bounds. |
128 | pub fn check_write<T: NorFlash>( |
129 | flash: &T, |
130 | offset: u32, |
131 | length: usize, |
132 | ) -> Result<(), NorFlashErrorKind> { |
133 | check_slice(flash, T::WRITE_SIZE, offset, length) |
134 | } |
135 | |
136 | fn check_slice<T: ReadNorFlash>( |
137 | flash: &T, |
138 | align: usize, |
139 | offset: u32, |
140 | length: usize, |
141 | ) -> Result<(), NorFlashErrorKind> { |
142 | let offset: usize = offset as usize; |
143 | if length > flash.capacity() || offset > flash.capacity() - length { |
144 | return Err(NorFlashErrorKind::OutOfBounds); |
145 | } |
146 | if offset % align != 0 || length % align != 0 { |
147 | return Err(NorFlashErrorKind::NotAligned); |
148 | } |
149 | Ok(()) |
150 | } |
151 | |
152 | impl<T: ErrorType> ErrorType for &mut T { |
153 | type Error = T::Error; |
154 | } |
155 | |
156 | impl<T: ReadNorFlash> ReadNorFlash for &mut T { |
157 | const READ_SIZE: usize = T::READ_SIZE; |
158 | |
159 | fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { |
160 | T::read(self, offset, bytes) |
161 | } |
162 | |
163 | fn capacity(&self) -> usize { |
164 | T::capacity(self) |
165 | } |
166 | } |
167 | |
168 | impl<T: NorFlash> NorFlash for &mut T { |
169 | const WRITE_SIZE: usize = T::WRITE_SIZE; |
170 | const ERASE_SIZE: usize = T::ERASE_SIZE; |
171 | |
172 | fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { |
173 | T::erase(self, from, to) |
174 | } |
175 | |
176 | fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { |
177 | T::write(self, offset, bytes) |
178 | } |
179 | } |
180 | |
181 | /// Marker trait for NorFlash relaxing the restrictions on `write`. |
182 | /// |
183 | /// Writes to the same word twice are now allowed. The result is the logical AND of the |
184 | /// previous data and the written data. That is, it is only possible to change 1 bits to 0 bits. |
185 | /// |
186 | /// If power is lost during write: |
187 | /// - Bits that were 1 on flash and are written to 1 are guaranteed to stay as 1 |
188 | /// - Bits that were 1 on flash and are written to 0 are undefined |
189 | /// - Bits that were 0 on flash are guaranteed to stay as 0 |
190 | /// - Rest of the bits in the page are guaranteed to be unchanged |
191 | pub trait MultiwriteNorFlash: NorFlash {} |
192 | |
193 | struct Page { |
194 | pub start: u32, |
195 | pub size: usize, |
196 | } |
197 | |
198 | impl Page { |
199 | fn new(index: u32, size: usize) -> Self { |
200 | Self { |
201 | start: index * size as u32, |
202 | size, |
203 | } |
204 | } |
205 | |
206 | /// The end address of the page |
207 | const fn end(&self) -> u32 { |
208 | self.start + self.size as u32 |
209 | } |
210 | } |
211 | |
212 | impl Region for Page { |
213 | /// Checks if an address offset is contained within the page |
214 | fn contains(&self, address: u32) -> bool { |
215 | (self.start <= address) && (self.end() > address) |
216 | } |
217 | } |
218 | |
219 | /// |
220 | pub struct RmwNorFlashStorage<'a, S> { |
221 | storage: S, |
222 | merge_buffer: &'a mut [u8], |
223 | } |
224 | |
225 | impl<'a, S> RmwNorFlashStorage<'a, S> |
226 | where |
227 | S: NorFlash, |
228 | { |
229 | /// Instantiate a new generic `Storage` from a `NorFlash` peripheral |
230 | /// |
231 | /// **NOTE** This will panic if the provided merge buffer, |
232 | /// is smaller than the erase size of the flash peripheral |
233 | pub fn new(nor_flash: S, merge_buffer: &'a mut [u8]) -> Self { |
234 | if merge_buffer.len() < S::ERASE_SIZE { |
235 | panic!("Merge buffer is too small" ); |
236 | } |
237 | |
238 | Self { |
239 | storage: nor_flash, |
240 | merge_buffer, |
241 | } |
242 | } |
243 | } |
244 | |
245 | impl<'a, S> ReadStorage for RmwNorFlashStorage<'a, S> |
246 | where |
247 | S: ReadNorFlash, |
248 | { |
249 | type Error = S::Error; |
250 | |
251 | fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { |
252 | // Nothing special to be done for reads |
253 | self.storage.read(offset, bytes) |
254 | } |
255 | |
256 | fn capacity(&self) -> usize { |
257 | self.storage.capacity() |
258 | } |
259 | } |
260 | |
261 | impl<'a, S> Storage for RmwNorFlashStorage<'a, S> |
262 | where |
263 | S: NorFlash, |
264 | { |
265 | fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { |
266 | // Perform read/modify/write operations on the byte slice. |
267 | let last_page = self.storage.capacity() / S::ERASE_SIZE; |
268 | |
269 | // `data` is the part of `bytes` contained within `page`, |
270 | // and `addr` in the address offset of `page` + any offset into the page as requested by `address` |
271 | for (data, page, addr) in (0..last_page as u32) |
272 | .map(move |i| Page::new(i, S::ERASE_SIZE)) |
273 | .overlaps(bytes, offset) |
274 | { |
275 | let offset_into_page = addr.saturating_sub(page.start) as usize; |
276 | |
277 | self.storage |
278 | .read(page.start, &mut self.merge_buffer[..S::ERASE_SIZE])?; |
279 | |
280 | // If we cannot write multiple times to the same page, we will have to erase it |
281 | self.storage.erase(page.start, page.end())?; |
282 | self.merge_buffer[..S::ERASE_SIZE] |
283 | .iter_mut() |
284 | .skip(offset_into_page) |
285 | .zip(data) |
286 | .for_each(|(byte, input)| *byte = *input); |
287 | self.storage |
288 | .write(page.start, &self.merge_buffer[..S::ERASE_SIZE])?; |
289 | } |
290 | Ok(()) |
291 | } |
292 | } |
293 | |
294 | /// |
295 | pub struct RmwMultiwriteNorFlashStorage<'a, S> { |
296 | storage: S, |
297 | merge_buffer: &'a mut [u8], |
298 | } |
299 | |
300 | impl<'a, S> RmwMultiwriteNorFlashStorage<'a, S> |
301 | where |
302 | S: MultiwriteNorFlash, |
303 | { |
304 | /// Instantiate a new generic `Storage` from a `NorFlash` peripheral |
305 | /// |
306 | /// **NOTE** This will panic if the provided merge buffer, |
307 | /// is smaller than the erase size of the flash peripheral |
308 | pub fn new(nor_flash: S, merge_buffer: &'a mut [u8]) -> Self { |
309 | if merge_buffer.len() < S::ERASE_SIZE { |
310 | panic!("Merge buffer is too small" ); |
311 | } |
312 | |
313 | Self { |
314 | storage: nor_flash, |
315 | merge_buffer, |
316 | } |
317 | } |
318 | } |
319 | |
320 | impl<'a, S> ReadStorage for RmwMultiwriteNorFlashStorage<'a, S> |
321 | where |
322 | S: ReadNorFlash, |
323 | { |
324 | type Error = S::Error; |
325 | |
326 | fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { |
327 | // Nothing special to be done for reads |
328 | self.storage.read(offset, bytes) |
329 | } |
330 | |
331 | fn capacity(&self) -> usize { |
332 | self.storage.capacity() |
333 | } |
334 | } |
335 | |
336 | impl<'a, S> Storage for RmwMultiwriteNorFlashStorage<'a, S> |
337 | where |
338 | S: MultiwriteNorFlash, |
339 | { |
340 | fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { |
341 | // Perform read/modify/write operations on the byte slice. |
342 | let last_page = self.storage.capacity() / S::ERASE_SIZE; |
343 | |
344 | // `data` is the part of `bytes` contained within `page`, |
345 | // and `addr` in the address offset of `page` + any offset into the page as requested by `address` |
346 | for (data, page, addr) in (0..last_page as u32) |
347 | .map(move |i| Page::new(i, S::ERASE_SIZE)) |
348 | .overlaps(bytes, offset) |
349 | { |
350 | let offset_into_page = addr.saturating_sub(page.start) as usize; |
351 | |
352 | self.storage |
353 | .read(page.start, &mut self.merge_buffer[..S::ERASE_SIZE])?; |
354 | |
355 | let rhs = &self.merge_buffer[offset_into_page..S::ERASE_SIZE]; |
356 | let is_subset = data.iter().zip(rhs.iter()).all(|(a, b)| *a & *b == *a); |
357 | |
358 | // Check if we can write the data block directly, under the limitations imposed by NorFlash: |
359 | // - We can only change 1's to 0's |
360 | if is_subset { |
361 | // Use `merge_buffer` as allocation for padding `data` to `WRITE_SIZE` |
362 | let offset = addr as usize % S::WRITE_SIZE; |
363 | let aligned_end = data.len() % S::WRITE_SIZE + offset + data.len(); |
364 | self.merge_buffer[..aligned_end].fill(0xff); |
365 | self.merge_buffer[offset..offset + data.len()].copy_from_slice(data); |
366 | self.storage |
367 | .write(addr - offset as u32, &self.merge_buffer[..aligned_end])?; |
368 | } else { |
369 | self.storage.erase(page.start, page.end())?; |
370 | self.merge_buffer[..S::ERASE_SIZE] |
371 | .iter_mut() |
372 | .skip(offset_into_page) |
373 | .zip(data) |
374 | .for_each(|(byte, input)| *byte = *input); |
375 | self.storage |
376 | .write(page.start, &self.merge_buffer[..S::ERASE_SIZE])?; |
377 | } |
378 | } |
379 | Ok(()) |
380 | } |
381 | } |
382 | |