Skip to main content

embedded_storage/
nor_flash.rs

1use crate::{ReadStorage, Region, Storage, iter::IterableByOverlaps};
2
3/// NOR flash errors.
4///
5/// NOR flash implementations must use an error type implementing this trait. This permits generic
6/// code to extract a generic error kind.
7pub trait NorFlashError: core::fmt::Debug {
8	/// Convert a specific NOR flash error into a generic error kind.
9	fn kind(&self) -> NorFlashErrorKind;
10}
11
12impl NorFlashError for core::convert::Infallible {
13	fn kind(&self) -> NorFlashErrorKind {
14		match *self {}
15	}
16}
17
18/// A trait that NorFlash implementations can use to share an error type.
19pub trait ErrorType {
20	/// Errors returned by this NOR flash.
21	type Error: NorFlashError;
22}
23
24/// NOR flash error kinds.
25///
26/// NOR flash implementations must map their error to those generic error kinds through the
27/// [`NorFlashError`] trait.
28#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
29#[cfg_attr(feature = "defmt", derive(defmt::Format))]
30#[non_exhaustive]
31pub enum NorFlashErrorKind {
32	/// The arguments are not properly aligned.
33	NotAligned,
34
35	/// The arguments are out of bounds.
36	OutOfBounds,
37
38	/// Error specific to the implementation.
39	Other,
40}
41
42impl NorFlashError for NorFlashErrorKind {
43	fn kind(&self) -> NorFlashErrorKind {
44		*self
45	}
46}
47
48impl core::fmt::Display for NorFlashErrorKind {
49	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
50		match self {
51			Self::NotAligned => write!(f, "Arguments are not properly aligned"),
52			Self::OutOfBounds => write!(f, "Arguments are out of bounds"),
53			Self::Other => write!(f, "An implementation specific error occurred"),
54		}
55	}
56}
57
58/// Read only NOR flash trait.
59pub trait ReadNorFlash: ErrorType {
60	/// The minumum number of bytes the storage peripheral can read
61	const READ_SIZE: usize;
62
63	/// Read a slice of data from the storage peripheral, starting the read
64	/// operation at the given address offset, and reading `bytes.len()` bytes.
65	///
66	/// # Errors
67	///
68	/// Returns an error if the arguments are not aligned or out of bounds. The implementation
69	/// can use the [`check_read`] helper function.
70	fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error>;
71
72	/// The capacity of the peripheral in bytes.
73	fn capacity(&self) -> usize;
74}
75
76/// Return whether a read operation is within bounds.
77pub fn check_read<T: ReadNorFlash>(
78	flash: &T,
79	offset: u32,
80	length: usize,
81) -> Result<(), NorFlashErrorKind> {
82	check_slice(flash, T::READ_SIZE, offset, length)
83}
84
85/// NOR flash trait.
86pub trait NorFlash: ReadNorFlash {
87	/// The minumum number of bytes the storage peripheral can write
88	const WRITE_SIZE: usize;
89
90	/// The minumum number of bytes the storage peripheral can erase
91	const ERASE_SIZE: usize;
92
93	/// Erase the given storage range, clearing all data within `from..to`.
94	/// The given range will contain all 1s afterwards.
95	///
96	/// If power is lost during erase, contents of the page are undefined.
97	///
98	/// `to` is exclusive.
99	///
100	/// # Errors
101	///
102	/// Returns an error if the arguments are not aligned or out of bounds (the case where `to >
103	/// from` is considered out of bounds). The implementation can use the [`check_erase`]
104	/// helper function.
105	fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error>;
106
107	/// If power is lost during write, the contents of the written words are undefined,
108	/// but the rest of the page is guaranteed to be unchanged.
109	/// It is not allowed to write to the same word twice.
110	///
111	/// # Errors
112	///
113	/// Returns an error if the arguments are not aligned or out of bounds. The implementation
114	/// can use the [`check_write`] helper function.
115	fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error>;
116}
117
118/// Return whether an erase operation is aligned and within bounds.
119pub fn check_erase<T: NorFlash>(flash: &T, from: u32, to: u32) -> Result<(), NorFlashErrorKind> {
120	let (from, to) = (from as usize, to as usize);
121	if from > to || to > flash.capacity() {
122		return Err(NorFlashErrorKind::OutOfBounds);
123	}
124	if from % T::ERASE_SIZE != 0 || to % T::ERASE_SIZE != 0 {
125		return Err(NorFlashErrorKind::NotAligned);
126	}
127	Ok(())
128}
129
130/// Return whether a write operation is aligned and within bounds.
131pub fn check_write<T: NorFlash>(
132	flash: &T,
133	offset: u32,
134	length: usize,
135) -> Result<(), NorFlashErrorKind> {
136	check_slice(flash, T::WRITE_SIZE, offset, length)
137}
138
139fn check_slice<T: ReadNorFlash>(
140	flash: &T,
141	align: usize,
142	offset: u32,
143	length: usize,
144) -> Result<(), NorFlashErrorKind> {
145	let offset = offset as usize;
146	if length > flash.capacity() || offset > flash.capacity() - length {
147		return Err(NorFlashErrorKind::OutOfBounds);
148	}
149	if !offset.is_multiple_of(align) || !length.is_multiple_of(align) {
150		return Err(NorFlashErrorKind::NotAligned);
151	}
152	Ok(())
153}
154
155impl<T: ErrorType> ErrorType for &mut T {
156	type Error = T::Error;
157}
158
159impl<T: ReadNorFlash> ReadNorFlash for &mut T {
160	const READ_SIZE: usize = T::READ_SIZE;
161
162	fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
163		T::read(self, offset, bytes)
164	}
165
166	fn capacity(&self) -> usize {
167		T::capacity(self)
168	}
169}
170
171impl<T: NorFlash> NorFlash for &mut T {
172	const WRITE_SIZE: usize = T::WRITE_SIZE;
173	const ERASE_SIZE: usize = T::ERASE_SIZE;
174
175	fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
176		T::erase(self, from, to)
177	}
178
179	fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
180		T::write(self, offset, bytes)
181	}
182}
183
184/// Marker trait for NorFlash relaxing the restrictions on `write`.
185///
186/// Writes to the same word twice are now allowed. The result is the logical AND of the
187/// previous data and the written data. That is, it is only possible to change 1 bits to 0 bits.
188///
189/// If power is lost during write:
190/// - Bits that were 1 on flash and are written to 1 are guaranteed to stay as 1
191/// - Bits that were 1 on flash and are written to 0 are undefined
192/// - Bits that were 0 on flash are guaranteed to stay as 0
193/// - Rest of the bits in the page are guaranteed to be unchanged
194pub trait MultiwriteNorFlash: NorFlash {}
195impl<T: MultiwriteNorFlash> MultiwriteNorFlash for &mut T {}
196
197struct Page {
198	pub start: u32,
199	pub size: usize,
200}
201
202impl Page {
203	fn new(index: u32, size: usize) -> Self {
204		Self {
205			start: index * size as u32,
206			size,
207		}
208	}
209
210	/// The end address of the page
211	const fn end(&self) -> u32 {
212		self.start + self.size as u32
213	}
214}
215
216impl Region for Page {
217	/// Checks if an address offset is contained within the page
218	fn contains(&self, address: u32) -> bool {
219		(self.start <= address) && (self.end() > address)
220	}
221}
222
223/// Read-Modify-Write (RMW) Nor Flash storage structure.
224pub struct RmwNorFlashStorage<'a, S> {
225	storage: S,
226	merge_buffer: &'a mut [u8],
227}
228
229impl<'a, S> RmwNorFlashStorage<'a, S>
230where
231	S: NorFlash,
232{
233	/// Instantiate a new generic `Storage` from a `NorFlash` peripheral
234	///
235	/// **NOTE** This will panic if the provided merge buffer,
236	/// is smaller than the erase size of the flash peripheral
237	pub fn new(nor_flash: S, merge_buffer: &'a mut [u8]) -> Self {
238		if merge_buffer.len() < S::ERASE_SIZE {
239			panic!("Merge buffer is too small");
240		}
241
242		Self {
243			storage: nor_flash,
244			merge_buffer,
245		}
246	}
247
248	/// Consume the generic `Storage` and return the underlying NorFlash peripheral
249	pub fn into_inner(self) -> S {
250		self.storage
251	}
252}
253
254impl<'a, S> ReadStorage for RmwNorFlashStorage<'a, S>
255where
256	S: ReadNorFlash,
257{
258	type Error = S::Error;
259
260	fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
261		// Nothing special to be done for reads
262		self.storage.read(offset, bytes)
263	}
264
265	fn capacity(&self) -> usize {
266		self.storage.capacity()
267	}
268}
269
270impl<'a, S> Storage for RmwNorFlashStorage<'a, S>
271where
272	S: NorFlash,
273{
274	fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
275		// Perform read/modify/write operations on the byte slice.
276		let last_page = self.storage.capacity() / S::ERASE_SIZE;
277
278		// `data` is the part of `bytes` contained within `page`,
279		// and `addr` in the address offset of `page` + any offset into the page as requested by `address`
280		for (data, page, addr) in (0..last_page as u32)
281			.map(move |i| Page::new(i, S::ERASE_SIZE))
282			.overlaps(bytes, offset)
283		{
284			let offset_into_page = addr.saturating_sub(page.start) as usize;
285
286			self.storage
287				.read(page.start, &mut self.merge_buffer[..S::ERASE_SIZE])?;
288
289			// If we cannot write multiple times to the same page, we will have to erase it
290			self.storage.erase(page.start, page.end())?;
291			self.merge_buffer[..S::ERASE_SIZE]
292				.iter_mut()
293				.skip(offset_into_page)
294				.zip(data)
295				.for_each(|(byte, input)| *byte = *input);
296			self.storage
297				.write(page.start, &self.merge_buffer[..S::ERASE_SIZE])?;
298		}
299		Ok(())
300	}
301}
302
303/// Read-Modify-Write (RMW) Multi-Write Nor Flash storage structure.
304pub struct RmwMultiwriteNorFlashStorage<'a, S> {
305	storage: S,
306	merge_buffer: &'a mut [u8],
307}
308
309impl<'a, S> RmwMultiwriteNorFlashStorage<'a, S>
310where
311	S: MultiwriteNorFlash,
312{
313	/// Instantiate a new generic `Storage` from a `NorFlash` peripheral
314	///
315	/// **NOTE** This will panic if the provided merge buffer,
316	/// is smaller than the erase size of the flash peripheral
317	pub fn new(nor_flash: S, merge_buffer: &'a mut [u8]) -> Self {
318		if merge_buffer.len() < S::ERASE_SIZE {
319			panic!("Merge buffer is too small");
320		}
321
322		Self {
323			storage: nor_flash,
324			merge_buffer,
325		}
326	}
327
328	/// Consume the generic `Storage` and return the underlying NorFlash peripheral
329	pub fn into_inner(self) -> S {
330		self.storage
331	}
332}
333
334impl<'a, S> ReadStorage for RmwMultiwriteNorFlashStorage<'a, S>
335where
336	S: ReadNorFlash,
337{
338	type Error = S::Error;
339
340	fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
341		// Nothing special to be done for reads
342		self.storage.read(offset, bytes)
343	}
344
345	fn capacity(&self) -> usize {
346		self.storage.capacity()
347	}
348}
349
350impl<'a, S> Storage for RmwMultiwriteNorFlashStorage<'a, S>
351where
352	S: MultiwriteNorFlash,
353{
354	fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
355		// Perform read/modify/write operations on the byte slice.
356		let last_page = self.storage.capacity() / S::ERASE_SIZE;
357
358		// `data` is the part of `bytes` contained within `page`,
359		// and `addr` in the address offset of `page` + any offset into the page as requested by `address`
360		for (data, page, addr) in (0..last_page as u32)
361			.map(move |i| Page::new(i, S::ERASE_SIZE))
362			.overlaps(bytes, offset)
363		{
364			let offset_into_page = addr.saturating_sub(page.start) as usize;
365
366			self.storage
367				.read(page.start, &mut self.merge_buffer[..S::ERASE_SIZE])?;
368
369			let rhs = &self.merge_buffer[offset_into_page..S::ERASE_SIZE];
370			let is_subset = data.iter().zip(rhs.iter()).all(|(a, b)| *a & *b == *a);
371
372			// Check if we can write the data block directly, under the limitations imposed by NorFlash:
373			// - We can only change 1's to 0's
374			if is_subset {
375				// Use `merge_buffer` as allocation for padding `data` to `WRITE_SIZE`
376				let offset = addr as usize % S::WRITE_SIZE;
377				let aligned_end = data.len() % S::WRITE_SIZE + offset + data.len();
378				self.merge_buffer[..aligned_end].fill(0xff);
379				self.merge_buffer[offset..offset + data.len()].copy_from_slice(data);
380				self.storage
381					.write(addr - offset as u32, &self.merge_buffer[..aligned_end])?;
382			} else {
383				self.storage.erase(page.start, page.end())?;
384				self.merge_buffer[..S::ERASE_SIZE]
385					.iter_mut()
386					.skip(offset_into_page)
387					.zip(data)
388					.for_each(|(byte, input)| *byte = *input);
389				self.storage
390					.write(page.start, &self.merge_buffer[..S::ERASE_SIZE])?;
391			}
392		}
393		Ok(())
394	}
395}