Skip to content

Commit 3b3671c

Browse files
authored
Merge pull request #14 from rust-embedded-community/enhancement/rmw-nor-flash
Enhancement: RmwNorFlash
2 parents a1b9805 + 51bed46 commit 3b3671c

File tree

3 files changed

+196
-4
lines changed

3 files changed

+196
-4
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818

1919
include:
2020
# Test MSRV
21-
- rust: 1.36.0
21+
- rust: 1.50.0
2222
TARGET: x86_64-unknown-linux-gnu
2323

2424
# Test nightly but don't fail

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ NOR-flash & NAND-flash, both external and internal.
88

99
## [API reference]
1010

11-
[API reference]: https://docs.rs/embedded-storage
12-
1311
## How-to: add a new trait
1412

1513
This is the suggested approach to adding a new trait to `embedded-storage`
@@ -37,7 +35,7 @@ These issues / PRs will be labeled as `proposal`s in the issue tracker.
3735

3836
## Minimum Supported Rust Version (MSRV)
3937

40-
This crate is guaranteed to compile on stable Rust 1.36.0 and up. It *might*
38+
This crate is guaranteed to compile on stable Rust 1.50.0 and up. It *might*
4139
compile with older versions but that may change in any new patch release.
4240

4341
## License
@@ -55,3 +53,5 @@ at your option.
5553
Unless you explicitly state otherwise, any contribution intentionally submitted
5654
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
5755
dual licensed as above, without any additional terms or conditions.
56+
57+
[API reference]: https://docs.rs/embedded-storage

src/nor_flash.rs

