aboutsummaryrefslogtreecommitdiff
path: root/embassy-nrf/src
diff options
context:
space:
mode:
authorUlf Lilleengen <[email protected]>2025-12-11 12:04:09 +0100
committerUlf Lilleengen <[email protected]>2025-12-11 12:22:12 +0100
commit55d0720b44cbdb2061866bf692de42e38e3d23b1 (patch)
tree2bf6597e8c13ed795be38da225833a8998eeb8a8 /embassy-nrf/src
parent32a1d0ef7ea52d3cee9959ff52d47fd13fc6b4b9 (diff)
feat: add support for buffered write mode for rram
Diffstat (limited to 'embassy-nrf/src')
-rw-r--r--embassy-nrf/src/rramc.rs173
1 files changed, 161 insertions, 12 deletions
diff --git a/embassy-nrf/src/rramc.rs b/embassy-nrf/src/rramc.rs
index 521ac4ee7..961e0a535 100644
--- a/embassy-nrf/src/rramc.rs
+++ b/embassy-nrf/src/rramc.rs
@@ -1,5 +1,6 @@
1//! Resistive Random-Access Memory Controller driver. 1//! Resistive Random-Access Memory Controller driver.
2 2
3use core::marker::PhantomData;
3use core::{ptr, slice}; 4use core::{ptr, slice};
4 5
5use embedded_storage::nor_flash::{ 6use embedded_storage::nor_flash::{
@@ -9,11 +10,28 @@ use embedded_storage::nor_flash::{
9use crate::peripherals::RRAMC; 10use crate::peripherals::RRAMC;
10use crate::{Peri, pac}; 11use crate::{Peri, pac};
11 12
13/// Unbuffered RRAMC mode.
14pub struct Unbuffered;
15
16/// Buffered RRAMC mode.
17pub struct Buffered<const BUFFER_SIZE_BYTES: usize>;
18
19trait SealedRramMode {}
20
21/// Operating modes for RRAMC
22#[allow(private_bounds)]
23pub trait RramMode: SealedRramMode {}
24
25impl SealedRramMode for Unbuffered {}
26impl RramMode for Unbuffered {}
27impl<const BUFFER_SIZE_BYTES: usize> SealedRramMode for Buffered<BUFFER_SIZE_BYTES> {}
28impl<const BUFFER_SIZE_BYTES: usize> RramMode for Buffered<BUFFER_SIZE_BYTES> {}
29
12// 30//
13// Export Nvmc alias and page size for downstream compatibility 31// Export Nvmc alias and page size for downstream compatibility
14// 32//
15/// RRAM-backed `Nvmc` compatibile driver. 33/// RRAM-backed `Nvmc` compatibile driver.
16pub type Nvmc<'d> = Rramc<'d>; 34pub type Nvmc<'d> = Rramc<'d, Unbuffered>;
17/// Emulated page size. RRAM does not use pages. This exists only for downstream compatibility. 35/// Emulated page size. RRAM does not use pages. This exists only for downstream compatibility.
18pub const PAGE_SIZE: usize = 4096; 36pub const PAGE_SIZE: usize = 4096;
19 37
@@ -44,16 +62,27 @@ impl NorFlashError for Error {
44 62
45/// Resistive Random-Access Memory Controller (RRAMC) that implements the `embedded-storage` 63/// Resistive Random-Access Memory Controller (RRAMC) that implements the `embedded-storage`
46/// traits. 64/// traits.
47pub struct Rramc<'d> { 65pub struct Rramc<'d, MODE: RramMode> {
48 _p: Peri<'d, RRAMC>, 66 _p: Peri<'d, RRAMC>,
67 _d: PhantomData<MODE>,
68}
69
70impl<'d> Rramc<'d, Unbuffered> {
71 /// Create Rramc driver.
72 pub fn new(_p: Peri<'d, RRAMC>) -> Rramc<'d, Unbuffered> {
73 Self { _p, _d: PhantomData }
74 }
49} 75}
50 76
51impl<'d> Rramc<'d> { 77impl<'d, const BUFFER_SIZE_BYTES: usize> Rramc<'d, Buffered<BUFFER_SIZE_BYTES>> {
52 /// Create Rramc driver. 78 /// Create Rramc driver.
53 pub fn new(_p: Peri<'d, RRAMC>) -> Self { 79 pub fn new_buffered(_p: Peri<'d, RRAMC>) -> Rramc<'d, Buffered<BUFFER_SIZE_BYTES>> {
54 Self { _p } 80 assert!(BUFFER_SIZE_BYTES > 0 && BUFFER_SIZE_BYTES <= 512);
81 Self { _p, _d: PhantomData }
55 } 82 }
83}
56 84
85impl<'d, MODE: RramMode> Rramc<'d, MODE> {
57 fn regs() -> pac::rramc::Rramc { 86 fn regs() -> pac::rramc::Rramc {
58 pac::RRAMC 87 pac::RRAMC
59 } 88 }
@@ -63,18 +92,48 @@ impl<'d> Rramc<'d> {
63 while !p.ready().read().ready() {} 92 while !p.ready().read().ready() {}
64 } 93 }
65 94
95 fn enable_read(&self) {
96 Self::regs().config().write(|w| w.set_wen(false));
97 }
98
99 fn finish_write(&mut self) {
100 self.enable_read();
101 self.wait_ready();
102 }
103}
104
105impl<'d> Rramc<'d, Unbuffered> {
106 fn wait_ready_write(&mut self) {
107 let p = Self::regs();
108 while !p.readynext().read().readynext() {}
109 }
110
111 fn enable_write(&self) {
112 Self::regs().config().write(|w| {
113 w.set_wen(true);
114 w.set_writebufsize(pac::rramc::vals::Writebufsize::UNBUFFERED)
115 });
116 }
117}
118
119impl<'d, const SIZE: usize> Rramc<'d, Buffered<SIZE>> {
66 fn wait_ready_write(&mut self) { 120 fn wait_ready_write(&mut self) {
67 let p = Self::regs(); 121 let p = Self::regs();
68 while !p.readynext().read().readynext() {} 122 while !p.readynext().read().readynext() {}
69 while !p.bufstatus().writebufempty().read().empty() {} 123 while !p.bufstatus().writebufempty().read().empty() {}
70 } 124 }
71 125
72 fn enable_read(&self) { 126 fn commit(&self) {
73 Self::regs().config().write(|w| w.set_wen(false)); 127 let p = Self::regs();
128 p.tasks_commitwritebuf().write_value(1);
129 while !p.bufstatus().writebufempty().read().empty() {}
74 } 130 }
75 131
76 fn enable_write(&self) { 132 fn enable_write(&self) {
77 Self::regs().config().write(|w| w.set_wen(true)); 133 Self::regs().config().write(|w| {
134 w.set_wen(true);
135 w.set_writebufsize(pac::rramc::vals::Writebufsize::from_bits(SIZE as _))
136 });
78 } 137 }
79} 138}
80 139
@@ -83,13 +142,13 @@ impl<'d> Rramc<'d> {
83// implement the traits for downstream compatibility. 142// implement the traits for downstream compatibility.
84// 143//
85 144
86impl<'d> MultiwriteNorFlash for Rramc<'d> {} 145impl<'d> MultiwriteNorFlash for Rramc<'d, Unbuffered> {}
87 146
88impl<'d> ErrorType for Rramc<'d> { 147impl<'d> ErrorType for Rramc<'d, Unbuffered> {
89 type Error = Error; 148 type Error = Error;
90} 149}
91 150
92impl<'d> ReadNorFlash for Rramc<'d> { 151impl<'d> ReadNorFlash for Rramc<'d, Unbuffered> {
93 const READ_SIZE: usize = 1; 152 const READ_SIZE: usize = 1;
94 153
95 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { 154 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
@@ -107,7 +166,7 @@ impl<'d> ReadNorFlash for Rramc<'d> {
107 } 166 }
108} 167}
109 168
110impl<'d> NorFlash for Rramc<'d> { 169impl<'d> NorFlash for Rramc<'d, Unbuffered> {
111 const WRITE_SIZE: usize = WRITE_LINE_SIZE; 170 const WRITE_SIZE: usize = WRITE_LINE_SIZE;
112 const ERASE_SIZE: usize = PAGE_SIZE; 171 const ERASE_SIZE: usize = PAGE_SIZE;
113 172
@@ -139,11 +198,100 @@ impl<'d> NorFlash for Rramc<'d> {
139 self.wait_ready_write(); 198 self.wait_ready_write();
140 } 199 }
141 } 200 }
201 self.finish_write();
202 Ok(())
203 }
204
205 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
206 if offset as usize + bytes.len() > FLASH_SIZE {
207 return Err(Error::OutOfBounds);
208 }
209 if offset as usize % Self::WRITE_SIZE != 0 || bytes.len() % Self::WRITE_SIZE != 0 {
210 return Err(Error::Unaligned);
211 }
212
213 self.enable_write();
214 self.wait_ready();
215
216 unsafe {
217 let p_src = bytes.as_ptr() as *const u32;
218 let p_dst = offset as *mut u32;
219 let words = bytes.len() / 4;
220 for i in 0..words {
221 let w = ptr::read_unaligned(p_src.add(i));
222 ptr::write_volatile(p_dst.add(i), w);
223 if (i + 1) % (Self::WRITE_SIZE / 4) == 0 {
224 self.wait_ready_write();
225 }
226 }
227 }
142 228
143 self.enable_read(); 229 self.enable_read();
144 self.wait_ready(); 230 self.wait_ready();
145 Ok(()) 231 Ok(())
146 } 232 }
233}
234
235impl<'d, const BUFFER_SIZE_BYTES: usize> MultiwriteNorFlash for Rramc<'d, Buffered<BUFFER_SIZE_BYTES>> {}
236
237impl<'d, const BUFFER_SIZE_BYTES: usize> ErrorType for Rramc<'d, Buffered<BUFFER_SIZE_BYTES>> {
238 type Error = Error;
239}
240
241impl<'d, const BUFFER_SIZE_BYTES: usize> ReadNorFlash for Rramc<'d, Buffered<BUFFER_SIZE_BYTES>> {
242 const READ_SIZE: usize = 1;
243
244 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
245 if offset as usize >= FLASH_SIZE || offset as usize + bytes.len() > FLASH_SIZE {
246 return Err(Error::OutOfBounds);
247 }
248
249 let flash_data = unsafe { slice::from_raw_parts(offset as *const u8, bytes.len()) };
250 bytes.copy_from_slice(flash_data);
251 Ok(())
252 }
253
254 fn capacity(&self) -> usize {
255 FLASH_SIZE
256 }
257}
258
259impl<'d, const BUFFER_SIZE_BYTES: usize> NorFlash for Rramc<'d, Buffered<BUFFER_SIZE_BYTES>> {
260 const WRITE_SIZE: usize = WRITE_LINE_SIZE;
261 const ERASE_SIZE: usize = PAGE_SIZE;
262
263 // RRAM can overwrite in-place, so emulate page erases by overwriting the page bytes with 0xFF.
264 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
265 if to < from || to as usize > FLASH_SIZE {
266 return Err(Error::OutOfBounds);
267 }
268 if from as usize % Self::ERASE_SIZE != 0 || to as usize % Self::ERASE_SIZE != 0 {
269 return Err(Error::Unaligned);
270 }
271
272 self.enable_write();
273 self.wait_ready();
274
275 // Treat each emulated page separately so callers can rely on post‑erase read‑back
276 // returning 0xFF just like on real NOR flash.
277 let buf = [0xFFu8; BUFFER_SIZE_BYTES];
278 for page_addr in (from..to).step_by(Self::ERASE_SIZE) {
279 let page_end = page_addr + Self::ERASE_SIZE as u32;
280 for line_addr in (page_addr..page_end).step_by(BUFFER_SIZE_BYTES) {
281 unsafe {
282 let src = buf.as_ptr() as *const u32;
283 let dst = line_addr as *mut u32;
284 for i in 0..(Self::WRITE_SIZE / 4) {
285 core::ptr::write_volatile(dst.add(i), core::ptr::read_unaligned(src.add(i)));
286 }
287 }
288 self.wait_ready_write();
289 }
290 }
291 self.commit();
292 self.finish_write();
293 Ok(())
294 }
147 295
148 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { 296 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
149 if offset as usize + bytes.len() > FLASH_SIZE { 297 if offset as usize + bytes.len() > FLASH_SIZE {
@@ -169,6 +317,7 @@ impl<'d> NorFlash for Rramc<'d> {
169 } 317 }
170 } 318 }
171 319
320 self.commit();
172 self.enable_read(); 321 self.enable_read();
173 self.wait_ready(); 322 self.wait_ready();
174 Ok(()) 323 Ok(())