task_watchdog/
lib.rs

1//! # task-watchdog
2//!
3//! A robust, flexible watchdog management library for embedded systems that
4//! multiplexes multiple task watchdogs into a single hardware watchdog timer,
5//! preventing system lockups when tasks fail to respond
6//!
7//! This crate provides a task registration pattern that monitors multiple
8//! tasks and ensures they are all still active, feeding the hardware
9//! watchdog only if all tasks are healthy.
10//!
11//! Tasks can be dynamically registered and deregistered when the system is
12//! running, to allow tasks that are created after startup to be monitoring,
13//! and to prevent tasks that are expected to block/pause from causing the
14//! device to restart.
15//!
16//! ![Multiplexed Task Diagram](https://raw.githubusercontent.com/piersfinlayson/task-watchdog/refs/heads/main/docs/images/multiplex.svg)
17//!
18//! ## Key Features
19//!
20//! - **Hardware Agnostic API**: Implements a consistent interface across
21//!   different embedded microcontrollers with extensible trait system for
22//!   hardware watchdog and clock types
23//! - **Task Multiplexing**: Consolidates multiple independent task watchdogs
24//!   into a single hardware watchdog, triggering if any task fails to check in
25//! - **Dynamic Task Management**: Tasks can be registered and deregistered
26//!   at runtime, allowing for flexible monitoring configurations
27//! - **Async and Sync Support**: Works with both synchronous (via device
28//!   HALs) and asynchronous (Embassy) execution environments
29//! - **No-Alloc Mode**: Functions in both `alloc` and `no_alloc` modes for
30//!   environments with or without heap availability
31//! - **Configurable Timeouts**: Individual timeout durations for each
32//!   registered task
33//! - **`no_std` Compatible**: Designed for resource-constrained embedded
34//!   environments without an operating system
35//!
36//! ## Usage
37//!
38//! The following is a complete, minimal, example for using the task-watchdog
39//! crate using embassy-rs on an RP2040 or RP2350 (Pico or Pico 2).
40//! It uses static allocation (no alloc), and creates two tasks with
41//! different timeouts, both of which are policed by task-watchdog, and in
42//! turn, the hardware watchdog.
43//!
44//! ```rust
45//! #![no_std]
46//! #![no_main]
47//!
48//! use task_watchdog::{WatchdogConfig, Id};
49//! use task_watchdog::embassy_rp::{WatchdogRunner, watchdog_run};
50//! use embassy_time::{Duration, Timer};
51//! use embassy_rp::config::Config;
52//! use embassy_executor::Spawner;
53//! use static_cell::StaticCell;
54//! use panic_probe as _;
55//!
56//! // Create a static to hold the task-watchdog object, so it has static
57//! // lifetime and can be shared with tasks.
58//! static WATCHDOG: StaticCell<WatchdogRunner<TaskId, NUM_TASKS>> = StaticCell::new();
59//!
60//! // Create an object to contain our task IDs.  It must implement the Id
61//! // trait, which, for simply TaskId types means deriving the following
62//! // traits:
63//! #[derive(Clone, Copy, PartialEq, Eq, Debug)]
64//! enum TaskId {
65//!     Main,
66//!     Second,
67//! }
68//! impl Id for TaskId {}  // Nothing else to implement as we derived the required traits
69//! const NUM_TASKS: usize = 2;
70//!
71//! #[embassy_executor::main]
72//! async fn main(spawner: Spawner) {
73//!     // Initialize the hardare peripherals
74//!     let p = embassy_rp::init(Config::default());
75//!
76//!     // Set up watchdog configuration, with a 5s hardware watchdog timeout, and
77//!     // with the task watchdog checking tasks every second.
78//!     let config = WatchdogConfig {
79//!         hardware_timeout: Duration::from_millis(5000),
80//!         check_interval: Duration::from_millis(1000),
81//!     };
82//!
83//!     // Create the watchdog runner and store it in the static cell
84//!     let watchdog = WatchdogRunner::new(p.WATCHDOG, config);
85//!     let watchdog = WATCHDOG.init(watchdog);
86//!
87//!     // Register our tasks with the task-watchdog.  Each can have a different timeout.
88//!     watchdog.register_task(&TaskId::Main, Duration::from_millis(2000)).await;
89//!     watchdog.register_task(&TaskId::Second, Duration::from_millis(4000)).await  ;
90//!
91//!     // Spawn tasks that will feed the watchdog
92//!     spawner.must_spawn(main_task(watchdog));
93//!     spawner.must_spawn(second_task(watchdog));
94//!
95//!     // Finally spawn the watchdog - this will start the hardware watchdog, and feed it
96//!     // for as long as _all_ tasks are healthy.
97//!     spawner.must_spawn(watchdog_task(watchdog));
98//! }
99//!
100//! // Provide a simple embassy task for the watchdog
101//! #[embassy_executor::task]
102//! async fn watchdog_task(watchdog: &'static WatchdogRunner<TaskId, NUM_TASKS>) -> ! {
103//!     watchdog_run(watchdog.create_task()).await
104//! }
105//!
106//! // Implement your main task
107//! #[embassy_executor::task]
108//! async fn main_task(watchdog: &'static WatchdogRunner<TaskId, NUM_TASKS>) -> !{
109//!    loop {
110//!         // Feed the watchdog
111//!         watchdog.feed(&TaskId::Main).await;
112//!
113//!         // Do some work
114//!         Timer::after(Duration::from_millis(1000)).await;
115//!    }
116//! }
117//!
118//! // Implement your second task
119//! #[embassy_executor::task]
120//! async fn second_task(watchdog: &'static WatchdogRunner<TaskId, NUM_TASKS>) -> !{
121//!    loop {
122//!         // Feed the watchdog
123//!         watchdog.feed(&TaskId::Second).await;
124//!
125//!         // Do some work
126//!         Timer::after(Duration::from_millis(2000)).await;
127//!    }
128//! }
129//!
130//! ```
131//! See the [`README`](https://github.com/piersfinlayson/task-watchdog/blob/main/README.md) and the [examples](https://github.com/piersfinlayson/task-watchdog/tree/main/examples/src) for more usage examples.
132//!
133//! ## Targets
134//!
135//! For embedded devices you need to install and specify your target when
136//! building.  Use:
137//! - RP2040 - `thumbv6m-none-eabi`
138//! - RP2350 - `thumbv8m.main-none-eabihf`
139//! - STM32 - `thumbv7m-none-eabi`
140//! - nRF - `thumbv7em-none-eabihf`
141//! 
142//! ## Feature Flags
143//!
144//! The following feature flags are supported
145//!
146//! ### Embassy support:
147//!
148//! - `rp2040-embassy`: Enable the RP2040-specific embassy implementation
149//! - `rp2350-embassy`: Enable the RP2350-specific embassy implementation
150//! - `stm32`: Enable the STM32-specific embassy implementation
151//! - `nrf`: Enable the nRF-specific embassy implementation
152//! - `defmt-embassy-rp`: Enable logging with defmt for the RP2040 and RP2350 embassy implementation
153//! - `defmt-embassy-stm32`: Enable logging with defmt for the STM32 embassy implementation
154//! - `defmt-embassy-nrf`: Enable logging with defmt for the nRF embassy implementation
155//!
156//! ### HAL/sync support:
157//!
158//! - `rp2040-hal`: Enable the RP2040 HAL implementation
159//! - `rp2350-hal`: Enable the RP2350 HAL implementation
160//! - `defmt`: Enable logging with defmt, for use with the HAL implementations
161//!
162//! ### Other
163//!
164//! - `alloc`: Enable features that require heap allocation but simplifies
165//! usage
166//!
167//! ### Example Feature/Target combination
168//!
169//! This builds the library for RP2040 with embassy and defmt support:
170//!
171//! ```bash
172//! cargo build --features rp2040-embassy,defmt-embassy-rp --target thumbv6m-none-eabi
173//! ```
174//!
175//! ## Embassy Objects
176//!
177//! If you want to use an include, off the shelf implementation that works with
178//! Embassy the objects, you need to use are:
179//!
180//! - [`WatchdogConfig`] - Used to configure the task-watchdog.
181//! - [`embassy_rp::WatchdogRunner`] - Create with the hardware watchdog
182//! peripheral and `WatchdogConfig`, and then use to operate the task-watchdog, including task management.  There is also an `embassy_stm32::WatchdogRunner` for STM32, and `embassy_nrf::WatchdogRunner` for nRF.
183//! - [`Id`] - Trait for task identifiers.  If you use an enum, derive the
184//! `Clone`, `Copy`, `PartialEq`, `Eq` and `Debug`/`Format` traits, and then
185//! implement `Id` for the enum.  The Id implementation can be empty, if you
186//! derive the required implementations.  You must also derive orimplement
187//! `PartialOrd` and `Ord` if you use the `alloc` feature.
188//! - [`embassy_rp::watchdog_run()`] - Create and spawn a simple embassy task
189//! that just calls this function.  This task will handle policing your other
190//! tasks and feeding the hardware watchdog.  There is also an
191//! `embassy_stm32::watchdog_run()` for STM32 and `embassy_nrf::watchdog_run()`
192//! for nRF.
193//!
194
195// Copyright (c) 2025 Piers Finlayson <piers@piers.rocks>
196//
197// Apache 2.0 or MIT licensed, at your option.
198
199#![no_std]
200
201#[cfg(feature = "alloc")]
202extern crate alloc;
203#[cfg(feature = "alloc")]
204use alloc::collections::BTreeMap;
205
206#[cfg(feature = "defmt")]
207#[allow(unused_imports)]
208use defmt::{debug, error, info, trace, warn};
209
210// A replacement for the defmt logging macros, when defmt is not provided
211#[cfg(not(feature = "defmt"))]
212mod log_impl {
213    #![allow(unused_macros)]
214    #![allow(unused_imports)]
215    // Macros are defined as _ to avoid conflicts with built-in attribute
216    // names
217    macro_rules! _trace {
218        ($($arg:tt)*) => {};
219    }
220    macro_rules! _debug {
221        ($($arg:tt)*) => {};
222    }
223    macro_rules! _info {
224        ($($arg:tt)*) => {};
225    }
226    macro_rules! _warn {
227        ($($arg:tt)*) => {};
228    }
229    macro_rules! _error {
230        ($($arg:tt)*) => {};
231    }
232    pub(crate) use _debug as debug;
233    pub(crate) use _error as error;
234    pub(crate) use _info as info;
235    pub(crate) use _trace as trace;
236    pub(crate) use _warn as warn;
237}
238#[cfg(not(feature = "defmt"))]
239use log_impl::*;
240
241/// Represents a hardware-level watchdog that can be fed and reset the system.
242pub trait HardwareWatchdog<C: Clock> {
243    /// Start the hardware watchdog with the given timeout.
244    fn start(&mut self, timeout: C::Duration);
245
246    /// Feed the hardware watchdog to prevent a system reset.
247    fn feed(&mut self);
248
249    /// Trigger a hardware reset.
250    fn trigger_reset(&mut self) -> !;
251
252    /// Get the reason for the last reset, if available.
253    fn reset_reason(&self) -> Option<ResetReason>;
254}
255
256/// Represents the reason for a system reset.
257#[derive(Debug, Clone, Copy, PartialEq, Eq)]
258#[cfg_attr(feature = "defmt", derive(defmt::Format))]
259pub enum ResetReason {
260    /// Reset was forced by software.
261    Forced,
262
263    /// Reset was caused by watchdog timeout.
264    TimedOut,
265}
266
267/// Configuration for the watchdog.
268#[derive(Debug, Clone, Copy)]
269pub struct WatchdogConfig<C: Clock> {
270    /// Timeout to start the hardware watchdog with.
271    pub hardware_timeout: C::Duration,
272
273    /// Interval at which to check if tasks have fed the watchdog.  Must be
274    /// less than the hardware timeout, or the hardware watchdog will reset
275    /// the system, before the task-watchdog has a chance to check tasks and
276    /// feed it.
277    pub check_interval: C::Duration,
278}
279
280impl<C: Clock> WatchdogConfig<C> {
281    /// Create a new configuration with specified timeout values
282    pub fn new(hardware_timeout_ms: u64, check_interval_ms: u64, clock: &C) -> Self {
283        Self {
284            hardware_timeout: clock.duration_from_millis(hardware_timeout_ms),
285            check_interval: clock.duration_from_millis(check_interval_ms),
286        }
287    }
288
289    /// Create a default configuration with standard timeout values:
290    /// - Hardware timeout: 5000ms
291    /// - Check interval: 1000ms
292    pub fn default(clock: &C) -> Self {
293        Self::new(5000, 1000, clock)
294    }
295}
296
297#[cfg(all(feature = "alloc", feature = "defmt"))]
298/// Trait for task identifiers.
299pub trait Id: PartialEq + Eq + Ord + defmt::Format + Clone + Copy {}
300#[cfg(all(feature = "alloc", not(feature = "defmt")))]
301/// Trait for task identifiers.
302pub trait Id: PartialEq + Eq + Ord + core::fmt::Debug + Clone + Copy {}
303#[cfg(all(not(feature = "alloc"), feature = "defmt"))]
304/// Trait for task identifiers.
305///
306/// You need an object implementing this trait (likely by using derive()) in
307/// order to identify tasks to the watchdog.  This can be any object you
308/// like implementing this trait, although an `enum` would probably be a
309/// good choice.
310pub trait Id: PartialEq + Eq + defmt::Format + Clone + Copy {}
311#[cfg(all(not(feature = "alloc"), not(feature = "defmt")))]
312/// Trait for task identifiers.
313pub trait Id: PartialEq + Eq + core::fmt::Debug + Clone + Copy {}
314
315/// Represents a task monitored by the watchdog.
316#[derive(Debug, Clone)]
317pub struct Task<I: Id, C: Clock> {
318    /// The task identifier.
319    #[allow(dead_code)]
320    id: I,
321
322    /// The last time the task was fed.
323    last_feed: C::Instant,
324
325    /// Maximum duration between feeds.
326    max_duration: C::Duration,
327}
328
329impl<I: Id, C: Clock> Task<I, C> {
330    /// Creates a new Task object for registration with the watchdog.
331    pub fn new(id: I, max_duration: C::Duration, clock: &C) -> Self {
332        Self {
333            id,
334            last_feed: clock.now(),
335            max_duration,
336        }
337    }
338
339    /// Feed the task to indicate it's still active.
340    fn feed(&mut self, clock: &C) {
341        self.last_feed = clock.now();
342    }
343
344    /// Check if this task has starved the watchdog.
345    fn is_starved(&self, clock: &C) -> bool {
346        clock.has_elapsed(self.last_feed, &self.max_duration)
347    }
348}
349
350/// A trait for time-keeping implementations.
351pub trait Clock {
352    /// A type representing a specific instant in time.
353    type Instant: Copy;
354
355    /// A type representing a duration of time
356    type Duration: Copy;
357
358    /// Get the current time.
359    fn now(&self) -> Self::Instant;
360
361    /// Calculate the duration elapsed since the given instant.
362    fn elapsed_since(&self, instant: Self::Instant) -> Self::Duration;
363
364    /// Check if a duration has passed since the given instant.
365    fn has_elapsed(&self, instant: Self::Instant, duration: &Self::Duration) -> bool;
366
367    /// Create a duration from milliseconds.
368    fn duration_from_millis(&self, millis: u64) -> Self::Duration;
369}
370
371/// A watchdog that monitors multiple tasks and resets the system if any task fails to feed.
372#[cfg(feature = "alloc")]
373pub struct Watchdog<I: Id, W: HardwareWatchdog<C>, C: Clock> {
374    /// The hardware watchdog.
375    hw_watchdog: W,
376
377    /// Tasks being monitored.
378    tasks: BTreeMap<I, Task<I, C>>,
379
380    /// Configuration.
381    config: WatchdogConfig<C>,
382
383    /// Clock for time-keeping.
384    clock: C,
385}
386
387#[cfg(feature = "alloc")]
388impl<I: Id, W: HardwareWatchdog<C>, C: Clock> Watchdog<I, W, C> {
389    /// Create a new watchdog with the given hardware watchdog and configuration.
390    pub fn new(hw_watchdog: W, config: WatchdogConfig<C>, clock: C) -> Self {
391        Self {
392            hw_watchdog,
393            tasks: BTreeMap::new(),
394            config,
395            clock,
396        }
397    }
398
399    /// Register a task with the watchdog.
400    pub fn register_task(&mut self, id: &I, max_duration: C::Duration) {
401        let task = Task::new(*id, max_duration, &self.clock);
402        self.tasks.insert(*id, task);
403        debug!("Registered task: {:?}", id);
404    }
405
406    /// Deregister a task from the watchdog.
407    pub fn deregister_task(&mut self, id: &I) {
408        #[allow(clippy::if_same_then_else)]
409        if self.tasks.remove(id).is_some() {
410            debug!("Deregistered task: {:?}", id);
411        } else {
412            debug!("Attempted to deregister unknown task: {:?}", id);
413        }
414    }
415
416    /// Feed the watchdog for a specific task.
417    pub fn feed(&mut self, id: &I) {
418        if let Some(task) = self.tasks.get_mut(id) {
419            task.feed(&self.clock);
420        } else {
421            warn!("Attempt to feed unknown task: {:?}", id);
422        }
423    }
424
425    /// Start the watchdog.
426    pub fn start(&mut self) {
427        // Feed all registered tasks
428        for task in self.tasks.values_mut() {
429            task.feed(&self.clock);
430        }
431
432        // Start the hardware watchdog
433        self.hw_watchdog.start(self.config.hardware_timeout);
434
435        info!("Watchdog started");
436    }
437
438    /// Check if any tasks have starved the watchdog and take appropriate action.
439    pub fn check(&mut self) -> bool {
440        // Check if any tasks have starved
441        let mut starved = false;
442        for task in self.tasks.values() {
443            if task.is_starved(&self.clock) {
444                error!("Task {:?} has starved the watchdog", task.id);
445                starved = true;
446            }
447        }
448
449        // Either feed the hardware watchdog or return that we have a starved task
450        if !starved {
451            self.hw_watchdog.feed();
452        }
453
454        starved
455    }
456
457    /// Trigger a system reset.
458    pub fn trigger_reset(&mut self) -> ! {
459        warn!("Triggering watchdog reset");
460        self.hw_watchdog.trigger_reset()
461    }
462
463    /// Get the reason for the last reset.
464    pub fn reset_reason(&self) -> Option<ResetReason> {
465        self.hw_watchdog.reset_reason()
466    }
467}
468
469/// A version of the Watchdog that doesn't require heap allocation.
470/// This uses a fixed-size array for task storage.
471#[cfg(not(feature = "alloc"))]
472pub struct Watchdog<I, const N: usize, W, C>
473where
474    I: Id,
475    W: HardwareWatchdog<C>,
476    C: Clock,
477{
478    /// The hardware watchdog.
479    hw_watchdog: W,
480
481    /// Tasks being monitored.
482    tasks: [Option<Task<I, C>>; N],
483
484    /// Configuration.
485    config: WatchdogConfig<C>,
486
487    /// Clock for time-keeping.
488    clock: C,
489}
490
491/// Errors that can occur when interacting with the watchdog.
492pub enum Error {
493    /// No slots available to register a task.
494    NoSlotsAvailable,
495}
496
497#[cfg(not(feature = "alloc"))]
498impl<I: Id, W: HardwareWatchdog<C>, C: Clock, const N: usize> Watchdog<I, N, W, C> {
499    /// Create a new watchdog with the given hardware watchdog and configuration.
500    ///
501    /// Arguments:
502    /// * `hw_watchdog` - The hardware watchdog to use.
503    /// * `config` - The configuration for the watchdog.
504    /// * `clock` - The clock implementation to use for time-keeping.
505    pub fn new(hw_watchdog: W, config: WatchdogConfig<C>, clock: C) -> Self {
506        Self {
507            hw_watchdog,
508            tasks: [const { None }; N],
509            config,
510            clock,
511        }
512    }
513
514    /// Register a task with the watchdog.
515    ///
516    /// The task will be monitored by the watchdog.
517    ///
518    /// Arguments:
519    /// * `id` - The task identifier.
520    /// * `max_duration` - The maximum duration between feeds.  If there is
521    ///                    a gap longer than this, the watchdog will trigger.
522    ///
523    /// # Errors
524    ///
525    /// If there are no available slots to register the task, an error will be
526    /// returned.
527    pub fn register_task(&mut self, id: &I, max_duration: C::Duration) -> Result<(), Error> {
528        // Find an empty slot
529        for slot in &mut self.tasks {
530            if slot.is_none() {
531                *slot = Some(Task::new(*id, max_duration, &self.clock));
532                debug!("Registered task: {:?}", id);
533                return Ok(());
534            }
535        }
536
537        // No empty slots available
538        error!("Failed to register task: {:?} - no slots available", id);
539        Err(Error::NoSlotsAvailable)
540    }
541
542    /// Deregister a task from the watchdog.
543    ///
544    /// The task will no longer be monitored by the watchdog.
545    ///
546    /// Arguments:
547    /// * `id` - The task identifier.
548    pub fn deregister_task(&mut self, id: &I) {
549        for slot in &mut self.tasks {
550            if let Some(task) = slot {
551                if core::mem::discriminant(&task.id) == core::mem::discriminant(id) {
552                    *slot = None;
553                    debug!("Deregistered task: {:?}", id);
554                    return;
555                }
556            }
557        }
558
559        info!("Attempted to deregister unknown task: {:?}", id);
560    }
561
562    /// Feed the watchdog for a specific task.
563    pub fn feed(&mut self, id: &I) {
564        let fed = self.tasks.iter_mut().flatten().any(|task| {
565            if core::mem::discriminant(&task.id) == core::mem::discriminant(id) {
566                task.feed(&self.clock);
567                true
568            } else {
569                false
570            }
571        });
572
573        if !fed {
574            warn!("Attempt to feed unknown task: {:?}", id);
575        }
576    }
577
578    /// Start the watchdog.
579    ///
580    /// This starts the hardware watchdog.  You must run the watchdog task
581    /// now to monitor the tasks.
582    pub fn start(&mut self) {
583        // Feed all registered tasks
584        self.tasks.iter_mut().flatten().for_each(|task| {
585            task.feed(&self.clock);
586        });
587
588        // Start the hardware watchdog
589        self.hw_watchdog.start(self.config.hardware_timeout);
590
591        info!("Watchdog started");
592    }
593
594    /// Check if any tasks have starved the watchdog and take appropriate action.
595    pub fn check(&mut self) -> bool {
596        // Check if any tasks have starved
597        let mut starved = false;
598        self.tasks.iter_mut().flatten().for_each(|task| {
599            if task.is_starved(&self.clock) {
600                error!("Task {:?} has starved the watchdog", task.id);
601                starved = true;
602            }
603        });
604
605        // Either feed the hardware watchdog or return that we have a starved
606        // task
607        if !starved {
608            self.hw_watchdog.feed();
609        }
610
611        starved
612    }
613
614    /// Trigger a system reset.
615    pub fn trigger_reset(&mut self) -> ! {
616        warn!("Triggering watchdog reset");
617        self.hw_watchdog.trigger_reset()
618    }
619
620    /// Get the reason for the last reset.
621    pub fn reset_reason(&self) -> Option<ResetReason> {
622        self.hw_watchdog.reset_reason()
623    }
624}
625
626/// A system clock implementation using core time types, which allows
627/// task-watchdog to work with different clock implementations.
628pub struct CoreClock;
629
630impl Clock for CoreClock {
631    type Instant = u64; // Simple millisecond counter
632    type Duration = core::time::Duration;
633
634    fn now(&self) -> Self::Instant {
635        // In real code, this would use a hardware timer
636        // This is just a simple example
637        static mut MILLIS: u64 = 0;
638        unsafe {
639            MILLIS += 1;
640            MILLIS
641        }
642    }
643
644    fn elapsed_since(&self, instant: Self::Instant) -> Self::Duration {
645        let now = self.now();
646        let elapsed_ms = now.saturating_sub(instant);
647        core::time::Duration::from_millis(elapsed_ms)
648    }
649
650    fn has_elapsed(&self, instant: Self::Instant, duration: &Self::Duration) -> bool {
651        self.elapsed_since(instant) >= *duration
652    }
653
654    fn duration_from_millis(&self, millis: u64) -> Self::Duration {
655        core::time::Duration::from_millis(millis)
656    }
657}
658
659/// A system clock implementation for Embassy.
660#[cfg(feature = "embassy")]
661pub struct EmbassyClock;
662
663#[cfg(feature = "embassy")]
664impl Clock for EmbassyClock {
665    type Instant = embassy_time::Instant;
666    type Duration = embassy_time::Duration;
667
668    fn now(&self) -> Self::Instant {
669        embassy_time::Instant::now()
670    }
671
672    fn elapsed_since(&self, instant: Self::Instant) -> Self::Duration {
673        embassy_time::Instant::now() - instant
674    }
675
676    fn has_elapsed(&self, instant: Self::Instant, duration: &Self::Duration) -> bool {
677        (embassy_time::Instant::now() - instant) >= *duration
678    }
679
680    fn duration_from_millis(&self, millis: u64) -> Self::Duration {
681        embassy_time::Duration::from_millis(millis)
682    }
683}
684
685/// An syncronous implementation of task-watchdog for use with the RP2040
686/// and RP2350 HALs.
687///
688/// This module requires either the `rp2040-hal` or `rp2350-hal` feature.
689///
690/// See the [`rp-sync`](https://github.com/piersfinlayson/task-watchdog/blob/main/examples/src/rp-sync.rs)
691/// example for how to use this module.
692#[cfg(any(feature = "rp2040-hal", feature = "rp2350-hal"))]
693pub mod rp_hal {
694    use super::{Clock, HardwareWatchdog, ResetReason};
695    use hal::fugit::{Duration as RpHalDuration, MicrosDurationU32};
696    #[cfg(feature = "rp2350-hal")]
697    use hal::timer::CopyableTimer0;
698    use hal::timer::{Instant as RpHalInstant, Timer as RpHalTimer};
699    use hal::watchdog::Watchdog as RpHalWatchdog;
700    #[cfg(feature = "rp2040-hal")]
701    use rp2040_hal as hal;
702    #[cfg(feature = "rp2350-hal")]
703    use rp235x_hal as hal;
704
705    /// A simple clock implementation based on hal::timer::Timer
706    #[cfg(feature = "rp2040-hal")]
707    pub struct RpHalClock {
708        inner: RpHalTimer,
709    }
710    #[cfg(feature = "rp2040-hal")]
711    impl RpHalClock {
712        pub fn new(timer: RpHalTimer) -> Self {
713            Self { inner: timer }
714        }
715    }
716    #[cfg(feature = "rp2350-hal")]
717    pub struct RpHalClock {
718        inner: RpHalTimer<CopyableTimer0>,
719    }
720    #[cfg(feature = "rp2350-hal")]
721    impl RpHalClock {
722        pub fn new(timer: RpHalTimer<CopyableTimer0>) -> Self {
723            Self { inner: timer }
724        }
725    }
726
727    /// Implement the Clock trait for [`RpHalClock`]
728    impl Clock for RpHalClock {
729        type Instant = RpHalInstant;
730        type Duration = RpHalDuration<u64, 1, 1_000_000>;
731
732        fn now(&self) -> Self::Instant {
733            self.inner.get_counter()
734        }
735
736        fn elapsed_since(&self, instant: Self::Instant) -> Self::Duration {
737            self.now().checked_duration_since(instant).unwrap()
738        }
739
740        fn has_elapsed(&self, instant: Self::Instant, duration: &Self::Duration) -> bool {
741            (self.now() - instant) >= *duration
742        }
743
744        fn duration_from_millis(&self, millis: u64) -> Self::Duration {
745            RpHalDuration::<u64, 1, 1_000_000>::millis(millis as u64)
746        }
747    }
748
749    /// A hardware watchdog implementation using the RP2040/RP2350 HAL.
750    pub struct RpHalTaskWatchdog {
751        inner: RpHalWatchdog,
752    }
753    impl RpHalTaskWatchdog {
754        pub fn new(watchdog: RpHalWatchdog) -> Self {
755            Self { inner: watchdog }
756        }
757    }
758
759    /// Implement the HardwareWatchdog trait for the HAL watchdog.
760    impl HardwareWatchdog<RpHalClock> for RpHalTaskWatchdog {
761        fn start(&mut self, timeout: <RpHalClock as Clock>::Duration) {
762            let timeout_micros = timeout.to_micros();
763            assert!(timeout_micros <= u32::MAX as u64);
764            let micros_dur_u32: MicrosDurationU32 =
765                MicrosDurationU32::micros(timeout_micros as u32);
766            self.inner.start(micros_dur_u32);
767        }
768
769        fn feed(&mut self) {
770            self.inner.feed();
771        }
772
773        fn trigger_reset(&mut self) -> ! {
774            // There is no reset() method on the hal watchdog, so we call
775            // hal::reset() directly
776            hal::reset()
777        }
778
779        fn reset_reason(&self) -> Option<ResetReason> {
780            // The hal watchdog does support support a way to check the last
781            // reset reason
782            None
783        }
784    }
785}
786
787/// An async implementation of task-watchdog for use with the RP2040 and RP2350
788/// embassy implementations.  There are also stm32 and nRF equivalents of this 
789/// module.
790///
791/// This module requires either the `rp2040-embassy` or `rp2350-embassy`
792/// feature.
793///
794/// See the [`embassy`](https://github.com/piersfinlayson/task-watchdog/blob/main/examples/src/embassy.rs)
795/// example for how to use this module.
796///
797/// There is an equivalent `embassy_stm32` module for STM32, but due to
798/// docs.rs limitations it is not documented here.  See the above example for
799/// usage of that module.  `embassy_nrf` and `embassy_rsp32` also exist.
800#[cfg(any(feature = "rp2040-embassy", feature = "rp2350-embassy"))]
801pub mod embassy_rp {
802    use super::{
803        info, Clock, EmbassyClock, HardwareWatchdog, Id, ResetReason, Watchdog, WatchdogConfig,
804    };
805    use embassy_rp::peripherals::WATCHDOG as P_RpWatchdog;
806    use embassy_rp::watchdog as rp_watchdog;
807    use embassy_time::{Instant, Timer};
808
809    /// RP2040/RP2350-specific watchdog implementation.
810    pub struct RpWatchdog {
811        inner: rp_watchdog::Watchdog,
812    }
813
814    impl RpWatchdog {
815        /// Create a new RP2040/RP2350 watchdog.
816        #[must_use]
817        pub fn new(peripheral: P_RpWatchdog) -> Self {
818            Self {
819                inner: rp_watchdog::Watchdog::new(peripheral),
820            }
821        }
822    }
823
824    /// Implement the HardwareWatchdog trait for the RP2040/RP2350 watchdog.
825    impl HardwareWatchdog<EmbassyClock> for RpWatchdog {
826        fn start(&mut self, timeout: <EmbassyClock as Clock>::Duration) {
827            self.inner.start(timeout);
828        }
829
830        fn feed(&mut self) {
831            self.inner.feed();
832        }
833
834        fn trigger_reset(&mut self) -> ! {
835            self.inner.trigger_reset();
836            panic!("Triggering reset via watchdog failed");
837        }
838
839        fn reset_reason(&self) -> Option<ResetReason> {
840            self.inner.reset_reason().map(|reason| match reason {
841                embassy_rp::watchdog::ResetReason::Forced => ResetReason::Forced,
842                embassy_rp::watchdog::ResetReason::TimedOut => ResetReason::TimedOut,
843            })
844        }
845    }
846
847    /// An Embassy RP2040/RP2350 watchdog runner.
848    #[cfg(feature = "alloc")]
849    pub struct WatchdogRunner<I>
850    where
851        I: Id,
852    {
853        watchdog: embassy_sync::mutex::Mutex<
854            embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex,
855            core::cell::RefCell<Watchdog<I, RpWatchdog, EmbassyClock>>,
856        >,
857    }
858
859    /// An Embassy RP2040/RP2350 watchdog runner.
860    ///
861    /// There is an equivalent version of this when using the `alloc` feature
862    /// which does not include the `const N: usize` type.
863    ///
864    /// There is also an equivalent STM32 watchdog runner in the
865    /// `embassy_stm32` module.
866    ///
867    /// Create the watchdog runner using the [`WatchdogRunner::new()`] method, and then use the
868    /// methods to register tasks and feed the watchdog.  You probably don't
869    /// want to access the other methods directly - use [`watchdog_run()`] to
870    /// handle running the task-watchdog.
871    #[cfg(not(feature = "alloc"))]
872    pub struct WatchdogRunner<I, const N: usize>
873    where
874        I: Id,
875    {
876        watchdog: embassy_sync::mutex::Mutex<
877            embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex,
878            core::cell::RefCell<Watchdog<I, N, RpWatchdog, EmbassyClock>>,
879        >,
880    }
881
882    #[cfg(feature = "alloc")]
883    impl<I> WatchdogRunner<I>
884    where
885        I: Id + 'static,
886    {
887        /// Create a new Embassy-compatible watchdog runner.
888        pub fn new(hw_watchdog: P_RpWatchdog, config: WatchdogConfig<EmbassyClock>) -> Self {
889            let hw_watchdog = RpWatchdog::new(hw_watchdog);
890            let watchdog = Watchdog::new(hw_watchdog, config, EmbassyClock);
891            Self {
892                watchdog: embassy_sync::mutex::Mutex::new(core::cell::RefCell::new(watchdog)),
893            }
894        }
895
896        /// Register a task with the watchdog.
897        pub async fn register_task(&self, id: &I, max_duration: <EmbassyClock as Clock>::Duration) {
898            self.watchdog
899                .lock()
900                .await
901                .borrow_mut()
902                .register_task(id, max_duration);
903        }
904
905        /// De-register a task with the watchdog.
906        pub async fn deregister_task(&self, id: &I) {
907            self.watchdog.lock().await.borrow_mut().deregister_task(id);
908        }
909
910        /// Feed the watchdog for a specific task.
911        pub async fn feed(&self, id: &I) {
912            self.watchdog.lock().await.borrow_mut().feed(id);
913        }
914
915        /// Start the watchdog.
916        pub async fn start(&self) {
917            self.watchdog.lock().await.borrow_mut().start();
918        }
919
920        /// Trigger a system reset.
921        pub async fn trigger_reset(&self) -> ! {
922            self.watchdog.lock().await.borrow_mut().trigger_reset()
923        }
924
925        /// Get the last reset reason.
926        pub async fn reset_reason(&self) -> Option<ResetReason> {
927            self.watchdog.lock().await.borrow().reset_reason()
928        }
929
930        /// Get the check interval
931        pub async fn get_check_interval(&self) -> <EmbassyClock as Clock>::Duration {
932            self.watchdog.lock().await.borrow().config.check_interval
933        }
934
935        /// Check if any tasks have starved
936        pub async fn check_tasks(&self) -> bool {
937            self.watchdog.lock().await.borrow_mut().check()
938        }
939    }
940
941    #[cfg(not(feature = "alloc"))]
942    impl<I, const N: usize> WatchdogRunner<I, N>
943    where
944        I: Id,
945    {
946        /// Create a new Embassy-compatible watchdog runner.
947        pub fn new(hw_watchdog: P_RpWatchdog, config: WatchdogConfig<EmbassyClock>) -> Self {
948            let hw_watchdog = RpWatchdog::new(hw_watchdog);
949            let watchdog = Watchdog::new(hw_watchdog, config, EmbassyClock);
950            Self {
951                watchdog: embassy_sync::mutex::Mutex::new(core::cell::RefCell::new(watchdog)),
952            }
953        }
954
955        /// Register a task with the watchdog.
956        pub async fn register_task(&self, id: &I, max_duration: <EmbassyClock as Clock>::Duration) {
957            self.watchdog
958                .lock()
959                .await
960                .borrow_mut()
961                .register_task(id, max_duration)
962                .ok();
963        }
964
965        /// Deregister a task with the watchdog.
966        pub async fn deregister_task(&self, id: &I) {
967            self.watchdog.lock().await.borrow_mut().deregister_task(id);
968        }
969
970        /// Feed the watchdog for a specific task.
971        pub async fn feed(&self, id: &I) {
972            self.watchdog.lock().await.borrow_mut().feed(id);
973        }
974
975        /// Start the watchdog.
976        pub async fn start(&self) {
977            self.watchdog.lock().await.borrow_mut().start();
978        }
979
980        /// Trigger a system reset.
981        pub async fn trigger_reset(&self) -> ! {
982            self.watchdog.lock().await.borrow_mut().trigger_reset()
983        }
984
985        /// Get the last reset reason.
986        pub async fn reset_reason(&self) -> Option<ResetReason> {
987            self.watchdog.lock().await.borrow().reset_reason()
988        }
989
990        /// Get the check interval
991        pub async fn get_check_interval(&self) -> <EmbassyClock as Clock>::Duration {
992            self.watchdog.lock().await.borrow().config.check_interval
993        }
994
995        /// Check if any tasks have starved
996        pub async fn check_tasks(&self) -> bool {
997            self.watchdog.lock().await.borrow_mut().check()
998        }
999    }
1000
1001    // For alloc feature
1002    #[cfg(feature = "alloc")]
1003    pub struct WatchdogTask<I>
1004    where
1005        I: 'static + Id,
1006    {
1007        runner: &'static WatchdogRunner<I>,
1008    }
1009
1010    #[cfg(feature = "alloc")]
1011    impl<I> WatchdogRunner<I>
1012    where
1013        I: 'static + Id,
1014    {
1015        pub fn create_task(&'static self) -> WatchdogTask<I> {
1016            WatchdogTask { runner: self }
1017        }
1018    }
1019
1020    #[cfg(feature = "alloc")]
1021    pub async fn watchdog_run<I>(task: WatchdogTask<I>) -> !
1022    where
1023        I: 'static + Id,
1024    {
1025        info!("Watchdog runner started");
1026
1027        // Start the watchdog
1028        task.runner.start().await;
1029
1030        // Get initial check interval
1031        let interval = task.runner.get_check_interval().await;
1032        let mut check_time = Instant::now() + interval;
1033
1034        loop {
1035            // Check for starved tasks.  We don't do anything
1036            let _ = task.runner.check_tasks().await;
1037
1038            // Wait before checking again
1039            Timer::at(check_time).await;
1040            check_time += interval;
1041        }
1042    }
1043
1044    /// A version of the Watchdog Task when not using the `alloc`` feature.
1045    ///
1046    /// There is an equivalent version of this when using the `alloc` feature
1047    /// which does not include the `const N: usize` type.
1048    #[cfg(not(feature = "alloc"))]
1049    pub struct NoAllocWatchdogTask<I, const N: usize>
1050    where
1051        I: 'static + Id,
1052    {
1053        runner: &'static WatchdogRunner<I, N>,
1054    }
1055
1056    #[cfg(not(feature = "alloc"))]
1057    impl<I, const N: usize> WatchdogRunner<I, N>
1058    where
1059        I: 'static + Id,
1060    {
1061        /// Used to create a watchdog task when not using the alloc feature.
1062        ///
1063        /// There is an equivalent version of this when using the `alloc` feature
1064        /// which does not include the `const N: usize` type.
1065        pub fn create_task(&'static self) -> NoAllocWatchdogTask<I, N> {
1066            NoAllocWatchdogTask { runner: self }
1067        }
1068    }
1069
1070    /// Watchdog Runner function, which will monitor tasks and reset the
1071    /// system if any.
1072    ///
1073    /// You must call this function from an async task to start and run the
1074    /// watchdog.  Using `spawner.must_spawn(watchdog_run(watchdog))` would
1075    /// likely be a good choice.
1076    #[cfg(not(feature = "alloc"))]
1077    pub async fn watchdog_run<I, const N: usize>(task: NoAllocWatchdogTask<I, N>) -> !
1078    where
1079        I: 'static + Id,
1080    {
1081        info!("Watchdog runner started");
1082
1083        // Start the watchdog
1084        task.runner.start().await;
1085
1086        // Get initial check interval
1087        let interval = task.runner.get_check_interval().await;
1088        let mut check_time = Instant::now() + interval;
1089
1090        loop {
1091            // Check for starved tasks.  We don't do anthing based on the
1092            // return code as check_tasks() handles feeding/starving the
1093            // hardware watchdog.
1094            let _ = task.runner.check_tasks().await;
1095
1096            // Wait before checking again
1097            Timer::at(check_time).await;
1098            check_time += interval;
1099        }
1100    }
1101}
1102
1103/// An async implementation of task-watchdog for use with the STM32 embassy
1104/// implementation.
1105///
1106/// This module requires the `stm32-embassy` feature.
1107#[cfg(feature = "stm32-embassy")]
1108pub mod embassy_stm32 {
1109    use super::{
1110        info, Clock, EmbassyClock, HardwareWatchdog, Id, ResetReason, Watchdog, WatchdogConfig,
1111    };
1112    use embassy_stm32::peripherals::IWDG;
1113    use embassy_stm32::wdg::IndependentWatchdog;
1114    use embassy_time::{Instant, Timer};
1115
1116    /// STM32 specific watchdog implementation.
1117    pub struct Stm32Watchdog {
1118        peripheral: Option<IWDG>,
1119        inner: Option<IndependentWatchdog<'static, IWDG>>,
1120    }
1121
1122    impl Stm32Watchdog {
1123        /// Create a new STM32 watchdog.
1124        #[must_use]
1125        pub fn new(peripheral: IWDG) -> Self {
1126            Self {
1127                peripheral: Some(peripheral),
1128                inner: None,
1129            }
1130        }
1131    }
1132
1133    impl HardwareWatchdog<EmbassyClock> for Stm32Watchdog {
1134        fn start(&mut self, timeout: embassy_time::Duration) {
1135            let timeout = timeout.as_micros();
1136            if timeout > u32::MAX as u64 {
1137                panic!("Watchdog timeout too large for STM32");
1138            }
1139            let peripheral = self
1140                .peripheral
1141                .take()
1142                .expect("STM32 Watchdog not properly initialized");
1143
1144            // Create the watchdog
1145            let mut wdg = IndependentWatchdog::new(peripheral, timeout as u32);
1146
1147            // Start it
1148            wdg.unleash();
1149
1150            // Store it
1151            self.inner = Some(wdg);
1152        }
1153
1154        fn feed(&mut self) {
1155            self.inner.as_mut().expect("Watchdog not started").pet();
1156        }
1157
1158        fn trigger_reset(&mut self) -> ! {
1159            cortex_m::peripheral::SCB::sys_reset();
1160        }
1161
1162        fn reset_reason(&self) -> Option<ResetReason> {
1163            None
1164        }
1165    }
1166
1167    /// An Embassy STM32 watchdog runner.
1168    #[cfg(feature = "alloc")]
1169    pub struct WatchdogRunner<I>
1170    where
1171        I: Id,
1172    {
1173        watchdog: embassy_sync::mutex::Mutex<
1174            embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex,
1175            core::cell::RefCell<Watchdog<I, Stm32Watchdog, EmbassyClock>>,
1176        >,
1177    }
1178
1179    #[cfg(not(feature = "alloc"))]
1180    pub struct WatchdogRunner<I, const N: usize>
1181    where
1182        I: Id,
1183    {
1184        watchdog: embassy_sync::mutex::Mutex<
1185            embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex,
1186            core::cell::RefCell<Watchdog<I, N, Stm32Watchdog, EmbassyClock>>,
1187        >,
1188    }
1189
1190    #[cfg(feature = "alloc")]
1191    impl<I> WatchdogRunner<I>
1192    where
1193        I: Id + 'static,
1194    {
1195        /// Create a new Embassy-compatible watchdog runner.
1196        pub fn new(hw_watchdog: IWDG, config: WatchdogConfig<EmbassyClock>) -> Self {
1197            let hw_watchdog = Stm32Watchdog::new(hw_watchdog);
1198            let watchdog = Watchdog::new(hw_watchdog, config, EmbassyClock);
1199            Self {
1200                watchdog: embassy_sync::mutex::Mutex::new(core::cell::RefCell::new(watchdog)),
1201            }
1202        }
1203
1204        /// Register a task with the watchdog.
1205        pub async fn register_task(&self, id: &I, max_duration: <EmbassyClock as Clock>::Duration) {
1206            self.watchdog
1207                .lock()
1208                .await
1209                .borrow_mut()
1210                .register_task(id, max_duration);
1211        }
1212
1213        /// De-register a task with the watchdog.
1214        pub async fn deregister_task(&self, id: &I) {
1215            self.watchdog.lock().await.borrow_mut().deregister_task(id);
1216        }
1217
1218        /// Feed the watchdog for a specific task.
1219        pub async fn feed(&self, id: &I) {
1220            self.watchdog.lock().await.borrow_mut().feed(id);
1221        }
1222
1223        /// Start the watchdog.
1224        pub async fn start(&self) {
1225            self.watchdog.lock().await.borrow_mut().start();
1226        }
1227
1228        /// Trigger a system reset.
1229        pub async fn trigger_reset(&self) -> ! {
1230            self.watchdog.lock().await.borrow_mut().trigger_reset()
1231        }
1232
1233        /// Get the last reset reason.
1234        pub async fn reset_reason(&self) -> Option<ResetReason> {
1235            self.watchdog.lock().await.borrow().reset_reason()
1236        }
1237
1238        /// Get the check interval
1239        pub async fn get_check_interval(&self) -> <EmbassyClock as Clock>::Duration {
1240            self.watchdog.lock().await.borrow().config.check_interval
1241        }
1242
1243        /// Check if any tasks have starved
1244        pub async fn check_tasks(&self) -> bool {
1245            self.watchdog.lock().await.borrow_mut().check()
1246        }
1247    }
1248
1249    #[cfg(not(feature = "alloc"))]
1250    impl<I, const N: usize> WatchdogRunner<I, N>
1251    where
1252        I: Id,
1253    {
1254        /// Create a new Embassy-compatible watchdog runner.
1255        pub fn new(hw_watchdog: IWDG, config: WatchdogConfig<EmbassyClock>) -> Self {
1256            let hw_watchdog = Stm32Watchdog::new(hw_watchdog);
1257            let watchdog = Watchdog::new(hw_watchdog, config, EmbassyClock);
1258            Self {
1259                watchdog: embassy_sync::mutex::Mutex::new(core::cell::RefCell::new(watchdog)),
1260            }
1261        }
1262
1263        /// Register a task with the watchdog.
1264        pub async fn register_task(&self, id: &I, max_duration: <EmbassyClock as Clock>::Duration) {
1265            self.watchdog
1266                .lock()
1267                .await
1268                .borrow_mut()
1269                .register_task(id, max_duration)
1270                .ok();
1271        }
1272
1273        /// Deregister a task with the watchdog.
1274        pub async fn deregister_task(&self, id: &I) {
1275            self.watchdog.lock().await.borrow_mut().deregister_task(id);
1276        }
1277
1278        /// Feed the watchdog for a specific task.
1279        pub async fn feed(&self, id: &I) {
1280            self.watchdog.lock().await.borrow_mut().feed(id);
1281        }
1282
1283        /// Start the watchdog.
1284        pub async fn start(&self) {
1285            self.watchdog.lock().await.borrow_mut().start();
1286        }
1287
1288        /// Trigger a system reset.
1289        pub async fn trigger_reset(&self) -> ! {
1290            self.watchdog.lock().await.borrow_mut().trigger_reset()
1291        }
1292
1293        /// Get the last reset reason.
1294        pub async fn reset_reason(&self) -> Option<ResetReason> {
1295            self.watchdog.lock().await.borrow().reset_reason()
1296        }
1297
1298        /// Get the check interval
1299        pub async fn get_check_interval(&self) -> <EmbassyClock as Clock>::Duration {
1300            self.watchdog.lock().await.borrow().config.check_interval
1301        }
1302
1303        /// Check if any tasks have starved
1304        pub async fn check_tasks(&self) -> bool {
1305            self.watchdog.lock().await.borrow_mut().check()
1306        }
1307    }
1308
1309    // For alloc feature
1310    #[cfg(feature = "alloc")]
1311    pub struct WatchdogTask<I>
1312    where
1313        I: 'static + Id,
1314    {
1315        runner: &'static WatchdogRunner<I>,
1316    }
1317
1318    #[cfg(feature = "alloc")]
1319    impl<I> WatchdogRunner<I>
1320    where
1321        I: 'static + Id,
1322    {
1323        pub fn create_task(&'static self) -> WatchdogTask<I> {
1324            WatchdogTask { runner: self }
1325        }
1326    }
1327
1328    /// Watchdog Runner function, which will monitor tasks and reset the
1329    /// system if any.  The user must call this function from an async task
1330    /// to start and run the watchdog.
1331    #[cfg(feature = "alloc")]
1332    pub async fn watchdog_run<I>(task: WatchdogTask<I>) -> !
1333    where
1334        I: 'static + Id,
1335    {
1336        info!("Watchdog runner started");
1337
1338        // Start the watchdog
1339        task.runner.start().await;
1340
1341        // Get initial check interval
1342        let interval = task.runner.get_check_interval().await;
1343        let mut check_time = Instant::now() + interval;
1344
1345        loop {
1346            // Check for starved tasks.  We don't do anthing based on the
1347            // return code as check_tasks() handles feeding/starving the
1348            // hardware watchdog.
1349            let _ = task.runner.check_tasks().await;
1350
1351            // Wait before checking again
1352            Timer::at(check_time).await;
1353            check_time += interval;
1354        }
1355    }
1356
1357    // For no_alloc feature
1358    #[cfg(not(feature = "alloc"))]
1359    pub struct NoAllocWatchdogTask<I, const N: usize>
1360    where
1361        I: 'static + Id,
1362    {
1363        runner: &'static WatchdogRunner<I, N>,
1364    }
1365
1366    #[cfg(not(feature = "alloc"))]
1367    impl<I, const N: usize> WatchdogRunner<I, N>
1368    where
1369        I: 'static + Id,
1370    {
1371        pub fn create_task(&'static self) -> NoAllocWatchdogTask<I, N> {
1372            NoAllocWatchdogTask { runner: self }
1373        }
1374    }
1375
1376    /// Watchdog Runner, which will monitor tasks and reset the system if any
1377    /// registered task fails to feed the watchdog.
1378    #[cfg(not(feature = "alloc"))]
1379    pub async fn watchdog_run<I, const N: usize>(task: NoAllocWatchdogTask<I, N>) -> !
1380    where
1381        I: 'static + Id,
1382    {
1383        info!("Watchdog runner started");
1384
1385        // Start the watchdog
1386        task.runner.start().await;
1387
1388        // Get initial check interval
1389        let interval = task.runner.get_check_interval().await;
1390        let mut check_time = Instant::now() + interval;
1391
1392        loop {
1393            // Check for starved tasks.  We don't do anthing based on the
1394            // return code as check_tasks() handles feeding/starving the
1395            // hardware watchdog.
1396            let _ = task.runner.check_tasks().await;
1397
1398            // Wait before checking again
1399            Timer::at(check_time).await;
1400            check_time += interval;
1401        }
1402    }
1403}
1404
1405/// An async implementation of task-watchdog for use with the nRF embassy
1406/// implementation.
1407///
1408/// This module requires the `nrf-embassy` feature.
1409#[cfg(feature = "nrf-embassy")]
1410pub mod embassy_nrf {
1411    use super::{
1412        info, warn, Clock, EmbassyClock, HardwareWatchdog, Id, ResetReason, Watchdog, WatchdogConfig,
1413    };
1414    use embassy_nrf::peripherals::WDT;
1415    use embassy_nrf::wdt::{Watchdog as NrfWatchdogStruct, WatchdogHandle, Config};
1416    use embassy_time::{Instant, Timer};
1417
1418    /// nRF specific watchdog implementation.
1419    pub struct NrfWatchdog {
1420        peripheral: Option<WDT>,
1421        inner: Option<WatchdogHandle>,
1422    }
1423
1424    impl NrfWatchdog {
1425        /// Create a new nRF watchdog.
1426        #[must_use]
1427        pub fn new(peripheral: WDT) -> Self {
1428            Self {
1429                peripheral: Some(peripheral),
1430                inner: None,
1431            }
1432        }
1433    }
1434
1435    impl HardwareWatchdog<EmbassyClock> for NrfWatchdog {
1436        fn start(&mut self, timeout: embassy_time::Duration) {
1437            // Convert the requested timeout to ticks (32,768Hz)
1438            let ticks = timeout.as_micros() * 1_000_000 / 32_768_000;
1439            if ticks < 15 {
1440                warn!("Watchdog timeout {} ticks too small for nRF - will be set to 15 ticks", ticks);
1441                panic!("Watchdog timeout too large for nRF");
1442            }
1443            let mut config = Config::default();
1444            if ticks > u32::MAX as u64 {
1445                panic!("Watchdog timeout {} ticks too large for nRF", ticks);
1446            }
1447            config.timeout_ticks = ticks as u32;
1448            let peripheral = self
1449                .peripheral
1450                .take()
1451                .expect("nRF Watchdog not properly initialized");
1452
1453            // Create the watchdog and store it
1454            let (_wdt, [handle]) = NrfWatchdogStruct::try_new(peripheral, config).unwrap_or_else(|_| panic!("Failed to create nRF watchdog"));
1455            self.inner = Some(handle);
1456        }
1457
1458        fn feed(&mut self) {
1459            self.inner.as_mut().expect("Watchdog not started").pet();
1460        }
1461
1462        fn trigger_reset(&mut self) -> ! {
1463            cortex_m::peripheral::SCB::sys_reset();
1464        }
1465
1466        fn reset_reason(&self) -> Option<ResetReason> {
1467            None
1468        }
1469    }
1470
1471    /// An Embassy Nrf watchdog runner.
1472    #[cfg(feature = "alloc")]
1473    pub struct WatchdogRunner<I>
1474    where
1475        I: Id,
1476    {
1477        watchdog: embassy_sync::mutex::Mutex<
1478            embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex,
1479            core::cell::RefCell<Watchdog<I, NrfWatchdog, EmbassyClock>>,
1480        >,
1481    }
1482
1483    #[cfg(not(feature = "alloc"))]
1484    pub struct WatchdogRunner<I, const N: usize>
1485    where
1486        I: Id,
1487    {
1488        watchdog: embassy_sync::mutex::Mutex<
1489            embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex,
1490            core::cell::RefCell<Watchdog<I, N, NrfWatchdog, EmbassyClock>>,
1491        >,
1492    }
1493
1494    #[cfg(feature = "alloc")]
1495    impl<I> WatchdogRunner<I>
1496    where
1497        I: Id + 'static,
1498    {
1499        /// Create a new Embassy-compatible watchdog runner.
1500        pub fn new(hw_watchdog: WDT, config: WatchdogConfig<EmbassyClock>) -> Self {
1501            let hw_watchdog = NrfWatchdog::new(hw_watchdog);
1502            let watchdog = Watchdog::new(hw_watchdog, config, EmbassyClock);
1503            Self {
1504                watchdog: embassy_sync::mutex::Mutex::new(core::cell::RefCell::new(watchdog)),
1505            }
1506        }
1507
1508        /// Register a task with the watchdog.
1509        pub async fn register_task(&self, id: &I, max_duration: <EmbassyClock as Clock>::Duration) {
1510            self.watchdog
1511                .lock()
1512                .await
1513                .borrow_mut()
1514                .register_task(id, max_duration);
1515        }
1516
1517        /// De-register a task with the watchdog.
1518        pub async fn deregister_task(&self, id: &I) {
1519            self.watchdog.lock().await.borrow_mut().deregister_task(id);
1520        }
1521
1522        /// Feed the watchdog for a specific task.
1523        pub async fn feed(&self, id: &I) {
1524            self.watchdog.lock().await.borrow_mut().feed(id);
1525        }
1526
1527        /// Start the watchdog.
1528        pub async fn start(&self) {
1529            self.watchdog.lock().await.borrow_mut().start();
1530        }
1531
1532        /// Trigger a system reset.
1533        pub async fn trigger_reset(&self) -> ! {
1534            self.watchdog.lock().await.borrow_mut().trigger_reset()
1535        }
1536
1537        /// Get the last reset reason.
1538        pub async fn reset_reason(&self) -> Option<ResetReason> {
1539            self.watchdog.lock().await.borrow().reset_reason()
1540        }
1541
1542        /// Get the check interval
1543        pub async fn get_check_interval(&self) -> <EmbassyClock as Clock>::Duration {
1544            self.watchdog.lock().await.borrow().config.check_interval
1545        }
1546
1547        /// Check if any tasks have starved
1548        pub async fn check_tasks(&self) -> bool {
1549            self.watchdog.lock().await.borrow_mut().check()
1550        }
1551    }
1552
1553    #[cfg(not(feature = "alloc"))]
1554    impl<I, const N: usize> WatchdogRunner<I, N>
1555    where
1556        I: Id,
1557    {
1558        /// Create a new Embassy-compatible watchdog runner.
1559        pub fn new(hw_watchdog: WDT, config: WatchdogConfig<EmbassyClock>) -> Self {
1560            let hw_watchdog = NrfWatchdog::new(hw_watchdog);
1561            let watchdog = Watchdog::new(hw_watchdog, config, EmbassyClock);
1562            Self {
1563                watchdog: embassy_sync::mutex::Mutex::new(core::cell::RefCell::new(watchdog)),
1564            }
1565        }
1566
1567        /// Register a task with the watchdog.
1568        pub async fn register_task(&self, id: &I, max_duration: <EmbassyClock as Clock>::Duration) {
1569            self.watchdog
1570                .lock()
1571                .await
1572                .borrow_mut()
1573                .register_task(id, max_duration)
1574                .ok();
1575        }
1576
1577        /// Deregister a task with the watchdog.
1578        pub async fn deregister_task(&self, id: &I) {
1579            self.watchdog.lock().await.borrow_mut().deregister_task(id);
1580        }
1581
1582        /// Feed the watchdog for a specific task.
1583        pub async fn feed(&self, id: &I) {
1584            self.watchdog.lock().await.borrow_mut().feed(id);
1585        }
1586
1587        /// Start the watchdog.
1588        pub async fn start(&self) {
1589            self.watchdog.lock().await.borrow_mut().start();
1590        }
1591
1592        /// Trigger a system reset.
1593        pub async fn trigger_reset(&self) -> ! {
1594            self.watchdog.lock().await.borrow_mut().trigger_reset()
1595        }
1596
1597        /// Get the last reset reason.
1598        pub async fn reset_reason(&self) -> Option<ResetReason> {
1599            self.watchdog.lock().await.borrow().reset_reason()
1600        }
1601
1602        /// Get the check interval
1603        pub async fn get_check_interval(&self) -> <EmbassyClock as Clock>::Duration {
1604            self.watchdog.lock().await.borrow().config.check_interval
1605        }
1606
1607        /// Check if any tasks have starved
1608        pub async fn check_tasks(&self) -> bool {
1609            self.watchdog.lock().await.borrow_mut().check()
1610        }
1611    }
1612
1613    // For alloc feature
1614    #[cfg(feature = "alloc")]
1615    pub struct WatchdogTask<I>
1616    where
1617        I: 'static + Id,
1618    {
1619        runner: &'static WatchdogRunner<I>,
1620    }
1621
1622    #[cfg(feature = "alloc")]
1623    impl<I> WatchdogRunner<I>
1624    where
1625        I: 'static + Id,
1626    {
1627        pub fn create_task(&'static self) -> WatchdogTask<I> {
1628            WatchdogTask { runner: self }
1629        }
1630    }
1631
1632    /// Watchdog Runner function, which will monitor tasks and reset the
1633    /// system if any.  The user must call this function from an async task
1634    /// to start and run the watchdog.
1635    #[cfg(feature = "alloc")]
1636    pub async fn watchdog_run<I>(task: WatchdogTask<I>) -> !
1637    where
1638        I: 'static + Id,
1639    {
1640        info!("Watchdog runner started");
1641
1642        // Start the watchdog
1643        task.runner.start().await;
1644
1645        // Get initial check interval
1646        let interval = task.runner.get_check_interval().await;
1647        let mut check_time = Instant::now() + interval;
1648
1649        loop {
1650            // Check for starved tasks.  We don't do anthing based on the
1651            // return code as check_tasks() handles feeding/starving the
1652            // hardware watchdog.
1653            let _ = task.runner.check_tasks().await;
1654
1655            // Wait before checking again
1656            Timer::at(check_time).await;
1657            check_time += interval;
1658        }
1659    }
1660
1661    // For no_alloc feature
1662    #[cfg(not(feature = "alloc"))]
1663    pub struct NoAllocWatchdogTask<I, const N: usize>
1664    where
1665        I: 'static + Id,
1666    {
1667        runner: &'static WatchdogRunner<I, N>,
1668    }
1669
1670    #[cfg(not(feature = "alloc"))]
1671    impl<I, const N: usize> WatchdogRunner<I, N>
1672    where
1673        I: 'static + Id,
1674    {
1675        pub fn create_task(&'static self) -> NoAllocWatchdogTask<I, N> {
1676            NoAllocWatchdogTask { runner: self }
1677        }
1678    }
1679
1680    /// Watchdog Runner, which will monitor tasks and reset the system if any
1681    /// registered task fails to feed the watchdog.
1682    #[cfg(not(feature = "alloc"))]
1683    pub async fn watchdog_run<I, const N: usize>(task: NoAllocWatchdogTask<I, N>) -> !
1684    where
1685        I: 'static + Id,
1686    {
1687        info!("Watchdog runner started");
1688
1689        // Start the watchdog
1690        task.runner.start().await;
1691
1692        // Get initial check interval
1693        let interval = task.runner.get_check_interval().await;
1694        let mut check_time = Instant::now() + interval;
1695
1696        loop {
1697            // Check for starved tasks.  We don't do anthing based on the
1698            // return code as check_tasks() handles feeding/starving the
1699            // hardware watchdog.
1700            let _ = task.runner.check_tasks().await;
1701
1702            // Wait before checking again
1703            Timer::at(check_time).await;
1704            check_time += interval;
1705        }
1706    }
1707}
1708
1709/// An async implementation of task-watchdog for use with the ESP32 embassy
1710/// implementation.
1711///
1712/// This module requires the `esp32-embassy` feature.
1713#[cfg(feature = "esp32-embassy")]
1714pub mod embassy_esp32 {
1715    use super::{
1716        info, Clock, EmbassyClock, HardwareWatchdog, Id, ResetReason, Watchdog, WatchdogConfig,
1717    };
1718    use embassy_time::{Instant, Timer};
1719    use esp_hal::peripherals::TIMG0;
1720    use esp_hal::timer::timg::MwdtStage;
1721    use esp_hal::timer::timg::TimerGroup;
1722    use esp_hal::timer::timg::Wdt;
1723
1724    /// ESP32 specific watchdog implementation.
1725    pub struct Esp32Watchdog {
1726        inner: Wdt<TIMG0>,
1727    }
1728
1729    impl Esp32Watchdog {
1730        /// Create a new ESP32 watchdog.
1731        ///
1732        /// Arguments:
1733        /// - `timg0` - The TimerGroup to use for the watchdog.
1734        #[must_use]
1735        pub fn new(timg0: TimerGroup<TIMG0>) -> Self {
1736            let wdt = timg0.wdt;
1737            Self { inner: wdt }
1738        }
1739    }
1740
1741    impl HardwareWatchdog<EmbassyClock> for Esp32Watchdog {
1742        fn start(&mut self, timeout: embassy_time::Duration) {
1743            self.inner.set_timeout(
1744                MwdtStage::Stage0,
1745                esp_hal::time::Duration::from_millis(timeout.as_millis()),
1746            );
1747            self.inner.enable();
1748        }
1749
1750        fn feed(&mut self) {
1751            self.inner.feed();
1752        }
1753
1754        fn trigger_reset(&mut self) -> ! {
1755            esp_hal::system::software_reset();
1756        }
1757
1758        fn reset_reason(&self) -> Option<ResetReason> {
1759            None
1760        }
1761    }
1762
1763    /// An Embassy ESP32 watchdog runner.
1764    #[cfg(feature = "alloc")]
1765    pub struct WatchdogRunner<I>
1766    where
1767        I: Id,
1768    {
1769        watchdog: embassy_sync::mutex::Mutex<
1770            embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex,
1771            core::cell::RefCell<Watchdog<I, Esp32Watchdog, EmbassyClock>>,
1772        >,
1773    }
1774
1775    #[cfg(not(feature = "alloc"))]
1776    pub struct WatchdogRunner<I, const N: usize>
1777    where
1778        I: Id,
1779    {
1780        watchdog: embassy_sync::mutex::Mutex<
1781            embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex,
1782            core::cell::RefCell<Watchdog<I, N, Esp32Watchdog, EmbassyClock>>,
1783        >,
1784    }
1785
1786    #[cfg(feature = "alloc")]
1787    impl<I> WatchdogRunner<I>
1788    where
1789        I: Id + 'static,
1790    {
1791        /// Create a new Embassy-compatible watchdog runner.
1792        pub fn new(timg0: TimerGroup<TIMG0>, config: WatchdogConfig<EmbassyClock>) -> Self {
1793            let hw_watchdog = Esp32Watchdog::new(timg0);
1794            let watchdog = Watchdog::new(hw_watchdog, config, EmbassyClock);
1795            Self {
1796                watchdog: embassy_sync::mutex::Mutex::new(core::cell::RefCell::new(watchdog)),
1797            }
1798        }
1799
1800        /// Register a task with the watchdog.
1801        pub async fn register_task(&self, id: &I, max_duration: <EmbassyClock as Clock>::Duration) {
1802            self.watchdog
1803                .lock()
1804                .await
1805                .borrow_mut()
1806                .register_task(id, max_duration);
1807        }
1808
1809        /// De-register a task with the watchdog.
1810        pub async fn deregister_task(&self, id: &I) {
1811            self.watchdog.lock().await.borrow_mut().deregister_task(id);
1812        }
1813
1814        /// Feed the watchdog for a specific task.
1815        pub async fn feed(&self, id: &I) {
1816            self.watchdog.lock().await.borrow_mut().feed(id);
1817        }
1818
1819        /// Start the watchdog.
1820        pub async fn start(&self) {
1821            self.watchdog.lock().await.borrow_mut().start();
1822        }
1823
1824        /// Trigger a system reset.
1825        pub async fn trigger_reset(&self) -> ! {
1826            self.watchdog.lock().await.borrow_mut().trigger_reset()
1827        }
1828
1829        /// Get the last reset reason.
1830        pub async fn reset_reason(&self) -> Option<ResetReason> {
1831            self.watchdog.lock().await.borrow().reset_reason()
1832        }
1833
1834        /// Get the check interval
1835        pub async fn get_check_interval(&self) -> <EmbassyClock as Clock>::Duration {
1836            self.watchdog.lock().await.borrow().config.check_interval
1837        }
1838
1839        /// Check if any tasks have starved
1840        pub async fn check_tasks(&self) -> bool {
1841            self.watchdog.lock().await.borrow_mut().check()
1842        }
1843    }
1844
1845    #[cfg(not(feature = "alloc"))]
1846    impl<I, const N: usize> WatchdogRunner<I, N>
1847    where
1848        I: Id,
1849    {
1850        /// Create a new Embassy-compatible watchdog runner.
1851        pub fn new(timg0: TimerGroup<TIMG0>, config: WatchdogConfig<EmbassyClock>) -> Self {
1852            let hw_watchdog = Esp32Watchdog::new(timg0);
1853            let watchdog = Watchdog::new(hw_watchdog, config, EmbassyClock);
1854            Self {
1855                watchdog: embassy_sync::mutex::Mutex::new(core::cell::RefCell::new(watchdog)),
1856            }
1857        }
1858
1859        /// Register a task with the watchdog.
1860        pub async fn register_task(&self, id: &I, max_duration: <EmbassyClock as Clock>::Duration) {
1861            self.watchdog
1862                .lock()
1863                .await
1864                .borrow_mut()
1865                .register_task(id, max_duration)
1866                .ok();
1867        }
1868
1869        /// Deregister a task with the watchdog.
1870        pub async fn deregister_task(&self, id: &I) {
1871            self.watchdog.lock().await.borrow_mut().deregister_task(id);
1872        }
1873
1874        /// Feed the watchdog for a specific task.
1875        pub async fn feed(&self, id: &I) {
1876            self.watchdog.lock().await.borrow_mut().feed(id);
1877        }
1878
1879        /// Start the watchdog.
1880        pub async fn start(&self) {
1881            self.watchdog.lock().await.borrow_mut().start();
1882        }
1883
1884        /// Trigger a system reset.
1885        pub async fn trigger_reset(&self) -> ! {
1886            self.watchdog.lock().await.borrow_mut().trigger_reset()
1887        }
1888
1889        /// Get the last reset reason.
1890        pub async fn reset_reason(&self) -> Option<ResetReason> {
1891            self.watchdog.lock().await.borrow().reset_reason()
1892        }
1893
1894        /// Get the check interval
1895        pub async fn get_check_interval(&self) -> <EmbassyClock as Clock>::Duration {
1896            self.watchdog.lock().await.borrow().config.check_interval
1897        }
1898
1899        /// Check if any tasks have starved
1900        pub async fn check_tasks(&self) -> bool {
1901            self.watchdog.lock().await.borrow_mut().check()
1902        }
1903    }
1904
1905    // For alloc feature
1906    #[cfg(feature = "alloc")]
1907    pub struct WatchdogTask<I>
1908    where
1909        I: 'static + Id,
1910    {
1911        runner: &'static WatchdogRunner<I>,
1912    }
1913
1914    #[cfg(feature = "alloc")]
1915    impl<I> WatchdogRunner<I>
1916    where
1917        I: 'static + Id,
1918    {
1919        pub fn create_task(&'static self) -> WatchdogTask<I> {
1920            WatchdogTask { runner: self }
1921        }
1922    }
1923
1924    /// Watchdog Runner function, which will monitor tasks and reset the
1925    /// system if any.  The user must call this function from an async task
1926    /// to start and run the watchdog.
1927    #[cfg(feature = "alloc")]
1928    pub async fn watchdog_run<I>(task: WatchdogTask<I>) -> !
1929    where
1930        I: 'static + Id,
1931    {
1932        info!("Watchdog runner started");
1933
1934        // Start the watchdog
1935        task.runner.start().await;
1936
1937        // Get initial check interval
1938        let interval = task.runner.get_check_interval().await;
1939        let mut check_time = Instant::now() + interval;
1940
1941        loop {
1942            // Check for starved tasks.  We don't do anthing based on the
1943            // return code as check_tasks() handles feeding/starving the
1944            // hardware watchdog.
1945            let _ = task.runner.check_tasks().await;
1946
1947            // Wait before checking again
1948            Timer::at(check_time).await;
1949            check_time += interval;
1950        }
1951    }
1952
1953    // For no_alloc feature
1954    #[cfg(not(feature = "alloc"))]
1955    pub struct NoAllocWatchdogTask<I, const N: usize>
1956    where
1957        I: 'static + Id,
1958    {
1959        runner: &'static WatchdogRunner<I, N>,
1960    }
1961
1962    #[cfg(not(feature = "alloc"))]
1963    impl<I, const N: usize> WatchdogRunner<I, N>
1964    where
1965        I: 'static + Id,
1966    {
1967        pub fn create_task(&'static self) -> NoAllocWatchdogTask<I, N> {
1968            NoAllocWatchdogTask { runner: self }
1969        }
1970    }
1971
1972    /// Watchdog Runner, which will monitor tasks and reset the system if any
1973    /// registered task fails to feed the watchdog.
1974    #[cfg(not(feature = "alloc"))]
1975    pub async fn watchdog_run<I, const N: usize>(task: NoAllocWatchdogTask<I, N>) -> !
1976    where
1977        I: 'static + Id,
1978    {
1979        info!("Watchdog runner started");
1980
1981        // Start the watchdog
1982        task.runner.start().await;
1983
1984        // Get initial check interval
1985        let interval = task.runner.get_check_interval().await;
1986        let mut check_time = Instant::now() + interval;
1987
1988        loop {
1989            // Check for starved tasks.  We don't do anthing based on the
1990            // return code as check_tasks() handles feeding/starving the
1991            // hardware watchdog.
1992            let _ = task.runner.check_tasks().await;
1993
1994            // Wait before checking again
1995            Timer::at(check_time).await;
1996            check_time += interval;
1997        }
1998    }
1999}