nrf53: add WDT support

This commit is contained in:
Matthew Tran 2025-04-20 20:14:13 -05:00
parent 9d62fba7d2
commit f535acfca0
17 changed files with 106 additions and 42 deletions

1
ci.sh
View File

@ -188,6 +188,7 @@ cargo batch \
--- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features 'embassy-rp/rp2040' \
--- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features 'embassy-rp/rp2040' \
--- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
--- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf5340-app-s \
--- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
--- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9120-ns \
--- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9151-ns \

View File

@ -8,7 +8,6 @@ pub use embassy_boot::{
FirmwareUpdater, FirmwareUpdaterConfig,
};
use embassy_nrf::nvmc::PAGE_SIZE;
use embassy_nrf::peripherals::WDT;
use embassy_nrf::{wdt, Peri};
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
@ -113,7 +112,7 @@ pub struct WatchdogFlash<FLASH> {
impl<FLASH> WatchdogFlash<FLASH> {
/// Start a new watchdog with a given flash and WDT peripheral and a timeout
pub fn start(flash: FLASH, wdt: Peri<'static, WDT>, config: wdt::Config) -> Self {
pub fn start(flash: FLASH, wdt: Peri<'static, impl wdt::Instance>, config: wdt::Config) -> Self {
let (_wdt, [wdt]) = match wdt::Watchdog::try_new(wdt, config) {
Ok(x) => x,
Err(_) => {

View File

@ -109,7 +109,7 @@ nrf9161-ns = ["nrf9120-ns"]
# 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.
_nrf5340-app = ["_nrf5340", "nrf-pac/nrf5340-app"]
_nrf5340-app = ["_nrf5340", "_multi_wdt", "nrf-pac/nrf5340-app"]
_nrf5340-net = ["_nrf5340", "nrf-pac/nrf5340-net"]
_nrf5340 = ["_gpio-p1", "_dppi"]
_nrf54l15-app = ["_nrf54l15", "nrf-pac/nrf54l15-app"]
@ -136,6 +136,9 @@ _gpio-p2 = []
# Errata workarounds
_nrf52832_anomaly_109 = []
# watchdog timer
_multi_wdt = []
[dependencies]
embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true }
embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true }

View File

@ -145,6 +145,8 @@ impl_pin!(P0_31, 0, 31);
impl_radio!(RADIO, RADIO, RADIO);
impl_wdt!(WDT, WDT, WDT, 0);
embassy_hal_internal::interrupt_mod!(
CLOCK_POWER,
RADIO,

View File

@ -221,6 +221,8 @@ impl_radio!(RADIO, RADIO, RADIO);
impl_egu!(EGU0, EGU0, EGU0_SWI0);
impl_egu!(EGU1, EGU1, EGU1_SWI1);
impl_wdt!(WDT, WDT, WDT, 0);
embassy_hal_internal::interrupt_mod!(
CLOCK_POWER,
RADIO,

View File

@ -247,6 +247,8 @@ impl_radio!(RADIO, RADIO, RADIO);
impl_egu!(EGU0, EGU0, EGU0_SWI0);
impl_egu!(EGU1, EGU1, EGU1_SWI1);
impl_wdt!(WDT, WDT, WDT, 0);
embassy_hal_internal::interrupt_mod!(
CLOCK_POWER,
RADIO,

View File

@ -249,6 +249,8 @@ impl_radio!(RADIO, RADIO, RADIO);
impl_egu!(EGU0, EGU0, EGU0_SWI0);
impl_egu!(EGU1, EGU1, EGU1_SWI1);
impl_wdt!(WDT, WDT, WDT, 0);
embassy_hal_internal::interrupt_mod!(
CLOCK_POWER,
RADIO,

View File

@ -244,6 +244,8 @@ impl_egu!(EGU3, EGU3, EGU3_SWI3);
impl_egu!(EGU4, EGU4, EGU4_SWI4);
impl_egu!(EGU5, EGU5, EGU5_SWI5);
impl_wdt!(WDT, WDT, WDT, 0);
embassy_hal_internal::interrupt_mod!(
CLOCK_POWER,
RADIO,

View File

@ -287,6 +287,8 @@ impl_egu!(EGU3, EGU3, EGU3_SWI3);
impl_egu!(EGU4, EGU4, EGU4_SWI4);
impl_egu!(EGU5, EGU5, EGU5_SWI5);
impl_wdt!(WDT, WDT, WDT, 0);
embassy_hal_internal::interrupt_mod!(
CLOCK_POWER,
RADIO,

View File

@ -329,6 +329,8 @@ impl_egu!(EGU3, EGU3, EGU3_SWI3);
impl_egu!(EGU4, EGU4, EGU4_SWI4);
impl_egu!(EGU5, EGU5, EGU5_SWI5);
impl_wdt!(WDT, WDT, WDT, 0);
embassy_hal_internal::interrupt_mod!(
CLOCK_POWER,
RADIO,

View File

@ -334,6 +334,8 @@ impl_egu!(EGU3, EGU3, EGU3_SWI3);
impl_egu!(EGU4, EGU4, EGU4_SWI4);
impl_egu!(EGU5, EGU5, EGU5_SWI5);
impl_wdt!(WDT, WDT, WDT, 0);
embassy_hal_internal::interrupt_mod!(
CLOCK_POWER,
RADIO,

View File

@ -171,7 +171,8 @@ embassy_hal_internal::peripherals! {
RTC1,
// WDT
WDT,
WDT0,
WDT1,
// NVMC
NVMC,
@ -473,6 +474,9 @@ impl_egu!(EGU3, EGU3, EGU3);
impl_egu!(EGU4, EGU4, EGU4);
impl_egu!(EGU5, EGU5, EGU5);
impl_wdt!(WDT0, WDT0, WDT0, 0);
impl_wdt!(WDT1, WDT1, WDT1, 1);
embassy_hal_internal::interrupt_mod!(
FPU,
CACHE,

View File

@ -299,6 +299,8 @@ impl_radio!(RADIO, RADIO, RADIO);
impl_egu!(EGU0, EGU0, EGU0);
impl_wdt!(WDT, WDT, WDT, 0);
embassy_hal_internal::interrupt_mod!(
CLOCK_POWER,
RADIO,

View File

@ -342,6 +342,8 @@ impl_egu!(EGU3, EGU3, EGU3);
impl_egu!(EGU4, EGU4, EGU4);
impl_egu!(EGU5, EGU5, EGU5);
impl_wdt!(WDT, WDT, WDT, 0);
embassy_hal_internal::interrupt_mod!(
SPU,
CLOCK_POWER,

View File

@ -342,6 +342,8 @@ impl_egu!(EGU3, EGU3, EGU3);
impl_egu!(EGU4, EGU4, EGU4);
impl_egu!(EGU5, EGU5, EGU5);
impl_wdt!(WDT, WDT, WDT, 0);
embassy_hal_internal::interrupt_mod!(
SPU,
CLOCK_POWER,

View File

@ -169,7 +169,6 @@ pub mod uarte;
))]
pub mod usb;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(not(feature = "_nrf5340"))]
pub mod wdt;
// This mod MUST go last, so that it sees all the `impl_foo!` macros

View File

@ -3,11 +3,15 @@
//! This HAL implements a basic watchdog timer with 1..=8 handles.
//! Once the watchdog has been started, it cannot be stopped.
use core::marker::PhantomData;
#![macro_use]
use core::hint::unreachable_unchecked;
use embassy_hal_internal::PeripheralType;
use crate::pac::wdt::vals;
pub use crate::pac::wdt::vals::{Halt as HaltConfig, Sleep as SleepConfig};
use crate::{peripherals, Peri};
use crate::{interrupt, pac, peripherals, Peri};
const MIN_TICKS: u32 = 15;
@ -30,12 +34,12 @@ pub struct Config {
impl Config {
/// Create a config structure from the current configuration of the WDT
/// peripheral.
pub fn try_new(_wdt: &peripherals::WDT) -> Option<Self> {
let r = crate::pac::WDT;
pub fn try_new<T: Instance>(_wdt: &Peri<'_, T>) -> Option<Self> {
let r = T::REGS;
#[cfg(not(feature = "_nrf91"))]
#[cfg(not(any(feature = "_nrf91", feature = "_nrf5340")))]
let runstatus = r.runstatus().read().runstatus();
#[cfg(feature = "_nrf91")]
#[cfg(any(feature = "_nrf91", feature = "_nrf5340"))]
let runstatus = r.runstatus().read().runstatuswdt();
if runstatus {
@ -62,11 +66,11 @@ impl Default for Config {
}
/// Watchdog driver.
pub struct Watchdog {
_wdt: Peri<'static, peripherals::WDT>,
pub struct Watchdog<T: Instance> {
_wdt: Peri<'static, T>,
}
impl Watchdog {
impl<T: Instance> Watchdog<T> {
/// Try to create a new watchdog driver.
///
/// This function will return an error if the watchdog is already active
@ -76,19 +80,19 @@ impl Watchdog {
/// `N` must be between 1 and 8, inclusive.
#[inline]
pub fn try_new<const N: usize>(
wdt: Peri<'static, peripherals::WDT>,
wdt: Peri<'static, T>,
config: Config,
) -> Result<(Self, [WatchdogHandle; N]), Peri<'static, peripherals::WDT>> {
) -> Result<(Self, [WatchdogHandle; N]), Peri<'static, T>> {
assert!(N >= 1 && N <= 8);
let r = crate::pac::WDT;
let r = T::REGS;
let crv = config.timeout_ticks.max(MIN_TICKS);
let rren = crate::pac::wdt::regs::Rren((1u32 << N) - 1);
#[cfg(not(feature = "_nrf91"))]
#[cfg(not(any(feature = "_nrf91", feature = "_nrf5340")))]
let runstatus = r.runstatus().read().runstatus();
#[cfg(feature = "_nrf91")]
#[cfg(any(feature = "_nrf91", feature = "_nrf5340"))]
let runstatus = r.runstatus().read().runstatuswdt();
if runstatus {
@ -114,17 +118,9 @@ impl Watchdog {
let this = Self { _wdt: wdt };
let mut handles = [const {
WatchdogHandle {
_wdt: PhantomData,
index: 0,
}
}; N];
let mut handles = [const { WatchdogHandle { index: 0 } }; N];
for i in 0..N {
handles[i] = WatchdogHandle {
_wdt: PhantomData,
index: i as u8,
};
handles[i] = unsafe { WatchdogHandle::steal(T::INDEX, i as u8) };
handles[i].pet();
}
@ -139,7 +135,7 @@ impl Watchdog {
/// interrupt has been enabled.
#[inline(always)]
pub fn enable_interrupt(&mut self) {
crate::pac::WDT.intenset().write(|w| w.set_timeout(true));
T::REGS.intenset().write(|w| w.set_timeout(true));
}
/// Disable the watchdog interrupt.
@ -147,7 +143,7 @@ impl Watchdog {
/// NOTE: This has no effect on the reset caused by the Watchdog.
#[inline(always)]
pub fn disable_interrupt(&mut self) {
crate::pac::WDT.intenclr().write(|w| w.set_timeout(true));
T::REGS.intenclr().write(|w| w.set_timeout(true));
}
/// Is the watchdog still awaiting pets from any handle?
@ -156,7 +152,7 @@ impl Watchdog {
/// handles to prevent a reset this time period.
#[inline(always)]
pub fn awaiting_pets(&self) -> bool {
let r = crate::pac::WDT;
let r = T::REGS;
let enabled = r.rren().read().0;
let status = r.reqstatus().read().0;
(status & enabled) == 0
@ -165,11 +161,26 @@ impl Watchdog {
/// Watchdog handle.
pub struct WatchdogHandle {
_wdt: PhantomData<Peri<'static, peripherals::WDT>>,
index: u8,
}
impl WatchdogHandle {
fn regs(&self) -> pac::wdt::Wdt {
match self.index / 8 {
#[cfg(not(feature = "_multi_wdt"))]
peripherals::WDT::INDEX => peripherals::WDT::REGS,
#[cfg(feature = "_multi_wdt")]
peripherals::WDT0::INDEX => peripherals::WDT0::REGS,
#[cfg(feature = "_multi_wdt")]
peripherals::WDT1::INDEX => peripherals::WDT1::REGS,
_ => unsafe { unreachable_unchecked() },
}
}
fn rr_index(&self) -> usize {
usize::from(self.index % 8)
}
/// Pet the watchdog.
///
/// This function pets the given watchdog handle.
@ -178,25 +189,50 @@ impl WatchdogHandle {
/// prevent a reset from occurring.
#[inline]
pub fn pet(&mut self) {
let r = crate::pac::WDT;
r.rr(self.index as usize).write(|w| w.set_rr(vals::Rr::RELOAD));
let r = self.regs();
r.rr(self.rr_index()).write(|w| w.set_rr(vals::Rr::RELOAD));
}
/// Has this handle been pet within the current window?
pub fn is_pet(&self) -> bool {
let r = crate::pac::WDT;
!r.reqstatus().read().rr(self.index as usize)
let r = self.regs();
!r.reqstatus().read().rr(self.rr_index())
}
/// Steal a watchdog handle by index.
///
/// # Safety
/// Watchdog must be initialized and `index` must be between `0` and `N-1`
/// where `N` is the handle count when initializing.
pub unsafe fn steal(index: u8) -> Self {
/// Watchdog must be initialized, `instance_index` must be the index
/// [`Instance::INDEX`], and `index` must be between `0` and `N-1` where `N`
/// is the handle count when initializing.
pub unsafe fn steal(instance_index: u8, index: u8) -> Self {
Self {
_wdt: PhantomData,
index,
index: instance_index * 8 + index,
}
}
}
pub(crate) trait SealedInstance {
const REGS: pac::wdt::Wdt;
}
/// WDT instance.
#[allow(private_bounds)]
pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
/// Index of the watchdog instance.
const INDEX: u8;
}
macro_rules! impl_wdt {
($type:ident, $pac_type:ident, $irq:ident, $index:literal) => {
impl crate::wdt::SealedInstance for peripherals::$type {
const REGS: pac::wdt::Wdt = pac::$pac_type;
}
impl crate::wdt::Instance for peripherals::$type {
type Interrupt = crate::interrupt::typelevel::$irq;
const INDEX: u8 = $index;
}
};
}