embassy_embedded_hal/flash/
concat_flash.rs

1use embedded_storage::nor_flash::{ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, ReadNorFlash};
2use embedded_storage_async::nor_flash::{
3    MultiwriteNorFlash as AsyncMultiwriteNorFlash, NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash,
4};
5
6/// Convenience helper for concatenating two consecutive flashes into one.
7/// This is especially useful if used with "flash regions", where one may
8/// want to concatenate multiple regions into one larger region.
9pub struct ConcatFlash<First, Second>(First, Second);
10
11impl<First, Second> ConcatFlash<First, Second> {
12    /// Create a new flash that concatenates two consecutive flashes.
13    pub fn new(first: First, second: Second) -> Self {
14        Self(first, second)
15    }
16}
17
18const fn get_read_size(first_read_size: usize, second_read_size: usize) -> usize {
19    if first_read_size != second_read_size {
20        panic!("The read size for the concatenated flashes must be the same");
21    }
22    first_read_size
23}
24
25const fn get_write_size(first_write_size: usize, second_write_size: usize) -> usize {
26    if first_write_size != second_write_size {
27        panic!("The write size for the concatenated flashes must be the same");
28    }
29    first_write_size
30}
31
32const fn get_max_erase_size(first_erase_size: usize, second_erase_size: usize) -> usize {
33    let max_erase_size = if first_erase_size > second_erase_size {
34        first_erase_size
35    } else {
36        second_erase_size
37    };
38    if max_erase_size % first_erase_size != 0 || max_erase_size % second_erase_size != 0 {
39        panic!("The erase sizes for the concatenated flashes must have have a gcd equal to the max erase size");
40    }
41    max_erase_size
42}
43
44impl<First, Second, E> ErrorType for ConcatFlash<First, Second>
45where
46    First: ErrorType<Error = E>,
47    Second: ErrorType<Error = E>,
48    E: NorFlashError,
49{
50    type Error = E;
51}
52
53impl<First, Second, E> ReadNorFlash for ConcatFlash<First, Second>
54where
55    First: ReadNorFlash<Error = E>,
56    Second: ReadNorFlash<Error = E>,
57    E: NorFlashError,
58{
59    const READ_SIZE: usize = get_read_size(First::READ_SIZE, Second::READ_SIZE);
60
61    fn read(&mut self, mut offset: u32, mut bytes: &mut [u8]) -> Result<(), E> {
62        if offset < self.0.capacity() as u32 {
63            let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len());
64            self.0.read(offset, &mut bytes[..len])?;
65            offset += len as u32;
66            bytes = &mut bytes[len..];
67        }
68
69        if !bytes.is_empty() {
70            self.1.read(offset - self.0.capacity() as u32, bytes)?;
71        }
72
73        Ok(())
74    }
75
76    fn capacity(&self) -> usize {
77        self.0.capacity() + self.1.capacity()
78    }
79}
80
81impl<First, Second, E> NorFlash for ConcatFlash<First, Second>
82where
83    First: NorFlash<Error = E>,
84    Second: NorFlash<Error = E>,
85    E: NorFlashError,
86{
87    const WRITE_SIZE: usize = get_write_size(First::WRITE_SIZE, Second::WRITE_SIZE);
88    const ERASE_SIZE: usize = get_max_erase_size(First::ERASE_SIZE, Second::ERASE_SIZE);
89
90    fn write(&mut self, mut offset: u32, mut bytes: &[u8]) -> Result<(), E> {
91        if offset < self.0.capacity() as u32 {
92            let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len());
93            self.0.write(offset, &bytes[..len])?;
94            offset += len as u32;
95            bytes = &bytes[len..];
96        }
97
98        if !bytes.is_empty() {
99            self.1.write(offset - self.0.capacity() as u32, bytes)?;
100        }
101
102        Ok(())
103    }
104
105    fn erase(&mut self, mut from: u32, to: u32) -> Result<(), E> {
106        if from < self.0.capacity() as u32 {
107            let to = core::cmp::min(self.0.capacity() as u32, to);
108            self.0.erase(from, to)?;
109            from = self.0.capacity() as u32;
110        }
111
112        if from < to {
113            self.1
114                .erase(from - self.0.capacity() as u32, to - self.0.capacity() as u32)?;
115        }
116
117        Ok(())
118    }
119}
120
121impl<First, Second, E> MultiwriteNorFlash for ConcatFlash<First, Second>
122where
123    First: MultiwriteNorFlash<Error = E>,
124    Second: MultiwriteNorFlash<Error = E>,
125    E: NorFlashError,
126{
127}
128
129impl<First, Second, E> AsyncReadNorFlash for ConcatFlash<First, Second>
130where
131    First: AsyncReadNorFlash<Error = E>,
132    Second: AsyncReadNorFlash<Error = E>,
133    E: NorFlashError,
134{
135    const READ_SIZE: usize = get_read_size(First::READ_SIZE, Second::READ_SIZE);
136
137    async fn read(&mut self, mut offset: u32, mut bytes: &mut [u8]) -> Result<(), E> {
138        if offset < self.0.capacity() as u32 {
139            let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len());
140            self.0.read(offset, &mut bytes[..len]).await?;
141            offset += len as u32;
142            bytes = &mut bytes[len..];
143        }
144
145        if !bytes.is_empty() {
146            self.1.read(offset - self.0.capacity() as u32, bytes).await?;
147        }
148
149        Ok(())
150    }
151
152    fn capacity(&self) -> usize {
153        self.0.capacity() + self.1.capacity()
154    }
155}
156
157impl<First, Second, E> AsyncNorFlash for ConcatFlash<First, Second>
158where
159    First: AsyncNorFlash<Error = E>,
160    Second: AsyncNorFlash<Error = E>,
161    E: NorFlashError,
162{
163    const WRITE_SIZE: usize = get_write_size(First::WRITE_SIZE, Second::WRITE_SIZE);
164    const ERASE_SIZE: usize = get_max_erase_size(First::ERASE_SIZE, Second::ERASE_SIZE);
165
166    async fn write(&mut self, mut offset: u32, mut bytes: &[u8]) -> Result<(), E> {
167        if offset < self.0.capacity() as u32 {
168            let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len());
169            self.0.write(offset, &bytes[..len]).await?;
170            offset += len as u32;
171            bytes = &bytes[len..];
172        }
173
174        if !bytes.is_empty() {
175            self.1.write(offset - self.0.capacity() as u32, bytes).await?;
176        }
177
178        Ok(())
179    }
180
181    async fn erase(&mut self, mut from: u32, to: u32) -> Result<(), E> {
182        if from < self.0.capacity() as u32 {
183            let to = core::cmp::min(self.0.capacity() as u32, to);
184            self.0.erase(from, to).await?;
185            from = self.0.capacity() as u32;
186        }
187
188        if from < to {
189            self.1
190                .erase(from - self.0.capacity() as u32, to - self.0.capacity() as u32)
191                .await?;
192        }
193
194        Ok(())
195    }
196}
197
198impl<First, Second, E> AsyncMultiwriteNorFlash for ConcatFlash<First, Second>
199where
200    First: AsyncMultiwriteNorFlash<Error = E>,
201    Second: AsyncMultiwriteNorFlash<Error = E>,
202    E: NorFlashError,
203{
204}
205
206#[cfg(test)]
207mod tests {
208    use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
209
210    use super::ConcatFlash;
211    use crate::flash::mem_flash::MemFlash;
212
213    #[test]
214    fn can_write_and_read_across_flashes() {
215        let first = MemFlash::<64, 16, 4>::default();
216        let second = MemFlash::<64, 64, 4>::default();
217        let mut f = ConcatFlash::new(first, second);
218
219        f.write(60, &[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]).unwrap();
220
221        assert_eq!(&[0x11, 0x22, 0x33, 0x44], &f.0.mem[60..]);
222        assert_eq!(&[0x55, 0x66, 0x77, 0x88], &f.1.mem[0..4]);
223
224        let mut read_buf = [0; 8];
225        f.read(60, &mut read_buf).unwrap();
226
227        assert_eq!(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88], &read_buf);
228    }
229
230    #[test]
231    fn can_erase_across_flashes() {
232        let first = MemFlash::<128, 16, 4>::new(0x00);
233        let second = MemFlash::<128, 64, 4>::new(0x00);
234        let mut f = ConcatFlash::new(first, second);
235
236        f.erase(64, 192).unwrap();
237
238        assert_eq!(&[0x00; 64], &f.0.mem[0..64]);
239        assert_eq!(&[0xff; 64], &f.0.mem[64..128]);
240        assert_eq!(&[0xff; 64], &f.1.mem[0..64]);
241        assert_eq!(&[0x00; 64], &f.1.mem[64..128]);
242    }
243}