mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-05 13:44:32 +02:00
sleep: fix deadlock in esp_timer_impl_advance after light sleep
When light sleep is started, the other CPU gets halted using DPORT stall mechanism. This can happen while it is inside an esp_timer critical section, which may lead to a deadlock. This change adds functions to take and release esp_timer lock before entering DPORT critical section, preventing the deadlock.
This commit is contained in:
@@ -165,6 +165,16 @@ static inline void IRAM_ATTR timer_count_reload(void)
|
|||||||
REG_WRITE(FRC_TIMER_LOAD_REG(1), REG_READ(FRC_TIMER_COUNT_REG(1)) - ALARM_OVERFLOW_VAL);
|
REG_WRITE(FRC_TIMER_LOAD_REG(1), REG_READ(FRC_TIMER_COUNT_REG(1)) - ALARM_OVERFLOW_VAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void esp_timer_impl_lock()
|
||||||
|
{
|
||||||
|
portENTER_CRITICAL(&s_time_update_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void esp_timer_impl_unlock()
|
||||||
|
{
|
||||||
|
portEXIT_CRITICAL(&s_time_update_lock);
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t IRAM_ATTR esp_timer_impl_get_time()
|
uint64_t IRAM_ATTR esp_timer_impl_get_time()
|
||||||
{
|
{
|
||||||
uint32_t timer_val;
|
uint32_t timer_val;
|
||||||
|
@@ -84,3 +84,18 @@ uint64_t esp_timer_impl_get_time();
|
|||||||
* @return minimal period of periodic timer, in microseconds
|
* @return minimal period of periodic timer, in microseconds
|
||||||
*/
|
*/
|
||||||
uint64_t esp_timer_impl_get_min_period_us();
|
uint64_t esp_timer_impl_get_min_period_us();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief obtain internal critical section used esp_timer implementation
|
||||||
|
* This can be used when a sequence of calls to esp_timer has to be made,
|
||||||
|
* and it is necessary that the state of the timer is consistent between
|
||||||
|
* the calls. Should be treated in the same way as a spinlock.
|
||||||
|
* Call esp_timer_impl_unlock to release the lock
|
||||||
|
*/
|
||||||
|
void esp_timer_impl_lock();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief counterpart of esp_timer_impl_lock
|
||||||
|
*/
|
||||||
|
void esp_timer_impl_unlock();
|
||||||
|
@@ -279,6 +279,11 @@ esp_err_t esp_light_sleep_start()
|
|||||||
{
|
{
|
||||||
static portMUX_TYPE light_sleep_lock = portMUX_INITIALIZER_UNLOCKED;
|
static portMUX_TYPE light_sleep_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||||
portENTER_CRITICAL(&light_sleep_lock);
|
portENTER_CRITICAL(&light_sleep_lock);
|
||||||
|
/* We will be calling esp_timer_impl_advance inside DPORT access critical
|
||||||
|
* section. Make sure the code on the other CPU is not holding esp_timer
|
||||||
|
* lock, otherwise there will be deadlock.
|
||||||
|
*/
|
||||||
|
esp_timer_impl_lock();
|
||||||
s_config.rtc_ticks_at_sleep_start = rtc_time_get();
|
s_config.rtc_ticks_at_sleep_start = rtc_time_get();
|
||||||
uint64_t frc_time_at_start = esp_timer_get_time();
|
uint64_t frc_time_at_start = esp_timer_get_time();
|
||||||
DPORT_STALL_OTHER_CPU_START();
|
DPORT_STALL_OTHER_CPU_START();
|
||||||
@@ -332,6 +337,7 @@ esp_err_t esp_light_sleep_start()
|
|||||||
}
|
}
|
||||||
esp_set_time_from_rtc();
|
esp_set_time_from_rtc();
|
||||||
|
|
||||||
|
esp_timer_impl_unlock();
|
||||||
DPORT_STALL_OTHER_CPU_END();
|
DPORT_STALL_OTHER_CPU_END();
|
||||||
rtc_wdt_disable();
|
rtc_wdt_disable();
|
||||||
portEXIT_CRITICAL(&light_sleep_lock);
|
portEXIT_CRITICAL(&light_sleep_lock);
|
||||||
|
@@ -91,6 +91,35 @@ TEST_CASE("light sleep stress test", "[deepsleep]")
|
|||||||
vSemaphoreDelete(done);
|
vSemaphoreDelete(done);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("light sleep stress test with periodic esp_timer", "[deepsleep]")
|
||||||
|
{
|
||||||
|
void timer_func(void* arg)
|
||||||
|
{
|
||||||
|
ets_delay_us(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
SemaphoreHandle_t done = xSemaphoreCreateCounting(2, 0);
|
||||||
|
esp_sleep_enable_timer_wakeup(1000);
|
||||||
|
esp_timer_handle_t timer;
|
||||||
|
esp_timer_create_args_t config = {
|
||||||
|
.callback = &timer_func,
|
||||||
|
};
|
||||||
|
TEST_ESP_OK(esp_timer_create(&config, &timer));
|
||||||
|
esp_timer_start_periodic(timer, 500);
|
||||||
|
xTaskCreatePinnedToCore(&test_light_sleep, "ls1", 4096, done, UNITY_FREERTOS_PRIORITY + 1, NULL, 0);
|
||||||
|
#if portNUM_PROCESSORS == 2
|
||||||
|
xTaskCreatePinnedToCore(&test_light_sleep, "ls1", 4096, done, UNITY_FREERTOS_PRIORITY + 1, NULL, 1);
|
||||||
|
#endif
|
||||||
|
xSemaphoreTake(done, portMAX_DELAY);
|
||||||
|
#if portNUM_PROCESSORS == 2
|
||||||
|
xSemaphoreTake(done, portMAX_DELAY);
|
||||||
|
#endif
|
||||||
|
vSemaphoreDelete(done);
|
||||||
|
esp_timer_stop(timer);
|
||||||
|
esp_timer_delete(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL
|
#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL
|
||||||
#define MAX_SLEEP_TIME_ERROR_US 200
|
#define MAX_SLEEP_TIME_ERROR_US 200
|
||||||
#else
|
#else
|
||||||
|
Reference in New Issue
Block a user