esp32c2: support rtc time feature depend on rtc memory, since c2 does not have rtc memory

This commit is contained in:
jingli 2022-07-13 21:10:17 +08:00
parent fb1902773f
commit f8cc2ec86d
7 changed files with 80 additions and 108 deletions

View File

@ -52,8 +52,9 @@ extern uint32_t g_ticks_per_us_app;
static portMUX_TYPE s_esp_rtc_time_lock = portMUX_INITIALIZER_UNLOCKED; static portMUX_TYPE s_esp_rtc_time_lock = portMUX_INITIALIZER_UNLOCKED;
// TODO: IDF-4239 #if SOC_RTC_FAST_MEM_SUPPORTED
static RTC_NOINIT_ATTR uint64_t s_esp_rtc_time_us, s_rtc_last_ticks; static RTC_NOINIT_ATTR uint64_t s_esp_rtc_time_us, s_rtc_last_ticks;
#endif
inline static int IRAM_ATTR s_get_cpu_freq_mhz(void) inline static int IRAM_ATTR s_get_cpu_freq_mhz(void)
{ {
@ -99,18 +100,18 @@ void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us)
uint64_t esp_rtc_get_time_us(void) uint64_t esp_rtc_get_time_us(void)
{ {
#if !SOC_RTC_FAST_MEM_SUPPORTED
//IDF-3901
return 0;
#endif
portENTER_CRITICAL_SAFE(&s_esp_rtc_time_lock); portENTER_CRITICAL_SAFE(&s_esp_rtc_time_lock);
const uint32_t cal = esp_clk_slowclk_cal_get(); const uint32_t cal = esp_clk_slowclk_cal_get();
#if SOC_RTC_FAST_MEM_SUPPORTED
if (cal == 0) { if (cal == 0) {
s_esp_rtc_time_us = 0; s_esp_rtc_time_us = 0;
s_rtc_last_ticks = 0; s_rtc_last_ticks = 0;
} }
const uint64_t rtc_this_ticks = rtc_time_get(); const uint64_t rtc_this_ticks = rtc_time_get();
const uint64_t ticks = rtc_this_ticks - s_rtc_last_ticks; const uint64_t ticks = rtc_this_ticks - s_rtc_last_ticks;
#else
const uint64_t ticks = rtc_time_get();
#endif
/* RTC counter result is up to 2^48, calibration factor is up to 2^24, /* RTC counter result is up to 2^48, calibration factor is up to 2^24,
* for a 32kHz clock. We need to calculate (assuming no overflow): * for a 32kHz clock. We need to calculate (assuming no overflow):
* (ticks * cal) >> RTC_CLK_CAL_FRACT * (ticks * cal) >> RTC_CLK_CAL_FRACT
@ -126,10 +127,16 @@ uint64_t esp_rtc_get_time_us(void)
const uint64_t ticks_high = ticks >> 32; const uint64_t ticks_high = ticks >> 32;
const uint64_t delta_time_us = ((ticks_low * cal) >> RTC_CLK_CAL_FRACT) + const uint64_t delta_time_us = ((ticks_low * cal) >> RTC_CLK_CAL_FRACT) +
((ticks_high * cal) << (32 - RTC_CLK_CAL_FRACT)); ((ticks_high * cal) << (32 - RTC_CLK_CAL_FRACT));
#if SOC_RTC_FAST_MEM_SUPPORTED
s_esp_rtc_time_us += delta_time_us; s_esp_rtc_time_us += delta_time_us;
s_rtc_last_ticks = rtc_this_ticks; s_rtc_last_ticks = rtc_this_ticks;
portEXIT_CRITICAL_SAFE(&s_esp_rtc_time_lock); portEXIT_CRITICAL_SAFE(&s_esp_rtc_time_lock);
return s_esp_rtc_time_us; return s_esp_rtc_time_us;
#else
uint64_t esp_rtc_time_us = delta_time_us + clk_ll_rtc_slow_load_rtc_fix_us();
portEXIT_CRITICAL_SAFE(&s_esp_rtc_time_lock);
return esp_rtc_time_us;
#endif
} }
void esp_clk_slowclk_cal_set(uint32_t new_cal) void esp_clk_slowclk_cal_set(uint32_t new_cal)
@ -138,7 +145,34 @@ void esp_clk_slowclk_cal_set(uint32_t new_cal)
/* To force monotonic time values even when clock calibration value changes, /* To force monotonic time values even when clock calibration value changes,
* we adjust esp_rtc_time * we adjust esp_rtc_time
*/ */
#if SOC_RTC_FAST_MEM_SUPPORTED
esp_rtc_get_time_us(); esp_rtc_get_time_us();
#else
portENTER_CRITICAL_SAFE(&s_esp_rtc_time_lock);
uint32_t old_cal = clk_ll_rtc_slow_load_cal();
if (old_cal != 0) {
/**
* The logic of time correction is:
* old_rtc_us = ticks * old_cal >> RTC_CLK_CAL_FRACT + old_fix_us
* new_rtc_us = ticks * new_cal >> RTC_CLK_CAL_FRACT + new_fix_us
*
* Keep "old_rtc_us == new_rtc_us" to make time monotonically increasing,
* then we can get new_fix_us:
* new_fix_us = (ticks * old_cal >> RTC_CLK_CAL_FRACT + old_fix_us) - (ticks * new_cal >> RTC_CLK_CAL_FRACT)
*/
uint64_t ticks = rtc_time_get();
const uint64_t ticks_low = ticks & UINT32_MAX;
const uint64_t ticks_high = ticks >> 32;
uint64_t old_fix_us = clk_ll_rtc_slow_load_rtc_fix_us();
uint64_t new_fix_us;
old_fix_us += ((ticks_low * old_cal) >> RTC_CLK_CAL_FRACT) + ((ticks_high * old_cal) << (32 - RTC_CLK_CAL_FRACT));
new_fix_us = ((ticks_low * new_cal) >> RTC_CLK_CAL_FRACT) + ((ticks_high * new_cal) << (32 - RTC_CLK_CAL_FRACT));
new_fix_us = old_fix_us - new_fix_us;
clk_ll_rtc_slow_store_rtc_fix_us(new_fix_us);
}
portEXIT_CRITICAL_SAFE(&s_esp_rtc_time_lock);
#endif // SOC_RTC_FAST_MEM_SUPPORTED
#endif // CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER #endif // CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER
clk_ll_rtc_slow_store_cal(new_cal); clk_ll_rtc_slow_store_cal(new_cal);
} }

View File

@ -490,14 +490,7 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
//ToDo: if we are to allow placing interrupt handlers into the 0x400c0000—0x400c2000 region, //ToDo: if we are to allow placing interrupt handlers into the 0x400c0000—0x400c2000 region,
//we need to make sure the interrupt is connected to the CPU0. //we need to make sure the interrupt is connected to the CPU0.
//CPU1 does not have access to the RTC fast memory through this region. //CPU1 does not have access to the RTC fast memory through this region.
if ((flags & ESP_INTR_FLAG_IRAM) if ((flags & ESP_INTR_FLAG_IRAM) && handler && !esp_ptr_in_iram(handler) && !esp_ptr_in_rtc_iram_fast(handler)) {
&& handler
&& !esp_ptr_in_iram(handler)
#if SOC_RTC_FAST_MEM_SUPPORTED
// IDF-3901.
&& !esp_ptr_in_rtc_iram_fast(handler)
#endif
) {
return ESP_ERR_INVALID_ARG; return ESP_ERR_INVALID_ARG;
} }

View File

@ -176,85 +176,6 @@ uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp
return rtc_sleep_finish(lslp_mem_inf_fpu); return rtc_sleep_finish(lslp_mem_inf_fpu);
} }
#define STR2(X) #X
#define STR(X) STR2(X)
uint32_t rtc_deep_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt)
{
REG_SET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_ENA, wakeup_opt);
WRITE_PERI_REG(RTC_CNTL_SLP_REJECT_CONF_REG, reject_opt);
SET_PERI_REG_MASK(RTC_CNTL_INT_CLR_REG,
RTC_CNTL_SLP_REJECT_INT_CLR | RTC_CNTL_SLP_WAKEUP_INT_CLR);
/* Calculate RTC Fast Memory CRC (for wake stub) & go to deep sleep
Because we may be running from RTC memory as stack, we can't easily call any
functions to do this (as registers will spill to stack, corrupting the CRC).
Instead, load all the values we need into registers then use register ops only to calculate
the CRC value, write it to the RTC CRC value register, and immediately go into deep sleep.
*/
/* Values used to set the SYSTEM_RTC_FASTMEM_CONFIG_REG value */
const unsigned CRC_START_ADDR = 0;
const unsigned CRC_LEN = 0x7ff;
asm volatile(
/* Start CRC calculation */
"sw %1, 0(%0)\n" // set RTC_MEM_CRC_ADDR & RTC_MEM_CRC_LEN
"or t0, %1, %2\n"
"sw t0, 0(%0)\n" // set RTC_MEM_CRC_START
/* Wait for the CRC calculation to finish */
".Lwaitcrc:\n"
"fence\n"
"lw t0, 0(%0)\n"
"li t1, "STR(SYSTEM_RTC_MEM_CRC_FINISH)"\n"
"and t0, t0, t1\n"
"beqz t0, .Lwaitcrc\n"
"not %2, %2\n" // %2 -> ~DPORT_RTC_MEM_CRC_START
"and t0, t0, %2\n"
"sw t0, 0(%0)\n" // clear RTC_MEM_CRC_START
"fence\n"
"not %2, %2\n" // %2 -> DPORT_RTC_MEM_CRC_START, probably unnecessary but gcc assumes inputs unchanged
/* Store the calculated value in RTC_MEM_CRC_REG */
"lw t0, 0(%3)\n"
"sw t0, 0(%4)\n"
"fence\n"
/* Set register bit to go into deep sleep */
"lw t0, 0(%5)\n"
"or t0, t0, %6\n"
"sw t0, 0(%5)\n"
"fence\n"
/* Wait for sleep reject interrupt (never finishes if successful) */
".Lwaitsleep:"
"fence\n"
"lw t0, 0(%7)\n"
"and t0, t0, %8\n"
"beqz t0, .Lwaitsleep\n"
:
:
"r" (SYSTEM_RTC_FASTMEM_CONFIG_REG), // %0
"r" ( (CRC_START_ADDR << SYSTEM_RTC_MEM_CRC_START_S)
| (CRC_LEN << SYSTEM_RTC_MEM_CRC_LEN_S)), // %1
"r" (SYSTEM_RTC_MEM_CRC_START), // %2
"r" (SYSTEM_RTC_FASTMEM_CRC_REG), // %3
"r" (RTC_MEMORY_CRC_REG), // %4
"r" (RTC_CNTL_STATE0_REG), // %5
"r" (RTC_CNTL_SLEEP_EN), // %6
"r" (RTC_CNTL_INT_RAW_REG), // %7
"r" (RTC_CNTL_SLP_REJECT_INT_RAW | RTC_CNTL_SLP_WAKEUP_INT_RAW) // %8
: "t0", "t1" // working registers
);
return rtc_sleep_finish(0);
}
static uint32_t rtc_sleep_finish(uint32_t lslp_mem_inf_fpu) static uint32_t rtc_sleep_finish(uint32_t lslp_mem_inf_fpu)
{ {
/* In deep sleep mode, we never get here */ /* In deep sleep mode, we never get here */

View File

@ -217,6 +217,7 @@ static void touch_wakeup_prepare(void);
static void gpio_deep_sleep_wakeup_prepare(void); static void gpio_deep_sleep_wakeup_prepare(void);
#endif #endif
#if SOC_RTC_FAST_MEM_SUPPORTED
#if SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY #if SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY
static RTC_FAST_ATTR esp_deep_sleep_wake_stub_fn_t wake_stub_fn_handler = NULL; static RTC_FAST_ATTR esp_deep_sleep_wake_stub_fn_t wake_stub_fn_handler = NULL;
@ -297,6 +298,7 @@ void RTC_IRAM_ATTR esp_default_wake_deep_sleep(void)
} }
void __attribute__((weak, alias("esp_default_wake_deep_sleep"))) esp_wake_deep_sleep(void); void __attribute__((weak, alias("esp_default_wake_deep_sleep"))) esp_wake_deep_sleep(void);
#endif // SOC_RTC_FAST_MEM_SUPPORTED
void esp_deep_sleep(uint64_t time_in_us) void esp_deep_sleep(uint64_t time_in_us)
{ {
@ -544,8 +546,7 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags)
#else #else
#if !CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP #if !CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP
/* If not possible stack is in RTC FAST memory, use the ROM function to calculate the CRC and save ~140 bytes IRAM */ /* If not possible stack is in RTC FAST memory, use the ROM function to calculate the CRC and save ~140 bytes IRAM */
#if !CONFIG_IDF_TARGET_ESP32C2 #if SOC_RTC_FAST_MEM_SUPPORTED
// RTC has no rtc memory, IDF-3901
set_rtc_memory_crc(); set_rtc_memory_crc();
#endif #endif
result = call_rtc_sleep_start(reject_triggers, config.lslp_mem_inf_fpu, 0); result = call_rtc_sleep_start(reject_triggers, config.lslp_mem_inf_fpu, 0);
@ -614,10 +615,12 @@ void IRAM_ATTR esp_deep_sleep_start(void)
// record current RTC time // record current RTC time
s_config.rtc_ticks_at_sleep_start = rtc_time_get(); s_config.rtc_ticks_at_sleep_start = rtc_time_get();
#if SOC_RTC_FAST_MEM_SUPPORTED
// Configure wake stub // Configure wake stub
if (esp_get_deep_sleep_wake_stub() == NULL) { if (esp_get_deep_sleep_wake_stub() == NULL) {
esp_set_deep_sleep_wake_stub(esp_wake_deep_sleep); esp_set_deep_sleep_wake_stub(esp_wake_deep_sleep);
} }
#endif // SOC_RTC_FAST_MEM_SUPPORTED
// Decide which power domains can be powered down // Decide which power domains can be powered down
uint32_t pd_flags = get_power_down_flags(); uint32_t pd_flags = get_power_down_flags();

View File

@ -45,29 +45,35 @@ extern "C" {
* *
************************************************************************************* *************************************************************************************
* RTC store registers usage * RTC store registers usage
* RTC_CNTL_STORE0_REG Reserved * RTC_CNTL_STORE0_REG RTC fix us, high 32 bits
* RTC_CNTL_STORE1_REG RTC_SLOW_CLK calibration value * RTC_CNTL_STORE1_REG RTC_SLOW_CLK calibration value
* RTC_CNTL_STORE2_REG Boot time, low word * RTC_CNTL_STORE2_REG Boot time, low word
* RTC_CNTL_STORE3_REG Boot time, high word * RTC_CNTL_STORE3_REG Boot time, high word
* RTC_CNTL_STORE4_REG External XTAL frequency * RTC_CNTL_STORE4_REG External XTAL frequency
* RTC_CNTL_STORE5_REG APB bus frequency * RTC_CNTL_STORE5_REG APB bus frequency
* RTC_CNTL_STORE6_REG FAST_RTC_MEMORY_ENTRY * RTC_CNTL_STORE6_REG rtc reset cause
* RTC_CNTL_STORE7_REG FAST_RTC_MEMORY_CRC * RTC_CNTL_STORE7_REG RTC fix us, low 32 bits
************************************************************************************* *************************************************************************************
*
* Since esp32c2 does not support RTC fast mem, so use RTC store regs to record rtc time:
*
* |------------------------|----------------------------------------|
* | RTC_CNTL_STORE0_REG | RTC_CNTL_STORE7_REG |
* | rtc_fix_us(MSB) | rtc_fix_us(LSB) |
* |------------------------|----------------------------------------|
*/ */
#define RTC_FIX_US_HIGH_REG RTC_CNTL_STORE0_REG
#define RTC_SLOW_CLK_CAL_REG RTC_CNTL_STORE1_REG #define RTC_SLOW_CLK_CAL_REG RTC_CNTL_STORE1_REG
#define RTC_BOOT_TIME_LOW_REG RTC_CNTL_STORE2_REG #define RTC_BOOT_TIME_LOW_REG RTC_CNTL_STORE2_REG
#define RTC_BOOT_TIME_HIGH_REG RTC_CNTL_STORE3_REG #define RTC_BOOT_TIME_HIGH_REG RTC_CNTL_STORE3_REG
#define RTC_XTAL_FREQ_REG RTC_CNTL_STORE4_REG #define RTC_XTAL_FREQ_REG RTC_CNTL_STORE4_REG
#define RTC_APB_FREQ_REG RTC_CNTL_STORE5_REG #define RTC_APB_FREQ_REG RTC_CNTL_STORE5_REG
#define RTC_ENTRY_ADDR_REG RTC_CNTL_STORE6_REG
#define RTC_RESET_CAUSE_REG RTC_CNTL_STORE6_REG #define RTC_RESET_CAUSE_REG RTC_CNTL_STORE6_REG
#define RTC_MEMORY_CRC_REG RTC_CNTL_STORE7_REG #define RTC_FIX_US_LOW_REG RTC_CNTL_STORE7_REG
#define RTC_DISABLE_ROM_LOG ((1 << 0) | (1 << 16)) //!< Disable logging from the ROM code. #define RTC_DISABLE_ROM_LOG ((1 << 0) | (1 << 16)) //!< Disable logging from the ROM code.
typedef enum { typedef enum {
AWAKE = 0, //<CPU ON AWAKE = 0, //<CPU ON
LIGHT_SLEEP = BIT0, //CPU waiti, PLL ON. We don't need explicitly set this mode. LIGHT_SLEEP = BIT0, //CPU waiti, PLL ON. We don't need explicitly set this mode.

View File

@ -65,16 +65,10 @@ esp_reset_reason_t esp_reset_reason(void)
return s_reset_reason; return s_reset_reason;
} }
/* Reset reason hint is stored in RTC_RESET_CAUSE_REG, a.k.a. RTC_CNTL_STORE6_REG, /* Reset reason hint is stored in RTC_RESET_CAUSE_REG, a.k.a. RTC_CNTL_STORE6_REG.
* a.k.a. RTC_ENTRY_ADDR_REG. It is safe to use this register both for the
* deep sleep wake stub entry address and for reset reason hint, since wake stub
* is only used for deep sleep reset, and in this case the reason provided by
* esp_rom_get_reset_reason is unambiguous.
* *
* Same layout is used as for RTC_APB_FREQ_REG (a.k.a. RTC_CNTL_STORE5_REG): * Same layout is used as for RTC_APB_FREQ_REG (a.k.a. RTC_CNTL_STORE5_REG):
* the value is replicated in low and high half-words. In addition to that, * the value is replicated in low and high half-words.
* MSB is set to 1, which doesn't happen when RTC_CNTL_STORE6_REG contains
* deep sleep wake stub address.
*/ */
#define RST_REASON_BIT 0x80000000 #define RST_REASON_BIT 0x80000000

View File

@ -600,6 +600,27 @@ static inline uint32_t clk_ll_rtc_slow_load_cal(void)
return REG_READ(RTC_SLOW_CLK_CAL_REG); return REG_READ(RTC_SLOW_CLK_CAL_REG);
} }
/**
* @brief Store rtc_fix_us in RTC storage register
*
* @param rtc_fix_us The value used to correct the time obtained from the rtc timer when the calibration value changes
*/
static inline void clk_ll_rtc_slow_store_rtc_fix_us(uint64_t rtc_fix_us)
{
REG_WRITE(RTC_FIX_US_LOW_REG, rtc_fix_us);
REG_WRITE(RTC_FIX_US_HIGH_REG, rtc_fix_us >> 32);
}
/**
* @brief Load the rtc_fix_ticks from RTC storage register
*
* @return The value used to correct the time obtained from the rtc timer when the calibration value changes
*/
static inline uint64_t clk_ll_rtc_slow_load_rtc_fix_us(void)
{
return REG_READ(RTC_FIX_US_LOW_REG) | ((uint64_t)REG_READ(RTC_FIX_US_HIGH_REG) << 32);
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif