esp_storage/
nor_flash.rs

1use embedded_storage::nor_flash::{
2    ErrorType,
3    MultiwriteNorFlash,
4    NorFlash,
5    NorFlashError,
6    NorFlashErrorKind,
7    ReadNorFlash,
8};
9
10#[cfg(feature = "bytewise-read")]
11use crate::buffer::FlashWordBuffer;
12use crate::{
13    FlashStorage,
14    FlashStorageError,
15    buffer::{FlashSectorBuffer, uninit_slice, uninit_slice_mut},
16};
17
18impl FlashStorage {
19    #[inline(always)]
20    fn is_word_aligned(bytes: &[u8]) -> bool {
21        // TODO: Use is_aligned_to when stabilized (see `pointer_is_aligned`)
22        (bytes.as_ptr() as usize) % (Self::WORD_SIZE as usize) == 0
23    }
24}
25
26impl NorFlashError for FlashStorageError {
27    fn kind(&self) -> NorFlashErrorKind {
28        match self {
29            Self::NotAligned => NorFlashErrorKind::NotAligned,
30            Self::OutOfBounds => NorFlashErrorKind::OutOfBounds,
31            _ => NorFlashErrorKind::Other,
32        }
33    }
34}
35
36impl ErrorType for FlashStorage {
37    type Error = FlashStorageError;
38}
39
40impl ReadNorFlash for FlashStorage {
41    #[cfg(not(feature = "bytewise-read"))]
42    const READ_SIZE: usize = Self::WORD_SIZE as _;
43
44    #[cfg(feature = "bytewise-read")]
45    const READ_SIZE: usize = 1;
46
47    fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
48        self.check_alignment::<{ Self::READ_SIZE as _ }>(offset, bytes.len())?;
49        self.check_bounds(offset, bytes.len())?;
50
51        #[cfg(feature = "bytewise-read")]
52        let (offset, bytes) = {
53            let byte_offset = (offset % Self::WORD_SIZE) as usize;
54            if byte_offset > 0 {
55                let mut word_buffer = FlashWordBuffer::uninit();
56
57                let offset = offset - byte_offset as u32;
58                let length = bytes.len().min(Self::WORD_SIZE as usize - byte_offset);
59
60                self.internal_read(offset, word_buffer.as_bytes_mut())?;
61                let word_buffer = unsafe { word_buffer.assume_init_bytes_mut() };
62                bytes[..length].copy_from_slice(&word_buffer[byte_offset..][..length]);
63
64                (offset + Self::WORD_SIZE, &mut bytes[length..])
65            } else {
66                (offset, bytes)
67            }
68        };
69
70        if Self::is_word_aligned(bytes) {
71            // Bytes buffer is word-aligned so we can read directly to it
72            for (offset, chunk) in (offset..)
73                .step_by(Self::SECTOR_SIZE as _)
74                .zip(bytes.chunks_mut(Self::SECTOR_SIZE as _))
75            {
76                // Chunk already is word aligned so we can read directly to it
77                #[cfg(not(feature = "bytewise-read"))]
78                self.internal_read(offset, uninit_slice_mut(chunk))?;
79
80                #[cfg(feature = "bytewise-read")]
81                {
82                    let length = chunk.len();
83                    let byte_length = length % Self::WORD_SIZE as usize;
84                    let length = length - byte_length;
85
86                    self.internal_read(offset, &mut uninit_slice_mut(chunk)[..length])?;
87
88                    // Read not aligned rest of data
89                    if byte_length > 0 {
90                        let mut word_buffer = FlashWordBuffer::uninit();
91
92                        self.internal_read(offset + length as u32, word_buffer.as_bytes_mut())?;
93                        let word_buffer = unsafe { word_buffer.assume_init_bytes_mut() };
94                        chunk[length..].copy_from_slice(&word_buffer[..byte_length]);
95                    }
96                }
97            }
98        } else {
99            // Bytes buffer isn't word-aligned so we might read only via aligned buffer
100            let mut buffer = FlashSectorBuffer::uninit();
101
102            for (offset, chunk) in (offset..)
103                .step_by(Self::SECTOR_SIZE as _)
104                .zip(bytes.chunks_mut(Self::SECTOR_SIZE as _))
105            {
106                // Read to temporary buffer first (chunk length is aligned)
107                #[cfg(not(feature = "bytewise-read"))]
108                self.internal_read(offset, &mut buffer.as_bytes_mut()[..chunk.len()])?;
109
110                // Read to temporary buffer first (chunk length is not aligned)
111                #[cfg(feature = "bytewise-read")]
112                {
113                    let length = chunk.len();
114                    let byte_length = length % Self::WORD_SIZE as usize;
115                    let length = if byte_length > 0 {
116                        length - byte_length + Self::WORD_SIZE as usize
117                    } else {
118                        length
119                    };
120
121                    self.internal_read(offset, &mut buffer.as_bytes_mut()[..length])?;
122                }
123                let buffer = unsafe { buffer.assume_init_bytes() };
124
125                // Copy to bytes buffer
126                chunk.copy_from_slice(&buffer[..chunk.len()]);
127            }
128        }
129
130        Ok(())
131    }
132
133    fn capacity(&self) -> usize {
134        self.capacity
135    }
136}
137
138impl NorFlash for FlashStorage {
139    const WRITE_SIZE: usize = Self::WORD_SIZE as _;
140    const ERASE_SIZE: usize = Self::SECTOR_SIZE as _;
141
142    fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
143        self.check_alignment::<{ Self::WORD_SIZE }>(offset, bytes.len())?;
144        self.check_bounds(offset, bytes.len())?;
145
146        if Self::is_word_aligned(bytes) {
147            // Bytes buffer is word-aligned so we can write directly from it
148            for (offset, chunk) in (offset..)
149                .step_by(Self::SECTOR_SIZE as _)
150                .zip(bytes.chunks(Self::SECTOR_SIZE as _))
151            {
152                // Chunk already is word aligned so we can write directly from it
153                self.internal_write(offset, chunk)?;
154            }
155        } else {
156            // Bytes buffer isn't word-aligned so we might write only via aligned buffer
157            let mut buffer = FlashSectorBuffer::uninit();
158
159            for (offset, chunk) in (offset..)
160                .step_by(Self::SECTOR_SIZE as _)
161                .zip(bytes.chunks(Self::SECTOR_SIZE as _))
162            {
163                // Copy to temporary buffer first
164                buffer.as_bytes_mut()[..chunk.len()].copy_from_slice(uninit_slice(chunk));
165                // Write from temporary buffer
166                self.internal_write(offset, unsafe {
167                    &buffer.assume_init_bytes()[..chunk.len()]
168                })?;
169            }
170        }
171
172        Ok(())
173    }
174
175    fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
176        let len = (to - from) as _;
177        self.check_alignment::<{ Self::SECTOR_SIZE }>(from, len)?;
178        self.check_bounds(from, len)?;
179
180        for sector in from / Self::SECTOR_SIZE..to / Self::SECTOR_SIZE {
181            self.internal_erase(sector)?;
182        }
183
184        Ok(())
185    }
186}
187
188impl MultiwriteNorFlash for FlashStorage {}
189
190// Run the tests with `--test-threads=1` - the emulation is not multithread safe
191#[cfg(test)]
192mod test {
193    use super::*;
194
195    const WORD_SIZE: u32 = 4;
196    const SECTOR_SIZE: u32 = 4 << 10;
197    const NUM_SECTORS: u32 = 3;
198    const FLASH_SIZE: u32 = SECTOR_SIZE * NUM_SECTORS;
199    const MAX_OFFSET: u32 = SECTOR_SIZE * 1;
200    const MAX_LENGTH: u32 = SECTOR_SIZE * 2;
201
202    #[repr(C, align(4))]
203    struct TestBuffer {
204        data: [u8; FLASH_SIZE as _],
205    }
206
207    impl TestBuffer {
208        const fn seq() -> Self {
209            let mut data = [0u8; FLASH_SIZE as _];
210            let mut index = 0;
211            while index < FLASH_SIZE {
212                data[index as usize] = (index & 0xff) as u8;
213                index += 1;
214            }
215            Self { data }
216        }
217    }
218
219    impl Default for TestBuffer {
220        fn default() -> Self {
221            Self {
222                data: [0u8; FLASH_SIZE as usize],
223            }
224        }
225    }
226
227    #[cfg(not(miri))]
228    fn range_gen<const ALIGN: u32, const MAX_OFF: u32, const MAX_LEN: u32>(
229        aligned: Option<bool>,
230    ) -> impl Iterator<Item = (u32, u32)> {
231        (0..=MAX_OFF).flat_map(move |off| {
232            (0..=MAX_LEN - off)
233                .filter(move |len| {
234                    aligned
235                        .map(|aligned| aligned == (off % ALIGN == 0 && len % ALIGN == 0))
236                        .unwrap_or(true)
237                })
238                .map(move |len| (off, len))
239        })
240    }
241
242    #[cfg(miri)]
243    fn range_gen<const ALIGN: u32, const MAX_OFF: u32, const MAX_LEN: u32>(
244        aligned: Option<bool>,
245    ) -> impl Iterator<Item = (u32, u32)> {
246        // MIRI is very slow - just use a couple of combinations
247        match aligned {
248            Some(true) => vec![(0, 4), (0, 8), (0, 16), (0, 32), (0, 1024)],
249            Some(false) => vec![(3, 7), (11, 11)],
250            None => vec![
251                (0, 4),
252                (0, 8),
253                (0, 16),
254                (0, 32),
255                (0, 1024),
256                (3, 7),
257                (11, 11),
258                (0, 4098),
259            ],
260        }
261        .into_iter()
262    }
263
264    #[test]
265    #[cfg(not(feature = "bytewise-read"))]
266    fn aligned_read() {
267        let mut flash = FlashStorage::new();
268        flash.capacity = 4 * 4096;
269        let src = TestBuffer::seq();
270        let mut data = TestBuffer::default();
271
272        flash.erase(0, FLASH_SIZE).unwrap();
273        flash.write(0, &src.data).unwrap();
274
275        for (off, len) in range_gen::<WORD_SIZE, MAX_OFFSET, MAX_LENGTH>(Some(true)) {
276            flash.read(off, &mut data.data[..len as usize]).unwrap();
277            assert_eq!(
278                data.data[..len as usize],
279                src.data[off as usize..][..len as usize]
280            );
281        }
282    }
283
284    #[test]
285    #[cfg(not(feature = "bytewise-read"))]
286    fn not_aligned_read_aligned_buffer() {
287        let mut flash = FlashStorage::new();
288        flash.capacity = 4 * 4096;
289        let mut data = TestBuffer::default();
290
291        for (off, len) in range_gen::<WORD_SIZE, MAX_OFFSET, MAX_LENGTH>(Some(false)) {
292            flash.read(off, &mut data.data[..len as usize]).unwrap_err();
293        }
294    }
295
296    #[test]
297    #[cfg(not(feature = "bytewise-read"))]
298    fn aligned_read_not_aligned_buffer() {
299        let mut flash = FlashStorage::new();
300        flash.capacity = 4 * 4096;
301        let src = TestBuffer::seq();
302        let mut data = TestBuffer::default();
303
304        flash.erase(0, FLASH_SIZE).unwrap();
305        flash.write(0, &src.data).unwrap();
306
307        for (off, len) in range_gen::<WORD_SIZE, MAX_OFFSET, MAX_LENGTH>(Some(true)) {
308            flash
309                .read(off, &mut data.data[1..][..len as usize])
310                .unwrap();
311            assert_eq!(
312                data.data[1..][..len as usize],
313                src.data[off as usize..][..len as usize]
314            );
315        }
316    }
317
318    #[test]
319    #[cfg(feature = "bytewise-read")]
320    fn bytewise_read_aligned_buffer() {
321        let mut flash = FlashStorage::new();
322        flash.capacity = 4 * 4096;
323        let src = TestBuffer::seq();
324        let mut data = TestBuffer::default();
325
326        flash.erase(0, FLASH_SIZE).unwrap();
327        flash.write(0, &src.data).unwrap();
328
329        for (off, len) in range_gen::<WORD_SIZE, MAX_OFFSET, MAX_LENGTH>(None) {
330            flash.read(off, &mut data.data[..len as usize]).unwrap();
331            assert_eq!(
332                data.data[..len as usize],
333                src.data[off as usize..][..len as usize]
334            );
335        }
336    }
337
338    #[test]
339    #[cfg(feature = "bytewise-read")]
340    fn bytewise_read_not_aligned_buffer() {
341        let mut flash = FlashStorage::new();
342        flash.capacity = 4 * 4096;
343        let src = TestBuffer::seq();
344        let mut data = TestBuffer::default();
345
346        flash.erase(0, FLASH_SIZE).unwrap();
347        flash.write(0, &src.data).unwrap();
348
349        for (off, len) in range_gen::<WORD_SIZE, MAX_OFFSET, MAX_LENGTH>(None) {
350            flash
351                .read(off, &mut data.data[1..][..len as usize])
352                .unwrap();
353            assert_eq!(
354                data.data[1..][..len as usize],
355                src.data[off as usize..][..len as usize]
356            );
357        }
358    }
359
360    #[test]
361    fn write_not_aligned_buffer() {
362        let mut flash = FlashStorage::new();
363        flash.capacity = 4 * 4096;
364        let mut read_data = TestBuffer::default();
365        let write_data = TestBuffer::seq();
366
367        flash.erase(0, FLASH_SIZE).unwrap();
368        flash.write(0, &write_data.data[1..129]).unwrap();
369
370        flash.read(0, &mut read_data.data[..128]).unwrap();
371
372        assert_eq!(&read_data.data[..128], &write_data.data[1..129]);
373    }
374}