From eaad0cc1dc09e604d137b3a1bdfd3438ff990621 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sat, 24 Dec 2022 02:51:06 +0100 Subject: [PATCH 1/2] embassy-rp: Add Watchdog --- embassy-rp/src/lib.rs | 3 + embassy-rp/src/watchdog.rs | 111 ++++++++++++++++++++++++++++++++ examples/rp/src/bin/watchdog.rs | 48 ++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 embassy-rp/src/watchdog.rs create mode 100644 examples/rp/src/bin/watchdog.rs diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 0ea97aec..9e99b2fb 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -36,6 +36,7 @@ pub mod clocks; pub mod flash; pub mod multicore; mod reset; +pub mod watchdog; // Reexports @@ -119,6 +120,8 @@ embassy_hal_common::peripherals! { PIO0, PIO1, + + WATCHDOG, } #[link_section = ".boot2"] diff --git a/embassy-rp/src/watchdog.rs b/embassy-rp/src/watchdog.rs new file mode 100644 index 00000000..01509ff4 --- /dev/null +++ b/embassy-rp/src/watchdog.rs @@ -0,0 +1,111 @@ +//! 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. +//! +//! Credit: based on `rp-hal` implementation (also licensed Apache+MIT) + +use core::marker::PhantomData; + +use embassy_time::Duration; + +use crate::pac; +use crate::peripherals::WATCHDOG; + +/// Watchdog peripheral +pub struct Watchdog<'d> { + phantom: PhantomData<&'d WATCHDOG>, + load_value: u32, // decremented by 2 per tick (µs) +} + +impl<'d> Watchdog<'d> { + /// Create a new `Watchdog` + pub fn new(_watchdog: WATCHDOG) -> Self { + Self { + phantom: PhantomData, + load_value: 0, + } + } + + /// Start tick generation on clk_tick which is driven from clk_ref. + /// + /// # 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: u8) { + const WATCHDOG_TICK_ENABLE_BITS: u32 = 0x200; + unsafe { + let watchdog = pac::WATCHDOG; + watchdog + .tick() + .write_value(pac::watchdog::regs::Tick(WATCHDOG_TICK_ENABLE_BITS | cycles as u32)) + } + } + + /// Defines whether or not the watchdog timer should be paused when processor(s) are in debug mode + /// or when JTAG is accessing bus fabric + pub fn pause_on_debug(&mut self, pause: bool) { + unsafe { + let watchdog = pac::WATCHDOG; + watchdog.ctrl().write(|w| { + w.set_pause_dbg0(pause); + w.set_pause_dbg1(pause); + w.set_pause_jtag(pause); + }) + } + } + + fn load_counter(&self, counter: u32) { + unsafe { + let watchdog = pac::WATCHDOG; + watchdog.load().write_value(pac::watchdog::regs::Load(counter)); + } + } + + fn enable(&self, bit: bool) { + unsafe { + let watchdog = pac::WATCHDOG; + watchdog.ctrl().write(|w| w.set_enable(bit)) + } + } + + // Configure which hardware will be reset by the watchdog + // (everything except ROSC, XOSC) + unsafe fn configure_wdog_reset_triggers(&self) { + let psm = pac::PSM; + psm.wdsel().write_value(pac::psm::regs::Wdsel( + 0x0001ffff & !(0x01 << 0usize) & !(0x01 << 1usize), + )); + } + + /// Feed the watchdog timer + pub fn feed(&mut self) { + self.load_counter(self.load_value) + } + + /// Start the watchdog timer + pub fn start(&mut self, period: Duration) { + const MAX_PERIOD: u32 = 0xFFFFFF; + + let delay_us = period.as_micros() as u32; + if delay_us > MAX_PERIOD / 2 { + panic!( + "Period cannot exceed maximum load value of {} ({} microseconds))", + MAX_PERIOD, + MAX_PERIOD / 2 + ); + } + // Due to a logic error, the watchdog decrements by 2 and + // the load value must be compensated; see RP2040-E1 + self.load_value = delay_us * 2; + + self.enable(false); + unsafe { + self.configure_wdog_reset_triggers(); + } + self.load_counter(self.load_value); + self.enable(true); + } +} diff --git a/examples/rp/src/bin/watchdog.rs b/examples/rp/src/bin/watchdog.rs new file mode 100644 index 00000000..13af22a2 --- /dev/null +++ b/examples/rp/src/bin/watchdog.rs @@ -0,0 +1,48 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_rp::gpio; +use embassy_rp::watchdog::*; +use embassy_time::{Duration, Timer}; +use gpio::{Level, Output}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello world!"); + + let mut watchdog = Watchdog::new(p.WATCHDOG); + let mut led = Output::new(p.PIN_25, Level::Low); + + // Set the LED high for 2 seconds so we know when we're about to start the watchdog + led.set_high(); + Timer::after(Duration::from_secs(2)).await; + + // Set to watchdog to reset if it's not reloaded within 1.05 seconds, and start it + watchdog.start(Duration::from_millis(1_050)); + info!("Started the watchdog timer"); + + // Blink once a second for 5 seconds, refreshing the watchdog timer once a second to avoid a reset + for _ in 1..=5 { + led.set_low(); + Timer::after(Duration::from_millis(500)).await; + led.set_high(); + Timer::after(Duration::from_millis(500)).await; + info!("Feeding watchdog"); + watchdog.feed(); + } + + info!("Stopped feeding, device will reset in 1.05 seconds"); + // Blink 10 times per second, not feeding the watchdog. + // The processor should reset in 1.05 seconds, or 5 blinks time + loop { + led.set_low(); + Timer::after(Duration::from_millis(100)).await; + led.set_high(); + Timer::after(Duration::from_millis(100)).await; + } +} From e090ab19151fbea6736d5eac0e1497ef9c36626b Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sat, 24 Dec 2022 03:22:51 +0100 Subject: [PATCH 2/2] Remove lifetime, use pac fields --- embassy-rp/src/watchdog.rs | 14 +++++++------- examples/rp/src/bin/watchdog.rs | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/embassy-rp/src/watchdog.rs b/embassy-rp/src/watchdog.rs index 01509ff4..f4f165b2 100644 --- a/embassy-rp/src/watchdog.rs +++ b/embassy-rp/src/watchdog.rs @@ -14,12 +14,12 @@ use crate::pac; use crate::peripherals::WATCHDOG; /// Watchdog peripheral -pub struct Watchdog<'d> { - phantom: PhantomData<&'d WATCHDOG>, +pub struct Watchdog { + phantom: PhantomData, load_value: u32, // decremented by 2 per tick (µs) } -impl<'d> Watchdog<'d> { +impl Watchdog { /// Create a new `Watchdog` pub fn new(_watchdog: WATCHDOG) -> Self { Self { @@ -35,12 +35,12 @@ impl<'d> Watchdog<'d> { /// * `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: u8) { - const WATCHDOG_TICK_ENABLE_BITS: u32 = 0x200; unsafe { let watchdog = pac::WATCHDOG; - watchdog - .tick() - .write_value(pac::watchdog::regs::Tick(WATCHDOG_TICK_ENABLE_BITS | cycles as u32)) + watchdog.tick().write(|w| { + w.set_enable(true); + w.set_cycles(cycles.into()) + }); } } diff --git a/examples/rp/src/bin/watchdog.rs b/examples/rp/src/bin/watchdog.rs index 13af22a2..ece5cfe3 100644 --- a/examples/rp/src/bin/watchdog.rs +++ b/examples/rp/src/bin/watchdog.rs @@ -22,11 +22,11 @@ async fn main(_spawner: Spawner) { led.set_high(); Timer::after(Duration::from_secs(2)).await; - // Set to watchdog to reset if it's not reloaded within 1.05 seconds, and start it + // Set to watchdog to reset if it's not fed within 1.05 seconds, and start it watchdog.start(Duration::from_millis(1_050)); info!("Started the watchdog timer"); - // Blink once a second for 5 seconds, refreshing the watchdog timer once a second to avoid a reset + // Blink once a second for 5 seconds, feed the watchdog timer once a second to avoid a reset for _ in 1..=5 { led.set_low(); Timer::after(Duration::from_millis(500)).await; @@ -38,7 +38,7 @@ async fn main(_spawner: Spawner) { info!("Stopped feeding, device will reset in 1.05 seconds"); // Blink 10 times per second, not feeding the watchdog. - // The processor should reset in 1.05 seconds, or 5 blinks time + // The processor should reset in 1.05 seconds. loop { led.set_low(); Timer::after(Duration::from_millis(100)).await;