esp_storage/
nor_flash.rs

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