diff options
| author | nerwalt <[email protected]> | 2025-08-07 09:07:29 -0600 |
|---|---|---|
| committer | nerwalt <[email protected]> | 2025-08-08 09:56:00 -0600 |
| commit | a165339276e751909a88f685760f4cabb71e50d1 (patch) | |
| tree | 23aedb8e0913e1f25a45576d51d71214815ba9ee /embassy-nrf | |
| parent | f2be66a5f94a655696407e2e7bc81c322aab3ea1 (diff) | |
Adds RRAMC support for the nrf54l15
Adds an Nvmc driver alias for compatibility
Diffstat (limited to 'embassy-nrf')
| -rw-r--r-- | embassy-nrf/src/chips/nrf54l15_app.rs | 8 | ||||
| -rw-r--r-- | embassy-nrf/src/lib.rs | 6 | ||||
| -rw-r--r-- | embassy-nrf/src/rramc.rs | 176 |
3 files changed, 188 insertions, 2 deletions
diff --git a/embassy-nrf/src/chips/nrf54l15_app.rs b/embassy-nrf/src/chips/nrf54l15_app.rs index b133eb565..3a4e1ae4a 100644 --- a/embassy-nrf/src/chips/nrf54l15_app.rs +++ b/embassy-nrf/src/chips/nrf54l15_app.rs | |||
| @@ -201,9 +201,15 @@ pub mod pac { | |||
| 201 | pub const EASY_DMA_SIZE: usize = (1 << 16) - 1; | 201 | pub const EASY_DMA_SIZE: usize = (1 << 16) - 1; |
| 202 | //pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; | 202 | //pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; |
| 203 | 203 | ||
| 204 | //pub const FLASH_SIZE: usize = 1024 * 1024; | 204 | // 1.5 MB NVM |
| 205 | #[allow(unused)] | ||
| 206 | pub const FLASH_SIZE: usize = 1536 * 1024; | ||
| 205 | 207 | ||
| 206 | embassy_hal_internal::peripherals! { | 208 | embassy_hal_internal::peripherals! { |
| 209 | #[cfg(feature = "_s")] | ||
| 210 | // RRAMC | ||
| 211 | RRAMC, | ||
| 212 | |||
| 207 | // GPIO port 0 | 213 | // GPIO port 0 |
| 208 | P0_00, | 214 | P0_00, |
| 209 | P0_01, | 215 | P0_01, |
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index ba8206d13..d3c4b3bb1 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs | |||
| @@ -98,8 +98,12 @@ pub mod ipc; | |||
| 98 | feature = "_nrf5340-app" | 98 | feature = "_nrf5340-app" |
| 99 | ))] | 99 | ))] |
| 100 | pub mod nfct; | 100 | pub mod nfct; |
| 101 | #[cfg(not(feature = "_nrf54l"))] // TODO | 101 | #[cfg(not(feature = "_nrf54l"))] |
| 102 | pub mod nvmc; | 102 | pub mod nvmc; |
| 103 | #[cfg(feature = "nrf54l15-app-s")] | ||
| 104 | pub mod rramc; | ||
| 105 | #[cfg(feature = "nrf54l15-app-s")] | ||
| 106 | pub use rramc as nvmc; | ||
| 103 | #[cfg(not(feature = "_nrf54l"))] // TODO | 107 | #[cfg(not(feature = "_nrf54l"))] // TODO |
| 104 | #[cfg(any( | 108 | #[cfg(any( |
| 105 | feature = "nrf52810", | 109 | feature = "nrf52810", |
diff --git a/embassy-nrf/src/rramc.rs b/embassy-nrf/src/rramc.rs new file mode 100644 index 000000000..7cb5660cb --- /dev/null +++ b/embassy-nrf/src/rramc.rs | |||
| @@ -0,0 +1,176 @@ | |||
| 1 | //! Resistive Random-Access Memory Controller driver. | ||
| 2 | |||
| 3 | use core::{ptr, slice}; | ||
| 4 | |||
| 5 | use embedded_storage::nor_flash::{ | ||
| 6 | ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, | ||
| 7 | }; | ||
| 8 | |||
| 9 | use crate::peripherals::RRAMC; | ||
| 10 | use crate::{pac, Peri}; | ||
| 11 | |||
| 12 | // | ||
| 13 | // Export Nvmc alias and page size for downstream compatibility | ||
| 14 | // | ||
| 15 | /// RRAM-backed `Nvmc` compatibile driver. | ||
| 16 | pub type Nvmc<'d> = Rramc<'d>; | ||
| 17 | /// Emulated page size. RRAM does not use pages. This exists only for downstream compatibility. | ||
| 18 | pub const PAGE_SIZE: usize = 4096; | ||
| 19 | |||
| 20 | // In bytes, one line is 128 bits | ||
| 21 | const WRITE_LINE_SIZE: usize = 16; | ||
| 22 | |||
| 23 | /// Size of RRAM flash in bytes. | ||
| 24 | pub const FLASH_SIZE: usize = crate::chip::FLASH_SIZE; | ||
| 25 | |||
| 26 | /// Error type for RRAMC operations. | ||
| 27 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
| 28 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 29 | pub enum Error { | ||
| 30 | /// Operation using a location not in flash. | ||
| 31 | OutOfBounds, | ||
| 32 | /// Unaligned operation or using unaligned buffers. | ||
| 33 | Unaligned, | ||
| 34 | } | ||
| 35 | |||
| 36 | impl NorFlashError for Error { | ||
| 37 | fn kind(&self) -> NorFlashErrorKind { | ||
| 38 | match self { | ||
| 39 | Self::OutOfBounds => NorFlashErrorKind::OutOfBounds, | ||
| 40 | Self::Unaligned => NorFlashErrorKind::NotAligned, | ||
| 41 | } | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | /// Resistive Random-Access Memory Controller (RRAMC) that implements the `embedded-storage` | ||
| 46 | /// traits. | ||
| 47 | pub struct Rramc<'d> { | ||
| 48 | _p: Peri<'d, RRAMC>, | ||
| 49 | } | ||
| 50 | |||
| 51 | impl<'d> Rramc<'d> { | ||
| 52 | /// Create Rramc driver. | ||
| 53 | pub fn new(_p: Peri<'d, RRAMC>) -> Self { | ||
| 54 | Self { _p } | ||
| 55 | } | ||
| 56 | |||
| 57 | fn regs() -> pac::rramc::Rramc { | ||
| 58 | pac::RRAMC | ||
| 59 | } | ||
| 60 | |||
| 61 | fn wait_ready(&mut self) { | ||
| 62 | let p = Self::regs(); | ||
| 63 | while !p.ready().read().ready() {} | ||
| 64 | } | ||
| 65 | |||
| 66 | fn wait_ready_write(&mut self) { | ||
| 67 | let p = Self::regs(); | ||
| 68 | while !p.readynext().read().readynext() {} | ||
| 69 | while !p.bufstatus().writebufempty().read().empty() {} | ||
| 70 | } | ||
| 71 | |||
| 72 | fn enable_read(&self) { | ||
| 73 | Self::regs().config().write(|w| w.set_wen(false)); | ||
| 74 | } | ||
| 75 | |||
| 76 | fn enable_write(&self) { | ||
| 77 | Self::regs().config().write(|w| w.set_wen(true)); | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | // | ||
| 82 | // RRAM is not NOR flash, but many crates require embedded-storage NorFlash traits. We therefore | ||
| 83 | // implement the traits for downstream compatibility. | ||
| 84 | // | ||
| 85 | |||
| 86 | impl<'d> MultiwriteNorFlash for Rramc<'d> {} | ||
| 87 | |||
| 88 | impl<'d> ErrorType for Rramc<'d> { | ||
| 89 | type Error = Error; | ||
| 90 | } | ||
| 91 | |||
| 92 | impl<'d> ReadNorFlash for Rramc<'d> { | ||
| 93 | const READ_SIZE: usize = 1; | ||
| 94 | |||
| 95 | fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { | ||
| 96 | if offset as usize >= FLASH_SIZE || offset as usize + bytes.len() > FLASH_SIZE { | ||
| 97 | return Err(Error::OutOfBounds); | ||
| 98 | } | ||
| 99 | |||
| 100 | let flash_data = unsafe { slice::from_raw_parts(offset as *const u8, bytes.len()) }; | ||
| 101 | bytes.copy_from_slice(flash_data); | ||
| 102 | Ok(()) | ||
| 103 | } | ||
| 104 | |||
| 105 | fn capacity(&self) -> usize { | ||
| 106 | FLASH_SIZE | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | impl<'d> NorFlash for Rramc<'d> { | ||
| 111 | const WRITE_SIZE: usize = WRITE_LINE_SIZE; | ||
| 112 | const ERASE_SIZE: usize = PAGE_SIZE; | ||
| 113 | |||
| 114 | // RRAM can overwrite in-place, so emulate page erases by overwriting the page bytes with 0xFF. | ||
| 115 | fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { | ||
| 116 | if to < from || to as usize > FLASH_SIZE { | ||
| 117 | return Err(Error::OutOfBounds); | ||
| 118 | } | ||
| 119 | if from as usize % Self::ERASE_SIZE != 0 || to as usize % Self::ERASE_SIZE != 0 { | ||
| 120 | return Err(Error::Unaligned); | ||
| 121 | } | ||
| 122 | |||
| 123 | self.enable_write(); | ||
| 124 | self.wait_ready(); | ||
| 125 | |||
| 126 | // Treat each emulated page separately so callers can rely on post‑erase read‑back | ||
| 127 | // returning 0xFF just like on real NOR flash. | ||
| 128 | let buf = [0xFFu8; Self::WRITE_SIZE]; | ||
| 129 | for page_addr in (from..to).step_by(Self::ERASE_SIZE) { | ||
| 130 | let page_end = page_addr + Self::ERASE_SIZE as u32; | ||
| 131 | for line_addr in (page_addr..page_end).step_by(Self::WRITE_SIZE) { | ||
| 132 | unsafe { | ||
| 133 | let src = buf.as_ptr() as *const u32; | ||
| 134 | let dst = line_addr as *mut u32; | ||
| 135 | for i in 0..(Self::WRITE_SIZE / 4) { | ||
| 136 | core::ptr::write_volatile(dst.add(i), core::ptr::read_unaligned(src.add(i))); | ||
| 137 | } | ||
| 138 | } | ||
| 139 | self.wait_ready_write(); | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | self.enable_read(); | ||
| 144 | self.wait_ready(); | ||
| 145 | Ok(()) | ||
| 146 | } | ||
| 147 | |||
| 148 | fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { | ||
| 149 | if offset as usize + bytes.len() > FLASH_SIZE { | ||
| 150 | return Err(Error::OutOfBounds); | ||
| 151 | } | ||
| 152 | if offset as usize % Self::WRITE_SIZE != 0 || bytes.len() % Self::WRITE_SIZE != 0 { | ||
| 153 | return Err(Error::Unaligned); | ||
| 154 | } | ||
| 155 | |||
| 156 | self.enable_write(); | ||
| 157 | self.wait_ready(); | ||
| 158 | |||
| 159 | unsafe { | ||
| 160 | let p_src = bytes.as_ptr() as *const u32; | ||
| 161 | let p_dst = offset as *mut u32; | ||
| 162 | let words = bytes.len() / 4; | ||
| 163 | for i in 0..words { | ||
| 164 | let w = ptr::read_unaligned(p_src.add(i)); | ||
| 165 | ptr::write_volatile(p_dst.add(i), w); | ||
| 166 | if (i + 1) % (Self::WRITE_SIZE / 4) == 0 { | ||
| 167 | self.wait_ready_write(); | ||
| 168 | } | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | self.enable_read(); | ||
| 173 | self.wait_ready(); | ||
| 174 | Ok(()) | ||
| 175 | } | ||
| 176 | } | ||
