embassy_nrf/
wdt.rs

1//! Watchdog Timer (WDT) driver.
2//!
3//! This HAL implements a basic watchdog timer with 1..=8 handles.
4//! Once the watchdog has been started, it cannot be stopped.
5
6#![macro_use]
7
8use core::hint::unreachable_unchecked;
9
10use embassy_hal_internal::PeripheralType;
11
12use crate::pac::wdt::vals;
13pub use crate::pac::wdt::vals::{Halt as HaltConfig, Sleep as SleepConfig};
14use crate::{interrupt, pac, peripherals, Peri};
15
16const MIN_TICKS: u32 = 15;
17
18/// WDT configuration.
19#[non_exhaustive]
20pub struct Config {
21    /// Number of 32768 Hz ticks in each watchdog period.
22    ///
23    /// Note: there is a minimum of 15 ticks (458 microseconds). If a lower
24    /// number is provided, 15 ticks will be used as the configured value.
25    pub timeout_ticks: u32,
26
27    /// Should the watchdog continue to count during sleep modes?
28    pub action_during_sleep: SleepConfig,
29
30    /// Should the watchdog continue to count when the CPU is halted for debug?
31    pub action_during_debug_halt: HaltConfig,
32}
33
34impl Config {
35    /// Create a config structure from the current configuration of the WDT
36    /// peripheral.
37    pub fn try_new<T: Instance>(_wdt: &Peri<'_, T>) -> Option<Self> {
38        let r = T::REGS;
39
40        #[cfg(not(any(feature = "_nrf91", feature = "_nrf5340", feature = "_nrf54l")))]
41        let runstatus = r.runstatus().read().runstatus();
42        #[cfg(any(feature = "_nrf91", feature = "_nrf5340", feature = "_nrf54l"))]
43        let runstatus = r.runstatus().read().runstatuswdt();
44
45        if runstatus {
46            let config = r.config().read();
47            Some(Self {
48                timeout_ticks: r.crv().read(),
49                action_during_sleep: config.sleep(),
50                action_during_debug_halt: config.halt(),
51            })
52        } else {
53            None
54        }
55    }
56}
57
58impl Default for Config {
59    fn default() -> Self {
60        Self {
61            timeout_ticks: 32768, // 1 second
62            action_during_debug_halt: HaltConfig::RUN,
63            action_during_sleep: SleepConfig::RUN,
64        }
65    }
66}
67
68/// Watchdog driver.
69pub struct Watchdog {
70    r: pac::wdt::Wdt,
71}
72
73impl Watchdog {
74    /// Try to create a new watchdog driver.
75    ///
76    /// This function will return an error if the watchdog is already active
77    /// with a `config` different to the requested one, or a different number of
78    /// enabled handles.
79    ///
80    /// `N` must be between 1 and 8, inclusive.
81    #[inline]
82    pub fn try_new<T: Instance, const N: usize>(
83        wdt: Peri<'static, T>,
84        config: Config,
85    ) -> Result<(Self, [WatchdogHandle; N]), Peri<'static, T>> {
86        assert!(N >= 1 && N <= 8);
87
88        let r = T::REGS;
89
90        let crv = config.timeout_ticks.max(MIN_TICKS);
91        let rren = crate::pac::wdt::regs::Rren((1u32 << N) - 1);
92
93        #[cfg(not(any(feature = "_nrf91", feature = "_nrf5340", feature = "_nrf54l")))]
94        let runstatus = r.runstatus().read().runstatus();
95        #[cfg(any(feature = "_nrf91", feature = "_nrf5340", feature = "_nrf54l"))]
96        let runstatus = r.runstatus().read().runstatuswdt();
97
98        if runstatus {
99            let curr_config = r.config().read();
100            if curr_config.halt() != config.action_during_debug_halt
101                || curr_config.sleep() != config.action_during_sleep
102                || r.crv().read() != crv
103                || r.rren().read() != rren
104            {
105                return Err(wdt);
106            }
107        } else {
108            r.config().write(|w| {
109                w.set_sleep(config.action_during_sleep);
110                w.set_halt(config.action_during_debug_halt);
111            });
112            r.intenset().write(|w| w.set_timeout(true));
113
114            r.crv().write_value(crv);
115            r.rren().write_value(rren);
116            r.tasks_start().write_value(1);
117        }
118
119        let this = Self { r: T::REGS };
120
121        let mut handles = [const { WatchdogHandle { index: 0 } }; N];
122        for i in 0..N {
123            handles[i] = unsafe { WatchdogHandle::steal::<T>(i as u8) };
124            handles[i].pet();
125        }
126
127        Ok((this, handles))
128    }
129
130    /// Enable the watchdog interrupt.
131    ///
132    /// NOTE: Although the interrupt will occur, there is no way to prevent
133    /// the reset from occurring. From the time the event was fired, the
134    /// system will reset two LFCLK ticks later (61 microseconds) if the
135    /// interrupt has been enabled.
136    #[inline(always)]
137    pub fn enable_interrupt(&mut self) {
138        self.r.intenset().write(|w| w.set_timeout(true));
139    }
140
141    /// Disable the watchdog interrupt.
142    ///
143    /// NOTE: This has no effect on the reset caused by the Watchdog.
144    #[inline(always)]
145    pub fn disable_interrupt(&mut self) {
146        self.r.intenclr().write(|w| w.set_timeout(true));
147    }
148
149    /// Is the watchdog still awaiting pets from any handle?
150    ///
151    /// This reports whether sufficient pets have been received from all
152    /// handles to prevent a reset this time period.
153    #[inline(always)]
154    pub fn awaiting_pets(&self) -> bool {
155        let enabled = self.r.rren().read().0;
156        let status = self.r.reqstatus().read().0;
157        (status & enabled) == 0
158    }
159}
160
161/// Watchdog handle.
162pub struct WatchdogHandle {
163    index: u8,
164}
165
166impl WatchdogHandle {
167    fn regs(&self) -> pac::wdt::Wdt {
168        match self.index / 8 {
169            #[cfg(not(feature = "_multi_wdt"))]
170            peripherals::WDT::INDEX => peripherals::WDT::REGS,
171            #[cfg(feature = "_multi_wdt")]
172            peripherals::WDT0::INDEX => peripherals::WDT0::REGS,
173            #[cfg(feature = "_multi_wdt")]
174            peripherals::WDT1::INDEX => peripherals::WDT1::REGS,
175            _ => unsafe { unreachable_unchecked() },
176        }
177    }
178
179    fn rr_index(&self) -> usize {
180        usize::from(self.index % 8)
181    }
182
183    /// Pet the watchdog.
184    ///
185    /// This function pets the given watchdog handle.
186    ///
187    /// NOTE: All active handles must be pet within the time interval to
188    /// prevent a reset from occurring.
189    #[inline]
190    pub fn pet(&mut self) {
191        let r = self.regs();
192        r.rr(self.rr_index()).write(|w| w.set_rr(vals::Rr::RELOAD));
193    }
194
195    /// Has this handle been pet within the current window?
196    pub fn is_pet(&self) -> bool {
197        let r = self.regs();
198        !r.reqstatus().read().rr(self.rr_index())
199    }
200
201    /// Steal a watchdog handle by index.
202    ///
203    /// # Safety
204    /// Watchdog must be initialized and `index` must be between `0` and `N-1`
205    /// where `N` is the handle count when initializing.
206    pub unsafe fn steal<T: Instance>(index: u8) -> Self {
207        Self {
208            index: T::INDEX * 8 + index,
209        }
210    }
211}
212
213pub(crate) trait SealedInstance {
214    const REGS: pac::wdt::Wdt;
215    const INDEX: u8;
216}
217
218/// WDT instance.
219#[allow(private_bounds)]
220pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
221    /// Interrupt for this peripheral.
222    type Interrupt: interrupt::typelevel::Interrupt;
223}
224
225macro_rules! impl_wdt {
226    ($type:ident, $pac_type:ident, $irq:ident, $index:literal) => {
227        impl crate::wdt::SealedInstance for peripherals::$type {
228            const REGS: pac::wdt::Wdt = pac::$pac_type;
229            const INDEX: u8 = $index;
230        }
231        impl crate::wdt::Instance for peripherals::$type {
232            type Interrupt = crate::interrupt::typelevel::$irq;
233        }
234    };
235}