diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e33a40ef..ff30a09a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -54,6 +54,9 @@ jobs: - package: embassy-nrf target: thumbv7em-none-eabi features: nrf52833 + - package: embassy-nrf + target: thumbv8m.main-none-eabihf + features: nrf9160 - package: embassy-nrf target: thumbv7em-none-eabi features: nrf52840 diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index d472069e..7c5f8d32 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -25,6 +25,7 @@ nrf52820 = ["nrf52820-pac"] nrf52832 = ["nrf52832-pac"] nrf52833 = ["nrf52833-pac"] nrf52840 = ["nrf52840-pac"] +nrf9160 = ["nrf9160-pac"] # Features starting with `_` are for internal use only. They're not intended # to be enabled by other crates, and are not covered by semver guarantees. @@ -55,3 +56,4 @@ nrf52820-pac = { version = "0.10.1", optional = true, features = [ "rt" ] } nrf52832-pac = { version = "0.10.1", optional = true, features = [ "rt" ] } nrf52833-pac = { version = "0.10.1", optional = true, features = [ "rt" ] } nrf52840-pac = { version = "0.10.1", optional = true, features = [ "rt" ] } +nrf9160-pac = { version = "0.10.1", optional = true, features = [ "rt" ] } diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs new file mode 100644 index 00000000..42053d08 --- /dev/null +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -0,0 +1,248 @@ +#[allow(unused_imports)] +pub mod pac { + // The nRF9160 has a secure and non-secure (NS) mode. + // For now we only support the NS mode, but those peripherals have `_ns` appended to them. + // To avoid cfg spam, weŕe going to rename the ones we use here. + #[rustfmt::skip] + pub(crate) use nrf9160_pac::{ + p0_ns as p0, + pwm0_ns as pwm0, + rtc0_ns as rtc0, + spim0_ns as spim0, + timer0_ns as timer0, + twim0_ns as twim0, + uarte0_ns as uarte0, + DPPIC_NS as PPI, + GPIOTE1_NS as GPIOTE, + P0_NS as P0, + RTC1_NS as RTC1, + WDT_NS as WDT, + saadc_ns as saadc, + SAADC_NS as SAADC, + CLOCK_NS as CLOCK, + }; + + pub use nrf9160_pac::*; +} + +/// The maximum buffer size that the EasyDMA can send/recv in one operation. +pub const EASY_DMA_SIZE: usize = (1 << 13) - 1; +pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; + +embassy_hal_common::peripherals! { + // RTC + RTC0, + RTC1, + + // WDT + WDT, + + // UARTE, TWI & SPI + UARTETWISPI0, + UARTETWISPI1, + UARTETWISPI2, + UARTETWISPI3, + + // SAADC + SAADC, + + // PWM + PWM0, + PWM1, + PWM2, + PWM3, + + // TIMER + TIMER0, + TIMER1, + TIMER2, + + // GPIOTE + GPIOTE_CH0, + GPIOTE_CH1, + GPIOTE_CH2, + GPIOTE_CH3, + GPIOTE_CH4, + GPIOTE_CH5, + GPIOTE_CH6, + GPIOTE_CH7, + + // PPI + PPI_CH0, + PPI_CH1, + PPI_CH2, + PPI_CH3, + PPI_CH4, + PPI_CH5, + PPI_CH6, + PPI_CH7, + PPI_CH8, + PPI_CH9, + PPI_CH10, + PPI_CH11, + PPI_CH12, + PPI_CH13, + PPI_CH14, + PPI_CH15, + + PPI_GROUP0, + PPI_GROUP1, + PPI_GROUP2, + PPI_GROUP3, + PPI_GROUP4, + PPI_GROUP5, + + // GPIO port 0 + P0_00, + P0_01, + P0_02, + P0_03, + P0_04, + P0_05, + P0_06, + P0_07, + P0_08, + P0_09, + P0_10, + P0_11, + P0_12, + P0_13, + P0_14, + P0_15, + P0_16, + P0_17, + P0_18, + P0_19, + P0_20, + P0_21, + P0_22, + P0_23, + P0_24, + P0_25, + P0_26, + P0_27, + P0_28, + P0_29, + P0_30, + P0_31, +} + +impl_uarte!(UARTETWISPI0, UARTE0_NS, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); +impl_uarte!(UARTETWISPI1, UARTE1_NS, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); +impl_uarte!(UARTETWISPI2, UARTE2_NS, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); +impl_uarte!(UARTETWISPI3, UARTE3_NS, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); + +impl_spim!(UARTETWISPI0, SPIM0_NS, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); +impl_spim!(UARTETWISPI1, SPIM1_NS, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); +impl_spim!(UARTETWISPI2, SPIM2_NS, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); +impl_spim!(UARTETWISPI3, SPIM3_NS, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); + +impl_twim!(UARTETWISPI0, TWIM0_NS, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); +impl_twim!(UARTETWISPI1, TWIM1_NS, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); +impl_twim!(UARTETWISPI2, TWIM2_NS, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); +impl_twim!(UARTETWISPI3, TWIM3_NS, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); + +impl_pwm!(PWM0, PWM0_NS, PWM0); +impl_pwm!(PWM1, PWM1_NS, PWM1); +impl_pwm!(PWM2, PWM2_NS, PWM2); +impl_pwm!(PWM3, PWM3_NS, PWM3); + +impl_timer!(TIMER0, TIMER0_NS, TIMER0); +impl_timer!(TIMER1, TIMER1_NS, TIMER1); +impl_timer!(TIMER2, TIMER2_NS, TIMER2); + +impl_pin!(P0_00, 0, 0); +impl_pin!(P0_01, 0, 1); +impl_pin!(P0_02, 0, 2); +impl_pin!(P0_03, 0, 3); +impl_pin!(P0_04, 0, 4); +impl_pin!(P0_05, 0, 5); +impl_pin!(P0_06, 0, 6); +impl_pin!(P0_07, 0, 7); +impl_pin!(P0_08, 0, 8); +impl_pin!(P0_09, 0, 9); +impl_pin!(P0_10, 0, 10); +impl_pin!(P0_11, 0, 11); +impl_pin!(P0_12, 0, 12); +impl_pin!(P0_13, 0, 13); +impl_pin!(P0_14, 0, 14); +impl_pin!(P0_15, 0, 15); +impl_pin!(P0_16, 0, 16); +impl_pin!(P0_17, 0, 17); +impl_pin!(P0_18, 0, 18); +impl_pin!(P0_19, 0, 19); +impl_pin!(P0_20, 0, 20); +impl_pin!(P0_21, 0, 21); +impl_pin!(P0_22, 0, 22); +impl_pin!(P0_23, 0, 23); +impl_pin!(P0_24, 0, 24); +impl_pin!(P0_25, 0, 25); +impl_pin!(P0_26, 0, 26); +impl_pin!(P0_27, 0, 27); +impl_pin!(P0_28, 0, 28); +impl_pin!(P0_29, 0, 29); +impl_pin!(P0_30, 0, 30); +impl_pin!(P0_31, 0, 31); + +impl_ppi_channel!(PPI_CH0, 0); +impl_ppi_channel!(PPI_CH1, 1); +impl_ppi_channel!(PPI_CH2, 2); +impl_ppi_channel!(PPI_CH3, 3); +impl_ppi_channel!(PPI_CH4, 4); +impl_ppi_channel!(PPI_CH5, 5); +impl_ppi_channel!(PPI_CH6, 6); +impl_ppi_channel!(PPI_CH7, 7); +impl_ppi_channel!(PPI_CH8, 8); +impl_ppi_channel!(PPI_CH9, 9); +impl_ppi_channel!(PPI_CH10, 10); +impl_ppi_channel!(PPI_CH11, 11); +impl_ppi_channel!(PPI_CH12, 12); +impl_ppi_channel!(PPI_CH13, 13); +impl_ppi_channel!(PPI_CH14, 14); +impl_ppi_channel!(PPI_CH15, 15); + +impl_saadc_input!(P0_13, ANALOGINPUT0); +impl_saadc_input!(P0_14, ANALOGINPUT1); +impl_saadc_input!(P0_15, ANALOGINPUT2); +impl_saadc_input!(P0_16, ANALOGINPUT3); +impl_saadc_input!(P0_17, ANALOGINPUT4); +impl_saadc_input!(P0_18, ANALOGINPUT5); +impl_saadc_input!(P0_19, ANALOGINPUT6); +impl_saadc_input!(P0_20, ANALOGINPUT7); + +pub mod irqs { + use crate::pac::Interrupt as InterruptEnum; + use embassy_macros::interrupt_declare as declare; + + declare!(SPU); + declare!(CLOCK_POWER); + declare!(UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); + declare!(UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); + declare!(UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); + declare!(UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); + declare!(GPIOTE0); + declare!(SAADC); + declare!(TIMER0); + declare!(TIMER1); + declare!(TIMER2); + declare!(RTC0); + declare!(RTC1); + declare!(WDT); + declare!(EGU0); + declare!(EGU1); + declare!(EGU2); + declare!(EGU3); + declare!(EGU4); + declare!(EGU5); + declare!(PWM0); + declare!(PWM1); + declare!(PWM2); + declare!(PDM); + declare!(PWM3); + declare!(I2S); + declare!(IPC); + declare!(FPU); + declare!(GPIOTE1); + declare!(KMU); + declare!(CRYPTOCELL); +} diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index e30df7e7..be0fac2b 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -17,7 +17,7 @@ use self::sealed::Pin as _; /// A GPIO port with up to 32 pins. #[derive(Debug, Eq, PartialEq)] pub enum Port { - /// Port 0, available on all nRF52 and nRF51 MCUs. + /// Port 0, available on nRF9160 and all nRF52 and nRF51 MCUs. Port0, /// Port 1, only available on some nRF52 MCUs. diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 25d09db4..bab49ceb 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -55,7 +55,11 @@ pub(crate) fn init(irq_prio: crate::interrupt::Priority) { // Enable interrupts + #[cfg(not(feature = "nrf9160"))] let irq = unsafe { interrupt::GPIOTE::steal() }; + #[cfg(feature = "nrf9160")] + let irq = unsafe { interrupt::GPIOTE1::steal() }; + irq.unpend(); irq.set_priority(irq_prio); irq.enable(); @@ -65,8 +69,19 @@ pub(crate) fn init(irq_prio: crate::interrupt::Priority) { g.intenset.write(|w| w.port().set()); } +#[cfg(not(feature = "nrf9160"))] #[interrupt] -unsafe fn GPIOTE() { +fn GPIOTE() { + unsafe { handle_gpiote_interrupt() }; +} + +#[cfg(feature = "nrf9160")] +#[interrupt] +fn GPIOTE1() { + unsafe { handle_gpiote_interrupt() }; +} + +unsafe fn handle_gpiote_interrupt() { let g = &*pac::GPIOTE::ptr(); for i in 0..CHANNEL_COUNT { diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 9a0e9c3a..00c719a1 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -34,6 +34,7 @@ pub mod ppi; pub mod pwm; #[cfg(feature = "nrf52840")] pub mod qspi; +#[cfg(not(feature = "nrf9160"))] pub mod rng; #[cfg(not(feature = "nrf52820"))] pub mod saadc; @@ -65,6 +66,9 @@ mod chip; #[cfg(feature = "nrf52840")] #[path = "chips/nrf52840.rs"] mod chip; +#[cfg(feature = "nrf9160")] +#[path = "chips/nrf9160.rs"] +mod chip; pub use chip::EASY_DMA_SIZE; @@ -73,6 +77,7 @@ pub use chip::pac; #[cfg(not(feature = "unstable-pac"))] pub(crate) use chip::pac; +use crate::pac::CLOCK; pub use chip::{peripherals, Peripherals}; pub mod interrupt { @@ -91,9 +96,12 @@ pub mod config { pub enum LfclkSource { InternalRC, + #[cfg(not(feature = "nrf9160"))] Synthesized, ExternalXtal, + #[cfg(not(feature = "nrf9160"))] ExternalLowSwing, + #[cfg(not(feature = "nrf9160"))] ExternalFullSwing, } @@ -129,7 +137,7 @@ pub fn init(config: config::Config) -> Peripherals { // before doing anything important. let peripherals = Peripherals::take(); - let r = unsafe { &*pac::CLOCK::ptr() }; + let r = unsafe { &*CLOCK::ptr() }; // Start HFCLK. match config.hfclk_source { @@ -143,6 +151,7 @@ pub fn init(config: config::Config) -> Peripherals { } // Configure LFCLK. + #[cfg(not(feature = "nrf9160"))] match config.lfclk_source { config::LfclkSource::InternalRC => r.lfclksrc.write(|w| w.src().rc()), config::LfclkSource::Synthesized => r.lfclksrc.write(|w| w.src().synth()), @@ -162,6 +171,11 @@ pub fn init(config: config::Config) -> Peripherals { w }), } + #[cfg(feature = "nrf9160")] + match config.lfclk_source { + config::LfclkSource::InternalRC => r.lfclksrc.write(|w| w.src().lfrc()), + config::LfclkSource::ExternalXtal => r.lfclksrc.write(|w| w.src().lfxo()), + } // Start LFCLK. // Datasheet says this could take 100us from synth source diff --git a/embassy-nrf/src/ppi.rs b/embassy-nrf/src/ppi.rs index 1b12b1e9..f5e06c69 100644 --- a/embassy-nrf/src/ppi.rs +++ b/embassy-nrf/src/ppi.rs @@ -11,13 +11,12 @@ //! On nRF52 devices, there is also a fork task endpoint, where the user can configure one more task //! to be triggered by the same event, even fixed PPI channels have a configurable fork task. +use crate::{pac, peripherals}; use core::marker::PhantomData; use core::ptr::NonNull; use embassy::util::Unborrow; use embassy_hal_common::{unborrow, unsafe_impl_unborrow}; -use crate::{pac, peripherals}; - // ====================== // driver @@ -29,11 +28,13 @@ pub struct Ppi<'d, C: Channel> { impl<'d, C: Channel> Ppi<'d, C> { pub fn new(ch: impl Unborrow + 'd) -> Self { unborrow!(ch); + + #[allow(unused_mut)] let mut this = Self { ch, phantom: PhantomData, }; - #[cfg(not(feature = "nrf51"))] + #[cfg(not(any(feature = "nrf51", feature = "nrf9160")))] this.clear_fork_task(); this } @@ -52,7 +53,7 @@ impl<'d, C: Channel> Ppi<'d, C> { .write(|w| unsafe { w.bits(1 << self.ch.number()) }); } - #[cfg(not(feature = "nrf51"))] + #[cfg(not(any(feature = "nrf51", feature = "nrf9160")))] /// Sets the fork task that must be triggered when the configured event occurs. The user must /// provide a reference to the task. pub fn set_fork_task(&mut self, task: Task) { @@ -62,12 +63,25 @@ impl<'d, C: Channel> Ppi<'d, C> { .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) }) } - #[cfg(not(feature = "nrf51"))] + #[cfg(not(any(feature = "nrf51", feature = "nrf9160")))] /// Clear the fork task endpoint. Previously set task will no longer be triggered. pub fn clear_fork_task(&mut self) { let r = unsafe { &*pac::PPI::ptr() }; r.fork[self.ch.number()].tep.write(|w| unsafe { w.bits(0) }) } + + #[cfg(feature = "nrf9160")] + /// Sets the fork task that must be triggered when the configured event occurs. The user must + /// provide a reference to the task. + pub fn set_fork_task(&mut self, _task: Task) { + todo!("Tasks not yet implemented for nrf9160"); + } + + #[cfg(feature = "nrf9160")] + /// Clear the fork task endpoint. Previously set task will no longer be triggered. + pub fn clear_fork_task(&mut self) { + todo!("Tasks not yet implemented for nrf9160"); + } } impl<'d, C: Channel> Drop for Ppi<'d, C> { @@ -76,6 +90,7 @@ impl<'d, C: Channel> Drop for Ppi<'d, C> { } } +#[cfg(not(feature = "nrf9160"))] impl<'d, C: ConfigurableChannel> Ppi<'d, C> { /// Sets the task to be triggered when the configured event occurs. pub fn set_task(&mut self, task: Task) { @@ -94,6 +109,19 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C> { } } +#[cfg(feature = "nrf9160")] +impl<'d, C: ConfigurableChannel> Ppi<'d, C> { + /// Sets the task to be triggered when the configured event occurs. + pub fn set_task(&mut self, _task: Task) { + todo!("Tasks not yet implemented for nrf9160") + } + + /// Sets the event that will trigger the chosen task(s). + pub fn set_event(&mut self, _event: Event) { + todo!("Events not yet implemented for nrf9160") + } +} + // ====================== // traits diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 32042606..024a86c9 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -68,8 +68,6 @@ impl<'d, T: Instance> Uarte<'d, T> { let r = T::regs(); - assert!(r.enable.read().enable().is_disabled()); - rxd.conf().write(|w| w.input().connect().drive().h0h1()); r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) }); @@ -114,6 +112,7 @@ impl<'d, T: Instance> Uarte<'d, T> { irq.enable(); // Enable + Self::apply_workaround_for_enable_anomaly(); r.enable.write(|w| w.enable().enabled()); Self { @@ -121,6 +120,61 @@ impl<'d, T: Instance> Uarte<'d, T> { } } + #[cfg(not(any(feature = "nrf9160", feature = "nrf5340")))] + fn apply_workaround_for_enable_anomaly() { + // Do nothing + } + + #[cfg(any(feature = "nrf9160", feature = "nrf5340"))] + fn apply_workaround_for_enable_anomaly() { + use core::ops::Deref; + + let r = T::regs(); + + // Apply workaround for anomalies: + // - nRF9160 - anomaly 23 + // - nRF5340 - anomaly 44 + let rxenable_reg: *const u32 = ((r.deref() as *const _ as usize) + 0x564) as *const u32; + let txenable_reg: *const u32 = ((r.deref() as *const _ as usize) + 0x568) as *const u32; + + // NB Safety: This is taken from Nordic's driver - + // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197 + if unsafe { core::ptr::read_volatile(txenable_reg) } == 1 { + r.tasks_stoptx.write(|w| unsafe { w.bits(1) }); + } + + // NB Safety: This is taken from Nordic's driver - + // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197 + if unsafe { core::ptr::read_volatile(rxenable_reg) } == 1 { + r.enable.write(|w| w.enable().enabled()); + r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); + + let mut workaround_succeded = false; + // The UARTE is able to receive up to four bytes after the STOPRX task has been triggered. + // On lowest supported baud rate (1200 baud), with parity bit and two stop bits configured + // (resulting in 12 bits per data byte sent), this may take up to 40 ms. + for _ in 0..40000 { + // NB Safety: This is taken from Nordic's driver - + // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197 + if unsafe { core::ptr::read_volatile(rxenable_reg) } == 0 { + workaround_succeded = true; + break; + } else { + // Need to sleep for 1us here + } + } + + if !workaround_succeded { + panic!("Failed to apply workaround for UART"); + } + + let errors = r.errorsrc.read().bits(); + // NB Safety: safe to write back the bits we just read to clear them + r.errorsrc.write(|w| unsafe { w.bits(errors) }); + r.enable.write(|w| w.enable().disabled()); + } + } + fn on_interrupt(_: *mut ()) { let r = T::regs(); let s = T::state(); diff --git a/embassy-nrf/src/wdt.rs b/embassy-nrf/src/wdt.rs index cd62d0d6..eddfa758 100644 --- a/embassy-nrf/src/wdt.rs +++ b/embassy-nrf/src/wdt.rs @@ -58,7 +58,12 @@ impl Watchdog { let crv = config.timeout_ticks.max(MIN_TICKS); let rren = (1u32 << N) - 1; - if r.runstatus.read().runstatus().bit() { + #[cfg(not(feature = "nrf9160"))] + let runstatus = r.runstatus.read().runstatus().bit(); + #[cfg(feature = "nrf9160")] + let runstatus = r.runstatus.read().runstatuswdt().bit(); + + if runstatus { let curr_config = r.config.read(); if curr_config.halt().bit() != config.run_during_debug_halt || curr_config.sleep().bit() != config.run_during_sleep diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 8f347343..00901f82 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -3,4 +3,4 @@ [toolchain] channel = "nightly-2021-08-18" components = [ "rust-src", "rustfmt" ] -targets = [ "thumbv7em-none-eabi", "thumbv7m-none-eabi", "thumbv6m-none-eabi", "thumbv7em-none-eabihf", "wasm32-unknown-unknown" ] +targets = [ "thumbv7em-none-eabi", "thumbv7m-none-eabi", "thumbv6m-none-eabi", "thumbv7em-none-eabihf", "thumbv8m.main-none-eabihf", "wasm32-unknown-unknown" ]