diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index d4d4ab8d8..fc1ec6b01 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -49,6 +49,7 @@ futures = { version = "0.3.17", default-features = false } critical-section = "0.2.3" rand_core = "0.6.3" fixed = "1.10.0" +embedded-storage = "0.2.0" nrf52805-pac = { version = "0.10.1", optional = true, features = [ "rt" ] } nrf52810-pac = { version = "0.10.1", optional = true, features = [ "rt" ] } diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 317e6ed66..d68697936 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -4,6 +4,8 @@ pub use nrf52805_pac as pac; pub const EASY_DMA_SIZE: usize = (1 << 14) - 1; pub const FORCE_COPY_BUFFER_SIZE: usize = 256; +pub const FLASH_SIZE: usize = 192 * 1024; + embassy_hal_common::peripherals! { // RTC RTC0, @@ -12,6 +14,9 @@ embassy_hal_common::peripherals! { // WDT WDT, + // NVMC + NVMC, + // RNG RNG, diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index b26f30cbf..c119e7cdb 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -4,6 +4,8 @@ pub use nrf52810_pac as pac; pub const EASY_DMA_SIZE: usize = (1 << 10) - 1; pub const FORCE_COPY_BUFFER_SIZE: usize = 256; +pub const FLASH_SIZE: usize = 192 * 1024; + embassy_hal_common::peripherals! { // RTC RTC0, @@ -12,6 +14,9 @@ embassy_hal_common::peripherals! { // WDT WDT, + // NVMC + NVMC, + // RNG RNG, diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 9fbe3594e..3c9df40fe 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -4,6 +4,8 @@ pub use nrf52811_pac as pac; pub const EASY_DMA_SIZE: usize = (1 << 14) - 1; pub const FORCE_COPY_BUFFER_SIZE: usize = 256; +pub const FLASH_SIZE: usize = 192 * 1024; + embassy_hal_common::peripherals! { // RTC RTC0, @@ -12,6 +14,9 @@ embassy_hal_common::peripherals! { // WDT WDT, + // NVMC + NVMC, + // RNG RNG, diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index abbdba7ac..8a8e23a9e 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -4,6 +4,8 @@ pub use nrf52820_pac as pac; pub const EASY_DMA_SIZE: usize = (1 << 15) - 1; pub const FORCE_COPY_BUFFER_SIZE: usize = 512; +pub const FLASH_SIZE: usize = 256 * 1024; + embassy_hal_common::peripherals! { // RTC RTC0, @@ -12,6 +14,9 @@ embassy_hal_common::peripherals! { // WDT WDT, + // NVMC + NVMC, + // RNG RNG, diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index de6dd7a8c..c1b02a0bf 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -4,6 +4,12 @@ pub use nrf52832_pac as pac; pub const EASY_DMA_SIZE: usize = (1 << 8) - 1; pub const FORCE_COPY_BUFFER_SIZE: usize = 255; +// There are two variants. We set the higher size to make the entire flash +// usable in xxAA, but we'll probably split this in two cargi features later. +// nrf52832xxAA = 512kb +// nrf52832xxAB = 256kb +pub const FLASH_SIZE: usize = 512 * 1024; + embassy_hal_common::peripherals! { // RTC RTC0, @@ -13,6 +19,9 @@ embassy_hal_common::peripherals! { // WDT WDT, + // NVMC + NVMC, + // RNG RNG, diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index c5e1c9d33..4f5c26002 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -4,6 +4,8 @@ pub use nrf52833_pac as pac; pub const EASY_DMA_SIZE: usize = (1 << 16) - 1; pub const FORCE_COPY_BUFFER_SIZE: usize = 512; +pub const FLASH_SIZE: usize = 512 * 1024; + embassy_hal_common::peripherals! { // RTC RTC0, @@ -13,6 +15,9 @@ embassy_hal_common::peripherals! { // WDT WDT, + // NVMC + NVMC, + // RNG RNG, diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 76cabe6f1..1006acd95 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -4,6 +4,8 @@ pub use nrf52840_pac as pac; pub const EASY_DMA_SIZE: usize = (1 << 16) - 1; pub const FORCE_COPY_BUFFER_SIZE: usize = 512; +pub const FLASH_SIZE: usize = 1024 * 1024; + embassy_hal_common::peripherals! { // RTC RTC0, @@ -13,6 +15,9 @@ embassy_hal_common::peripherals! { // WDT WDT, + // NVMC + NVMC, + // RNG RNG, diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 961a97ce9..c21c4d398 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -29,6 +29,8 @@ pub mod buffered_uarte; pub mod gpio; #[cfg(feature = "gpiote")] pub mod gpiote; +#[cfg(not(feature = "nrf9160"))] +pub mod nvmc; pub mod ppi; #[cfg(not(any(feature = "nrf52805", feature = "nrf52820")))] pub mod pwm; diff --git a/embassy-nrf/src/nvmc.rs b/embassy-nrf/src/nvmc.rs new file mode 100644 index 000000000..312e1d5a6 --- /dev/null +++ b/embassy-nrf/src/nvmc.rs @@ -0,0 +1,123 @@ +//! Nvmcerature sensor interface. + +use crate::pac; +use crate::peripherals::NVMC; + +use core::marker::PhantomData; +use core::ptr; +use core::slice; +use embassy::util::Unborrow; +use embassy_hal_common::unborrow; +use embedded_storage::nor_flash::{MultiwriteNorFlash, NorFlash, ReadNorFlash}; + +const PAGE_SIZE: usize = 4096; +const FLASH_SIZE: usize = crate::chip::FLASH_SIZE; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + OutOfBounds, + Unaligned, +} + +pub struct Nvmc<'d> { + _p: PhantomData<&'d NVMC>, +} + +impl<'d> Nvmc<'d> { + pub fn new(_p: impl Unborrow + 'd) -> Self { + unborrow!(_p); + + Self { _p: PhantomData } + } + + fn regs() -> &'static pac::nvmc::RegisterBlock { + unsafe { &*pac::NVMC::ptr() } + } + + fn wait_ready(&mut self) { + let p = Self::regs(); + while p.ready.read().ready().is_busy() {} + } +} + +impl<'d> MultiwriteNorFlash for Nvmc<'d> {} + +impl<'d> ReadNorFlash for Nvmc<'d> { + type Error = Error; + + const READ_SIZE: usize = 1; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + if offset as usize >= FLASH_SIZE || offset as usize + bytes.len() > FLASH_SIZE { + return Err(Error::OutOfBounds); + } + + let flash_data = unsafe { slice::from_raw_parts(offset as *const u8, bytes.len()) }; + bytes.copy_from_slice(flash_data); + Ok(()) + } + + fn capacity(&self) -> usize { + FLASH_SIZE + } +} + +impl<'d> NorFlash for Nvmc<'d> { + const WRITE_SIZE: usize = 4; + const ERASE_SIZE: usize = PAGE_SIZE; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + if to < from || to as usize > FLASH_SIZE { + return Err(Error::OutOfBounds); + } + if from as usize % PAGE_SIZE != 0 || to as usize % PAGE_SIZE != 0 { + return Err(Error::Unaligned); + } + + let p = Self::regs(); + + p.config.write(|w| w.wen().een()); + self.wait_ready(); + + for page in (from..to).step_by(PAGE_SIZE) { + p.erasepage().write(|w| unsafe { w.bits(page) }); + self.wait_ready(); + } + + p.config.reset(); + self.wait_ready(); + + Ok(()) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + if offset as usize + bytes.len() > FLASH_SIZE { + return Err(Error::OutOfBounds); + } + if offset as usize % 4 != 0 || bytes.len() as usize % 4 != 0 { + return Err(Error::Unaligned); + } + + let p = Self::regs(); + + p.config.write(|w| w.wen().wen()); + self.wait_ready(); + + unsafe { + let p_src = bytes.as_ptr() as *const u32; + let p_dst = offset as *mut u32; + let words = bytes.len() / 4; + for i in 0..words { + let w = ptr::read_unaligned(p_src.add(i)); + ptr::write_volatile(p_dst.add(i), w); + self.wait_ready(); + } + } + + p.config.reset(); + self.wait_ready(); + + Ok(()) + } +} diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index b71dfa0d4..b89aa513f 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -30,3 +30,4 @@ embedded-hal = "0.2.6" panic-probe = { version = "0.2.0", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } rand = { version = "0.8.4", default-features = false } +embedded-storage = "0.2.0" diff --git a/examples/nrf/src/bin/nvmc.rs b/examples/nrf/src/bin/nvmc.rs new file mode 100644 index 000000000..f36895636 --- /dev/null +++ b/examples/nrf/src/bin/nvmc.rs @@ -0,0 +1,44 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +#[path = "../example_common.rs"] +mod example_common; +use embassy::executor::Spawner; +use embassy::time::{Duration, Timer}; +use embassy_nrf::nvmc::Nvmc; +use embassy_nrf::Peripherals; +use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use example_common::*; + +#[embassy::main] +async fn main(_spawner: Spawner, p: Peripherals) { + info!("Hello NVMC!"); + + // probe-run breaks without this, I'm not sure why. + Timer::after(Duration::from_secs(1)).await; + + let mut f = Nvmc::new(p.NVMC); + const ADDR: u32 = 0x80000; + + info!("Reading..."); + let mut buf = [0u8; 4]; + unwrap!(f.read(ADDR, &mut buf)); + info!("Read: {=[u8]:x}", buf); + + info!("Erasing..."); + unwrap!(f.erase(ADDR, ADDR + 4096)); + + info!("Reading..."); + let mut buf = [0u8; 4]; + unwrap!(f.read(ADDR, &mut buf)); + info!("Read: {=[u8]:x}", buf); + + info!("Writing..."); + unwrap!(f.write(ADDR, &[1, 2, 3, 4])); + + info!("Reading..."); + let mut buf = [0u8; 4]; + unwrap!(f.read(ADDR, &mut buf)); + info!("Read: {=[u8]:x}", buf); +}