1use 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.
7pub trait NorFlashError: core::fmt::Debug {
8 /// Convert a specific NOR flash error into a generic error kind.
9 fn kind(&self) -> NorFlashErrorKind;
10}
11
12impl 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.
19pub 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]
30pub 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
41impl NorFlashError for NorFlashErrorKind {
42 fn kind(&self) -> NorFlashErrorKind {
43 *self
44 }
45}
46
47impl 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.
58pub 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.
76pub 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.
85pub 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.
116pub 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.
128pub 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
136fn 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
152impl<T: ErrorType> ErrorType for &mut T {
153 type Error = T::Error;
154}
155
156impl<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
168impl<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
191pub trait MultiwriteNorFlash: NorFlash {}
192
193struct Page {
194 pub start: u32,
195 pub size: usize,
196}
197
198impl 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
212impl 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///
220pub struct RmwNorFlashStorage<'a, S> {
221 storage: S,
222 merge_buffer: &'a mut [u8],
223}
224
225impl<'a, S> RmwNorFlashStorage<'a, S>
226where
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
245impl<'a, S> ReadStorage for RmwNorFlashStorage<'a, S>
246where
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
261impl<'a, S> Storage for RmwNorFlashStorage<'a, S>
262where
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///
295pub struct RmwMultiwriteNorFlashStorage<'a, S> {
296 storage: S,
297 merge_buffer: &'a mut [u8],
298}
299
300impl<'a, S> RmwMultiwriteNorFlashStorage<'a, S>
301where
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
320impl<'a, S> ReadStorage for RmwMultiwriteNorFlashStorage<'a, S>
321where
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
336impl<'a, S> Storage for RmwMultiwriteNorFlashStorage<'a, S>
337where
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