aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKenneth Bell <[email protected]>2023-08-06 22:15:47 +0100
committerRon Evans <[email protected]>2023-08-15 11:50:07 +0200
commitf4375d045213c416fad2d19773e7700b6b02f685 (patch)
treebd24ba00ec42f0b8e767bf66d852493a3f393a39
parent756cdf44ed5a83a0fd37a63bdf596328c279f4f7 (diff)
downloadtinygo-f4375d045213c416fad2d19773e7700b6b02f685.tar.gz
tinygo-f4375d045213c416fad2d19773e7700b6b02f685.zip
samd51,rp2040,nrf528xx,stm32: implement watchdog
-rw-r--r--Makefile2
-rw-r--r--src/examples/watchdog/main.go42
-rw-r--r--src/machine/machine_atsamd51.go49
-rw-r--r--src/machine/machine_nrf528xx.go43
-rw-r--r--src/machine/machine_rp2040_clocks.go2
-rw-r--r--src/machine/machine_rp2040_watchdog.go65
-rw-r--r--src/machine/machine_stm32_iwdg.go66
-rw-r--r--src/machine/watchdog.go34
8 files changed, 291 insertions, 12 deletions
diff --git a/Makefile b/Makefile
index 1bb84463b..e51536497 100644
--- a/Makefile
+++ b/Makefile
@@ -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