Skip to main content

ra_hal/watchdog/
iwdt.rs

1//! Independent Watchdog Timer (`IWDT`) ยง26.
2//!
3//! This watchdog operates off of its own 15 kHz clock independently of the peripheral clock that drives `WDT`.
4
5use core::marker::PhantomData;
6
7use cortex_m::asm;
8use embassy_hal_internal::{Peri, PeripheralType, interrupt::InterruptExt as _};
9
10use crate::{
11    event_link::{IcuInterrupt as _, InterruptEvent},
12    interrupt,
13    interrupt::typelevel::{Handler as InterruptHandler, Interrupt as InterruptType},
14    pac::{
15        self,
16        dbg::vals::DbgstopIwdt,
17        osm::vals::{IwdtCks, IwdtTops, StartMode},
18    },
19    peripherals,
20    watchdog::Action,
21};
22
23/// Interrupt handler for the watchdog timer, used with [`Watchdog::new_handler`].
24pub struct IwdtInterruptHandler<I: Instance> {
25    _phantom: PhantomData<I>,
26}
27
28/// Watchdog driver.
29pub struct Watchdog<'d, I: Instance> {
30    phantom: PhantomData<&'d I>,
31}
32
33/// Watchdog instance.
34#[allow(private_bounds)]
35pub trait Instance: SealedInstance + PeripheralType {}
36
37pub(crate) trait SealedInstance {
38    fn regs() -> pac::iwdt::Iwdt;
39}
40
41impl<'d, I: Instance> Watchdog<'d, I> {
42    /// Creates a new `IWDT` driver.
43    ///
44    /// # Notes
45    /// `IWDT` can only be configured via `OFS0` and cannot be configured or enabled at runtime.
46    pub fn new(peri: Peri<'d, I>, action: Action) -> Self {
47        let _ = peri;
48
49        let osm = pac::OSM;
50        let ofs0 = osm.ofs0().read();
51
52        if ofs0.iwdtstrt() != StartMode::AutoStart {
53            warn!("IWDT: Not enabled");
54        } else if ofs0.iwdtstpctl() {
55            warn!("IWDT: Suspended in sleep mode, may not behave as expected with e.g. Embassy")
56        }
57
58        match action {
59            Action::Interrupt => {
60                debug!("IWDT: Enabling NMI");
61
62                cfg_select! {
63                    not(ra8m1) => {
64                        let icu = pac::ICU;
65
66                        icu.nmicr().write(|r| r.set_nflten(false));
67                        icu.nmicr().write(|r| r.set_nmimd(true));
68                        icu.nmiclr().write(|r| r.set_nmiclr(true));
69                    },
70                    ra8m1 => {
71                        let icu = pac::ICU;
72                        let icu_common = pac::ICU_COMMON;
73
74                        icu_common.nmicr().write(|r| r.set_nflten(false));
75                        icu_common.nmicr().write(|r| r.set_nmimd(true));
76                        icu.nmiclr().write(|r| r.set_nmiclr(true));
77                    },
78                }
79
80                icu.nmier().write(|r| r.set_iwdten(true));
81            }
82            Action::Reset => {}
83            Action::Handler => {}
84        }
85
86        // The timer will never run in debug mode while execution is stopped.
87        // This just enables the timer when running in debug mode.
88        let dbg = pac::DBG;
89        dbg.dbgstopcr()
90            .write(|r| r.set_dbgstop_iwdt(DbgstopIwdt::Enable));
91
92        {
93            let osm = pac::OSM;
94            let ofs0 = osm.ofs0().read();
95            let divider: u16 = match ofs0.iwdtcks() {
96                IwdtCks::_1 => 1,
97                IwdtCks::_16 => 16,
98                IwdtCks::_32 => 32,
99                IwdtCks::_64 => 64,
100                IwdtCks::_128 => 128,
101                IwdtCks::_256 => 256,
102                _ => unreachable!(),
103            };
104            let period: u16 = match ofs0.iwdttops() {
105                IwdtTops::_128 => 128,
106                IwdtTops::_512 => 512,
107                IwdtTops::_1024 => 1024,
108                IwdtTops::_2048 => 2048,
109            };
110
111            let timeout = f32::from(period) / (15_000.0 / f32::from(divider));
112            debug!("IWDT: timeout: {} sec", timeout);
113        }
114
115        Self {
116            phantom: PhantomData,
117        }
118    }
119
120    /// Configures `IWDT` to generate an interrupt, attaches a handler, and returns a new driver.
121    #[inline]
122    pub fn new_handler<IwdtInt: InterruptType>(
123        peri: Peri<'d, I>,
124        irq: impl interrupt::typelevel::Binding<IwdtInt, IwdtInterruptHandler<I>>,
125    ) -> Self {
126        let _ = peri;
127        let _ = irq;
128
129        // Safety: The interrupt handler is defined by the irq argument and thus the interrupt is safe to enable.
130        unsafe {
131            IwdtInt::IRQ.enable();
132            IwdtInt::IRQ.icu_enable(InterruptEvent::IwdtUnderflow);
133        }
134
135        Self::new(peri, Action::Handler)
136    }
137
138    /// Refreshes the `IWDT` counter to prevent a timeout.
139    pub fn refresh(&mut self) {
140        let iwdt = I::regs();
141
142        asm::dsb();
143        iwdt.iwdtrr().write_value(0x00);
144        asm::dsb();
145        iwdt.iwdtrr().write_value(0xFF);
146    }
147
148    /// Returns the current value of the `IWDT` counter.
149    #[inline(always)]
150    pub fn value(&self) -> u16 {
151        let iwdt = I::regs();
152
153        iwdt.iwdtsr().read().cntval()
154    }
155}
156
157impl Instance for peripherals::IWDT {}
158
159impl SealedInstance for peripherals::IWDT {
160    #[inline(always)]
161    fn regs() -> pac::iwdt::Iwdt {
162        pac::IWDT
163    }
164}
165
166impl<I: Instance, IwdtInt: InterruptType> InterruptHandler<IwdtInt> for IwdtInterruptHandler<I> {
167    unsafe fn on_interrupt() {
168        IwdtInt::IRQ.icu_unpend();
169
170        panic!("Independent watchdog underflow");
171    }
172}