Skip to content

Commit f4375d0

Browse files
kenbelldeadprogram
authored andcommitted
samd51,rp2040,nrf528xx,stm32: implement watchdog
1 parent 756cdf4 commit f4375d0

File tree

8 files changed

+291
-12
lines changed

8 files changed

+291
-12
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,8 @@ smoketest:
490490
@$(MD5SUM) test.hex
491491
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/i2c-target
492492
@$(MD5SUM) test.hex
493+
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/watchdog
494+
@$(MD5SUM) test.hex
493495
# test simulated boards on play.tinygo.org
494496
ifneq ($(WASM), 0)
495497
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1

src/examples/watchdog/main.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"machine"
6+
"time"
7+
)
8+
9+
func main() {
10+
//sleep for 2 secs for console
11+
time.Sleep(2 * time.Second)
12+
13+
config := machine.WatchdogConfig{
14+
TimeoutMillis: 1000,
15+
}
16+
17+
println("configuring watchdog for max 1 second updates")
18+
machine.Watchdog.Configure(config)
19+
20+
// From this point the watchdog is running and Update must be
21+
// called periodically, per the config
22+
machine.Watchdog.Start()
23+
24+
// This loop should complete because watchdog update is called
25+
// every 100ms.
26+
start := time.Now()
27+
println("updating watchdog for 3 seconds")
28+
for i := 0; i < 30; i++ {
29+
time.Sleep(100 * time.Millisecond)
30+
machine.Watchdog.Update()
31+
fmt.Printf("alive @ %v\n", time.Now().Sub(start))
32+
}
33+
34+
// This loop should cause a watchdog reset after 1s since
35+
// there is no update call.
36+
start = time.Now()
37+
println("entering tight loop")
38+
for {
39+
time.Sleep(100 * time.Millisecond)
40+
fmt.Printf("alive @ %v\n", time.Now().Sub(start))
41+
}
42+
}

src/machine/machine_atsamd51.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2299,3 +2299,52 @@ func checkFlashError() error {
22992299

23002300
return nil
23012301
}
2302+
2303+
// Watchdog provides access to the hardware watchdog available
2304+
// in the SAMD51.
2305+
var Watchdog = &watchdogImpl{}
2306+
2307+
const (
2308+
// WatchdogMaxTimeout in milliseconds (16s)
2309+
WatchdogMaxTimeout = (16384 * 1000) / 1024 // CYC16384/1024kHz
2310+
)
2311+
2312+
type watchdogImpl struct{}
2313+
2314+
// Configure the watchdog.
2315+
//
2316+
// This method should not be called after the watchdog is started and on
2317+
// some platforms attempting to reconfigure after starting the watchdog
2318+
// is explicitly forbidden / will not work.
2319+
func (wd *watchdogImpl) Configure(config WatchdogConfig) error {
2320+
// 1.024kHz clock
2321+
cycles := int((int64(config.TimeoutMillis) * 1024) / 1000)
2322+
2323+
// period is expressed as a power-of-two, starting at 8 / 1024ths of a second
2324+
period := uint8(0)
2325+
cfgCycles := 8
2326+
for cfgCycles < cycles {
2327+
period++
2328+
cfgCycles <<= 1
2329+
2330+
if period >= 0xB {
2331+
break
2332+
}
2333+
}
2334+
2335+
sam.WDT.CONFIG.Set(period << sam.WDT_CONFIG_PER_Pos)
2336+
2337+
return nil
2338+
}
2339+
2340+
// Starts the watchdog.
2341+
func (wd *watchdogImpl) Start() error {
2342+
sam.WDT.CTRLA.SetBits(sam.WDT_CTRLA_ENABLE)
2343+
return nil
2344+
}
2345+
2346+
// Update the watchdog, indicating that `source` is healthy.
2347+
func (wd *watchdogImpl) Update() {
2348+
// 0xA5 = magic value (see datasheet)
2349+
sam.WDT.CLEAR.Set(0xA5)
2350+
}

