Skip to content

Commit

Permalink
Merge branch 'bugfix/rtc_and_restart_fixes' into 'master'
Browse files Browse the repository at this point in the history
rtc_clk and esp_restart fixes

See merge request !1458
  • Loading branch information
jack0c committed Nov 3, 2017
2 parents 88514f9 + eb5752c commit abacf8d
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 31 deletions.
42 changes: 23 additions & 19 deletions components/esp32/system_api.c
Expand Up @@ -266,15 +266,10 @@ void IRAM_ATTR esp_restart(void)
*/
void IRAM_ATTR esp_restart_noos()
{
const uint32_t core_id = xPortGetCoreID();
const uint32_t other_core_id = core_id == 0 ? 1 : 0;
esp_cpu_stall(other_core_id);

// other core is now stalled, can access DPORT registers directly
esp_dport_access_int_pause();
// Disable interrupts
xt_ints_off(0xFFFFFFFF);

// We need to disable TG0/TG1 watchdogs
// First enable RTC watchdog for 1 second
// Enable RTC watchdog for 1 second
REG_WRITE(RTC_CNTL_WDTWPROTECT_REG, RTC_CNTL_WDT_WKEY_VALUE);
REG_WRITE(RTC_CNTL_WDTCONFIG0_REG,
RTC_CNTL_WDT_FLASHBOOT_MOD_EN_M |
Expand All @@ -284,6 +279,18 @@ void IRAM_ATTR esp_restart_noos()
(1 << RTC_CNTL_WDT_CPU_RESET_LENGTH_S) );
REG_WRITE(RTC_CNTL_WDTCONFIG1_REG, rtc_clk_slow_freq_get_hz() * 1);

// Reset and stall the other CPU.
// CPU must be reset before stalling, in case it was running a s32c1i
// instruction. This would cause memory pool to be locked by arbiter
// to the stalled CPU, preventing current CPU from accessing this pool.
const uint32_t core_id = xPortGetCoreID();
const uint32_t other_core_id = (core_id == 0) ? 1 : 0;
esp_cpu_reset(other_core_id);
esp_cpu_stall(other_core_id);

// Other core is now stalled, can access DPORT registers directly
esp_dport_access_int_abort();

// Disable TG0/TG1 watchdogs
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
TIMERG0.wdt_config0.en = 0;
Expand All @@ -292,8 +299,10 @@ void IRAM_ATTR esp_restart_noos()
TIMERG1.wdt_config0.en = 0;
TIMERG1.wdt_wprotect=0;

// Disable all interrupts
xt_ints_off(0xFFFFFFFF);
// Flush any data left in UART FIFOs
uart_tx_wait_idle(0);
uart_tx_wait_idle(1);
uart_tx_wait_idle(2);

// Disable cache
Cache_Read_Disable(0);
Expand All @@ -310,11 +319,6 @@ void IRAM_ATTR esp_restart_noos()
WRITE_PERI_REG(GPIO_FUNC5_IN_SEL_CFG_REG, 0x30);
#endif

// Flush any data left in UART FIFOs
uart_tx_wait_idle(0);
uart_tx_wait_idle(1);
uart_tx_wait_idle(2);

// Reset wifi/bluetooth/ethernet/sdio (bb/mac)
DPORT_SET_PERI_REG_MASK(DPORT_CORE_RST_EN_REG,
DPORT_BB_RST | DPORT_FE_RST | DPORT_MAC_RST |
Expand All @@ -337,14 +341,14 @@ void IRAM_ATTR esp_restart_noos()
// Reset CPUs
if (core_id == 0) {
// Running on PRO CPU: APP CPU is stalled. Can reset both CPUs.
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG,
RTC_CNTL_SW_PROCPU_RST_M | RTC_CNTL_SW_APPCPU_RST_M);
esp_cpu_reset(1);
esp_cpu_reset(0);
} else {
// Running on APP CPU: need to reset PRO CPU and unstall it,
// then reset APP CPU
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_PROCPU_RST_M);
esp_cpu_reset(0);
esp_cpu_unstall(0);
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_APPCPU_RST_M);
esp_cpu_reset(1);
}
while(true) {
;
Expand Down
6 changes: 6 additions & 0 deletions components/soc/esp32/cpu_util.c
Expand Up @@ -44,6 +44,12 @@ void IRAM_ATTR esp_cpu_unstall(int cpu_id)
}
}

void IRAM_ATTR esp_cpu_reset(int cpu_id)
{
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG,
cpu_id == 0 ? RTC_CNTL_SW_PROCPU_RST_M : RTC_CNTL_SW_APPCPU_RST_M);
}

