Skip to main content

esp_storage/
nor_flash.rs

1#[cfg(feature = "bytewise-read")]
2use crate::buffer::FlashWordBuffer;
3use crate::{
4    FlashStorage,
5    FlashStorageError,
6    buffer::{FlashSectorBuffer, uninit_slice, uninit_slice_mut},
7};
8
9impl FlashStorage<'_> {
10    #[cfg(not(feature = "bytewise-read"))]
11    /// Minimum read size in bytes for [`FlashStorage::read_nor`].
12    pub const READ_SIZE: usize = Self::WORD_SIZE as _;
13
14    #[cfg(feature = "bytewise-read")]
15    /// Minimum read size in bytes for [`FlashStorage::read_nor`].
16    pub const READ_SIZE: usize = 1;
17
18    /// Minimum write size in bytes for [`FlashStorage::write_nor`].
19    pub const WRITE_SIZE: usize = Self::WORD_SIZE as _;
20
21    /// Minimum erase size in bytes for [`FlashStorage::erase`].
22    pub const ERASE_SIZE: usize = Self::SECTOR_SIZE as _;
23
24    #[inline(always)]
25    fn is_word_aligned(bytes: &[u8]) -> bool {
26        // TODO: Use is_aligned_to when stabilized (see `pointer_is_aligned`)
27        (bytes.as_ptr() as usize).is_multiple_of(Self::WORD_SIZE as usize)
28    }
29
30    /// Read bytes from flash using NOR flash semantics.
31    ///
32    /// `offset` and `bytes.len()` must be aligned to [`Self::READ_SIZE`].
33    ///
34    /// # Note
35    ///
36    /// If `bytes` is not word-aligned (4-byte), this function allocates a
37    /// [`Self::SECTOR_SIZE`]-byte buffer on the stack and copies through it.
38    /// See the
39    /// [crate-level documentation](crate#buffer-alignment-and-stack-usage).
40    ///
41    /// # Errors
42    ///
43    /// Returns [`FlashStorageError::NotAligned`] if `offset` or `bytes.len()` is
44    /// not aligned to [`Self::READ_SIZE`].
45    ///
46    /// Returns [`FlashStorageError::OutOfBounds`] if the read would extend past
47    /// the end of the flash.
48    pub fn read_nor(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), FlashStorageError> {
49        const RS: u32 = FlashStorage::READ_SIZE as u32;
50        self.check_alignment::<{ RS }>(offset, bytes.len())?;
51        self.check_bounds(offset, bytes.len())?;
52
53        #[cfg(feature = "bytewise-read")]
54        let (offset, bytes) = {
55            let byte_offset = (offset % Self::WORD_SIZE) as usize;
56            if byte_offset > 0 {
57                let mut word_buffer = FlashWordBuffer::uninit();
58
59                let offset = offset - byte_offset as u32;
60                let length = bytes.len().min(Self::WORD_SIZE as usize - byte_offset);
61
62                self.internal_read(offset, word_buffer.as_bytes_mut())?;
63                let word_buffer = unsafe { word_buffer.assume_init_bytes_mut() };
64                bytes[..length].copy_from_slice(&word_buffer[byte_offset..][..length]);
65
66                (offset + Self::WORD_SIZE, &mut bytes[length..])
67            } else {
68                (offset, bytes)
69            }
70        };
71
72        if Self::is_word_aligned(bytes) {
73            // Bytes buffer is word-aligned so we can read directly to it
74            for (offset, chunk) in (offset..)
75                .step_by(Self::SECTOR_SIZE as _)
76                .zip(bytes.chunks_mut(Self::SECTOR_SIZE as _))
77            {
78                // Chunk already is word aligned so we can read directly to it
79                #[cfg(not(feature = "bytewise-read"))]
80                self.internal_read(offset, uninit_slice_mut(chunk))?;
81
82                #[cfg(feature = "bytewise-read")]
83                {
84                    let length = chunk.len();
85                    let byte_length = length % Self::WORD_SIZE as usize;
86                    let length = length - byte_length;
87
88                    self.internal_read(offset, &mut uninit_slice_mut(chunk)[..length])?;
89
90                    // Read not aligned rest of data
91                    if byte_length > 0 {
92                        let mut word_buffer = FlashWordBuffer::uninit();
93
94                        self.internal_read(offset + length as u32, word_buffer.as_bytes_mut())?;
95                        let word_buffer = unsafe { word_buffer.assume_init_bytes_mut() };
96                        chunk[length..].copy_from_slice(&word_buffer[..byte_length]);
97                    }
98                }
99            }
100        } else {
101            // Bytes buffer isn't word-aligned so we might read only via aligned buffer
102            let mut buffer = FlashSectorBuffer::uninit();
103
104            for (offset, chunk) in (offset..)
105                .step_by(Self::SECTOR_SIZE as _)
106                .zip(bytes.chunks_mut(Self::SECTOR_SIZE as _))
107            {
108                // Read to temporary buffer first (chunk length is aligned)
109                #[cfg(not(feature = "bytewise-read"))]
110                self.internal_read(offset, &mut buffer.as_bytes_mut()[..chunk.len()])?;
111
112                // Read to temporary buffer first (chunk length is not aligned)
113                #[cfg(feature = "bytewise-read")]
114                {
115                    let length = chunk.len();
116                    let byte_length = length % Self::WORD_SIZE as usize;
117                    let length = if byte_length > 0 {
118                        length - byte_length + Self::WORD_SIZE as usize
119                    } else {
120                        length
121                    };
122
123                    self.internal_read(offset, &mut buffer.as_bytes_mut()[..length])?;
124                }
125                let buffer = unsafe { buffer.assume_init_bytes() };
126
127                // Copy to bytes buffer
128                chunk.copy_from_slice(&buffer[..chunk.len()]);
129            }
130        }
131
132        Ok(())
133    }
134
135    /// Write bytes to flash using NOR flash semantics.
136    ///
137    /// NOR flash can only change bits from 1 to 0. Setting a bit from 0 to 1
138    /// requires erasing the containing sector first (see [`Self::erase`]). Each
139    /// byte is updated by ANDing it with the data being written; if the target
140    /// still contains 0 bits where you need 1s, the stored value will not match
141    /// what you passed in even though the operation succeeds.
142    ///
143    /// `offset` and `bytes.len()` must be aligned to [`Self::WRITE_SIZE`].
144    ///
145    /// # Note
146    ///
147    /// If `bytes` is not word-aligned (4-byte), this function allocates a
148    /// [`Self::SECTOR_SIZE`]-byte buffer on the stack and copies through it.
149    /// See the
150    /// [crate-level documentation](crate#buffer-alignment-and-stack-usage).
151    ///
152    /// # Errors
153    ///
154    /// Returns [`FlashStorageError::NotAligned`] if `offset` or `bytes.len()` is
155    /// not aligned to [`Self::WRITE_SIZE`].
156    ///
157    /// Returns [`FlashStorageError::OutOfBounds`] if the write would extend past
158    /// the end of the flash.
159    pub fn write_nor(&mut self, offset: u32, bytes: &[u8]) -> Result<(), FlashStorageError> {
160        const WS: u32 = FlashStorage::WORD_SIZE;
161        self.check_alignment::<{ WS }>(offset, bytes.len())?;
162        self.check_bounds(offset, bytes.len())?;
163
164        if Self::is_word_aligned(bytes) {
165            // Bytes buffer is word-aligned so we can write directly from it
166            for (offset, chunk) in (offset..)
167                .step_by(Self::SECTOR_SIZE as _)
168                .zip(bytes.chunks(Self::SECTOR_SIZE as _))
169            {
170                // Chunk already is word aligned so we can write directly from it
171                self.internal_write(offset, chunk)?;
172            }
173        } else {
174            // Bytes buffer isn't word-aligned so we might write only via aligned buffer
175            let mut buffer = FlashSectorBuffer::uninit();
176
177            for (offset, chunk) in (offset..)
178                .step_by(Self::SECTOR_SIZE as _)
179                .zip(bytes.chunks(Self::SECTOR_SIZE as _))
180            {
181                // Copy to temporary buffer first
182                buffer.as_bytes_mut()[..chunk.len()].copy_from_slice(uninit_slice(chunk));
183                // Write from temporary buffer
184                self.internal_write(offset, unsafe {
185                    &buffer.assume_init_bytes()[..chunk.len()]
186                })?;
187            }
188        }
189
190        Ok(())
191    }
192
193    /// Erase flash from `from` up to but not including `to`.
194    ///
195    /// Erased bytes are set to `0xFF`. Both addresses must be aligned to
196    /// [`Self::ERASE_SIZE`], and `to - from` must be a multiple of
197    /// [`Self::ERASE_SIZE`].
198    ///
199    /// # Errors
200    ///
201    /// Returns [`FlashStorageError::NotAligned`] if `from` or `to - from` is
202    /// not aligned to [`Self::ERASE_SIZE`].
203    ///
204    /// Returns [`FlashStorageError::OutOfBounds`] if the range would extend past
205    /// the end of the flash, or if `to` is less than `from`.
206    pub fn erase(&mut self, from: u32, to: u32) -> Result<(), FlashStorageError> {
207        if to < from {
208            return Err(FlashStorageError::OutOfBounds);
209        }
210
211        let len = (to - from) as _;
212        const SZ: u32 = FlashStorage::SECTOR_SIZE;
213        self.check_alignment::<{ SZ }>(from, len)?;
214        self.check_bounds(from, len)?;
215
216        // First erase by sector up to the block boundary.
217        let mut address = from;
218        while address < to && !address.is_multiple_of(Self::BLOCK_SIZE) {
219            self.internal_erase_sector(address / Self::SECTOR_SIZE)?;
220            address += Self::SECTOR_SIZE;
221        }
222
223        // Then erase as much as possible by blocks.
224        while (to - address) >= Self::BLOCK_SIZE {
225            self.internal_erase_block(address / Self::BLOCK_SIZE)?;
226            address += Self::BLOCK_SIZE;
227        }
228
229        // Finally, erase any remaining sectors.
230        while address < to {
231            self.internal_erase_sector(address / Self::SECTOR_SIZE)?;
232            address += Self::SECTOR_SIZE;
233        }
234
235        Ok(())
236    }
237}
238
239#[cfg(feature = "embedded-storage")]
240mod embedded_storage_traits {
241    use ::embedded_storage::nor_flash::{
242        ErrorType,
243        MultiwriteNorFlash,
244        NorFlash,
245        NorFlashError,
246        NorFlashErrorKind,
247        ReadNorFlash,
248    };
249
250    use super::*;
251
252    impl NorFlashError for FlashStorageError {
253        fn kind(&self) -> NorFlashErrorKind {
254            match self {
255                FlashStorageError::NotAligned => NorFlashErrorKind::NotAligned,
256                FlashStorageError::OutOfBounds => NorFlashErrorKind::OutOfBounds,
257                _ => NorFlashErrorKind::Other,
258            }
259        }
260    }
261
262    impl ErrorType for FlashStorage<'_> {
263        type Error = FlashStorageError;
264    }
265
266    impl ReadNorFlash for FlashStorage<'_> {
267        const READ_SIZE: usize = FlashStorage::READ_SIZE;
268
269        fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
270            FlashStorage::read_nor(self, offset, bytes)
271        }
272
273        fn capacity(&self) -> usize {
274            FlashStorage::capacity(self)
275        }
276    }
277
278    impl NorFlash for FlashStorage<'_> {
279        const WRITE_SIZE: usize = FlashStorage::WRITE_SIZE;
280        const ERASE_SIZE: usize = FlashStorage::ERASE_SIZE;
281
282        fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
283            FlashStorage::write_nor(self, offset, bytes)
284        }
285
286        fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
287            FlashStorage::erase(self, from, to)
288        }
289    }
290
291    impl MultiwriteNorFlash for FlashStorage<'_> {}
292}
293
294// Run the tests with `--test-threads=1` - the emulation is not multithread safe
295#[cfg(test)]
296mod tests {
297    use super::*;
298    use crate::common::Flash;
299
300    const WORD_SIZE: u32 = 4;
301    const SECTOR_SIZE: u32 = 4 << 10;
302    const BLOCK_SIZE: u32 = 4 << 14;
303    const NUM_BLOCKS: u32 = 3;
304    const FLASH_SIZE: u32 = BLOCK_SIZE * NUM_BLOCKS;
305    const MAX_OFFSET: u32 = SECTOR_SIZE * 1;
306    const MAX_LENGTH: u32 = SECTOR_SIZE * 2;
307
308    #[repr(C, align(4))]
309    struct TestBuffer {
310        data: [u8; FLASH_SIZE as _],
311    }
312
313    impl TestBuffer {
314        const fn seq() -> Self {
315            let mut data = [0u8; FLASH_SIZE as _];
316            let mut index = 0;
317            while index < FLASH_SIZE {
318                data[index as usize] = (index & 0xff) as u8;
319                index += 1;
320            }
321            Self { data }
322        }
323    }
324
325    impl Default for TestBuffer {
326        fn default() -> Self {
327            Self {
328                data: [0u8; FLASH_SIZE as usize],
329            }
330        }
331    }
332
333    #[cfg(not(miri))]
334    fn range_gen<const ALIGN: u32, const MAX_OFF: u32, const MAX_LEN: u32>(
335        aligned: Option<bool>,
336    ) -> impl Iterator<Item = (u32, u32)> {
337        (0..=MAX_OFF).flat_map(move |off| {
338            (0..=MAX_LEN - off)
339                .filter(move |len| {
340                    aligned
341                        .map(|aligned| aligned == (off % ALIGN == 0 && len % ALIGN == 0))
342                        .unwrap_or(true)
343                })
344                .map(move |len| (off, len))
345        })
346    }
347
348    #[cfg(miri)]
349    fn range_gen<const ALIGN: u32, const MAX_OFF: u32, const MAX_LEN: u32>(
350        aligned: Option<bool>,
351    ) -> impl Iterator<Item = (u32, u32)> {
352        // MIRI is very slow - just use a couple of combinations
353        match aligned {
354            Some(true) => vec![(0, 4), (0, 8), (0, 16), (0, 32), (0, 1024)],
355            Some(false) => vec![(3, 7), (11, 11)],
356            None => vec![
357                (0, 4),
358                (0, 8),
359                (0, 16),
360                (0, 32),
361                (0, 1024),
362                (3, 7),
363                (11, 11),
364                (0, 4098),
365            ],
366        }
367        .into_iter()
368    }
369
370    #[test]
371    #[cfg(not(feature = "bytewise-read"))]
372    fn aligned_read() {
373        let mut flash = FlashStorage::new(Flash::new());
374        flash.capacity = FLASH_SIZE as usize;
375        let src = TestBuffer::seq();
376        let mut data = TestBuffer::default();
377
378        flash.erase(0, FLASH_SIZE).unwrap();
379        flash.write_nor(0, &src.data).unwrap();
380
381        for (off, len) in range_gen::<WORD_SIZE, MAX_OFFSET, MAX_LENGTH>(Some(true)) {
382            flash.read_nor(off, &mut data.data[..len as usize]).unwrap();
383            assert_eq!(
384                data.data[..len as usize],
385                src.data[off as usize..][..len as usize]
386            );
387        }
388    }
389
390    #[test]
391    #[cfg(not(feature = "bytewise-read"))]
392    fn not_aligned_read_aligned_buffer() {
393        let mut flash = FlashStorage::new(Flash::new());
394        flash.capacity = FLASH_SIZE as usize;
395        let mut data = TestBuffer::default();
396
397        for (off, len) in range_gen::<WORD_SIZE, MAX_OFFSET, MAX_LENGTH>(Some(false)) {
398            flash
399                .read_nor(off, &mut data.data[..len as usize])
400                .unwrap_err();
401        }
402    }
403
404    #[test]
405    #[cfg(not(feature = "bytewise-read"))]
406    fn aligned_read_not_aligned_buffer() {
407        let mut flash = FlashStorage::new(Flash::new());
408        flash.capacity = FLASH_SIZE as usize;
409        let src = TestBuffer::seq();
410        let mut data = TestBuffer::default();
411
412        flash.erase(0, FLASH_SIZE).unwrap();
413        flash.write_nor(0, &src.data).unwrap();
414
415        for (off, len) in range_gen::<WORD_SIZE, MAX_OFFSET, MAX_LENGTH>(Some(true)) {
416            flash
417                .read_nor(off, &mut data.data[1..][..len as usize])
418                .unwrap();
419            assert_eq!(
420                data.data[1..][..len as usize],
421                src.data[off as usize..][..len as usize]
422            );
423        }
424    }
425
426    #[test]
427    #[cfg(feature = "bytewise-read")]
428    fn bytewise_read_aligned_buffer() {
429        let mut flash = FlashStorage::new(Flash::new());
430
431        flash.capacity = FLASH_SIZE as usize;
432        let src = TestBuffer::seq();
433        let mut data = TestBuffer::default();
434
435        flash.erase(0, FLASH_SIZE).unwrap();
436        flash.write_nor(0, &src.data).unwrap();
437
438        for (off, len) in range_gen::<WORD_SIZE, MAX_OFFSET, MAX_LENGTH>(None) {
439            flash.read_nor(off, &mut data.data[..len as usize]).unwrap();
440            assert_eq!(
441                data.data[..len as usize],
442                src.data[off as usize..][..len as usize]
443            );
444        }
445    }
446
447    #[test]
448    #[cfg(feature = "bytewise-read")]
449    fn bytewise_read_not_aligned_buffer() {
450        let mut flash = FlashStorage::new(Flash::new());
451
452        flash.capacity = FLASH_SIZE as usize;
453        let src = TestBuffer::seq();
454        let mut data = TestBuffer::default();
455
456        flash.erase(0, FLASH_SIZE).unwrap();
457        flash.write_nor(0, &src.data).unwrap();
458
459        for (off, len) in range_gen::<WORD_SIZE, MAX_OFFSET, MAX_LENGTH>(None) {
460            flash
461                .read_nor(off, &mut data.data[1..][..len as usize])
462                .unwrap();
463            assert_eq!(
464                data.data[1..][..len as usize],
465                src.data[off as usize..][..len as usize]
466            );
467        }
468    }
469
470    #[test]
471    fn write_not_aligned_buffer() {
472        let mut flash = FlashStorage::new(Flash::new());
473        flash.capacity = FLASH_SIZE as usize;
474        let mut read_data = TestBuffer::default();
475        let write_data = TestBuffer::seq();
476
477        flash.erase(0, FLASH_SIZE).unwrap();
478        flash.write_nor(0, &write_data.data[1..129]).unwrap();
479
480        flash.read_nor(0, &mut read_data.data[..128]).unwrap();
481
482        assert_eq!(&read_data.data[..128], &write_data.data[1..129]);
483    }
484
485    #[test]
486    fn erase_up_to_end_of_flash() {
487        let mut flash = FlashStorage::new(Flash::new());
488        flash.capacity = FLASH_SIZE as usize;
489        let mut read_data = TestBuffer::default();
490        let write_data = [0u8; SECTOR_SIZE as usize];
491
492        flash.erase(FLASH_SIZE - BLOCK_SIZE, FLASH_SIZE).unwrap();
493        flash
494            .write_nor(FLASH_SIZE - 2 * SECTOR_SIZE, &write_data)
495            .unwrap();
496        flash
497            .write_nor(FLASH_SIZE - SECTOR_SIZE, &write_data)
498            .unwrap();
499
500        // Erasing the last sector of the flash must be allowed
501        flash.erase(FLASH_SIZE - SECTOR_SIZE, FLASH_SIZE).unwrap();
502
503        flash
504            .read(
505                FLASH_SIZE - 2 * SECTOR_SIZE,
506                &mut read_data.data[..(2 * SECTOR_SIZE) as usize],
507            )
508            .unwrap();
509        assert!(
510            read_data.data[..SECTOR_SIZE as usize]
511                .iter()
512                .all(|v| *v == 0x0)
513        );
514        assert!(
515            read_data.data[SECTOR_SIZE as usize..(2 * SECTOR_SIZE) as usize]
516                .iter()
517                .all(|v| *v == 0xFF)
518        );
519
520        // Erasing the last block of the flash must be allowed
521        flash.erase(FLASH_SIZE - BLOCK_SIZE, FLASH_SIZE).unwrap();
522
523        flash
524            .read(
525                FLASH_SIZE - 2 * SECTOR_SIZE,
526                &mut read_data.data[..(2 * SECTOR_SIZE) as usize],
527            )
528            .unwrap();
529        assert!(
530            read_data.data[..(2 * SECTOR_SIZE) as usize]
531                .iter()
532                .all(|v| *v == 0xFF)
533        );
534    }
535
536    #[test]
537    fn erase_reversed_range_is_rejected() {
538        let mut flash = FlashStorage::new(Flash::new());
539        flash.capacity = FLASH_SIZE as usize;
540
541        assert_eq!(
542            flash.erase(SECTOR_SIZE, 0),
543            Err(FlashStorageError::OutOfBounds)
544        );
545    }
546
547    #[test]
548    fn erase_across_blocks() {
549        let mut flash = FlashStorage::new(Flash::new());
550        flash.capacity = FLASH_SIZE as usize;
551        let mut read_data = TestBuffer::default();
552        let write_data = [0u8; SECTOR_SIZE as usize];
553
554        // An entire block and some sectors before and after
555        let from = BLOCK_SIZE - (2 * SECTOR_SIZE);
556        let to = (2 * BLOCK_SIZE) + (1 * SECTOR_SIZE);
557
558        // Clear area before and after erase
559        flash.erase(from - SECTOR_SIZE, from).unwrap();
560        flash.write_nor(from - SECTOR_SIZE, &write_data).unwrap();
561        flash.erase(to, to + SECTOR_SIZE).unwrap();
562        flash.write_nor(to, &write_data).unwrap();
563
564        // Erase and verify that only the desired parts were touched
565        flash.erase(from, to).unwrap();
566        flash.read(0, &mut read_data.data).unwrap();
567        for i in from..to {
568            assert_eq!(read_data.data[i as usize], 0xFF);
569        }
570        assert_eq!(read_data.data[(from - 1) as usize], 0x0);
571        assert_eq!(read_data.data[to as usize], 0x0);
572    }
573}