src/machine/machine_nrf528xx.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,46 @@ func twisError(val uint32) error {
214214

215215
return errI2CBusError
216216
}
217+
218+
var (
219+
Watchdog = &watchdogImpl{}
220+
)
221+
222+
const (
223+
// WatchdogMaxTimeout in milliseconds (approx 36h)
224+
WatchdogMaxTimeout = (0xffffffff * 1000) / 32768
225+
)
226+
227+
type watchdogImpl struct {
228+
}
229+
230+
// Configure the watchdog.
231+
//
232+
// This method should not be called after the watchdog is started and on
233+
// some platforms attempting to reconfigure after starting the watchdog
234+
// is explicitly forbidden / will not work.
235+
func (wd *watchdogImpl) Configure(config WatchdogConfig) error {
236+
// 32.768kHz counter
237+
crv := int32((int64(config.TimeoutMillis) * 32768) / 1000)
238+
nrf.WDT.CRV.Set(uint32(crv))
239+
240+
// One source
241+
nrf.WDT.RREN.Set(0x1)
242+
243+
// Run during sleep
244+
nrf.WDT.CONFIG.Set(nrf.WDT_CONFIG_SLEEP_Run)
245+
246+
return nil
247+
}
248+
249+
// Starts the watchdog.
250+
func (wd *watchdogImpl) Start() error {
251+
nrf.WDT.TASKS_START.Set(nrf.WDT_TASKS_START_TASKS_START)
252+
return nil
253+
}
254+
255+
// Update the watchdog, indicating that `source` is healthy.
256+
func (wd *watchdogImpl) Update() {
257+
// 0x6E524635 = magic value from datasheet
258+
nrf.WDT.RR[0].Set(0x6E524635)
259+
}

