-
Notifications
You must be signed in to change notification settings - Fork 284
Expand file tree
/
Copy pathwatchdog.rs
More file actions
250 lines (227 loc) · 7.9 KB
/
watchdog.rs
File metadata and controls
250 lines (227 loc) · 7.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
//! Watchdog
//!
//! The watchdog is a countdown timer that can restart parts of the chip if it reaches zero. This can be used to restart the
//! processor if software gets stuck in an infinite loop. The programmer must periodically write a value to the watchdog to
//! stop it from reaching zero.
//!
//! See [Section 12.9](https://rptl.io/rp2350-datasheet) of the datasheet for more details
//!
//! ## Usage
//! ```no_run
//! use fugit::ExtU32;
//! use rp235x_hal::{self as hal, clocks::init_clocks_and_plls, watchdog::Watchdog};
//! let mut pac = hal::pac::Peripherals::take().unwrap();
//! let mut watchdog = Watchdog::new(pac.WATCHDOG);
//! let _clocks = init_clocks_and_plls(
//! 12_000_000,
//! pac.XOSC,
//! pac.CLOCKS,
//! pac.PLL_SYS,
//! pac.PLL_USB,
//! &mut pac.RESETS,
//! &mut watchdog,
//! )
//! .ok()
//! .unwrap();
//! // Set to watchdog to reset if it's not reloaded within 1.05 seconds, and start it
//! watchdog.start(1_050_000.micros());
//! // Feed the watchdog once per cycle to avoid reset
//! for _ in 1..=10000 {
//! hal::arch::delay(100_000);
//! watchdog.feed();
//! }
//! // Stop feeding, now we'll reset
//! loop {}
//! ```
//! See [examples/watchdog.rs](https://github.com/rp-rs/rp-hal/tree/main/rp235x-hal-examples/src/bin/watchdog.rs) for a more complete example
// Embedded HAL 1.0.0 doesn't have an ADC trait, so use the one from 0.2
use embedded_hal_0_2::watchdog;
use fugit::MicrosDurationU32;
use crate::pac::{self, WATCHDOG};
/// Watchdog peripheral
pub struct Watchdog {
watchdog: WATCHDOG,
load_value: u32, // decremented by 2 per tick (µs)
}
/// Watchdog reset reason
pub enum ResetReason {
/// A manual watchdog trigger caused the reset.
Force,
/// A watchdog timeout caused the reset.
Timeout,
}
#[derive(Debug)]
#[allow(missing_docs)]
/// Scratch registers of the watchdog peripheral
pub enum ScratchRegister {
Scratch0,
Scratch1,
Scratch2,
Scratch3,
Scratch4,
Scratch5,
Scratch6,
Scratch7,
}
impl Watchdog {
/// Create a new [`Watchdog`]
pub fn new(watchdog: WATCHDOG) -> Self {
Self {
watchdog,
load_value: 0,
}
}
/// Starts tick generation on all the ticks.
///
/// # Arguments
///
/// * `cycles` - Total number of tick cycles before the next tick is generated.
/// It is expected to be the frequency in MHz of clk_ref.
pub fn enable_tick_generation(&mut self, cycles: u16) {
// now we have separate ticks for every destination
// we own the watchdog, so no-one else can be writing to this register
let ticks = unsafe { &*pac::TICKS::ptr() };
for ticker in ticks.tick_iter() {
// TODO: work out how to rename proc0_cycles to cycles in the SVD patch YAML
ticker
.cycles()
.write(|w| unsafe { w.proc0_cycles().bits(cycles) });
ticker.ctrl().write(|w| w.enable().set_bit());
}
}
/// Defines whether or not the watchdog timer should be paused when processor(s) are in debug mode
/// or when JTAG is accessing bus fabric
///
/// # Arguments
///
/// * `pause` - If true, watchdog timer will be paused
pub fn pause_on_debug(&mut self, pause: bool) {
self.watchdog.ctrl().write(|w| {
w.pause_dbg0()
.bit(pause)
.pause_dbg1()
.bit(pause)
.pause_jtag()
.bit(pause)
})
}
fn load_counter(&self, counter: u32) {
self.watchdog.load().write(|w| unsafe { w.bits(counter) });
}
fn enable(&self, bit: bool) {
self.watchdog.ctrl().write(|w| w.enable().bit(bit))
}
/// Get the watchdog reset reason.
pub fn reason(&self) -> Option<ResetReason> {
let bits = self.watchdog.reason().read().bits();
if bits & 0b10 != 0 {
Some(ResetReason::Force)
} else if bits & 0b01 != 0 {
Some(ResetReason::Timeout)
} else {
None
}
}
/// Read a scratch register
pub fn read_scratch(&self, reg: ScratchRegister) -> u32 {
match reg {
ScratchRegister::Scratch0 => self.watchdog.scratch0().read().bits(),
ScratchRegister::Scratch1 => self.watchdog.scratch1().read().bits(),
ScratchRegister::Scratch2 => self.watchdog.scratch2().read().bits(),
ScratchRegister::Scratch3 => self.watchdog.scratch3().read().bits(),
ScratchRegister::Scratch4 => self.watchdog.scratch4().read().bits(),
ScratchRegister::Scratch5 => self.watchdog.scratch5().read().bits(),
ScratchRegister::Scratch6 => self.watchdog.scratch6().read().bits(),
ScratchRegister::Scratch7 => self.watchdog.scratch7().read().bits(),
}
}
/// Write a scratch register
pub fn write_scratch(&mut self, reg: ScratchRegister, value: u32) {
match reg {
ScratchRegister::Scratch0 => {
self.watchdog.scratch0().write(|w| unsafe { w.bits(value) })
}
ScratchRegister::Scratch1 => {
self.watchdog.scratch1().write(|w| unsafe { w.bits(value) })
}
ScratchRegister::Scratch2 => {
self.watchdog.scratch2().write(|w| unsafe { w.bits(value) })
}
ScratchRegister::Scratch3 => {
self.watchdog.scratch3().write(|w| unsafe { w.bits(value) })
}
ScratchRegister::Scratch4 => {
self.watchdog.scratch4().write(|w| unsafe { w.bits(value) })
}
ScratchRegister::Scratch5 => {
self.watchdog.scratch5().write(|w| unsafe { w.bits(value) })
}
ScratchRegister::Scratch6 => {
self.watchdog.scratch6().write(|w| unsafe { w.bits(value) })
}
ScratchRegister::Scratch7 => {
self.watchdog.scratch7().write(|w| unsafe { w.bits(value) })
}
}
}
/// Configure which hardware will be reset by the watchdog
/// the default is everything except ROSC, XOSC
///
/// Safety: ensure no other device is writing to psm.wdsel
/// This is easy at the moment, since nothing else uses PSM
unsafe fn configure_wdog_reset_triggers(&self) {
let psm = &*pac::PSM::ptr();
psm.wdsel().write_with_zero(|w| {
w.bits(0x0001ffff);
w.xosc().clear_bit();
w.rosc().clear_bit();
w
});
}
/// Set the watchdog counter back to its load value, making sure
/// that the watchdog reboot will not be triggered for the configured
/// period.
pub fn feed(&self) {
self.load_counter(self.load_value)
}
/// Start the watchdog. This enables a timer which will reboot the
/// rp2350 if [`Watchdog::feed()`] does not get called for the configured period.
pub fn start<T: Into<MicrosDurationU32>>(&mut self, period: T) {
const MAX_PERIOD: u32 = 0xFFFFFF;
let delay_us = period.into().to_micros();
if delay_us > MAX_PERIOD / 2 {
panic!(
"Period cannot exceed maximum load value of {} ({} microseconds))",
MAX_PERIOD,
MAX_PERIOD / 2
);
}
self.load_value = delay_us;
self.enable(false);
unsafe {
self.configure_wdog_reset_triggers();
}
self.load_counter(self.load_value);
self.enable(true);
}
/// Disable the watchdog timer.
pub fn disable(&self) {
self.enable(false)
}
}
impl watchdog::Watchdog for Watchdog {
fn feed(&mut self) {
(*self).feed()
}
}
impl watchdog::WatchdogEnable for Watchdog {
type Time = MicrosDurationU32;
fn start<T: Into<Self::Time>>(&mut self, period: T) {
self.start(period)
}
}
impl watchdog::WatchdogDisable for Watchdog {
fn disable(&mut self) {
(*self).disable()
}
}