Skip to main content

gd32e2_hal/watchdog/
wwdgt.rs

1//! Window watchdog timer.
2//!
3//! Resets the chip for feeding it too early as well as too late. The early
4//! bound is the point of this watchdog: code that has run away into a loop
5//! still calling `feed` keeps a plain watchdog happy, but breaks the pace this
6//! one checks.
7//!
8//! Clocked from `PCLK1`, so unlike [`Fwdgt`](crate::watchdog::Fwdgt) its timeout
9//! moves with the bus clock and spans tens of milliseconds rather than seconds.
10
11use crate::pac;
12use crate::rcu::{Enable, Rcu, Reset};
13
14/// Largest period, in counter ticks: the timeout is counted by `CNT[5:0]`.
15const TICKS_MAX: u8 = 0x3F;
16/// `CNT[6]`, standing for "the counter is still alive" — the chip resets the
17/// moment it goes low, so it sits above the timeout rather than inside it.
18const ALIVE_BIT: u8 = 1 << 6;
19
20/// Prescaler on the counter clock, named after the `PSC` field it writes.
21///
22/// The names count that field alone: the counter runs at `PCLK1 / 4096 / N`, so
23/// even [`Div1`](Self::Div1) is already `PCLK1` divided by 4096.
24#[derive(Clone, Copy, PartialEq, Eq)]
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26#[allow(missing_docs)]
27pub enum WwdgtPsc {
28    Div1 = 0b00,
29    Div2 = 0b01,
30    Div4 = 0b10,
31    Div8 = 0b11,
32}
33
34/// Checks a period and a window and returns them as the register expects them.
35///
36/// # Panics
37///
38/// If either value needs more than 6 bits, or if `win` exceeds `cnt`.
39fn checked_bits(cnt: u8, win: u8) -> (u8, u8) {
40    assert!(cnt <= TICKS_MAX, "WWDGT period must fit in 6 bits");
41    assert!(win <= TICKS_MAX, "WWDGT window must fit in 6 bits");
42    assert!(
43        win <= cnt,
44        "WWDGT window must not outlast the period it opens in"
45    );
46    (cnt | ALIVE_BIT, win | ALIVE_BIT)
47}
48
49/// The watchdog before it is started, the only state it can be configured in.
50pub struct Wwdgt {
51    wwdgt: pac::Wwdgt,
52}
53
54impl Wwdgt {
55    /// Sets the period and the window and starts counting down; there is no way
56    /// back.
57    ///
58    /// Both `cnt` and `win` are counted in ticks of `PCLK1 / 4096 / psc`, from 0
59    /// to 63 — the register's own top bit is added here, so a value that would
60    /// reset the chip on the spot cannot be passed in. The period lasts
61    /// `cnt + 1` ticks, and the window opens `cnt - win` ticks into it: feeding
62    /// before that resets the chip just as failing to feed does.
63    ///
64    /// # Panics
65    ///
66    /// If either value needs more than 6 bits, or if `win` exceeds `cnt`.
67    pub fn start(self, psc: WwdgtPsc, cnt: u8, win: u8) -> WwdgtRunning {
68        let (cnt, win) = checked_bits(cnt, win);
69        self.wwdgt
70            .cfg()
71            .modify(|_, w| w.psc().bits(psc as u8).win().bits(win));
72        self.wwdgt
73            .ctl()
74            .modify(|_, w| w.cnt().bits(cnt).wdgten().enabled());
75        WwdgtRunning {
76            wwdgt: self.wwdgt,
77            cnt,
78        }
79    }
80}
81
82/// The watchdog once it is counting down.
83///
84/// No way out: `WDGTEN` ignores a written zero and only a hardware reset clears
85/// it, so neither the peripheral nor the period comes back.
86#[must_use = "the watchdog keeps running; without this value nothing can feed it"]
87pub struct WwdgtRunning {
88    wwdgt: pac::Wwdgt,
89    cnt: u8,
90}
91
92impl WwdgtRunning {
93    /// Reloads the counter, which must happen inside the window: too early
94    /// resets the chip exactly as too late does.
95    pub fn feed(&mut self) {
96        self.wwdgt.ctl().write(|w| w.cnt().bits(self.cnt));
97    }
98    /// Changes the period and the window, taking effect at the next
99    /// [`feed`](Self::feed).
100    ///
101    /// The new window applies at once, so the next feed is already judged by it.
102    ///
103    /// # Panics
104    ///
105    /// On the same three conditions as [`Wwdgt::start`].
106    pub fn set_period(&mut self, cnt: u8, win: u8) {
107        let (cnt, win) = checked_bits(cnt, win);
108        self.wwdgt.cfg().modify(|_, w| w.win().bits(win));
109        self.cnt = cnt;
110    }
111
112    /// Lets the counter reaching `0x40` raise an interrupt — one tick before
113    /// the reset, the last moment anything can still run.
114    ///
115    /// Irreversible, like the watchdog itself: `EWIE` ignores a written zero, so
116    /// there is no `unlisten`. Unmasking the line in the NVIC is the caller's.
117    pub fn listen(&mut self) {
118        self.wwdgt.cfg().modify(|_, w| w.ewie().enable());
119    }
120    /// Whether the early wakeup interrupt is enabled.
121    pub fn is_listening(&self) -> bool {
122        self.wwdgt.cfg().read().ewie().bit_is_set()
123    }
124    /// Whether the counter has reached `0x40`.
125    ///
126    /// Hardware sets the flag whether or not [`listen`](Self::listen) was
127    /// called, so this doubles as a plain poll for "one tick left".
128    pub fn is_pending(&self) -> bool {
129        self.wwdgt.stat().read().ewif().is_pending()
130    }
131    /// Clears the flag, which a handler must do before returning — the flag is
132    /// the request, and hardware never drops it.
133    ///
134    /// The flag rises again for as long as the counter sits at `0x40`, so a
135    /// handler that only clears it is entered over and over until the reset.
136    /// For exactly one entry, mask the line in the NVIC inside the handler.
137    pub fn clear_interrupt(&mut self) {
138        self.wwdgt.stat().write(|w| w.ewif().finished());
139    }
140}
141
142/// Entry point on the raw peripheral.
143pub trait WwdgtExt {
144    /// Enables the peripheral's clock and resets it.
145    ///
146    /// Nothing is started here — the counter only begins running once
147    /// [`start`](Wwdgt::start) sets `WDGTEN`.
148    fn constrain(self, rcu: &mut Rcu) -> Wwdgt;
149}
150
151impl WwdgtExt for pac::Wwdgt {
152    fn constrain(self, rcu: &mut Rcu) -> Wwdgt {
153        <pac::Wwdgt as Enable>::enable(rcu);
154        <pac::Wwdgt as Reset>::reset(rcu);
155        Wwdgt { wwdgt: self }
156    }
157}