src/machine/machine_rp2040_clocks.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ func (clk *clock) configure(src, auxsrc, srcFreq, freq uint32) {
182182
// Must be called before any other clock function.
183183
func (clks *clocksType) init() {
184184
// Start the watchdog tick
185-
watchdog.startTick(xoscFreq)
185+
Watchdog.startTick(xoscFreq)
186186

187187
// Disable resus that may be enabled from previous software
188188
clks.resus.ctrl.Set(0)

src/machine/machine_rp2040_watchdog.go

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,67 @@ package machine
44

55
import (
66
"device/rp"
7-
"runtime/volatile"
8-
"unsafe"
97
)
108

11-
type watchdogType struct {
12-
ctrl volatile.Register32
13-
load volatile.Register32
14-
reason volatile.Register32
15-
scratch [8]volatile.Register32
16-
tick volatile.Register32
9+
// Watchdog provides access to the hardware watchdog available
10+
// in the RP2040.
11+
var Watchdog = &watchdogImpl{}
12+
13+
const (
14+
// WatchdogMaxTimeout in milliseconds (approx 8.3s).
15+
//
16+
// Nominal 1us per watchdog tick, 24-bit counter,
17+
// but due to errata two ticks consumed per 1us.
18+
// See: Errata RP2040-E1
19+
WatchdogMaxTimeout = (rp.WATCHDOG_LOAD_LOAD_Msk / 1000) / 2
20+
)
21+
22+
type watchdogImpl struct {
23+
// The value to reset the counter to on each Update
24+
loadValue uint32
1725
}
1826

19-
var watchdog = (*watchdogType)(unsafe.Pointer(rp.WATCHDOG))
27+
// Configure the watchdog.
28+
//
29+
// This method should not be called after the watchdog is started and on
30+
// some platforms attempting to reconfigure after starting the watchdog
31+
// is explicitly forbidden / will not work.
32+
func (wd *watchdogImpl) Configure(config WatchdogConfig) error {
33+
// x2 due to errata RP2040-E1
34+
wd.loadValue = config.TimeoutMillis * 1000 * 2
35+
if wd.loadValue > rp.WATCHDOG_LOAD_LOAD_Msk {
36+
wd.loadValue = rp.WATCHDOG_LOAD_LOAD_Msk
37+
}
38+
39+
rp.WATCHDOG.CTRL.ClearBits(rp.WATCHDOG_CTRL_ENABLE)
40+
41+
// Reset everything apart from ROSC and XOSC
42+
rp.PSM.WDSEL.Set(0x0001ffff &^ (rp.PSM_WDSEL_ROSC | rp.PSM_WDSEL_XOSC))
43+
44+
// Pause watchdog during debug
45+
rp.WATCHDOG.CTRL.SetBits(rp.WATCHDOG_CTRL_PAUSE_DBG0 | rp.WATCHDOG_CTRL_PAUSE_DBG1 | rp.WATCHDOG_CTRL_PAUSE_JTAG)
46+
47+
// Load initial counter
48+
rp.WATCHDOG.LOAD.Set(wd.loadValue)
49+
50+
return nil
51+
}
52+
53+
// Starts the watchdog.
54+
func (wd *watchdogImpl) Start() error {
55+
rp.WATCHDOG.CTRL.SetBits(rp.WATCHDOG_CTRL_ENABLE)
56+
return nil
57+
}
58+
59+
// Update the watchdog, indicating that the app is healthy.
60+
func (wd *watchdogImpl) Update() {
61+
rp.WATCHDOG.LOAD.Set(wd.loadValue)
62+
}
2063

2164
// startTick starts the watchdog tick.
2265
// cycles needs to be a divider that when applied to the xosc input,
2366
// produces a 1MHz clock. So if the xosc frequency is 12MHz,
2467
// this will need to be 12.
25-
func (wd *watchdogType) startTick(cycles uint32) {
26-
wd.tick.Set(cycles | rp.WATCHDOG_TICK_ENABLE)
68+
func (wd *watchdogImpl) startTick(cycles uint32) {
69+
rp.WATCHDOG.TICK.Set(cycles | rp.WATCHDOG_TICK_ENABLE)
2770
}

src/machine/machine_stm32_iwdg.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//go:build stm32
2+
3+
package machine
4+
5+
import "device/stm32"
6+
7+
var (
8+
Watchdog = &watchdogImpl{}
9+
)
10+
11+
const (
12+
// WatchdogMaxTimeout in milliseconds (32.768s)
13+
//
14+
// Timeout is based on 12-bit counter with /256 divider on
15+
// 32.768kHz clock. See 21.3.3 of RM0090 for table.
16+
WatchdogMaxTimeout = ((0xfff + 1) * 256 * 1024) / 32768
17+
)
18+
19+
const (
20+
// Enable access to PR, RLR and WINR registers (0x5555)
21+
iwdgKeyEnable = 0x5555
22+
// Reset the watchdog value (0xAAAA)
23+
iwdgKeyReset = 0xaaaa
24+
// Start the watchdog (0xCCCC)
25+
iwdgKeyStart = 0xcccc
26+
// Divide by 256
27+
iwdgDiv256 = 6
28+
)
29+
30+
type watchdogImpl struct {
31+
}
32+
33+
// Configure the watchdog.
34+
//
35+
// This method should not be called after the watchdog is started and on
36+
// some platforms attempting to reconfigure after starting the watchdog
37+
// is explicitly forbidden / will not work.
38+
func (wd *watchdogImpl) Configure(config WatchdogConfig) error {
39+
40+
// Enable configuration of IWDG
41+
stm32.IWDG.KR.Set(iwdgKeyEnable)
42+
43+
// Unconditionally divide by /256 since we don't really need accuracy
44+
// less than 8ms
45+
stm32.IWDG.PR.Set(iwdgDiv256)
46+
47+
timeout := config.TimeoutMillis
48+
if timeout > WatchdogMaxTimeout {
49+
timeout = WatchdogMaxTimeout
50+
}
51+
52+
// Set reload value based on /256 divider
53+
stm32.IWDG.RLR.Set(((config.TimeoutMillis*32768 + (256 * 1024) - 1) / (256 * 1024)) - 1)
54+
return nil
55+
}
56+
57+
// Starts the watchdog.
58+
func (wd *watchdogImpl) Start() error {
59+
stm32.IWDG.KR.Set(iwdgKeyStart)
60+
return nil
61+
}
62+
63+
// Update the watchdog, indicating that `source` is healthy.
64+
func (wd *watchdogImpl) Update() {
65+
stm32.IWDG.KR.Set(iwdgKeyReset)
66+
}

src/machine/watchdog.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//go:build nrf52840 || nrf52833 || rp2040 || atsamd51 || atsame5x || stm32
2+
3+
package machine
4+
5+
// WatchdogConfig holds configuration for the watchdog timer.
6+
type WatchdogConfig struct {
7+
// The timeout (in milliseconds) before the watchdog fires.
8+
//
9+
// If the requested timeout exceeds `MaxTimeout` it will be rounded
10+
// down.
11+
TimeoutMillis uint32
12+
}
13+
14+
// watchdog must be implemented by any platform supporting watchdog functionality
15+
type watchdog interface {
16+
// Configure the watchdog.
17+
//
18+
// This method should not be called after the watchdog is started and on
19+
// some platforms attempting to reconfigure after starting the watchdog
20+
// is explicitly forbidden / will not work.
21+
Configure(config WatchdogConfig) error
22+
23+
// Starts the watchdog.
24+
Start() error
25+
26+
// Update the watchdog, indicating that the app is healthy.
27+
Update()
28+
}
29+
30+
// Ensure required public symbols var exists and meets interface spec
31+
var _ = watchdog(Watchdog)
32+
33+
// Ensure required public constants exist
34+
const _ = WatchdogMaxTimeout

0 commit comments

Comments
 (0)