Initial Release
This commit is contained in:
1
src/rp2_common/hardware_watchdog/CMakeLists.txt
Normal file
1
src/rp2_common/hardware_watchdog/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
pico_simple_hardware_target(watchdog)
|
87
src/rp2_common/hardware_watchdog/include/hardware/watchdog.h
Normal file
87
src/rp2_common/hardware_watchdog/include/hardware/watchdog.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _HARDWARE_WATCHDOG_H
|
||||
#define _HARDWARE_WATCHDOG_H
|
||||
|
||||
#include "pico.h"
|
||||
|
||||
/** \file hardware/watchdog.h
|
||||
* \defgroup hardware_watchdog hardware_watchdog
|
||||
*
|
||||
* Hardware Watchdog Timer API
|
||||
*
|
||||
* Supporting functions for the Pico hardware watchdog timer.
|
||||
*
|
||||
* The RP2040 has a built in HW watchdog Timer. This is a countdown timer that can restart parts of the chip if it reaches zero.
|
||||
* For example, this can be used to restart the processor if the software running on it gets stuck in an infinite loop
|
||||
* or similar. The programmer has to periodically write a value to the watchdog to stop it reaching zero.
|
||||
*
|
||||
* \subsection watchdog_example Example
|
||||
* \addtogroup hardware_watchdog
|
||||
* \include hello_watchdog.c
|
||||
*/
|
||||
|
||||
/*! \brief Define actions to perform at watchdog timeout
|
||||
* \ingroup hardware_watchdog
|
||||
*
|
||||
* \note If \ref watchdog_start_tick value does not give a 1MHz clock to the watchdog system, then the \ref delay_ms
|
||||
* parameter will not be in microseconds. See the datasheet for more details.
|
||||
*
|
||||
* By default the SDK assumes a 12MHz XOSC and sets the \ref watchdog_start_tick appropriately.
|
||||
*
|
||||
* \param pc If Zero, a standard boot will be performed, if non-zero this is the program counter to jump to on reset.
|
||||
* \param sp If \p pc is non-zero, this will be the stack pointer used.
|
||||
* \param delay_ms Initial load value. Maximum value 0x7fffff, approximately 8.3s.
|
||||
*/
|
||||
void watchdog_reboot(uint32_t pc, uint32_t sp, uint32_t delay_ms);
|
||||
|
||||
/*! \brief Start the watchdog tick
|
||||
* \ingroup hardware_watchdog
|
||||
*
|
||||
* \param cycles This needs to be a divider that when applied to the XOSC input, produces a 1MHz clock. So if the XOSC is
|
||||
* 12MHz, this will need to be 12.
|
||||
*/
|
||||
void watchdog_start_tick(uint cycles);
|
||||
|
||||
/*! \brief Reload the watchdog counter with the amount of time set in watchdog_enable
|
||||
* \ingroup hardware_watchdog
|
||||
*
|
||||
*/
|
||||
void watchdog_update(void);
|
||||
|
||||
/**
|
||||
* \brief Enable the watchdog
|
||||
* \ingroup hardware_watchdog
|
||||
*
|
||||
* \note If \ref watchdog_start_tick value does not give a 1MHz clock to the watchdog system, then the \ref delay_ms
|
||||
* parameter will not be in microseconds. See the datasheet for more details.
|
||||
*
|
||||
* By default the SDK assumes a 12MHz XOSC and sets the \ref watchdog_start_tick appropriately.
|
||||
*
|
||||
* \param delay_ms Number of milliseconds before watchdog will reboot without watchdog_update being called. Maximum of 0x7fffff, which is approximately 8.3 seconds
|
||||
* \param pause_on_debug If the watchdog should be paused when the debugger is stepping through code
|
||||
*/
|
||||
void watchdog_enable(uint32_t delay_ms, bool pause_on_debug);
|
||||
|
||||
/**
|
||||
* \brief Did the watchdog cause the last reboot?
|
||||
* \ingroup hardware_watchdog
|
||||
*
|
||||
* @return true if the watchdog timer or a watchdog force caused the last reboot
|
||||
* @return false there has been no watchdog reboot since run has been
|
||||
*/
|
||||
bool watchdog_caused_reboot(void);
|
||||
|
||||
/**
|
||||
* @brief Returns the number of microseconds before the watchdog will reboot the chip.
|
||||
* \ingroup hardware_watchdog
|
||||
*
|
||||
* @return The number of microseconds before the watchdog will reboot the chip.
|
||||
*/
|
||||
uint32_t watchdog_get_count(void);
|
||||
|
||||
#endif
|
99
src/rp2_common/hardware_watchdog/watchdog.c
Normal file
99
src/rp2_common/hardware_watchdog/watchdog.c
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "hardware/watchdog.h"
|
||||
#include "hardware/structs/watchdog.h"
|
||||
#include "hardware/structs/psm.h"
|
||||
|
||||
/// \tag::watchdog_start_tick[]
|
||||
void watchdog_start_tick(uint cycles) {
|
||||
// Important: This function also provides a tick reference to the timer
|
||||
watchdog_hw->tick = cycles | WATCHDOG_TICK_ENABLE_BITS;
|
||||
}
|
||||
/// \end::watchdog_start_tick[]
|
||||
|
||||
// Value to load when updating the watchdog
|
||||
|
||||
// tag::watchdog_update[]
|
||||
static uint32_t load_value;
|
||||
|
||||
void watchdog_update(void) {
|
||||
watchdog_hw->load = load_value;
|
||||
}
|
||||
// end::watchdog_update[]
|
||||
|
||||
uint32_t watchdog_get_count(void) {
|
||||
return (watchdog_hw->ctrl & WATCHDOG_CTRL_TIME_BITS) / 2 ;
|
||||
}
|
||||
|
||||
// tag::watchdog_enable[]
|
||||
// Helper function used by both watchdog_enable and watchdog_reboot
|
||||
void _watchdog_enable(uint32_t delay_ms, bool pause_on_debug) {
|
||||
hw_clear_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS);
|
||||
|
||||
// Reset everything apart from ROSC and XOSC
|
||||
hw_set_bits(&psm_hw->wdsel, PSM_WDSEL_BITS & ~(PSM_WDSEL_ROSC_BITS | PSM_WDSEL_XOSC_BITS));
|
||||
|
||||
uint32_t dbg_bits = WATCHDOG_CTRL_PAUSE_DBG0_BITS |
|
||||
WATCHDOG_CTRL_PAUSE_DBG1_BITS |
|
||||
WATCHDOG_CTRL_PAUSE_JTAG_BITS;
|
||||
|
||||
if (pause_on_debug) {
|
||||
hw_set_bits(&watchdog_hw->ctrl, dbg_bits);
|
||||
} else {
|
||||
hw_clear_bits(&watchdog_hw->ctrl, dbg_bits);
|
||||
}
|
||||
|
||||
if (!delay_ms) delay_ms = 50;
|
||||
|
||||
// Note, we have x2 here as the watchdog HW currently decrements twice per tick
|
||||
load_value = delay_ms * 1000 * 2;
|
||||
|
||||
if (load_value > 0xffffffu)
|
||||
load_value = 0xffffffu;
|
||||
|
||||
|
||||
watchdog_update();
|
||||
|
||||
hw_set_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS);
|
||||
}
|
||||
// end::watchdog_enable[]
|
||||
|
||||
void watchdog_enable(uint32_t delay_ms, bool pause_on_debug) {
|
||||
// This watchdog enable doesn't reboot so clear scratch register
|
||||
// with magic word to jump into code
|
||||
watchdog_hw->scratch[4] = 0;
|
||||
_watchdog_enable(delay_ms, pause_on_debug);
|
||||
}
|
||||
|
||||
void watchdog_reboot(uint32_t pc, uint32_t sp, uint32_t delay_ms) {
|
||||
check_hw_layout(watchdog_hw_t, scratch[7], WATCHDOG_SCRATCH7_OFFSET);
|
||||
|
||||
// Clear enable before setting up scratch registers
|
||||
hw_clear_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS);
|
||||
|
||||
if (pc) {
|
||||
pc |= 1u; // thumb mode
|
||||
watchdog_hw->scratch[4] = 0xb007c0d3;
|
||||
watchdog_hw->scratch[5] = pc ^ -0xb007c0d3;
|
||||
watchdog_hw->scratch[6] = sp;
|
||||
watchdog_hw->scratch[7] = pc;
|
||||
// printf("rebooting %08x/%08x in %dms...\n", (uint) pc, (uint) sp, (uint) delay_ms);
|
||||
} else {
|
||||
watchdog_hw->scratch[4] = 0;
|
||||
// printf("rebooting (regular)) in %dms...\n", (uint) delay_ms);
|
||||
}
|
||||
|
||||
// Don't pause watchdog for debug
|
||||
_watchdog_enable(delay_ms, 0);
|
||||
}
|
||||
|
||||
bool watchdog_caused_reboot(void) {
|
||||
// If any reason bits are set this is true
|
||||
return watchdog_hw->reason;
|
||||
}
|
Reference in New Issue
Block a user