Skip to content

Commit e26be2b

Browse files
committed
test(esp_timer): add UT case for esp_timer period alarm with DFS
1 parent e8475d7 commit e26be2b

File tree

4 files changed

+233
-1
lines changed

4 files changed

+233
-1
lines changed
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

components/esp_timer/test_apps/main/CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ if(CONFIG_SOC_GPTIMER_SUPPORTED)
1515
list(APPEND srcs "test_esp_timer.c")
1616
endif()
1717

18+
if(CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD)
19+
list(APPEND srcs "test_esp_timer_dfs.c")
20+
endif()
21+
1822
idf_component_register(SRCS ${srcs}
1923
PRIV_INCLUDE_DIRS "../../private_include"
20-
PRIV_REQUIRES cmock test_utils esp_timer spi_flash esp_psram esp_driver_gpio
24+
PRIV_REQUIRES cmock test_utils esp_timer spi_flash esp_psram esp_driver_gpio esp_pm
2125
WHOLE_ARCHIVE)
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#include <stdio.h>
7+
#include <unistd.h>
8+
#include <stdlib.h>
9+
#include <math.h>
10+
#include <sys/time.h>
11+
#include <sys/param.h>
12+
#include "freertos/FreeRTOS.h"
13+
#include "freertos/task.h"
14+
#include "sdkconfig.h"
15+
#include "esp_timer.h"
16+
#include "unity.h"
17+
#include "esp_rom_sys.h"
18+
#include "esp_sleep.h"
19+
#include "esp_pm.h"
20+
#include "esp_log.h"
21+
#include "soc/soc_caps.h"
22+
#include "soc/rtc.h"
23+
#include "esp_rtc_time.h"
24+
#include "esp_private/esp_clk.h"
25+
26+
#define ALARM_PERIOD_MS 100
27+
#define ALARM_TIMES 200 // 200*100ms = 20s
28+
29+
static const char* TAG = "ESP_TIMER with DFS";
30+
31+
static uint32_t s_current_alarm = 0;
32+
static uint64_t s_alarm_records[ALARM_TIMES + 1] = {0};
33+
34+
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C5
35+
static uint32_t supported_freq[] = {10, 20, 40, 80, 160, 240};
36+
#elif CONFIG_IDF_TARGET_ESP32C2
37+
static uint32_t supported_freq[] = {10, 20, 40, 80, 120};
38+
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32C61
39+
static uint32_t supported_freq[] = {10, 20, 40, 80, 160};
40+
#elif CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32H21
41+
static uint32_t supported_freq[] = {8, 16, 32, 48, 64, 96};
42+
#elif CONFIG_IDF_TARGET_ESP32H4
43+
static uint32_t supported_freq[] = {8, 16, 24, 32};
44+
#elif CONFIG_IDF_TARGET_ESP32P4
45+
static uint32_t supported_freq[] = {10, 20, 40, 90, 180, 360};
46+
#endif
47+
48+
static SemaphoreHandle_t s_alarm_finished;
49+
50+
static IRAM_ATTR void periodic_timer_callback(void* arg)
51+
{
52+
s_alarm_records[s_current_alarm] = esp_rtc_get_time_us();
53+
BaseType_t yield;
54+
if (s_current_alarm == ALARM_TIMES) {
55+
xSemaphoreGiveFromISR(s_alarm_finished, &yield);
56+
}
57+
s_current_alarm++;
58+
if (yield) {
59+
portYIELD_FROM_ISR();
60+
}
61+
}
62+
63+
static int64_t measuring_periodic_timer_accuracy(uint64_t alarm_records[])
64+
{
65+
int64_t sum_jitter_us = 0;
66+
int64_t max_jitter_us = 0;
67+
int64_t jitter_array[ALARM_TIMES] = {0};
68+
69+
for (int i = 1; i <= ALARM_TIMES; ++i) {
70+
int64_t jitter_us = (int64_t)alarm_records[i] - (int64_t)alarm_records[i - 1] - ALARM_PERIOD_MS * 1000;
71+
jitter_array[i - 1] = jitter_us;
72+
if (llabs(jitter_us) > llabs(max_jitter_us)) {
73+
max_jitter_us = jitter_us;
74+
}
75+
sum_jitter_us += jitter_us;
76+
}
77+
int64_t avg_jitter_us = sum_jitter_us / ALARM_TIMES;
78+
79+
// Calculates the sum of squares of standard deviations
80+
int64_t sum_sq_diff = 0;
81+
for (int i = 0; i < ALARM_TIMES; ++i) {
82+
sum_sq_diff += (jitter_array[i] - avg_jitter_us) * (jitter_array[i] - avg_jitter_us);
83+
}
84+
85+
double variance = sum_sq_diff / ALARM_TIMES;
86+
double stddev = sqrt(variance);
87+
88+
printf("Average jitter us: %"PRIi64"\n", avg_jitter_us);
89+
printf("Max jitter us: %"PRIi64"\n", max_jitter_us);
90+
printf("Standard Deviation: %.3f\n", stddev);
91+
printf("Drift Percentage: %.3f%%\n", (double)avg_jitter_us / (ALARM_PERIOD_MS * 10));
92+
93+
// Reset measurement
94+
s_current_alarm = 0;
95+
bzero(s_alarm_records, sizeof(s_alarm_records));
96+
return avg_jitter_us;
97+
}
98+
99+
static int64_t test_periodic_timer_accuracy_on_dfs(esp_timer_handle_t timer)
100+
{
101+
// Calibrate slow clock.
102+
#if !CONFIG_ESP_SYSTEM_RTC_EXT_XTAL
103+
esp_clk_slowclk_cal_set(rtc_clk_cal(RTC_CAL_RTC_MUX, 8192));
104+
#endif
105+
106+
ESP_ERROR_CHECK(esp_timer_start_periodic(timer, ALARM_PERIOD_MS * 1000));
107+
108+
s_alarm_finished = xSemaphoreCreateBinary();
109+
// Each FreeRTOS tick will perform a min_freq_mhz->max_freq_mhz -> min_freq_mhz frequency switch
110+
xSemaphoreTake(s_alarm_finished, portMAX_DELAY);
111+
ESP_ERROR_CHECK(esp_timer_stop(timer));
112+
int64_t avg_jitter_us = measuring_periodic_timer_accuracy(s_alarm_records);
113+
vSemaphoreDelete(s_alarm_finished);
114+
return avg_jitter_us;
115+
}
116+
117+
// The results of this test are meaningful only if `CONFIG_ESP_SYSTEM_RTC_EXT_XTAL` is enabled
118+
TEST_CASE("Test the periodic timer timing accuracy when doing DFS", "[esp_timer][manual][ignore]")
119+
{
120+
esp_pm_config_t pm_config = {
121+
.light_sleep_enable = false
122+
};
123+
124+
const esp_timer_create_args_t periodic_timer_args = {
125+
.callback = &periodic_timer_callback,
126+
.dispatch_method = ESP_TIMER_ISR,
127+
.name = "periodic"
128+
};
129+
esp_timer_handle_t periodic_timer;
130+
ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer));
131+
132+
for (int min = 0; min < sizeof(supported_freq) / sizeof(uint32_t); min++) {
133+
for (int max = 0; max < sizeof(supported_freq) / sizeof(uint32_t); max++) {
134+
if (supported_freq[max] <= supported_freq[min]) {
135+
continue;
136+
}
137+
pm_config.max_freq_mhz = supported_freq[max];
138+
pm_config.min_freq_mhz = supported_freq[min];
139+
ESP_LOGI(TAG, "Testing esp_timer accuracy on %d <-> %d DFS ...", pm_config.min_freq_mhz, pm_config.max_freq_mhz);
140+
ESP_ERROR_CHECK(esp_pm_configure(&pm_config));
141+
test_periodic_timer_accuracy_on_dfs(periodic_timer);
142+
}
143+
}
144+
}
145+
146+
#if CONFIG_IDF_TARGET_ESP32
147+
int16_t test_lact_compensation_delay = 0;
148+
#define DO_MAGIC_TABLE_MEASUREMENT 0
149+
#if DO_MAGIC_TABLE_MEASUREMENT
150+
IRAM_ATTR int16_t rtc_clk_get_lact_compensation_delay(uint32_t cur_freq, uint32_t cpu_freq_mhz)
151+
{
152+
(void)cur_freq; (void)cpu_freq_mhz;
153+
return test_lact_compensation_delay;
154+
}
155+
#endif
156+
157+
// Test Notes:
158+
// 1. The test requires the slow clock source to be selected to `CONFIG_ESP_SYSTEM_RTC_EXT_XTAL`.
159+
// 2. Manually set DO_MAGIC_TABLE_MEASUREMENT to 1 before test.
160+
TEST_CASE("Test DFS lact conpensate magic table ", "[esp_timer][manual][ignore]")
161+
{
162+
esp_pm_config_t pm_config = {
163+
.light_sleep_enable = false
164+
};
165+
166+
const esp_timer_create_args_t periodic_timer_args = {
167+
.callback = &periodic_timer_callback,
168+
.dispatch_method = ESP_TIMER_ISR,
169+
.name = "periodic"
170+
};
171+
esp_timer_handle_t periodic_timer;
172+
ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer));
173+
174+
for (int min = 0; min < sizeof(supported_freq) / sizeof(uint32_t); min++) {
175+
for (int max = 0; max < sizeof(supported_freq) / sizeof(uint32_t); max++) {
176+
if ((supported_freq[max] <= supported_freq[min]) || (supported_freq[min] >= 80) || (supported_freq[max] <= 40)) {
177+
continue;
178+
}
179+
180+
pm_config.max_freq_mhz = supported_freq[max];
181+
pm_config.min_freq_mhz = supported_freq[min];
182+
ESP_LOGI(TAG, "Testing esp_timer accuracy on %d <-> %d DFS ...", pm_config.min_freq_mhz, pm_config.max_freq_mhz);
183+
esp_pm_configure(&pm_config);
184+
185+
int32_t max_delay = 300;
186+
int32_t min_delay = (pm_config.max_freq_mhz == 240) ? -4000 : 0;
187+
int32_t test_delay = (max_delay + min_delay) / 2;
188+
int32_t last_delay = 0;
189+
int32_t best_delay = 0;
190+
int64_t min_avg = INT64_MAX;
191+
192+
do {
193+
printf("Test delay %ld\n", test_delay);
194+
test_lact_compensation_delay = test_delay;
195+
int64_t avg = test_periodic_timer_accuracy_on_dfs(periodic_timer);
196+
last_delay = test_delay;
197+
if (avg < 0) {
198+
test_delay = (test_delay + max_delay) / 2;
199+
min_delay = last_delay;
200+
} else {
201+
test_delay = (test_delay + min_delay) / 2;
202+
max_delay = last_delay;
203+
}
204+
205+
if (llabs(avg) < llabs(min_avg)) {
206+
best_delay = last_delay;
207+
min_avg = avg;
208+
}
209+
} while (test_delay != last_delay);
210+
211+
printf("Switch between %d <-> %d\n", pm_config.min_freq_mhz, pm_config.max_freq_mhz);
212+
printf("Best delay is %ld\n", best_delay);
213+
printf("Min average is %lld\n", min_avg);
214+
}
215+
}
216+
ESP_ERROR_CHECK(esp_timer_delete(periodic_timer));
217+
}
218+
#endif
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
CONFIG_PM_ENABLE=y
2+
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
3+
CONFIG_RTC_CLK_SRC_EXT_CRYS=y
4+
CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD=y

0 commit comments

Comments
 (0)