Skip to content

Commit

Permalink
Merge #2118
Browse files Browse the repository at this point in the history
2118: stm32f3: Watchdog Timers r=bradjc a=krady21

### Pull Request Overview

This pull request adds both watchdogs supported by the stm32f3 boards. The difference between the two of them is thoroughly described [here](https://electronics.stackexchange.com/questions/123080/independent-watchdog-iwdg-or-window-watchdog-wwdg). TLDR: The independent watchdog  is clocked by its own dedicated low-speed clock, but is not as precise, while the window watchdog is more precise, has a configurable time window that can be used to detect early or late abnormalities and can also generate an interrupt just before resetting.

At this point, someone could configure the board to use one, both or neither of the two watchdogs. I don't know if there's need for both, but i wanted to consult with you first before deleting one or the other. I also tried to have this configuration done in the boards file, as it was discussed in a previous [pr](#1887 (comment)), but I am not entirely satisfied with how i did it.

### Testing Strategy

This pull request was tested using the stm32f3discovery board.


### TODO or Help Wanted

The main problem with both of the watchdogs is that none of them provides a way to suspend them once they are started, except by a full system reset. Since suspend and resume functions are unimplemented, sleeping in the kernel_loop [function](https://github.com/tock/tock/blob/ad9387a577405675b044d5bde85badf0274995c8/kernel/src/sched.rs#L495-L514) will probably end up causing a watchdog reset.

### Documentation Updated

- [x] Updated the relevant files in `/docs`, or no updates are required.

### Formatting

- [x] Ran `make prepush`.


Co-authored-by: Bogdan Grigoruta <bogdangrigoruta@gmail.com>
  • Loading branch information
bors[bot] and krady21 committed Oct 8, 2020
2 parents 243f583 + 6bbbfd4 commit ba867c2
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 2 deletions.
3 changes: 3 additions & 0 deletions boards/stm32f3discovery/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,9 @@ pub unsafe fn reset_handler() {
debug!("{:?}", err);
});

// Uncomment this to enable the watchdog
// chip.enable_watchdog();

let scheduler = components::sched::round_robin::RoundRobinComponent::new(&PROCESSES)
.finalize(components::rr_component_helper!(NUM_PROCS));

Expand Down
11 changes: 9 additions & 2 deletions chips/stm32f303xc/src/chip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ use crate::nvic;
use crate::spi;
use crate::tim2;
use crate::usart;
use crate::wdt;

pub struct Stm32f3xx {
mpu: cortexm4::mpu::MPU,
userspace_kernel_boundary: cortexm4::syscall::SysCall,
scheduler_timer: cortexm4::systick::SysTick,
watchdog: wdt::WindoWdg,
}

impl Stm32f3xx {
Expand All @@ -27,15 +29,20 @@ impl Stm32f3xx {
mpu: cortexm4::mpu::MPU::new(),
userspace_kernel_boundary: cortexm4::syscall::SysCall::new(),
scheduler_timer: cortexm4::systick::SysTick::new(),
watchdog: wdt::WindoWdg::new(),
}
}

pub fn enable_watchdog(&self) {
self.watchdog.enable();
}
}

impl Chip for Stm32f3xx {
type MPU = cortexm4::mpu::MPU;
type UserspaceKernelBoundary = cortexm4::syscall::SysCall;
type SchedulerTimer = cortexm4::systick::SysTick;
type WatchDog = ();
type WatchDog = wdt::WindoWdg;

fn service_pending_interrupts(&self) {
unsafe {
Expand Down Expand Up @@ -94,7 +101,7 @@ impl Chip for Stm32f3xx {
}

fn watchdog(&self) -> &Self::WatchDog {
&()
&self.watchdog
}

fn userspace_kernel_boundary(&self) -> &cortexm4::syscall::SysCall {
Expand Down
2 changes: 2 additions & 0 deletions chips/stm32f303xc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub mod spi;
pub mod syscfg;
pub mod tim2;
pub mod usart;
pub mod wdt;

use cortexm4::{
generic_isr, hard_fault_handler, svc_handler, systick_handler, unhandled_interrupt,
};
Expand Down
22 changes: 22 additions & 0 deletions chips/stm32f303xc/src/rcc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,20 @@ impl Rcc {
fn disable_adc34_clock(&self) {
self.registers.ahbenr.modify(AHBENR::ADC34EN::CLEAR);
}

// WWDG clock

fn is_enabled_wwdg_clock(&self) -> bool {
self.registers.apb1enr.is_set(APB1ENR::WWDGEN)
}

fn enable_wwdg_clock(&self) {
self.registers.apb1enr.modify(APB1ENR::WWDGEN::SET);
}

fn disable_wwdg_clock(&self) {
self.registers.apb1enr.modify(APB1ENR::WWDGEN::CLEAR);
}
}

/// Clock sources for CPU
Expand Down Expand Up @@ -667,6 +681,7 @@ pub enum PCLK1 {
USART2,
USART3,
I2C1,
WWDG,
// I2C2,
// SPI3,
}
Expand Down Expand Up @@ -697,6 +712,7 @@ impl ClockInterface for PeripheralClock {
PCLK1::USART2 => unsafe { RCC.is_enabled_usart2_clock() },
PCLK1::USART3 => unsafe { RCC.is_enabled_usart3_clock() },
PCLK1::I2C1 => unsafe { RCC.is_enabled_i2c1_clock() },
PCLK1::WWDG => unsafe { RCC.is_enabled_wwdg_clock() },
},
&PeripheralClock::APB2(ref v) => match v {
PCLK2::SPI1 => unsafe { RCC.is_enabled_spi1_clock() },
Expand Down Expand Up @@ -751,6 +767,9 @@ impl ClockInterface for PeripheralClock {
RCC.enable_i2c1_clock();
RCC.reset_i2c1();
},
PCLK1::WWDG => unsafe {
RCC.enable_wwdg_clock();
},
},
&PeripheralClock::APB2(ref v) => match v {
PCLK2::SYSCFG => unsafe {
Expand Down Expand Up @@ -810,6 +829,9 @@ impl ClockInterface for PeripheralClock {
PCLK1::I2C1 => unsafe {
RCC.disable_i2c1_clock();
},
PCLK1::WWDG => unsafe {
RCC.disable_wwdg_clock();
},
},
&PeripheralClock::APB2(ref v) => match v {
PCLK2::SYSCFG => unsafe {
Expand Down
164 changes: 164 additions & 0 deletions chips/stm32f303xc/src/wdt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
//! Window watchdog timer

use crate::rcc;
use core::cell::Cell;
use kernel::common::registers::{register_bitfields, ReadWrite};
use kernel::common::StaticRef;
use kernel::ClockInterface;

const WINDOW_WATCHDOG_BASE: StaticRef<WwdgRegisters> =
unsafe { StaticRef::new(0x4000_2C00 as *const WwdgRegisters) };

#[repr(C)]
pub struct WwdgRegisters {
cr: ReadWrite<u32, Control::Register>,
cfr: ReadWrite<u32, Config::Register>,
sr: ReadWrite<u32, Status::Register>,
}

register_bitfields![u32,
Control [
/// Watch dog activation
/// Set by software and only cleared by hardware after a reset.
/// When set, the watchdog can generate a reset.
WDGA OFFSET(7) NUMBITS(1) [],
/// 7 bit counter
/// These bits contain the value of the watchdog counter. It is
/// decremented every 4096 * 2^WDGTB PCLK cycles. A reset is produced
/// when it is decremented from 0x40 to 0x3F (T[6] becomes cleared).
T OFFSET(0) NUMBITS(7) []
],
Config [
/// Early wakeup interrupt
/// When set, interrupt occurs whenever the counter reaches the value
/// of 0x40. This interrupt is only cleared by hardware after a reset.
EWI OFFSET(9) NUMBITS(1) [],
/// Timer base
/// This allows modifying the time base of the prescaler.
WDGTB OFFSET(7) NUMBITS(2) [
/// CK Counter Clock (PCLK div 4096) div 1
DIVONE = 0,
/// CK Counter Clock (PCLK div 4096) div 2
DIVTWO = 1,
/// CK Counter Clock (PCLK div 4096) div 4
DIVFOUR = 2,
/// CK Counter Clock (PCLK div 4096) div 8
DIVEIGHT = 3
],
/// 7 bit window value
/// These bits contain the window value to be compared to the
/// downcounter.
W OFFSET(0) NUMBITS(7) []
],
Status [
/// Early wakeup interrupt flag
/// This is set when the counter has reached the value 0x40. It must be
/// cleared by software by writing 0. This bit is also set when the
/// interrupt is not enabled.
EWIF OFFSET(0) NUMBITS(1) []
]
];

pub struct WindoWdg {
registers: StaticRef<WwdgRegisters>,
clock: WdgClock,
enabled: Cell<bool>,
}

impl WindoWdg {
pub const fn new() -> WindoWdg {
WindoWdg {
registers: WINDOW_WATCHDOG_BASE,
clock: WdgClock(rcc::PeripheralClock::APB1(rcc::PCLK1::WWDG)),
enabled: Cell::new(false),
}
}

pub fn enable(&self) {
self.enabled.set(true);
}

fn set_window(&self, value: u32) {
// Set the window value to the biggest possible one.
self.registers.cfr.modify(Config::W.val(value));
}

/// Modifies the time base of the prescaler.
/// 0 - decrements the watchdog every clock cycle
/// 1 - decrements the watchdog every 2nd clock cycle
/// 2 - decrements the watchdog every 4th clock cycle
/// 3 - decrements the watchdog every 8th clock cycle
fn set_prescaler(&self, time_base: u8) {
match time_base {
0 => self.registers.cfr.modify(Config::WDGTB::DIVONE),
1 => self.registers.cfr.modify(Config::WDGTB::DIVTWO),
2 => self.registers.cfr.modify(Config::WDGTB::DIVFOUR),
3 => self.registers.cfr.modify(Config::WDGTB::DIVEIGHT),
_ => {}
}
}

pub fn start(&self) {
// Enable the APB1 clock for the watchdog.
self.clock.enable();

// This disables the window feature. Set this to a value smaller than
// 0x7F if you want to enable it.
self.set_window(0x7F);
self.set_prescaler(3);

// Set the T[6] bit to avoid a reset when the watchdog is activated.
self.tickle();

// With the APB1 clock running at 36Mhz we are getting timeout value of
// t_WWDG = (1 / 36000) * 4096 * 2^3 * (63 + 1) = 58ms
self.registers.cr.modify(Control::WDGA::SET);
}

pub fn tickle(&self) {
// Uses 63 as the value the watchdog starts counting from.
self.registers.cr.modify(Control::T.val(0x7F));
}
}

struct WdgClock(rcc::PeripheralClock);

impl ClockInterface for WdgClock {
fn is_enabled(&self) -> bool {
self.0.is_enabled()
}

fn enable(&self) {
self.0.enable();
}

fn disable(&self) {
self.0.disable();
}
}

impl kernel::watchdog::WatchDog for WindoWdg {
fn setup(&self) {
if self.enabled.get() {
self.start();
}
}

fn tickle(&self) {
if self.enabled.get() {
self.tickle();
}
}

fn suspend(&self) {
if self.enabled.get() {
self.clock.disable();
}
}

fn resume(&self) {
if self.enabled.get() {
self.clock.enable();
}
}
}

0 comments on commit ba867c2

Please sign in to comment.