diff options
author | Kenneth Bell <[email protected]> | 2023-08-06 22:15:47 +0100 |
---|---|---|
committer | Ron Evans <[email protected]> | 2023-08-15 11:50:07 +0200 |
commit | f4375d045213c416fad2d19773e7700b6b02f685 (patch) | |
tree | bd24ba00ec42f0b8e767bf66d852493a3f393a39 | |
parent | 756cdf44ed5a83a0fd37a63bdf596328c279f4f7 (diff) | |
download | tinygo-f4375d045213c416fad2d19773e7700b6b02f685.tar.gz tinygo-f4375d045213c416fad2d19773e7700b6b02f685.zip |
samd51,rp2040,nrf528xx,stm32: implement watchdog
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | src/examples/watchdog/main.go | 42 | ||||
-rw-r--r-- | src/machine/machine_atsamd51.go | 49 | ||||
-rw-r--r-- | src/machine/machine_nrf528xx.go | 43 | ||||
-rw-r--r-- | src/machine/machine_rp2040_clocks.go | 2 | ||||
-rw-r--r-- | src/machine/machine_rp2040_watchdog.go | 65 | ||||
-rw-r--r-- | src/machine/machine_stm32_iwdg.go | 66 | ||||
-rw-r--r-- | src/machine/watchdog.go | 34 |
8 files changed, 291 insertions, 12 deletions
@@ -490,6 +490,8 @@ smoketest: @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/i2c-target @$(MD5SUM) test.hex + $(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/watchdog + @$(MD5SUM) test.hex # test simulated boards on play.tinygo.org ifneq ($(WASM), 0) $(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1 diff --git a/src/examples/watchdog/main.go b/src/examples/watchdog/main.go new file mode 100644 index 000000000..7c17c82bb --- /dev/null +++ b/src/examples/watchdog/main.go @@ -0,0 +1,42 @@ +package main + +import ( + "fmt" + "machine" + "time" +) + +func main() { + //sleep for 2 secs for console + time.Sleep(2 * time.Second) + + config := machine.WatchdogConfig{ + TimeoutMillis: 1000, + } + + println("configuring watchdog for max 1 second updates") + machine.Watchdog.Configure(config) + + // From this point the watchdog is running and Update must be + // called periodically, per the config + machine.Watchdog.Start() + + // This loop should complete because watchdog update is called + // every 100ms. + start := time.Now() + println("updating watchdog for 3 seconds") + for i := 0; i < 30; i++ { + time.Sleep(100 * time.Millisecond) + machine.Watchdog.Update() + fmt.Printf("alive @ %v\n", time.Now().Sub(start)) + } + + // This loop should cause a watchdog reset after 1s since + // there is no update call. + start = time.Now() + println("entering tight loop") + for { + time.Sleep(100 * time.Millisecond) + fmt.Printf("alive @ %v\n", time.Now().Sub(start)) + } +} diff --git a/src/machine/machine_atsamd51.go b/src/machine/machine_atsamd51.go index 7c4c4d07c..8d37f2fa5 100644 --- a/src/machine/machine_atsamd51.go +++ b/src/machine/machine_atsamd51.go @@ -2299,3 +2299,52 @@ func checkFlashError() error { return nil } + +// Watchdog provides access to the hardware watchdog available +// in the SAMD51. +var Watchdog = &watchdogImpl{} + +const ( + // WatchdogMaxTimeout in milliseconds (16s) + WatchdogMaxTimeout = (16384 * 1000) / 1024 // CYC16384/1024kHz +) + +type watchdogImpl struct{} + +// Configure the watchdog. +// +// This method should not be called after the watchdog is started and on +// some platforms attempting to reconfigure after starting the watchdog +// is explicitly forbidden / will not work. +func (wd *watchdogImpl) Configure(config WatchdogConfig) error { + // 1.024kHz clock + cycles := int((int64(config.TimeoutMillis) * 1024) / 1000) + + // period is expressed as a power-of-two, starting at 8 / 1024ths of a second + period := uint8(0) + cfgCycles := 8 + for cfgCycles < cycles { + period++ + cfgCycles <<= 1 + + if period >= 0xB { + break + } + } + + sam.WDT.CONFIG.Set(period << sam.WDT_CONFIG_PER_Pos) + + return nil +} + +// Starts the watchdog. +func (wd *watchdogImpl) Start() error { + sam.WDT.CTRLA.SetBits(sam.WDT_CTRLA_ENABLE) + return nil +} + +// Update the watchdog, indicating that `source` is healthy. +func (wd *watchdogImpl) Update() { + // 0xA5 = magic value (see datasheet) + sam.WDT.CLEAR.Set(0xA5) +} diff --git a/src/machine/machine_nrf528xx.go b/src/machine/machine_nrf528xx.go index 019a66cf1..f8937aec0 100644 --- a/src/machine/machine_nrf528xx.go +++ b/src/machine/machine_nrf528xx.go @@ -214,3 +214,46 @@ func twisError(val uint32) error { return errI2CBusError } + +var ( + Watchdog = &watchdogImpl{} +) + +const ( + // WatchdogMaxTimeout in milliseconds (approx 36h) + WatchdogMaxTimeout = (0xffffffff * 1000) / 32768 +) + +type watchdogImpl struct { +} + +// Configure the watchdog. +// +// This method should not be called after the watchdog is started and on +// some platforms attempting to reconfigure after starting the watchdog +// is explicitly forbidden / will not work. +func (wd *watchdogImpl) Configure(config WatchdogConfig) error { + // 32.768kHz counter + crv := int32((int64(config.TimeoutMillis) * 32768) / 1000) + nrf.WDT.CRV.Set(uint32(crv)) + + // One source + nrf.WDT.RREN.Set(0x1) + + // Run during sleep + nrf.WDT.CONFIG.Set(nrf.WDT_CONFIG_SLEEP_Run) + + return nil +} + +// Starts the watchdog. +func (wd *watchdogImpl) Start() error { + nrf.WDT.TASKS_START.Set(nrf.WDT_TASKS_START_TASKS_START) + return nil +} + +// Update the watchdog, indicating that `source` is healthy. +func (wd *watchdogImpl) Update() { + // 0x6E524635 = magic value from datasheet + nrf.WDT.RR[0].Set(0x6E524635) +} diff --git a/src/machine/machine_rp2040_clocks.go b/src/machine/machine_rp2040_clocks.go index 093731049..57dfa68b0 100644 --- a/src/machine/machine_rp2040_clocks.go +++ b/src/machine/machine_rp2040_clocks.go @@ -182,7 +182,7 @@ func (clk *clock) configure(src, auxsrc, srcFreq, freq uint32) { // Must be called before any other clock function. func (clks *clocksType) init() { // Start the watchdog tick - watchdog.startTick(xoscFreq) + Watchdog.startTick(xoscFreq) // Disable resus that may be enabled from previous software clks.resus.ctrl.Set(0) diff --git a/src/machine/machine_rp2040_watchdog.go b/src/machine/machine_rp2040_watchdog.go index 6e9fb874f..a67df80ca 100644 --- a/src/machine/machine_rp2040_watchdog.go +++ b/src/machine/machine_rp2040_watchdog.go @@ -4,24 +4,67 @@ package machine import ( "device/rp" - "runtime/volatile" - "unsafe" ) -type watchdogType struct { - ctrl volatile.Register32 - load volatile.Register32 - reason volatile.Register32 - scratch [8]volatile.Register32 - tick volatile.Register32 +// Watchdog provides access to the hardware watchdog available +// in the RP2040. +var Watchdog = &watchdogImpl{} + +const ( + // WatchdogMaxTimeout in milliseconds (approx 8.3s). + // + // Nominal 1us per watchdog tick, 24-bit counter, + // but due to errata two ticks consumed per 1us. + // See: Errata RP2040-E1 + WatchdogMaxTimeout = (rp.WATCHDOG_LOAD_LOAD_Msk / 1000) / 2 +) + +type watchdogImpl struct { + // The value to reset the counter to on each Update + loadValue uint32 } -var watchdog = (*watchdogType)(unsafe.Pointer(rp.WATCHDOG)) +// Configure the watchdog. +// +// This method should not be called after the watchdog is started and on +// some platforms attempting to reconfigure after starting the watchdog +// is explicitly forbidden / will not work. +func (wd *watchdogImpl) Configure(config WatchdogConfig) error { + // x2 due to errata RP2040-E1 + wd.loadValue = config.TimeoutMillis * 1000 * 2 + if wd.loadValue > rp.WATCHDOG_LOAD_LOAD_Msk { + wd.loadValue = rp.WATCHDOG_LOAD_LOAD_Msk + } + + rp.WATCHDOG.CTRL.ClearBits(rp.WATCHDOG_CTRL_ENABLE) + + // Reset everything apart from ROSC and XOSC + rp.PSM.WDSEL.Set(0x0001ffff &^ (rp.PSM_WDSEL_ROSC | rp.PSM_WDSEL_XOSC)) + + // Pause watchdog during debug + rp.WATCHDOG.CTRL.SetBits(rp.WATCHDOG_CTRL_PAUSE_DBG0 | rp.WATCHDOG_CTRL_PAUSE_DBG1 | rp.WATCHDOG_CTRL_PAUSE_JTAG) + + // Load initial counter + rp.WATCHDOG.LOAD.Set(wd.loadValue) + + return nil +} + +// Starts the watchdog. +func (wd *watchdogImpl) Start() error { + rp.WATCHDOG.CTRL.SetBits(rp.WATCHDOG_CTRL_ENABLE) + return nil +} + +// Update the watchdog, indicating that the app is healthy. +func (wd *watchdogImpl) Update() { + rp.WATCHDOG.LOAD.Set(wd.loadValue) +} // startTick starts the watchdog tick. // cycles needs to be a divider that when applied to the xosc input, // produces a 1MHz clock. So if the xosc frequency is 12MHz, // this will need to be 12. -func (wd *watchdogType) startTick(cycles uint32) { - wd.tick.Set(cycles | rp.WATCHDOG_TICK_ENABLE) +func (wd *watchdogImpl) startTick(cycles uint32) { + rp.WATCHDOG.TICK.Set(cycles | rp.WATCHDOG_TICK_ENABLE) } diff --git a/src/machine/machine_stm32_iwdg.go b/src/machine/machine_stm32_iwdg.go new file mode 100644 index 000000000..1139d5461 --- /dev/null +++ b/src/machine/machine_stm32_iwdg.go @@ -0,0 +1,66 @@ +//go:build stm32 + +package machine + +import "device/stm32" + +var ( + Watchdog = &watchdogImpl{} +) + +const ( + // WatchdogMaxTimeout in milliseconds (32.768s) + // + // Timeout is based on 12-bit counter with /256 divider on + // 32.768kHz clock. See 21.3.3 of RM0090 for table. + WatchdogMaxTimeout = ((0xfff + 1) * 256 * 1024) / 32768 +) + +const ( + // Enable access to PR, RLR and WINR registers (0x5555) + iwdgKeyEnable = 0x5555 + // Reset the watchdog value (0xAAAA) + iwdgKeyReset = 0xaaaa + // Start the watchdog (0xCCCC) + iwdgKeyStart = 0xcccc + // Divide by 256 + iwdgDiv256 = 6 +) + +type watchdogImpl struct { +} + +// Configure the watchdog. +// +// This method should not be called after the watchdog is started and on +// some platforms attempting to reconfigure after starting the watchdog +// is explicitly forbidden / will not work. +func (wd *watchdogImpl) Configure(config WatchdogConfig) error { + + // Enable configuration of IWDG + stm32.IWDG.KR.Set(iwdgKeyEnable) + + // Unconditionally divide by /256 since we don't really need accuracy + // less than 8ms + stm32.IWDG.PR.Set(iwdgDiv256) + + timeout := config.TimeoutMillis + if timeout > WatchdogMaxTimeout { + timeout = WatchdogMaxTimeout + } + + // Set reload value based on /256 divider + stm32.IWDG.RLR.Set(((config.TimeoutMillis*32768 + (256 * 1024) - 1) / (256 * 1024)) - 1) + return nil +} + +// Starts the watchdog. +func (wd *watchdogImpl) Start() error { + stm32.IWDG.KR.Set(iwdgKeyStart) + return nil +} + +// Update the watchdog, indicating that `source` is healthy. +func (wd *watchdogImpl) Update() { + stm32.IWDG.KR.Set(iwdgKeyReset) +} diff --git a/src/machine/watchdog.go b/src/machine/watchdog.go new file mode 100644 index 000000000..d1516350d --- /dev/null +++ b/src/machine/watchdog.go @@ -0,0 +1,34 @@ +//go:build nrf52840 || nrf52833 || rp2040 || atsamd51 || atsame5x || stm32 + +package machine + +// WatchdogConfig holds configuration for the watchdog timer. +type WatchdogConfig struct { + // The timeout (in milliseconds) before the watchdog fires. + // + // If the requested timeout exceeds `MaxTimeout` it will be rounded + // down. + TimeoutMillis uint32 +} + +// watchdog must be implemented by any platform supporting watchdog functionality +type watchdog interface { + // Configure the watchdog. + // + // This method should not be called after the watchdog is started and on + // some platforms attempting to reconfigure after starting the watchdog + // is explicitly forbidden / will not work. + Configure(config WatchdogConfig) error + + // Starts the watchdog. + Start() error + + // Update the watchdog, indicating that the app is healthy. + Update() +} + +// Ensure required public symbols var exists and meets interface spec +var _ = watchdog(Watchdog) + +// Ensure required public constants exist +const _ = WatchdogMaxTimeout |