bool IRAM_ATTR esp_cpu_in_ocd_debug_mode()
{
#if CONFIG_ESP32_DEBUG_OCDAWARE
Expand Down
7 changes: 7 additions & 0 deletions components/soc/esp32/include/soc/cpu.h
Expand Up @@ -85,6 +85,13 @@ void esp_cpu_stall(int cpu_id);
*/
void esp_cpu_unstall(int cpu_id);

/**
* @brief Reset CPU using RTC controller
* @param cpu_id ID of the CPU to reset (0 = PRO, 1 = APP)
*/
void esp_cpu_reset(int cpu_id);


/**
* @brief Returns true if a JTAG debugger is attached to CPU
* OCD (on chip debug) port.
Expand Down
9 changes: 9 additions & 0 deletions components/soc/esp32/include/soc/rtc.h
Expand Up @@ -400,6 +400,15 @@ uint64_t rtc_time_slowclk_to_us(uint64_t rtc_cycles, uint32_t period);
*/
uint64_t rtc_time_get();

/**
* @brief Busy loop until next RTC_SLOW_CLK cycle
*
* This function returns not earlier than the next RTC_SLOW_CLK clock cycle.
* In some cases (e.g. when RTC_SLOW_CLK cycle is very close), it may return
* one RTC_SLOW_CLK cycle later.
*/
void rtc_clk_wait_for_slow_cycle();

/**
* @brief sleep configuration for rtc_sleep_init function
*/
Expand Down
33 changes: 22 additions & 11 deletions components/soc/esp32/rtc_clk.c
Expand Up @@ -72,9 +72,6 @@ static const char* TAG = "rtc_clk";
* All values are in microseconds.
* TODO: some of these are excessive, and should be reduced.
*/
#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_150K 20
#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_32K 160
#define DELAY_CPU_FREQ_SWITCH_TO_PLL 20
#define DELAY_PLL_DBIAS_RAISE 3
#define DELAY_PLL_ENABLE_WITH_150K 80
#define DELAY_PLL_ENABLE_WITH_32K 160
Expand Down Expand Up @@ -397,9 +394,12 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq)
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_XTL);
REG_SET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT, 0);
ets_update_cpu_frequency(xtal_freq);
uint32_t delay_xtal_switch = (rtc_clk_slow_freq_get() == RTC_SLOW_FREQ_RTC) ?
DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_150K : DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_32K;
ets_delay_us(delay_xtal_switch);

/* Frequency switch is synchronized to SLOW_CLK cycle. Wait until the switch
* is complete before disabling the PLL.
*/
rtc_clk_wait_for_slow_cycle();

DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0);
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG,
RTC_CNTL_BB_I2C_FORCE_PD | RTC_CNTL_BBPLL_FORCE_PD |
Expand Down Expand Up @@ -443,7 +443,7 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq)
s_pll_freq = 480;
}
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_PLL);
ets_delay_us(DELAY_CPU_FREQ_SWITCH_TO_PLL);
rtc_clk_wait_for_slow_cycle();
rtc_clk_apb_freq_update(80 * MHZ);
}
s_cur_freq = cpu_freq;
Expand Down Expand Up @@ -558,6 +558,13 @@ void rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq)

static rtc_xtal_freq_t rtc_clk_xtal_freq_estimate()
{
/* Enable 8M/256 clock if needed */
const bool clk_8m_enabled = rtc_clk_8m_enabled();
const bool clk_8md256_enabled = rtc_clk_8md256_enabled();
if (!clk_8md256_enabled) {
rtc_clk_8m_enable(true, true);
}

uint64_t cal_val = rtc_clk_cal_ratio(RTC_CAL_8MD256, XTAL_FREQ_EST_CYCLES);
/* cal_val contains period of 8M/256 clock in XTAL clock cycles
* (shifted by RTC_CLK_CAL_FRACT bits).
Expand All @@ -581,6 +588,8 @@ static rtc_xtal_freq_t rtc_clk_xtal_freq_estimate()
SOC_LOGW(TAG, "Bogus XTAL frequency: %d MHz", freq_mhz);
return RTC_XTAL_FREQ_AUTO;
}
/* Restore 8M and 8md256 clocks to original state */
rtc_clk_8m_enable(clk_8m_enabled, clk_8md256_enabled);
}

void rtc_clk_apb_freq_update(uint32_t apb_freq)
Expand Down Expand Up @@ -634,15 +643,14 @@ void rtc_clk_init(rtc_clk_config_t cfg)
CLEAR_PERI_REG_MASK(ANA_CONFIG_REG, I2C_APLL_M | I2C_BBPLL_M);

