Skip to content

Commit cd1601f

Browse files
author
Jiang Jiang Jian
committed
Merge branch 'fix/fix_esp_timer_accuracy_when_do_dfs_v5.5' into 'release/v5.5'
fix(esp_hw_support): improve esp timer accuracy on DFS for esp32 & esp32s2 (v5.5) See merge request espressif/esp-idf!39339
2 parents ba70c7f + e26be2b commit cd1601f

File tree

14 files changed

+331
-74
lines changed

14 files changed

+331
-74
lines changed

components/esp_hw_support/include/esp_private/systimer.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,30 @@
11
/*
2-
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

77
#pragma once
88

99
#include <stdint.h>
10+
#include "sdkconfig.h"
11+
12+
#if CONFIG_ESP_TIMER_IMPL_TG0_LAC
13+
/* Selects which Timer Group peripheral to use */
14+
#define LACT_MODULE 0
15+
16+
/* Desired number of timer ticks per microsecond.
17+
* This value should be small enough so that all possible APB frequencies
18+
* could be divided by it without remainder.
19+
* On the other hand, the smaller this value is, the longer we need to wait
20+
* after setting UPDATE_REG before the timer value can be read.
21+
* If LACT_TICKS_PER_US == 1, then we need to wait up to 1 microsecond, which
22+
* makes esp_timer_impl_get_time function take too much time.
23+
* The value LACT_TICKS_PER_US == 2 allows for most of the APB frequencies, and
24+
* allows reading the counter quickly enough.
25+
*/
26+
#define LACT_TICKS_PER_US 2
27+
#endif
1028

1129
// we assign the systimer resources statically
1230
#define SYSTIMER_COUNTER_ESPTIMER 0 // Counter used by esptimer, to generate the system level wall clock

components/esp_hw_support/linker.lf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ entries:
1616
if PM_SLP_IRAM_OPT = y:
1717
rtc_clk (noflash)
1818
rtc_time (noflash_text)
19+
if IDF_TARGET_ESP32 = y:
20+
rtc_clk:rtc_clk_cpu_freq_to_pll_mhz (noflash)
21+
rtc_clk:rtc_clk_cpu_freq_to_xtal (noflash)
1922
if SOC_CONFIGURABLE_VDDSDIO_SUPPORTED = y:
2023
rtc_init:rtc_vddsdio_get_config (noflash)
2124
rtc_init:rtc_vddsdio_set_config (noflash)

components/esp_hw_support/port/esp32/rtc_clk.c

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
#include "hal/clk_tree_ll.h"
2828
#include "soc/rtc_cntl_reg.h"
2929
#include "soc/io_mux_reg.h"
30+
#ifndef BOOTLOADER_BUILD
31+
#include "esp_private/systimer.h"
32+
#include "hal/timer_ll.h"
33+
#endif
3034

3135
#define XTAL_32K_BOOTSTRAP_TIME_US 7
3236

@@ -374,6 +378,9 @@ void rtc_clk_cpu_freq_to_xtal(int cpu_freq, int div)
374378
clk_ll_ref_tick_set_divider(SOC_CPU_CLK_SRC_XTAL, cpu_freq);
375379
/* switch clock source */
376380
clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_XTAL);
381+
#ifndef BOOTLOADER_BUILD
382+
timer_ll_set_lact_clock_prescale(TIMER_LL_GET_HW(LACT_MODULE), cpu_freq / LACT_TICKS_PER_US);
383+
#endif
377384
rtc_clk_apb_freq_update(cpu_freq * MHZ);
378385
/* lower the voltage */
379386
int dbias = (cpu_freq <= 2) ? DIG_DBIAS_2M : DIG_DBIAS_XTAL;
@@ -389,19 +396,55 @@ static void rtc_clk_cpu_freq_to_8m(void)
389396
clk_ll_ref_tick_set_divider(SOC_CPU_CLK_SRC_RC_FAST, 8);
390397
/* switch clock source */
391398
clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_RC_FAST);
399+
#ifndef BOOTLOADER_BUILD
400+
timer_ll_set_lact_clock_prescale(TIMER_LL_GET_HW(LACT_MODULE), SOC_CLK_RC_FAST_FREQ_APPROX / MHZ / LACT_TICKS_PER_US);
401+
#endif
392402
rtc_clk_apb_freq_update(SOC_CLK_RC_FAST_FREQ_APPROX);
393403
}
394404

