Merge #444
444: nrf: add NVMC driver. r=lulf a=Dirbaio I haven't implemented `embassy_traits::Flash` because I want to change it to match embedded_storage, which is much better designed. Either way, NVMC can't do async anyway, so the best we could do is implementing the async trait in a blocking way... Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
This commit is contained in:
		| @@ -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" ] } | ||||
|   | ||||
| @@ -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, | ||||
|  | ||||
|   | ||||
| @@ -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, | ||||
|  | ||||
|   | ||||
| @@ -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, | ||||
|  | ||||
|   | ||||
| @@ -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, | ||||
|  | ||||
|   | ||||
| @@ -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, | ||||
|  | ||||
|   | ||||
| @@ -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, | ||||
|  | ||||
|   | ||||
| @@ -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, | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
							
								
								
									
										123
									
								
								embassy-nrf/src/nvmc.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								embassy-nrf/src/nvmc.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<Target = NVMC> + '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(()) | ||||
|     } | ||||
| } | ||||
| @@ -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" | ||||
|   | ||||
							
								
								
									
										44
									
								
								examples/nrf/src/bin/nvmc.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								examples/nrf/src/bin/nvmc.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -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); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user