Eliminates the issue with the lock up in the bootloader due to a power drawdown during its operation.
Closes https://github.com/espressif/esp-idf/issues/1814
The GPIO must be held low continuously for this period of time after reset
before a factory reset or test partition boot (as applicable) is performed.
+config BOOTLOADER_WDT_ENABLE
+ bool "Use RTC watchdog in start code"
+ default y
+ help
+ Tracks the execution time of startup code.
+ If the execution time is exceeded, the RTC_WDT will restart system.
+ It is also useful to prevent a lock up in start code caused by an unstable power source.
+ NOTE: Tracks the execution time starts from the bootloader code - re-set timeout, while selecting the source for slow_clk - and ends calling app_main.
+ Re-set timeout is needed due to WDT uses a SLOW_CLK clock source. After changing a frequency slow_clk a time of WDT needs to re-set for new frequency.
+ slow_clk depends on ESP32_RTC_CLOCK_SOURCE (INTERNAL_RC or EXTERNAL_CRYSTAL).
+
+config BOOTLOADER_WDT_DISABLE_IN_USER_CODE
+ bool "Allows RTC watchdog disable in user code"
+ depends on BOOTLOADER_WDT_ENABLE
+ default n
+ help
+ If it is set, the client must itself reset or disable rtc_wdt in their code (app_main()).
+ Otherwise rtc_wdt will be disabled before calling app_main function.
+ Use function rtc_wdt_feed() for resetting counter of rtc_wdt.
+ Use function rtc_wdt_disable() for disabling rtc_wdt.
+
+config BOOTLOADER_WDT_TIME_MS
+ int "Timeout for RTC watchdog (ms)"
+ depends on BOOTLOADER_WDT_ENABLE
+ default 9000
+ range 0 120000
+ help
+ Verify that this parameter is correct and more then the execution time.
+ Pay attention to options such as reset to factory, trigger test partition and encryption on boot
+ - these options can increase the execution time.
+ Note: RTC_WDT will reset while encryption operations will be performed.
+
endmenu # Bootloader
* @note Take care not to power off the device while this function
* is running, or the partition currently being encrypted will be lost.
*
+ * @note RTC_WDT will reset while encryption operations will be performed (if RTC_WDT is configured).
+ *
* @return ESP_OK if all operations succeeded, ESP_ERR_INVALID_STATE
* if a fatal error occured during encryption of all partitions.
*/
/** @brief Encrypt-in-place a block of flash sectors
*
+ * @note This function resets RTC_WDT between operations with sectors.
* @param src_addr Source offset in flash. Should be multiple of 4096 bytes.
* @param data_length Length of data to encrypt in bytes. Will be rounded up to next multiple of 4096 bytes.
*
ESP_LOGI(TAG, "compile time " __TIME__ );
ets_set_appcpu_boot_addr(0);
+#ifdef CONFIG_BOOTLOADER_WDT_ENABLE
+ ESP_LOGD(TAG, "Enabling RTCWDT(%d ms)", CONFIG_BOOTLOADER_WDT_TIME_MS);
+ rtc_wdt_protect_off();
+ rtc_wdt_disable();
+ rtc_wdt_set_length_of_reset_signal(RTC_WDT_SYS_RESET_SIG, RTC_WDT_LENGTH_3_2us);
+ rtc_wdt_set_length_of_reset_signal(RTC_WDT_CPU_RESET_SIG, RTC_WDT_LENGTH_3_2us);
+ rtc_wdt_set_stage(RTC_WDT_STAGE0, RTC_WDT_STAGE_ACTION_RESET_RTC);
+ rtc_wdt_set_time(RTC_WDT_STAGE0, CONFIG_BOOTLOADER_WDT_TIME_MS);
+ rtc_wdt_enable();
+ rtc_wdt_protect_on();
+#else
/* disable watch dog here */
rtc_wdt_disable();
+#endif
+ REG_SET_FIELD(TIMG_WDTWPROTECT_REG(0), TIMG_WDT_WKEY, TIMG_WDT_WKEY_VALUE);
REG_CLR_BIT( TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN );
#ifndef CONFIG_SPI_FLASH_ROM_DRIVER_PATCH
#include "esp_efuse.h"
#include "esp_log.h"
#include "rom/secure_boot.h"
+#include "soc/rtc_wdt.h"
#include "rom/cache.h"
#include "rom/spi_flash.h" /* TODO: Remove this */
}
for (size_t i = 0; i < data_length; i += FLASH_SECTOR_SIZE) {
+ rtc_wdt_feed();
uint32_t sec_start = i + src_addr;
err = bootloader_flash_read(sec_start, buf, FLASH_SECTOR_SIZE, false);
if (err != ESP_OK) {
#include "rom/rtc.h"
#include "soc/soc.h"
#include "soc/rtc.h"
+#include "soc/rtc_wdt.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/i2s_reg.h"
#include "driver/periph_ctrl.h"
rtc_clk_fast_freq_set(RTC_FAST_FREQ_8M);
+#ifdef CONFIG_BOOTLOADER_WDT_ENABLE
+ // WDT uses a SLOW_CLK clock source. After a function select_rtc_slow_clk a frequency of this source can changed.
+ // If the frequency changes from 150kHz to 32kHz, then the timeout set for the WDT will increase 4.6 times.
+ // Therefore, for the time of frequency change, set a new lower timeout value (1.6 sec).
+ // This prevents excessive delay before resetting in case the supply voltage is drawdown.
+ // (If frequency is changed from 150kHz to 32kHz then WDT timeout will increased to 1.6sec * 150/32 = 7.5 sec).
+ rtc_wdt_protect_off();
+ rtc_wdt_feed();
+ rtc_wdt_set_time(RTC_WDT_STAGE0, 1600);
+ rtc_wdt_protect_on();
+#endif
+
#if defined(CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL)
select_rtc_slow_clk(SLOW_CLK_32K_XTAL);
#elif defined(CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC)
select_rtc_slow_clk(RTC_SLOW_FREQ_RTC);
#endif
+#ifdef CONFIG_BOOTLOADER_WDT_ENABLE
+ // After changing a frequency WDT timeout needs to be set for new frequency.
+ rtc_wdt_protect_off();
+ rtc_wdt_feed();
+ rtc_wdt_set_time(RTC_WDT_STAGE0, CONFIG_BOOTLOADER_WDT_TIME_MS);
+ rtc_wdt_protect_on();
+#endif
+
rtc_cpu_freq_config_t old_config, new_config;
rtc_clk_cpu_freq_get_config(&old_config);
const uint32_t old_freq_mhz = old_config.freq_mhz;
|| rst_reas[1] == RTCWDT_SYS_RESET || rst_reas[1] == TG0WDT_SYS_RESET
#endif
) {
+#ifndef CONFIG_BOOTLOADER_WDT_ENABLE
rtc_wdt_disable();
+#endif
}
//Clear BSS. Please do not attempt to do any complex stuff (like early logging) before this.
static void main_task(void* args)
{
- // Now that the application is about to start, disable boot watchdogs
- REG_CLR_BIT(TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN_S);
- rtc_wdt_disable();
#if !CONFIG_FREERTOS_UNICORE
// Wait for FreeRTOS initialization to finish on APP CPU, before replacing its startup stack
while (port_xSchedulerRunning[1] == 0) {
}
#endif
+ // Now that the application is about to start, disable boot watchdog
+#ifndef CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE
+ rtc_wdt_disable();
+#endif
app_main();
vTaskDelete(NULL);
}
return ESP_OK;
}
+esp_err_t rtc_wdt_get_timeout(rtc_wdt_stage_t stage, unsigned int* timeout_ms)
+{
+ if (stage > 3) {
+ return ESP_ERR_INVALID_ARG;
+ }
+ uint32_t time_tick;
+ if (stage == RTC_WDT_STAGE0) {
+ time_tick = READ_PERI_REG(RTC_CNTL_WDTCONFIG1_REG);
+ } else if (stage == RTC_WDT_STAGE1) {
+ time_tick = READ_PERI_REG(RTC_CNTL_WDTCONFIG2_REG);
+ } else if (stage == RTC_WDT_STAGE2) {
+ time_tick = READ_PERI_REG(RTC_CNTL_WDTCONFIG3_REG);
+ } else {
+ time_tick = READ_PERI_REG(RTC_CNTL_WDTCONFIG4_REG);
+ }
+
+ *timeout_ms = time_tick * 1000 / rtc_clk_slow_freq_get_hz();
+
+ return ESP_OK;
+}
+
esp_err_t rtc_wdt_set_stage(rtc_wdt_stage_t stage, rtc_wdt_stage_action_t stage_sel)
{
if (stage > 3 || stage_sel > 4) {
*/
esp_err_t rtc_wdt_set_time(rtc_wdt_stage_t stage, unsigned int timeout_ms);
+/**
+ * @brief Get the timeout set for the required stage.
+ *
+ * @param[in] stage Stage of rtc_wdt.
+ * @param[out] timeout_ms Timeout set for this stage. (not elapsed time).
+ *
+ * @return
+ * - ESP_OK In case of success
+ * - ESP_ERR_INVALID_ARG If stage has invalid value
+ */
+esp_err_t rtc_wdt_get_timeout(rtc_wdt_stage_t stage, unsigned int* timeout_ms);
+
/**
* @brief Set an action for required stage.
*