Skip to main content

embedded_storage_async/
nor_flash.rs

1use embedded_storage::Region;
2use embedded_storage::iter::IterableByOverlaps;
3pub use embedded_storage::nor_flash::{ErrorType, NorFlashError, NorFlashErrorKind};
4
5use crate::{ReadStorage, Storage};
6
7/// Read only NOR flash trait.
8pub trait ReadNorFlash: ErrorType {
9	/// The minumum number of bytes the storage peripheral can read
10	const READ_SIZE: usize;
11
12	/// Read a slice of data from the storage peripheral, starting the read
13	/// operation at the given address offset, and reading `bytes.len()` bytes.
14	///
15	/// # Errors
16	///
17	/// Returns an error if the arguments are not aligned or out of bounds. The implementation
18	/// can use the [`check_read`] helper function.
19	async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error>;
20
21	/// The capacity of the peripheral in bytes.
22	fn capacity(&self) -> usize;
23}
24
25/// NOR flash trait.
26pub trait NorFlash: ReadNorFlash {
27	/// The minumum number of bytes the storage peripheral can write
28	const WRITE_SIZE: usize;
29
30	/// The minumum number of bytes the storage peripheral can erase
31	const ERASE_SIZE: usize;
32
33	/// Erase the given storage range, clearing all data within `from..to`.
34	/// The given range will contain all 1s afterwards.
35	///
36	/// If power is lost during erase, contents of the page are undefined.
37	///
38	/// `to` is exclusive.
39	///
40	/// # Errors
41	///
42	/// Returns an error if the arguments are not aligned or out of bounds (the case where `to >
43	/// from` is considered out of bounds). The implementation can use the [`check_erase`]
44	/// helper function.
45	async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error>;
46
47	/// If power is lost during write, the contents of the written words are undefined,
48	/// but the rest of the page is guaranteed to be unchanged.
49	/// It is not allowed to write to the same word twice.
50	///
51	/// # Errors
52	///
53	/// Returns an error if the arguments are not aligned or out of bounds. The implementation
54	/// can use the [`check_write`] helper function.
55	async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error>;
56}
57
58impl<T: ReadNorFlash> ReadNorFlash for &mut T {
59	const READ_SIZE: usize = T::READ_SIZE;
60
61	async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
62		T::read(self, offset, bytes).await
63	}
64
65	fn capacity(&self) -> usize {
66		T::capacity(self)
67	}
68}
69
70impl<T: NorFlash> NorFlash for &mut T {
71	const WRITE_SIZE: usize = T::WRITE_SIZE;
72	const ERASE_SIZE: usize = T::ERASE_SIZE;
73
74	async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
75		T::erase(self, from, to).await
76	}
77
78	async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
79		T::write(self, offset, bytes).await
80	}
81}
82
83/// Marker trait for NorFlash relaxing the restrictions on `write`.
84///
85/// Writes to the same word twice are now allowed. The result is the logical AND of the
86/// previous data and the written data. That is, it is only possible to change 1 bits to 0 bits.
87///
88/// If power is lost during write:
89/// - Bits that were 1 on flash and are written to 1 are guaranteed to stay as 1
90/// - Bits that were 1 on flash and are written to 0 are undefined
91/// - Bits that were 0 on flash are guaranteed to stay as 0
92/// - Rest of the bits in the page are guaranteed to be unchanged
93pub trait MultiwriteNorFlash: NorFlash {}
94impl<T: MultiwriteNorFlash> MultiwriteNorFlash for &mut T {}
95
96struct Page {
97	pub start: u32,
98	pub size: usize,
99}
100
101impl Page {
102	fn new(index: u32, size: usize) -> Self {
103		Self {
104			start: index * size as u32,
105			size,
106		}
107	}
108
109	/// The end address of the page
110	const fn end(&self) -> u32 {
111		self.start + self.size as u32
112	}
113}
114
115impl Region for Page {
116	/// Checks if an address offset is contained within the page
117	fn contains(&self, address: u32) -> bool {
118		(self.start <= address) && (self.end() > address)
119	}
120}
121
122/// Read-Modify-Write (RMW) Multi-Write Nor Flash storage structure.
123#[derive(Debug)]
124pub struct RmwNorFlashStorage<'a, S> {
125	storage: S,
126	merge_buffer: &'a mut [u8],
127}
128
129impl<'a, S> RmwNorFlashStorage<'a, S>
130where
131	S: NorFlash,
132{
133	/// Instantiate a new generic `Storage` from a `NorFlash` peripheral
134	///
135	/// **NOTE** This will panic if the provided merge buffer,
136	/// is smaller than the erase size of the flash peripheral
137	pub fn new(nor_flash: S, merge_buffer: &'a mut [u8]) -> Self {
138		if merge_buffer.len() < S::ERASE_SIZE {
139			panic!("Merge buffer is too small");
140		}
141
142		Self {
143			storage: nor_flash,
144			merge_buffer,
145		}
146	}
147
148	/// Consume the generic `Storage` and return the underlying NorFlash peripheral
149	pub fn into_inner(self) -> S {
150		self.storage
151	}
152}
153
154impl<'a, S> ReadStorage for RmwNorFlashStorage<'a, S>
155where
156	S: ReadNorFlash,
157{
158	type Error = S::Error;
159
160	async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
161		// Nothing special to be done for reads
162		self.storage.read(offset, bytes).await
163	}
164
165	fn capacity(&self) -> usize {
166		self.storage.capacity()
167	}
168}
169
170impl<'a, S> Storage for RmwNorFlashStorage<'a, S>
171where
172	S: NorFlash,
173{
174	async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
175		// Perform read/modify/write operations on the byte slice.
176		let last_page = self.storage.capacity() / S::ERASE_SIZE;
177
178		// `data` is the part of `bytes` contained within `page`,
179		// and `addr` in the address offset of `page` + any offset into the page as requested by `address`
180		for (data, page, addr) in (0..last_page as u32)
181			.map(move |i| Page::new(i, S::ERASE_SIZE))
182			.overlaps(bytes, offset)
183		{
184			let offset_into_page = addr.saturating_sub(page.start) as usize;
185
186			self.storage
187				.read(page.start, &mut self.merge_buffer[..S::ERASE_SIZE])
188				.await?;
189
190			// If we cannot write multiple times to the same page, we will have to erase it
191			self.storage.erase(page.start, page.end()).await?;
192			self.merge_buffer[..S::ERASE_SIZE]
193				.iter_mut()
194				.skip(offset_into_page)
195				.zip(data)
196				.for_each(|(byte, input)| *byte = *input);
197			self.storage
198				.write(page.start, &self.merge_buffer[..S::ERASE_SIZE])
199				.await?;
200		}
201		Ok(())
202	}
203}
204
205/// Read-Modify-Write (RMW) Multi-Write Nor Flash storage structure.
206pub struct RmwMultiwriteNorFlashStorage<'a, S> {
207	storage: S,
208	merge_buffer: &'a mut [u8],
209}
210
211impl<'a, S> RmwMultiwriteNorFlashStorage<'a, S>
212where
213	S: MultiwriteNorFlash,
214{
215	/// Instantiate a new generic `Storage` from a `NorFlash` peripheral
216	///
217	/// **NOTE** This will panic if the provided merge buffer,
218	/// is smaller than the erase size of the flash peripheral
219	pub fn new(nor_flash: S, merge_buffer: &'a mut [u8]) -> Self {
220		if merge_buffer.len() < S::ERASE_SIZE {
221			panic!("Merge buffer is too small");
222		}
223
224		Self {
225			storage: nor_flash,
226			merge_buffer,
227		}
228	}
229
230	/// Consume the generic `Storage` and return the underlying NorFlash peripheral
231	pub fn into_inner(self) -> S {
232		self.storage
233	}
234}
235
236impl<'a, S> ReadStorage for RmwMultiwriteNorFlashStorage<'a, S>
237where
238	S: ReadNorFlash,
239{
240	type Error = S::Error;
241
242	async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
243		// Nothing special to be done for reads
244		self.storage.read(offset, bytes).await
245	}
246
247	fn capacity(&self) -> usize {
248		self.storage.capacity()
249	}
250}
251
252impl<'a, S> Storage for RmwMultiwriteNorFlashStorage<'a, S>
253where
254	S: MultiwriteNorFlash,
255{
256	async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
257		// Perform read/modify/write operations on the byte slice.
258		let last_page = self.storage.capacity() / S::ERASE_SIZE;
259
260		// `data` is the part of `bytes` contained within `page`,
261		// and `addr` in the address offset of `page` + any offset into the page as requested by `address`
262		for (data, page, addr) in (0..last_page as u32)
263			.map(move |i| Page::new(i, S::ERASE_SIZE))
264			.overlaps(bytes, offset)
265		{
266			let offset_into_page = addr.saturating_sub(page.start) as usize;
267
268			self.storage
269				.read(page.start, &mut self.merge_buffer[..S::ERASE_SIZE])
270				.await?;
271
272			let rhs = &self.merge_buffer[offset_into_page..S::ERASE_SIZE];
273			let is_subset = data.iter().zip(rhs.iter()).all(|(a, b)| *a & *b == *a);
274
275			// Check if we can write the data block directly, under the limitations imposed by NorFlash:
276			// - We can only change 1's to 0's
277			if is_subset {
278				// Use `merge_buffer` as allocation for padding `data` to `WRITE_SIZE`
279				let offset = addr as usize % S::WRITE_SIZE;
280				let aligned_end = data.len() % S::WRITE_SIZE + offset + data.len();
281				self.merge_buffer[..aligned_end].fill(0xff);
282				self.merge_buffer[offset..offset + data.len()].copy_from_slice(data);
283				self.storage
284					.write(addr - offset as u32, &self.merge_buffer[..aligned_end])
285					.await?;
286			} else {
287				self.storage.erase(page.start, page.end()).await?;
288				self.merge_buffer[..S::ERASE_SIZE]
289					.iter_mut()
290					.skip(offset_into_page)
291					.zip(data)
292					.for_each(|(byte, input)| *byte = *input);
293				self.storage
294					.write(page.start, &self.merge_buffer[..S::ERASE_SIZE])
295					.await?;
296			}
297		}
298		Ok(())
299	}
300}