/* Estimate XTAL frequency */
rtc_xtal_freq_t est_xtal_freq = rtc_clk_xtal_freq_estimate();
rtc_xtal_freq_t xtal_freq = cfg.xtal_freq;
if (xtal_freq == RTC_XTAL_FREQ_AUTO) {
if (clk_val_is_valid(READ_PERI_REG(RTC_XTAL_FREQ_REG))) {
/* XTAL frequency has already been set, use existing value */
xtal_freq = rtc_clk_xtal_freq_get();
} else {
/* Not set yet, estimate XTAL frequency based on RTC_FAST_CLK */
xtal_freq = est_xtal_freq;
xtal_freq = rtc_clk_xtal_freq_estimate();
if (xtal_freq == RTC_XTAL_FREQ_AUTO) {
SOC_LOGW(TAG, "Can't estimate XTAL frequency, assuming 26MHz");
xtal_freq = RTC_XTAL_FREQ_26M;
Expand All @@ -653,8 +661,11 @@ void rtc_clk_init(rtc_clk_config_t cfg)
* frequency is different. If autodetection failed, worst case we get a
* bit of garbage output.
*/
SOC_LOGW(TAG, "Possibly invalid CONFIG_ESP32_XTAL_FREQ setting (%dMHz). Detected %d MHz.",
xtal_freq, est_xtal_freq);
rtc_xtal_freq_t est_xtal_freq = rtc_clk_xtal_freq_estimate();
if (est_xtal_freq != xtal_freq) {
SOC_LOGW(TAG, "Possibly invalid CONFIG_ESP32_XTAL_FREQ setting (%dMHz). Detected %d MHz.",
xtal_freq, est_xtal_freq);
}
}
uart_tx_wait_idle(0);
rtc_clk_xtal_freq_update(xtal_freq);
Expand Down
17 changes: 17 additions & 0 deletions components/soc/esp32/rtc_time.c
Expand Up @@ -135,3 +135,20 @@ uint64_t rtc_time_get()
t |= ((uint64_t) READ_PERI_REG(RTC_CNTL_TIME1_REG)) << 32;
return t;
}

void rtc_clk_wait_for_slow_cycle()
{
REG_CLR_BIT(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START_CYCLING | TIMG_RTC_CALI_START);
REG_CLR_BIT(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_RDY);
REG_SET_FIELD(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_CLK_SEL, RTC_CAL_RTC_MUX);
/* Request to run calibration for 0 slow clock cycles.
* RDY bit will be set on the nearest slow clock cycle.
*/
REG_SET_FIELD(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_MAX, 0);
REG_SET_BIT(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START);
ets_delay_us(1); /* RDY needs some time to go low */
while (!GET_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_RDY)) {
ets_delay_us(1);
}
}

36 changes: 35 additions & 1 deletion components/soc/esp32/test/test_rtc_clk.c
@@ -1,15 +1,17 @@
#include <stdio.h>
#include "unity.h"
#include "rom/ets_sys.h"
#include "rom/uart.h"
#include "soc/rtc.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/rtc_io_reg.h"
#include "soc/sens_reg.h"
#include "soc/io_mux_reg.h"
#include "driver/rtc_io.h"

#include "test_utils.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"


#define CALIBRATE_ONE(cali_clk) calibrate_one(cali_clk, #cali_clk)
Expand Down Expand Up @@ -89,3 +91,35 @@ TEST_CASE("Output 8M XTAL clock to GPIO25", "[rtc_clk][ignore]")
SET_PERI_REG_MASK(RTC_IO_RTC_DEBUG_SEL_REG, RTC_IO_DEBUG_12M_NO_GATING);
pull_out_clk(RTC_IO_DEBUG_SEL0_8M);
}

static void test_clock_switching(void (*switch_func)(rtc_cpu_freq_t))
{
uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM);

const int test_duration_sec = 10;
ref_clock_init();
uint64_t t_start = ref_clock_get();

rtc_cpu_freq_t cur_freq = rtc_clk_cpu_freq_get();
int count = 0;
while (ref_clock_get() - t_start < test_duration_sec * 1000000) {
switch_func(RTC_CPU_FREQ_XTAL);
switch_func(cur_freq);
++count;
}
uint64_t t_end = ref_clock_get();
printf("Switch count: %d. Average time to switch PLL -> XTAL -> PLL: %d us\n", count, (int) ((t_end - t_start) / count));
ref_clock_deinit();
}

TEST_CASE("Test switching between PLL and XTAL", "[rtc_clk]")
{
test_clock_switching(rtc_clk_cpu_freq_set);
}

TEST_CASE("Test fast switching between PLL and XTAL", "[rtc_clk]")
{
test_clock_switching(rtc_clk_cpu_freq_set_fast);
}


0 comments on commit abacf8d

Please sign in to comment.