samd51,rp2040,nrf528xx,stm32: implement watchdog
Этот коммит содержится в:
		
							родитель
							
								
									756cdf44ed
								
							
						
					
					
						коммит
						f4375d0452
					
				
					 8 изменённых файлов: 291 добавлений и 12 удалений
				
			
		
							
								
								
									
										2
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
										
									
									
									
								
							|  | @ -490,6 +490,8 @@ smoketest: | ||||||
| 	@$(MD5SUM) test.hex | 	@$(MD5SUM) test.hex | ||||||
| 	$(TINYGO) build -size short -o test.hex -target=feather-rp2040      examples/i2c-target | 	$(TINYGO) build -size short -o test.hex -target=feather-rp2040      examples/i2c-target | ||||||
| 	@$(MD5SUM) test.hex | 	@$(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 | 	# test simulated boards on play.tinygo.org | ||||||
| ifneq ($(WASM), 0) | ifneq ($(WASM), 0) | ||||||
| 	$(TINYGO) build -size short -o test.wasm -tags=arduino              examples/blinky1 | 	$(TINYGO) build -size short -o test.wasm -tags=arduino              examples/blinky1 | ||||||
|  |  | ||||||
							
								
								
									
										42
									
								
								src/examples/watchdog/main.go
									
										
									
									
									
										Обычный файл
									
								
							
							
						
						
									
										42
									
								
								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)) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -2299,3 +2299,52 @@ func checkFlashError() error { | ||||||
| 
 | 
 | ||||||
| 	return nil | 	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) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -214,3 +214,46 @@ func twisError(val uint32) error { | ||||||
| 
 | 
 | ||||||
| 	return errI2CBusError | 	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) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -182,7 +182,7 @@ func (clk *clock) configure(src, auxsrc, srcFreq, freq uint32) { | ||||||
| // Must be called before any other clock function. | // Must be called before any other clock function. | ||||||
| func (clks *clocksType) init() { | func (clks *clocksType) init() { | ||||||
| 	// Start the watchdog tick | 	// Start the watchdog tick | ||||||
| 	watchdog.startTick(xoscFreq) | 	Watchdog.startTick(xoscFreq) | ||||||
| 
 | 
 | ||||||
| 	// Disable resus that may be enabled from previous software | 	// Disable resus that may be enabled from previous software | ||||||
| 	clks.resus.ctrl.Set(0) | 	clks.resus.ctrl.Set(0) | ||||||
|  |  | ||||||
|  | @ -4,24 +4,67 @@ package machine | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"device/rp" | 	"device/rp" | ||||||
| 	"runtime/volatile" |  | ||||||
| 	"unsafe" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type watchdogType struct { | // Watchdog provides access to the hardware watchdog available | ||||||
| 	ctrl    volatile.Register32 | // in the RP2040. | ||||||
| 	load    volatile.Register32 | var Watchdog = &watchdogImpl{} | ||||||
| 	reason  volatile.Register32 | 
 | ||||||
| 	scratch [8]volatile.Register32 | const ( | ||||||
| 	tick    volatile.Register32 | 	// 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. | // startTick starts the watchdog tick. | ||||||
| // cycles needs to be a divider that when applied to the xosc input, | // cycles needs to be a divider that when applied to the xosc input, | ||||||
| // produces a 1MHz clock. So if the xosc frequency is 12MHz, | // produces a 1MHz clock. So if the xosc frequency is 12MHz, | ||||||
| // this will need to be 12. | // this will need to be 12. | ||||||
| func (wd *watchdogType) startTick(cycles uint32) { | func (wd *watchdogImpl) startTick(cycles uint32) { | ||||||
| 	wd.tick.Set(cycles | rp.WATCHDOG_TICK_ENABLE) | 	rp.WATCHDOG.TICK.Set(cycles | rp.WATCHDOG_TICK_ENABLE) | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										66
									
								
								src/machine/machine_stm32_iwdg.go
									
										
									
									
									
										Обычный файл
									
								
							
							
						
						
									
										66
									
								
								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) | ||||||
|  | } | ||||||
							
								
								
									
										34
									
								
								src/machine/watchdog.go
									
										
									
									
									
										Обычный файл
									
								
							
							
						
						
									
										34
									
								
								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 | ||||||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Kenneth Bell
						Kenneth Bell