405+
#ifndef BOOTLOADER_BUILD
406+
static const DRAM_ATTR int16_t dfs_lact_conpensate_table[3][3] = { \
407+
/* From / To 80 160 240*/ \
408+
/* 10 */ {138, 220, 18}, \
409+
/* 20 */ {128, 205, -3579}, \
410+
/* 40 */ {34, 100, 0}, \
411+
};
412+
413+
__attribute__((weak)) IRAM_ATTR int16_t rtc_clk_get_lact_compensation_delay(uint32_t cur_freq, uint32_t tar_freq)
414+
{
415+
return dfs_lact_conpensate_table[(cur_freq == 10) ? 0 : (cur_freq == 20) ? 1 : 2][(tar_freq == 80) ? 0 : (tar_freq == 160) ? 1 : 2];
416+
}
417+
#endif
418+
395419
/**
396420
* Switch to one of PLL-based frequencies. Current frequency can be XTAL or PLL.
397421
* PLL must already be enabled.
398422
* @param cpu_freq new CPU frequency
399423
*/
400-
static void rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz)
424+
__attribute__((optimize("-O2")))
425+
NOINLINE_ATTR static void rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz)
401426
{
402427
int dbias = (cpu_freq_mhz == 240) ? DIG_DBIAS_240M : DIG_DBIAS_80M_160M;
403-
clk_ll_cpu_set_freq_mhz_from_pll(cpu_freq_mhz);
404428
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, dbias);
429+
#ifndef BOOTLOADER_BUILD
430+
uint32_t cur_freq = esp_rom_get_cpu_ticks_per_us();
431+
int16_t delay_cycle = rtc_clk_get_lact_compensation_delay(cur_freq, cpu_freq_mhz);
432+
if (cur_freq <= 40 && delay_cycle >= 0) {
433+
timer_ll_set_lact_clock_prescale(TIMER_LL_GET_HW(LACT_MODULE), 80 / LACT_TICKS_PER_US);
434+
for (int i = 0; i < delay_cycle; ++i) {
435+
__asm__ __volatile__("nop");
436+
}
437+
}
438+
#endif
439+
clk_ll_cpu_set_freq_mhz_from_pll(cpu_freq_mhz);
440+
#ifndef BOOTLOADER_BUILD
441+
if (cur_freq <= 40 && delay_cycle < 0) {
442+
for (int i = 0; i > delay_cycle; --i) {
443+
__asm__ __volatile__("nop");
444+
}
445+
timer_ll_set_lact_clock_prescale(TIMER_LL_GET_HW(LACT_MODULE), 80 / LACT_TICKS_PER_US);
446+
}
447+
#endif
405448
/* adjust ref_tick */
406449
clk_ll_ref_tick_set_divider(SOC_CPU_CLK_SRC_PLL, cpu_freq_mhz);
407450
/* switch clock source */

components/esp_hw_support/port/esp32s2/rtc_clk.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
#include "esp_hw_log.h"
2222
#include "sdkconfig.h"
2323
#include "hal/clk_tree_ll.h"
24+
#ifndef BOOTLOADER_BUILD
25+
#include "esp_private/systimer.h"
26+
#include "hal/systimer_ll.h"
27+
#endif
2428

2529
static const char *TAG = "rtc_clk";
2630

@@ -468,6 +472,9 @@ static void rtc_clk_cpu_freq_to_xtal(int cpu_freq, int div)
468472
/* no need to adjust the REF_TICK, default register value already set it to 1MHz with any cpu clock source */
469473
/* switch clock source */
470474
clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_XTAL);
475+
#ifndef BOOTLOADER_BUILD
476+
systimer_ll_set_step_for_xtal(&SYSTIMER, systimer_us_to_ticks(1) / cpu_freq);
477+
#endif
471478
rtc_clk_apb_freq_update(cpu_freq * MHZ);
472479