+192
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use crate::{iter::IterableByOverlaps, ReadStorage, Region, Storage};
2+
13
/// Read only NOR flash trait.
24
pub trait ReadNorFlash {
35
/// An enumeration of storage errors
@@ -52,3 +54,193 @@ pub trait NorFlash: ReadNorFlash {
5254
/// - Bits that were 0 on flash are guaranteed to stay as 0
5355
/// - Rest of the bits in the page are guaranteed to be unchanged
5456
pub trait MultiwriteNorFlash: NorFlash {}
57+
58+
struct Page {
59+
pub start: u32,
60+
pub size: usize,
61+
}
62+
63+
impl Page {
64+
fn new(index: u32, size: usize) -> Self {
65+
Self {
66+
start: index * size as u32,
67+
size,
68+
}
69+
}
70+
71+
/// The end address of the page
72+
const fn end(&self) -> u32 {
73+
self.start + self.size as u32
74+
}
75+
}
76+
77+
impl Region for Page {
78+
/// Checks if an address offset is contained within the page
79+
fn contains(&self, address: u32) -> bool {
80+
(self.start <= address) && (self.end() > address)
81+
}
82+
}
83+
84+
///
85+
pub struct RmwNorFlashStorage<'a, S> {
86+
storage: S,
87+
merge_buffer: &'a mut [u8],
88+
}
89+
90+
impl<'a, S> RmwNorFlashStorage<'a, S>
91+
where
92+
S: NorFlash,
93+
{
94+
/// Instantiate a new generic `Storage` from a `NorFlash` peripheral
95+
///
96+
/// **NOTE** This will panic if the provided merge buffer,
97+
/// is smaller than the erase size of the flash peripheral
98+
pub fn new(nor_flash: S, merge_buffer: &'a mut [u8]) -> Self {
99+
if merge_buffer.len() < S::ERASE_SIZE {
100+
panic!("Merge buffer is too small");
101+
}
102+
103+
Self {
104+
storage: nor_flash,
105+
merge_buffer,
106+
}
107+
}
108+
}
109+
110+
impl<'a, S> ReadStorage for RmwNorFlashStorage<'a, S>
111+
where
112+
S: ReadNorFlash,
113+
{
114+
type Error = S::Error;
115+
116+
fn try_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
117+
// Nothing special to be done for reads
118+
self.storage.try_read(offset, bytes)
119+
}
120+
121+
fn capacity(&self) -> usize {
122+
self.storage.capacity()
123+
}
124+
}
125+
126+
impl<'a, S> Storage for RmwNorFlashStorage<'a, S>
127+
where
128+
S: NorFlash,
129+
{
130+
fn try_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
131+
// Perform read/modify/write operations on the byte slice.
132+
let last_page = self.storage.capacity() / S::ERASE_SIZE;
133+
134+
// `data` is the part of `bytes` contained within `page`,
135+
// and `addr` in the address offset of `page` + any offset into the page as requested by `address`
136+
for (data, page, addr) in (0..last_page as u32)
137+
.map(move |i| Page::new(i, S::ERASE_SIZE))
138+
.overlaps(bytes, offset)
139+
{
140+
let offset_into_page = addr.saturating_sub(page.start) as usize;
141+
142+
self.storage
143+
.try_read(page.start, &mut self.merge_buffer[..S::ERASE_SIZE])?;
144+
145+
// If we cannot write multiple times to the same page, we will have to erase it
146+
self.storage.try_erase(page.start, page.end())?;
147+
self.merge_buffer[..S::ERASE_SIZE]
148+
.iter_mut()
149+
.skip(offset_into_page)
150+
.zip(data)
151+
.for_each(|(byte, input)| *byte = *input);
152+
self.storage
153+
.try_write(page.start, &self.merge_buffer[..S::ERASE_SIZE])?;
154+
}
155+
Ok(())
156+
}
157+
}
158+
159+
///
160+
pub struct RmwMultiwriteNorFlashStorage<'a, S> {
161+
storage: S,
162+
merge_buffer: &'a mut [u8],
163+
}
164+
165+
impl<'a, S> RmwMultiwriteNorFlashStorage<'a, S>
166+
where
167+
S: MultiwriteNorFlash,
168+
{
169+
/// Instantiate a new generic `Storage` from a `NorFlash` peripheral
170+
///
171+
/// **NOTE** This will panic if the provided merge buffer,
172+
/// is smaller than the erase size of the flash peripheral
173+
pub fn new(nor_flash: S, merge_buffer: &'a mut [u8]) -> Self {
174+
if merge_buffer.len() < S::ERASE_SIZE {
175+
panic!("Merge buffer is too small");
176+
}
177+
178+
Self {
179+
storage: nor_flash,
180+
merge_buffer,
181+
}
182+
}
183+
}
184+
185+
impl<'a, S> ReadStorage for RmwMultiwriteNorFlashStorage<'a, S>
186+
where
187+
S: ReadNorFlash,
188+
{
189+
type Error = S::Error;
190+
191+
fn try_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
192+
// Nothing special to be done for reads
193+
self.storage.try_read(offset, bytes)
194+
}
195+
196+
fn capacity(&self) -> usize {
197+
self.storage.capacity()
198+
}
199+
}
200+
201+
impl<'a, S> Storage for RmwMultiwriteNorFlashStorage<'a, S>
202+
where
203+
S: MultiwriteNorFlash,
204+
{
205+
fn try_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
206+
// Perform read/modify/write operations on the byte slice.
207+
let last_page = self.storage.capacity() / S::ERASE_SIZE;
208+
209+
// `data` is the part of `bytes` contained within `page`,
210+
// and `addr` in the address offset of `page` + any offset into the page as requested by `address`
211+
for (data, page, addr) in (0..last_page as u32)
212+
.map(move |i| Page::new(i, S::ERASE_SIZE))
213+
.overlaps(bytes, offset)
214+
{
215+
let offset_into_page = addr.saturating_sub(page.start) as usize;
216+
217+
self.storage
218+
.try_read(page.start, &mut self.merge_buffer[..S::ERASE_SIZE])?;
219+
220+
let rhs = &self.merge_buffer[offset_into_page..S::ERASE_SIZE];
221+
let is_subset = data.iter().zip(rhs.iter()).all(|(a, b)| *a & *b == *a);
222+
223+
// Check if we can write the data block directly, under the limitations imposed by NorFlash:
224+
// - We can only change 1's to 0's
225+
if is_subset {
226+
// Use `merge_buffer` as allocation for padding `data` to `WRITE_SIZE`
227+
let offset = addr as usize % S::WRITE_SIZE;
228+
let aligned_end = data.len() % S::WRITE_SIZE + offset + data.len();
229+
self.merge_buffer[..aligned_end].fill(0xff);
230+
self.merge_buffer[offset..offset + data.len()].copy_from_slice(data);
231+
self.storage
232+
.try_write(addr - offset as u32, &self.merge_buffer[..aligned_end])?;
233+
} else {
234+
self.storage.try_erase(page.start, page.end())?;
235+
self.merge_buffer[..S::ERASE_SIZE]
236+
.iter_mut()
237+
.skip(offset_into_page)
238+
.zip(data)
239+
.for_each(|(byte, input)| *byte = *input);
240+
self.storage
241+
.try_write(page.start, &self.merge_buffer[..S::ERASE_SIZE])?;
242+
}
243+
}
244+
Ok(())
245+
}
246+
}

0 commit comments

Comments
 (0)