diff --git a/boards/nucleo_u545re_q/src/main.rs b/boards/nucleo_u545re_q/src/main.rs index 7f8403b6b2..81634fa650 100644 --- a/boards/nucleo_u545re_q/src/main.rs +++ b/boards/nucleo_u545re_q/src/main.rs @@ -52,6 +52,8 @@ struct NucleoU545RE { stm32u545::tim::Tim2<'static>, >, >, + // Comment the line below if you don't need a watchdog + watchdog: &'static stm32u545::iwdg::Iwdg, } impl SyscallDriverLookup for NucleoU545RE { @@ -75,7 +77,10 @@ impl KernelResources for NucleoU545RE { type ProcessFault = (); type Scheduler = components::sched::round_robin::RoundRobinComponentType; type SchedulerTimer = cortexm33::systick::SysTick; - type WatchDog = (); + // Comment the line below if you do not need a watchdog + type WatchDog = stm32u545::iwdg::Iwdg; + // Uncomment the line below if you dont need a watchdog + // type WatchDog = (); type ContextSwitchCallback = (); fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup { @@ -94,7 +99,9 @@ impl KernelResources for NucleoU545RE { &self.systick } fn watchdog(&self) -> &Self::WatchDog { - &() + // Return &() if you don't need a watchdog + // &() + self.watchdog } fn context_switch_callback(&self) -> &Self::ContextSwitchCallback { &() @@ -151,6 +158,8 @@ unsafe fn start() -> ( stm32u545::usart::Usart::new(stm32u545::usart::USART1_BASE) ); usart1.register(); + // Comment the line blow if you do not need a watchdog + let iwdg = static_init!(stm32u545::iwdg::Iwdg, stm32u545::iwdg::Iwdg::new()); // Load Peripherals Bundle let periphs = static_init!( @@ -244,6 +253,7 @@ unsafe fn start() -> ( led, button, alarm, + watchdog: iwdg, // Comment this if you do not need a watchdog } ); diff --git a/chips/stm32u545/src/lib.rs b/chips/stm32u545/src/lib.rs index 9bb37ae830..3a80d1b9b4 100644 --- a/chips/stm32u545/src/lib.rs +++ b/chips/stm32u545/src/lib.rs @@ -4,7 +4,7 @@ #![no_std] -pub use stm32u5xx::{chip, dma, exti, gpio, rcc, tim, usart}; +pub use stm32u5xx::{chip, dma, exti, gpio, iwdg, rcc, tim, usart}; use cortexm33::{CortexM33, CortexMVariant}; diff --git a/chips/stm32u5xx/README.md b/chips/stm32u5xx/README.md index 7679a6c50f..afeb64836a 100644 --- a/chips/stm32u5xx/README.md +++ b/chips/stm32u5xx/README.md @@ -12,3 +12,4 @@ Currently supported peripherals: - USART (Universal Synchronous/Asynchronous Receiver Transmitter) - GPDMA (Global Programmable DMA) - TIM2 (Timer) +- IWDG (Independent Watchdog) diff --git a/chips/stm32u5xx/src/iwdg.rs b/chips/stm32u5xx/src/iwdg.rs new file mode 100644 index 0000000000..4689a1649f --- /dev/null +++ b/chips/stm32u5xx/src/iwdg.rs @@ -0,0 +1,136 @@ +// Licensed under the Apache License, Version 2.0 or the MIT License. +// SPDX-License-Identifier: Apache-2.0 OR MIT +// Copyright OxidOS Automotive 2026. + +use kernel::platform::watchdog::WatchDog; +use kernel::utilities::registers::interfaces::{Readable, Writeable}; +use kernel::utilities::registers::{ + register_bitfields, register_structs, ReadOnly, ReadWrite, WriteOnly, +}; +use kernel::utilities::StaticRef; + +register_structs! { + IwdgRegisters { + // Key register + (0x00 => kr: WriteOnly), + // Prescaler register + (0x04 => pr: ReadWrite), + // Reload register + (0x08 => rlr: ReadWrite), + // Status register + (0x0C => sr: ReadOnly), + // Window register + (0x10 => winr: ReadWrite), + // Early wake-up register + (0x14 => ewcr: ReadWrite), + (0x18 => @END), + } +} + +register_bitfields! [u32, + KR [ + KEY OFFSET(0) NUMBITS(16) [ + Reload = 0xAAAA, // Reset the timer + Unlock = 0x5555, // Unlock registers for writing + Start = 0xCCCC, // Start timer + ], + ], + + PR [ + // Prescaler divider + PR OFFSET(0) NUMBITS(4) [ + DivideBy4 = 0b0000, + DivideBy8 = 0b0001, + DivideBy16 = 0b0010, + DivideBy32 = 0b0011, + DivideBy64 = 0b0100, + DivideBy128 = 0b0101, + DivideBy256 = 0b0110, + DivideBy512 = 0b0111, + ], + ], + + RLR [ + // Watchdog counter reload value + RL OFFSET(0) NUMBITS(12) [ + Thousand = 1000u32, + ], + ], + + SR [ + // Watchdog early interrupt flag + EWIF OFFSET(14) NUMBITS(1) [], + + // Watchdog interrupt comparartor value update + EWU OFFSET(3) NUMBITS(1) [], + + // Watchdog counter window value update + WVU OFFSET(2) NUMBITS(1) [], + + // Watchdog counter reload value update + RVU OFFSET(1) NUMBITS(1) [], + + // Watchdog prescaler value update + PVU OFFSET(0) NUMBITS(1) [], + ], + + WINR [ + // Watchdog counter window value + WIN OFFSET(0) NUMBITS(12) [], + ], + + EWCR [ + + // Watchdog early interrupt enable + EWIE OFFSET(15) NUMBITS(1) [], + + // Watchdog early interrupt acknowledge + EWIC OFFSET(14) NUMBITS(1) [], + + // Watchdog counter early wake-up interrupt value + EWIT OFFSET(0) NUMBITS(12) [], + ] +]; + +const IWDG_BASE: StaticRef = + unsafe { StaticRef::new(0x40003000 as *const IwdgRegisters) }; + +pub struct Iwdg { + registers: StaticRef, +} + +impl Iwdg { + pub const fn new() -> Iwdg { + Iwdg { + registers: IWDG_BASE, + } + } +} + +impl WatchDog for Iwdg { + fn setup(&self) { + self.registers.kr.write(KR::KEY::Unlock); + + while self.registers.sr.is_set(SR::PVU) || self.registers.sr.is_set(SR::RVU) { + // Block the executor until hardware is ready + } + + // 32hz / 32 = 1hz => one tick per ms => 1000 to RL register means 1 second timeout before reboot + self.registers.pr.write(PR::PR::DivideBy32); + self.registers.rlr.write(RLR::RL::Thousand); + + self.registers.kr.write(KR::KEY::Start); + self.tickle(); + } + + fn tickle(&self) { + // Reset the counter + self.registers.kr.write(KR::KEY::Reload); + } + + fn suspend(&self) {} + + fn resume(&self) { + self.tickle(); + } +} diff --git a/chips/stm32u5xx/src/lib.rs b/chips/stm32u5xx/src/lib.rs index f9bc0e4651..6a80f3dc08 100644 --- a/chips/stm32u5xx/src/lib.rs +++ b/chips/stm32u5xx/src/lib.rs @@ -8,6 +8,7 @@ pub mod chip; pub mod dma; pub mod exti; pub mod gpio; +pub mod iwdg; pub mod nvic; pub mod rcc; pub mod tim;