473480
/* lower the voltage

components/esp_pm/pm_impl.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -572,13 +572,6 @@ void IRAM_ATTR esp_pm_impl_switch_mode(pm_mode_t mode,
572572
*/
573573
static void IRAM_ATTR on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_per_us)
574574
{
575-
uint32_t old_apb_ticks_per_us = MIN(old_ticks_per_us, 80);
576-
uint32_t apb_ticks_per_us = MIN(ticks_per_us, 80);
577-
/* Update APB frequency value used by the timer */
578-
if (old_apb_ticks_per_us != apb_ticks_per_us) {
579-
esp_timer_private_update_apb_freq(apb_ticks_per_us);
580-
}
581-
582575
#ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
583576
#ifdef XT_RTOS_TIMER_INT
584577
/* Calculate new tick divisor */

components/esp_timer/include/esp_private/esp_timer_private.h

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -24,16 +24,6 @@
2424
#ifdef __cplusplus
2525
extern "C" {
2626
#endif
27-
28-
/**
29-
* @brief Notify esp_timer implementation that APB frequency has changed
30-
*
31-
* Called by the frequency switching code.
32-
*
33-
* @param apb_ticks_per_us new number of APB clock ticks per microsecond
34-
*/
35-
void esp_timer_private_update_apb_freq(uint32_t apb_ticks_per_us);
36-
3727
/**
3828
* @brief Set esp_timer time to a certain value
3929
*

components/esp_timer/private_include/esp_timer_impl.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,6 @@ void esp_timer_impl_set_alarm(uint64_t timestamp);
7676
*/
7777
void esp_timer_impl_set_alarm_id(uint64_t timestamp, unsigned alarm_id);
7878

79-
/**
80-
* @brief Notify esp_timer implementation that APB frequency has changed
81-
*
82-
* Called by the frequency switching code.
83-
*
84-
* @param apb_ticks_per_us new number of APB clock ticks per microsecond
85-
*/
86-
void esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us);
87-
8879
/**
8980
* @brief Adjust current esp_timer time by a certain value
9081
*

components/esp_timer/src/esp_timer_impl_lac.c

Lines changed: 9 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "esp_log.h"
1717
#include "esp_private/esp_clk.h"
1818
#include "esp_private/periph_ctrl.h"
19+
#include "esp_private/systimer.h"
1920
#include "soc/soc.h"
2021
#include "soc/timer_group_reg.h"
2122
#include "soc/rtc.h"
@@ -32,9 +33,6 @@
3233
* The timer can be configured to produce an edge or a level interrupt.
3334
*/
3435

35-
/* Selects which Timer Group peripheral to use */
36-
#define LACT_MODULE 0
37-
3836
#if LACT_MODULE == 0
3937
#define INTR_SOURCE_LACT ETS_TG0_LACT_LEVEL_INTR_SOURCE
4038
#define PERIPH_LACT PERIPH_TIMG0_MODULE
@@ -45,18 +43,6 @@
4543
#error "Incorrect the number of LACT module (only 0 or 1)"
4644
#endif
4745

48-
/* Desired number of timer ticks per microsecond.
49-
* This value should be small enough so that all possible APB frequencies
50-
* could be divided by it without remainder.
51-
* On the other hand, the smaller this value is, the longer we need to wait
52-
* after setting UPDATE_REG before the timer value can be read.
53-
* If TICKS_PER_US == 1, then we need to wait up to 1 microsecond, which
54-
* makes esp_timer_impl_get_time function take too much time.
55-
* The value TICKS_PER_US == 2 allows for most of the APB frequencies, and
56-
* allows reading the counter quickly enough.
57-
*/
58-
#define TICKS_PER_US 2
59-
6046
/* Shorter register names, used in this file */
6147
#define CONFIG_REG (TIMG_LACTCONFIG_REG(LACT_MODULE))
6248
#define RTC_STEP_REG (TIMG_LACTRTC_REG(LACT_MODULE))
@@ -139,7 +125,7 @@ uint64_t ESP_TIMER_IRAM_ATTR esp_timer_impl_get_counter_reg(void)
139125

140126
int64_t ESP_TIMER_IRAM_ATTR esp_timer_impl_get_time(void)
141127
{
142-
return esp_timer_impl_get_counter_reg() / TICKS_PER_US;
128+
return esp_timer_impl_get_counter_reg() / LACT_TICKS_PER_US;
143129
}
144130

145131
int64_t esp_timer_get_time(void) __attribute__((alias("esp_timer_impl_get_time")));
@@ -151,9 +137,9 @@ void ESP_TIMER_IRAM_ATTR esp_timer_impl_set_alarm_id(uint64_t timestamp, unsigne
151137
timestamp_id[alarm_id] = timestamp;
152138
timestamp = MIN(timestamp_id[0], timestamp_id[1]);
153139
if (timestamp != UINT64_MAX) {
154-
int64_t offset = TICKS_PER_US * 2;
140+
int64_t offset = LACT_TICKS_PER_US * 2;
155141
uint64_t now_time = esp_timer_impl_get_counter_reg();
156-
timer_64b_reg_t alarm = { .val = MAX(timestamp * TICKS_PER_US, now_time + offset) };
142+
timer_64b_reg_t alarm = { .val = MAX(timestamp * LACT_TICKS_PER_US, now_time + offset) };
157143
do {
158144
REG_CLR_BIT(CONFIG_REG, TIMG_LACT_ALARM_EN);
159145
REG_WRITE(ALARM_LO_REG, alarm.lo);
@@ -163,7 +149,7 @@ void ESP_TIMER_IRAM_ATTR esp_timer_impl_set_alarm_id(uint64_t timestamp, unsigne
163149
int64_t delta = (int64_t)alarm.val - (int64_t)now_time;
164150
if (delta <= 0 && REG_GET_FIELD(INT_ST_REG, TIMG_LACT_INT_ST) == 0) {
165151
// new alarm is less than the counter and the interrupt flag is not set
166-
offset += llabs(delta) + TICKS_PER_US * 2;
152+
offset += llabs(delta) + LACT_TICKS_PER_US * 2;
167153
alarm.val = now_time + offset;
168154
} else {
169155
// finish if either (alarm > counter) or the interrupt flag is already set.
@@ -220,19 +206,10 @@ static void ESP_TIMER_IRAM_ATTR timer_alarm_isr(void *arg)
220206
#endif // ISR_HANDLERS != 1
221207
}
222208

223-
void ESP_TIMER_IRAM_ATTR esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us)
224-
{
225-
portENTER_CRITICAL_SAFE(&s_time_update_lock);
226-
assert(apb_ticks_per_us >= 3 && "divider value too low");
227-
assert(apb_ticks_per_us % TICKS_PER_US == 0 && "APB frequency (in MHz) should be divisible by TICK_PER_US");
228-
REG_SET_FIELD(CONFIG_REG, TIMG_LACT_DIVIDER, apb_ticks_per_us / TICKS_PER_US);
229-
portEXIT_CRITICAL_SAFE(&s_time_update_lock);
230-
}
231-
232209
void esp_timer_impl_set(uint64_t new_us)
233210
{
234211
portENTER_CRITICAL(&s_time_update_lock);
235-
timer_64b_reg_t dst = { .val = new_us * TICKS_PER_US };
212+
timer_64b_reg_t dst = { .val = new_us * LACT_TICKS_PER_US };
236213
REG_WRITE(LOAD_LO_REG, dst.lo);
237214
REG_WRITE(LOAD_HI_REG, dst.hi);
238215
REG_WRITE(LOAD_REG, 1);
@@ -261,7 +238,7 @@ esp_err_t esp_timer_impl_early_init(void)
261238
REG_WRITE(ALARM_HI_REG, UINT32_MAX);
262239
REG_WRITE(LOAD_REG, 1);
263240
REG_SET_BIT(INT_CLR_REG, TIMG_LACT_INT_CLR);
264-
REG_SET_FIELD(CONFIG_REG, TIMG_LACT_DIVIDER, APB_CLK_FREQ / 1000000 / TICKS_PER_US);
241+
REG_SET_FIELD(CONFIG_REG, TIMG_LACT_DIVIDER, APB_CLK_FREQ / 1000000 / LACT_TICKS_PER_US);
265242
REG_SET_BIT(CONFIG_REG, TIMG_LACT_INCREASE |
266243
TIMG_LACT_LEVEL_INT_EN |
267244
TIMG_LACT_EN);
@@ -299,12 +276,10 @@ esp_err_t esp_timer_impl_init(intr_handler_t alarm_handler)
299276
* will not cause issues in practice.
300277
*/
301278
REG_SET_BIT(INT_ENA_REG, TIMG_LACT_INT_ENA);
302-
303-
esp_timer_impl_update_apb_freq(esp_clk_apb_freq() / 1000000);
304-
279+
timer_ll_set_lact_clock_prescale(TIMER_LL_GET_HW(LACT_MODULE), esp_clk_apb_freq() / MHZ / LACT_TICKS_PER_US);
305280
// Set the step for the sleep mode when the timer will work
306281
// from a slow_clk frequency instead of the APB frequency.
307-
uint32_t slowclk_ticks_per_us = esp_clk_slowclk_cal_get() * TICKS_PER_US;
282+
uint32_t slowclk_ticks_per_us = esp_clk_slowclk_cal_get() * LACT_TICKS_PER_US;
308283
REG_SET_FIELD(RTC_STEP_REG, TIMG_LACT_RTC_STEP_LEN, slowclk_ticks_per_us);
309284
}
310285

@@ -347,6 +322,5 @@ uint64_t esp_timer_impl_get_alarm_reg(void)
347322
return alarm.val;
348323
}
349324

350-
void esp_timer_private_update_apb_freq(uint32_t apb_ticks_per_us) __attribute__((alias("esp_timer_impl_update_apb_freq")));
351325
void esp_timer_private_set(uint64_t new_us) __attribute__((alias("esp_timer_impl_set")));
352326
void esp_timer_private_advance(int64_t time_diff_us) __attribute__((alias("esp_timer_impl_advance")));

components/esp_timer/src/esp_timer_impl_systimer.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,6 @@ static void ESP_TIMER_IRAM_ATTR timer_alarm_isr(void *arg)
129129
#endif // ISR_HANDLERS != 1
130130
}
131131

132-
void ESP_TIMER_IRAM_ATTR esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us)
133-
{
134-
#if !SOC_SYSTIMER_FIXED_DIVIDER
135-
systimer_hal_on_apb_freq_update(&systimer_hal, apb_ticks_per_us);
136-
#endif
137-
}
138-
139132
void esp_timer_impl_set(uint64_t new_us)
140133
{
141134
portENTER_CRITICAL_SAFE(&s_time_update_lock);
@@ -253,6 +246,5 @@ uint64_t esp_timer_impl_get_alarm_reg(void)
253246
return val;
254247
}
255248

256-
void esp_timer_private_update_apb_freq(uint32_t apb_ticks_per_us) __attribute__((alias("esp_timer_impl_update_apb_freq")));
257249
void esp_timer_private_set(uint64_t new_us) __attribute__((alias("esp_timer_impl_set")));
258250
void esp_timer_private_advance(int64_t time_diff_us) __attribute__((alias("esp_timer_impl_advance")));
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
2+
3+
components/esp_timer/test_apps:
4+
disable:
5+
- if: CONFIG_NAME == "dfs" and SOC_CLK_XTAL32K_SUPPORTED != 1
6+
reason: The test requires the XTAL32K clock to measure the esp_timer timing accuracy

0 commit comments

Comments
 (0)