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