mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 02:30:52 +03:00
Merge branch 'ci/add_base_size_metric_apps' into 'master'
ci(size_metrics): add base size metric test apps See merge request espressif/esp-idf!46208
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
#This is the project CMakeLists.txt file for the test subproject
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
list(PREPEND SDKCONFIG_DEFAULTS "$ENV{IDF_PATH}/tools/test_apps/configs/sdkconfig.debug_helpers" "sdkconfig.defaults")
|
||||
|
||||
if(NOT "${IDF_TARGET}" STREQUAL "linux")
|
||||
list(APPEND SDKCONFIG_DEFAULTS "sdkconfig.defaults.no_linux")
|
||||
endif()
|
||||
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
set(COMPONENTS main)
|
||||
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/test_apps/components")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(esp_timer_test)
|
||||
@@ -0,0 +1,3 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 | Linux |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- | ----- |
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
idf_build_get_property(target IDF_TARGET)
|
||||
|
||||
set(srcs "test_app_main.c")
|
||||
|
||||
if(CONFIG_SOC_LIGHT_SLEEP_SUPPORTED)
|
||||
list(APPEND srcs "test_esp_timer_light_sleep.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_SYSTIMER_SUPPORT_ETM)
|
||||
list(APPEND srcs "test_esp_timer_etm.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_GPTIMER_SUPPORTED OR CONFIG_IDF_TARGET_LINUX)
|
||||
list(APPEND srcs "test_esp_timer.c")
|
||||
endif()
|
||||
|
||||
if(NOT ${target} STREQUAL "linux")
|
||||
|
||||
if(CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD)
|
||||
list(APPEND srcs "test_esp_timer_dfs.c")
|
||||
endif()
|
||||
|
||||
set(priv_requires cmock test_utils esp_timer spi_flash esp_psram esp_driver_gpio esp_pm)
|
||||
list(APPEND srcs "test_ets_timer.c")
|
||||
else()
|
||||
set(priv_requires cmock test_utils esp_timer)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_INCLUDE_DIRS "../../../private_include" "../../include"
|
||||
PRIV_REQUIRES "${priv_requires}"
|
||||
WHOLE_ARCHIVE)
|
||||
|
||||
if(CONFIG_IDF_TARGET_LINUX AND NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
|
||||
# Linux host heap integrity checks are stubs, so use ASan to catch dump overflows.
|
||||
set(asan_options -fsanitize=address -fno-omit-frame-pointer)
|
||||
idf_component_get_property(esp_timer_lib esp_timer COMPONENT_LIB)
|
||||
target_compile_options(${esp_timer_lib} PRIVATE ${asan_options})
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE ${asan_options})
|
||||
idf_build_set_property(LINK_OPTIONS -fsanitize=address APPEND)
|
||||
endif()
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include "unity.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "esp_private/esp_timer_private.h"
|
||||
|
||||
// On C2 due to only have a single HW WDT we use the timer
|
||||
// so some tests will interfere with the WDT
|
||||
#if !CONFIG_IDF_TARGET_ESP32C2 && !CONFIG_IDF_TARGET_LINUX
|
||||
#define WDT_MONITOR_UNITY_TASK
|
||||
#endif
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
#ifdef WDT_MONITOR_UNITY_TASK
|
||||
esp_task_wdt_add(NULL);
|
||||
esp_task_wdt_reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
#ifdef WDT_MONITOR_UNITY_TASK
|
||||
esp_task_wdt_reset();
|
||||
esp_task_wdt_delete(NULL);
|
||||
#endif
|
||||
|
||||
// Give some time for the esp_timer task to clean up between test runs, to avoid interference with the next test run.
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// Configure the task watchdog timer to catch any tests that hang
|
||||
#ifdef WDT_MONITOR_UNITY_TASK
|
||||
esp_task_wdt_config_t config = {
|
||||
.timeout_ms = 25 * 1000, // 25 seconds, smaller than the default pytest timeout
|
||||
.idle_core_mask = 0,
|
||||
.trigger_panic = true,
|
||||
};
|
||||
|
||||
esp_task_wdt_init(&config);
|
||||
#endif
|
||||
|
||||
vTaskPrioritySet(NULL, CONFIG_UNITY_FREERTOS_PRIORITY);
|
||||
unity_run_menu();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/param.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_timer.h"
|
||||
#include "unity.h"
|
||||
#include "esp_rom_sys.h"
|
||||
#include "esp_sleep.h"
|
||||
#include "esp_pm.h"
|
||||
#include "esp_log.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "esp_rtc_time.h"
|
||||
#include "esp_private/esp_clk.h"
|
||||
|
||||
#define ALARM_PERIOD_MS 100
|
||||
#define ALARM_TIMES 200 // 200*100ms = 20s
|
||||
|
||||
ESP_LOG_ATTR_TAG(TAG, "ESP_TIMER with DFS");
|
||||
|
||||
static uint32_t s_current_alarm = 0;
|
||||
static uint64_t s_alarm_records[ALARM_TIMES + 1] = {0};
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C5
|
||||
static uint32_t supported_freq[] = {10, 20, 40, 80, 160, 240};
|
||||
#elif CONFIG_IDF_TARGET_ESP32C2
|
||||
static uint32_t supported_freq[] = {10, 20, 40, 80, 120};
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32C61
|
||||
static uint32_t supported_freq[] = {10, 20, 40, 80, 160};
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32H21
|
||||
static uint32_t supported_freq[] = {8, 16, 32, 48, 64, 96};
|
||||
#elif CONFIG_IDF_TARGET_ESP32H4
|
||||
static uint32_t supported_freq[] = {8, 16, 24, 32};
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
static uint32_t supported_freq[] = {10, 20, 40, 90, 180, 360};
|
||||
#elif CONFIG_IDF_TARGET_ESP32S31
|
||||
static uint32_t supported_freq[] = {10, 20, 40, 90, 180, 320};
|
||||
#endif
|
||||
|
||||
static SemaphoreHandle_t s_alarm_finished;
|
||||
|
||||
static IRAM_ATTR void periodic_timer_callback(void* arg)
|
||||
{
|
||||
s_alarm_records[s_current_alarm] = esp_rtc_get_time_us();
|
||||
BaseType_t yield;
|
||||
if (s_current_alarm == ALARM_TIMES) {
|
||||
xSemaphoreGiveFromISR(s_alarm_finished, &yield);
|
||||
}
|
||||
s_current_alarm++;
|
||||
if (yield) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
static int64_t measuring_periodic_timer_accuracy(uint64_t alarm_records[])
|
||||
{
|
||||
int64_t sum_jitter_us = 0;
|
||||
int64_t max_jitter_us = -(ALARM_PERIOD_MS * 1000);
|
||||
int64_t min_jitter_us = ALARM_PERIOD_MS * 1000;
|
||||
int64_t jitter_array[ALARM_TIMES] = {0};
|
||||
|
||||
for (int i = 1; i <= ALARM_TIMES; ++i) {
|
||||
int64_t jitter_us = (int64_t)alarm_records[i] - (int64_t)alarm_records[i - 1] - ALARM_PERIOD_MS * 1000;
|
||||
jitter_array[i - 1] = jitter_us;
|
||||
if (jitter_us > max_jitter_us) {
|
||||
max_jitter_us = jitter_us;
|
||||
}
|
||||
if (jitter_us < min_jitter_us) {
|
||||
min_jitter_us = jitter_us;
|
||||
}
|
||||
sum_jitter_us += jitter_us;
|
||||
}
|
||||
int64_t avg_jitter_us = sum_jitter_us / ALARM_TIMES;
|
||||
|
||||
// Calculates the sum of squares of standard deviations
|
||||
int64_t sum_sq_diff = 0;
|
||||
for (int i = 0; i < ALARM_TIMES; ++i) {
|
||||
sum_sq_diff += (jitter_array[i] - avg_jitter_us) * (jitter_array[i] - avg_jitter_us);
|
||||
}
|
||||
|
||||
double variance = sum_sq_diff / ALARM_TIMES;
|
||||
double stddev = sqrt(variance);
|
||||
|
||||
printf("Average jitter us: %"PRIi64"\n", avg_jitter_us);
|
||||
if (max_jitter_us > 0) {
|
||||
printf("\e[1;31mMax jitter us: %"PRIi64"\n\e[0m", max_jitter_us);
|
||||
} else {
|
||||
printf("Max jitter us: %"PRIi64"\n", max_jitter_us);
|
||||
}
|
||||
printf("Min jitter us: %"PRIi64"\n", min_jitter_us);
|
||||
printf("Standard Deviation: %.3f\n", stddev);
|
||||
printf("Drift Percentage: %.3f%%\n", (double)avg_jitter_us / (ALARM_PERIOD_MS * 10));
|
||||
|
||||
// Reset measurement
|
||||
s_current_alarm = 0;
|
||||
bzero(s_alarm_records, sizeof(s_alarm_records));
|
||||
return max_jitter_us;
|
||||
}
|
||||
|
||||
static int64_t test_periodic_timer_accuracy_on_dfs(esp_timer_handle_t timer)
|
||||
{
|
||||
// Calibrate slow clock.
|
||||
#if !CONFIG_ESP_SYSTEM_RTC_EXT_XTAL
|
||||
esp_clk_slowclk_cal_set(rtc_clk_cal(CLK_CAL_RTC_SLOW, 8192));
|
||||
#endif
|
||||
|
||||
ESP_ERROR_CHECK(esp_timer_start_periodic(timer, ALARM_PERIOD_MS * 1000));
|
||||
|
||||
s_alarm_finished = xSemaphoreCreateBinary();
|
||||
// Each FreeRTOS tick will perform a min_freq_mhz->max_freq_mhz -> min_freq_mhz frequency switch
|
||||
xSemaphoreTake(s_alarm_finished, portMAX_DELAY);
|
||||
ESP_ERROR_CHECK(esp_timer_stop(timer));
|
||||
int64_t max_jitter_us = measuring_periodic_timer_accuracy(s_alarm_records);
|
||||
vSemaphoreDelete(s_alarm_finished);
|
||||
return max_jitter_us;
|
||||
}
|
||||
|
||||
// The results of this test are meaningful only if `CONFIG_ESP_SYSTEM_RTC_EXT_XTAL` is enabled
|
||||
TEST_CASE("Test the periodic timer timing accuracy when doing DFS", "[esp_timer][manual][ignore]")
|
||||
{
|
||||
esp_pm_config_t pm_config = {
|
||||
.light_sleep_enable = false
|
||||
};
|
||||
|
||||
const esp_timer_create_args_t periodic_timer_args = {
|
||||
.callback = &periodic_timer_callback,
|
||||
.dispatch_method = ESP_TIMER_ISR,
|
||||
.name = "periodic"
|
||||
};
|
||||
esp_timer_handle_t periodic_timer;
|
||||
ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer));
|
||||
|
||||
for (int min = 0; min < sizeof(supported_freq) / sizeof(uint32_t); min++) {
|
||||
for (int max = 0; max < sizeof(supported_freq) / sizeof(uint32_t); max++) {
|
||||
if (supported_freq[max] <= supported_freq[min]) {
|
||||
continue;
|
||||
}
|
||||
pm_config.max_freq_mhz = supported_freq[max];
|
||||
pm_config.min_freq_mhz = supported_freq[min];
|
||||
ESP_LOGI(TAG, "Testing esp_timer accuracy on %d <-> %d DFS ...", pm_config.min_freq_mhz, pm_config.max_freq_mhz);
|
||||
ESP_ERROR_CHECK(esp_pm_configure(&pm_config));
|
||||
test_periodic_timer_accuracy_on_dfs(periodic_timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
int16_t test_lact_compensation_delay = 0;
|
||||
#define DO_MAGIC_TABLE_MEASUREMENT 0
|
||||
#if DO_MAGIC_TABLE_MEASUREMENT
|
||||
IRAM_ATTR int16_t rtc_clk_get_lact_compensation_delay(uint32_t cur_freq, uint32_t cpu_freq_mhz)
|
||||
{
|
||||
(void)cur_freq; (void)cpu_freq_mhz;
|
||||
return test_lact_compensation_delay;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Test Notes:
|
||||
// 1. The test requires the slow clock source to be selected to `CONFIG_ESP_SYSTEM_RTC_EXT_XTAL`.
|
||||
// 2. Manually set DO_MAGIC_TABLE_MEASUREMENT to 1 before test.
|
||||
TEST_CASE("Test DFS lact conpensate magic table ", "[esp_timer][manual][ignore]")
|
||||
{
|
||||
esp_pm_config_t pm_config = {
|
||||
.light_sleep_enable = false
|
||||
};
|
||||
|
||||
const esp_timer_create_args_t periodic_timer_args = {
|
||||
.callback = &periodic_timer_callback,
|
||||
.dispatch_method = ESP_TIMER_ISR,
|
||||
.name = "periodic"
|
||||
};
|
||||
esp_timer_handle_t periodic_timer;
|
||||
ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer));
|
||||
|
||||
for (int min = 0; min < sizeof(supported_freq) / sizeof(uint32_t); min++) {
|
||||
for (int max = 0; max < sizeof(supported_freq) / sizeof(uint32_t); max++) {
|
||||
if ((supported_freq[max] <= supported_freq[min]) || (supported_freq[min] >= 80) || (supported_freq[max] <= 40)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pm_config.max_freq_mhz = supported_freq[max];
|
||||
pm_config.min_freq_mhz = supported_freq[min];
|
||||
ESP_LOGI(TAG, "Testing esp_timer accuracy on %d <-> %d DFS ...", pm_config.min_freq_mhz, pm_config.max_freq_mhz);
|
||||
esp_pm_configure(&pm_config);
|
||||
|
||||
int32_t max_delay = 300;
|
||||
int32_t min_delay = (pm_config.max_freq_mhz == 240) ? -4000 : -300;
|
||||
int32_t test_delay = (max_delay + min_delay) / 2;
|
||||
int32_t last_delay = 0;
|
||||
int32_t best_delay = 0;
|
||||
int64_t min_avg = INT64_MAX;
|
||||
|
||||
do {
|
||||
printf("Test delay %ld\n", test_delay);
|
||||
test_lact_compensation_delay = test_delay;
|
||||
int64_t max_jitter_us = test_periodic_timer_accuracy_on_dfs(periodic_timer);
|
||||
last_delay = test_delay;
|
||||
if (max_jitter_us > 0) {
|
||||
test_delay = (test_delay + min_delay) / 2;
|
||||
max_delay = last_delay;
|
||||
} else {
|
||||
test_delay = (test_delay + max_delay) / 2;
|
||||
min_delay = last_delay;
|
||||
}
|
||||
|
||||
if (llabs(max_jitter_us) < llabs(min_avg)) {
|
||||
best_delay = last_delay;
|
||||
min_avg = max_jitter_us;
|
||||
}
|
||||
} while (test_delay != last_delay);
|
||||
|
||||
printf("Switch between %d <-> %d\n", pm_config.min_freq_mhz, pm_config.max_freq_mhz);
|
||||
printf("Best delay is %ld\n", best_delay);
|
||||
printf("Min average is %lld\n", min_avg);
|
||||
}
|
||||
}
|
||||
ESP_ERROR_CHECK(esp_timer_delete(periodic_timer));
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "unity.h"
|
||||
#include "unity_test_utils.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_etm.h"
|
||||
#include "driver/gpio_etm.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_timer.h"
|
||||
|
||||
static void periodic_timer_callback(void *arg)
|
||||
{
|
||||
}
|
||||
|
||||
TEST_CASE("esp_timer_etm_event", "[etm]")
|
||||
{
|
||||
// systimer alarm ---> EMT channel ---> GPIO toggle
|
||||
const uint32_t output_gpio = 1;
|
||||
printf("allocate etm channels\r\n");
|
||||
esp_etm_channel_config_t etm_config = {};
|
||||
esp_etm_channel_handle_t etm_channel_a = NULL;
|
||||
TEST_ESP_OK(esp_etm_new_channel(&etm_config, &etm_channel_a));
|
||||
|
||||
printf("allocate GPIO etm task\r\n");
|
||||
esp_etm_task_handle_t gpio_task = NULL;
|
||||
gpio_etm_task_config_t gpio_task_config = {
|
||||
.action = GPIO_ETM_TASK_ACTION_CLR,
|
||||
};
|
||||
TEST_ESP_OK(gpio_new_etm_task(&gpio_task_config, &gpio_task));
|
||||
|
||||
// bind GPIO to the task
|
||||
TEST_ESP_OK(gpio_etm_task_add_gpio(gpio_task, output_gpio));
|
||||
|
||||
printf("initialize gpio\r\n");
|
||||
gpio_config_t task_gpio_config = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_INPUT_OUTPUT,
|
||||
.pin_bit_mask = 1ULL << output_gpio,
|
||||
};
|
||||
TEST_ESP_OK(gpio_config(&task_gpio_config));
|
||||
|
||||
// put the GPIO into initial state
|
||||
TEST_ESP_OK(gpio_set_level(output_gpio, 1));
|
||||
|
||||
printf("acquire esp_timer etm event\r\n");
|
||||
esp_etm_event_handle_t esp_timer_event = NULL;
|
||||
TEST_ESP_OK(esp_timer_new_etm_alarm_event(&esp_timer_event));
|
||||
|
||||
printf("connect event and task to the channel\r\n");
|
||||
TEST_ESP_OK(esp_etm_channel_connect(etm_channel_a, esp_timer_event, gpio_task));
|
||||
TEST_ESP_OK(esp_etm_channel_enable(etm_channel_a));
|
||||
|
||||
printf("create a periodic esp_timer\r\n");
|
||||
const esp_timer_create_args_t periodic_timer_args = {
|
||||
.callback = periodic_timer_callback,
|
||||
.name = "periodic"
|
||||
};
|
||||
esp_timer_handle_t periodic_timer = NULL;
|
||||
TEST_ESP_OK(esp_timer_create(&periodic_timer_args, &periodic_timer));
|
||||
TEST_ESP_OK(esp_timer_start_periodic(periodic_timer, 500000));
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
// etm event not active yet, so the GPIO level should still be the initial state
|
||||
TEST_ASSERT_EQUAL(1, gpio_get_level(output_gpio));
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
// etm event should have cleared the GPIO level
|
||||
TEST_ASSERT_EQUAL(0, gpio_get_level(output_gpio));
|
||||
|
||||
TEST_ESP_OK(esp_timer_stop(periodic_timer));
|
||||
TEST_ESP_OK(esp_timer_delete(periodic_timer));
|
||||
|
||||
// delete etm primitives
|
||||
TEST_ESP_OK(gpio_etm_task_rm_gpio(gpio_task, output_gpio));
|
||||
TEST_ESP_OK(esp_etm_del_task(gpio_task));
|
||||
TEST_ESP_OK(esp_etm_del_event(esp_timer_event));
|
||||
TEST_ESP_OK(esp_etm_channel_disable(etm_channel_a));
|
||||
TEST_ESP_OK(esp_etm_del_channel(etm_channel_a));
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/param.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_timer.h"
|
||||
#include "unity.h"
|
||||
#include "esp_rom_sys.h"
|
||||
#include "esp_sleep.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
static void timer_cb1(void *arg)
|
||||
{
|
||||
++*((int*) arg);
|
||||
}
|
||||
|
||||
TEST_CASE("Test the periodic timer does not handle lost events during light sleep", "[esp_timer][timeout=20]")
|
||||
{
|
||||
|
||||
int count_calls;
|
||||
const esp_timer_create_args_t timer_args = {
|
||||
.name = "timer_cb1",
|
||||
.arg = &count_calls,
|
||||
.callback = &timer_cb1,
|
||||
.skip_unhandled_events = true,
|
||||
};
|
||||
esp_timer_handle_t periodic_timer;
|
||||
esp_timer_create(&timer_args, &periodic_timer);
|
||||
|
||||
int period_cb_ms = 10;
|
||||
int interval_ms = 50;
|
||||
printf("Run light sleep\n");
|
||||
printf("count_calls should be around = %d\n", interval_ms / period_cb_ms);
|
||||
TEST_ESP_OK(esp_sleep_enable_timer_wakeup(interval_ms * 1000));
|
||||
TEST_ESP_OK(esp_timer_start_periodic(periodic_timer, period_cb_ms * 1000));
|
||||
for (int i = 0; i < 3; i++) {
|
||||
do {
|
||||
count_calls = 0;
|
||||
} while (esp_light_sleep_start() != ESP_OK);
|
||||
esp_rom_delay_us(interval_ms * 1000);
|
||||
int saved_count_calls = count_calls;
|
||||
printf("count_calls = %d\n", saved_count_calls);
|
||||
TEST_ESP_OK(esp_timer_dump(stdout));
|
||||
TEST_ASSERT_INT_WITHIN(2, interval_ms / period_cb_ms, saved_count_calls);
|
||||
}
|
||||
TEST_ESP_OK(esp_timer_stop(periodic_timer));
|
||||
// times_skipped is about 12 (4 from each sleep time).
|
||||
TEST_ESP_OK(esp_timer_dump(stdout));
|
||||
TEST_ESP_OK(esp_timer_delete(periodic_timer));
|
||||
vTaskDelay(3); // wait for the esp_timer task to delete all timers
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include "unity.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "spi_flash_mmap.h"
|
||||
#include "esp_rom_sys.h"
|
||||
#include "esp_private/spi_flash_os.h"
|
||||
#include "rom/ets_sys.h"
|
||||
#include "unity_test_utils_cache.h"
|
||||
|
||||
static void test_correct_delay_timer_func(void* arg)
|
||||
{
|
||||
struct timeval* ptv = (struct timeval*) arg;
|
||||
gettimeofday(ptv, NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("ets_timer produces correct delay", "[ets_timer]")
|
||||
{
|
||||
ETSTimer timer1 = {0};
|
||||
|
||||
const int delays_ms[] = {20, 100, 200, 250};
|
||||
const size_t delays_count = sizeof(delays_ms) / sizeof(delays_ms[0]);
|
||||
|
||||
for (size_t i = 0; i < delays_count; ++i) {
|
||||
struct timeval tv_end = {0};
|
||||
|
||||
ets_timer_setfn(&timer1, &test_correct_delay_timer_func, &tv_end);
|
||||
struct timeval tv_start;
|
||||
gettimeofday(&tv_start, NULL);
|
||||
|
||||
ets_timer_arm(&timer1, delays_ms[i], false);
|
||||
|
||||
vTaskDelay(delays_ms[i] * 2 / portTICK_PERIOD_MS);
|
||||
int32_t ms_diff = (tv_end.tv_sec - tv_start.tv_sec) * 1000 +
|
||||
(tv_end.tv_usec - tv_start.tv_usec) / 1000;
|
||||
printf("%d %"PRIi32"\n", delays_ms[i], ms_diff);
|
||||
|
||||
TEST_ASSERT_INT32_WITHIN(portTICK_PERIOD_MS, delays_ms[i], ms_diff);
|
||||
}
|
||||
|
||||
ets_timer_disarm(&timer1);
|
||||
ets_timer_done(&timer1);
|
||||
}
|
||||
|
||||
// no, we can't make this a const size_t (§6.7.5.2)
|
||||
#define NUM_INTERVALS 16
|
||||
|
||||
typedef struct {
|
||||
ETSTimer *timer;
|
||||
size_t cur_interval;
|
||||
int intervals[NUM_INTERVALS];
|
||||
struct timeval tv_start;
|
||||
} test_periodic_correct_delays_args_t;
|
||||
|
||||
static void test_periodic_correct_delays_timer_func(void* arg)
|
||||
{
|
||||
test_periodic_correct_delays_args_t *p_args = (test_periodic_correct_delays_args_t *) arg;
|
||||
struct timeval tv_now;
|
||||
gettimeofday(&tv_now, NULL);
|
||||
int32_t ms_diff = (tv_now.tv_sec - p_args->tv_start.tv_sec) * 1000 +
|
||||
(tv_now.tv_usec - p_args->tv_start.tv_usec) / 1000;
|
||||
printf("timer #%d %"PRIi32"ms\n", p_args->cur_interval, ms_diff);
|
||||
p_args->intervals[p_args->cur_interval++] = ms_diff;
|
||||
// Deliberately make timer handler run longer.
|
||||
// We check that this doesn't affect the result.
|
||||
esp_rom_delay_us(10 * 1000);
|
||||
if (p_args->cur_interval == NUM_INTERVALS) {
|
||||
printf("done\n");
|
||||
ets_timer_disarm(p_args->timer);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("periodic ets_timer produces correct delays", "[ets_timer]")
|
||||
{
|
||||
const int delay_ms = 100;
|
||||
ETSTimer timer1 = {0};
|
||||
test_periodic_correct_delays_args_t args = {0};
|
||||
|
||||
args.timer = &timer1;
|
||||
gettimeofday(&args.tv_start, NULL);
|
||||
ets_timer_setfn(&timer1, &test_periodic_correct_delays_timer_func, &args);
|
||||
ets_timer_arm(&timer1, delay_ms, true);
|
||||
vTaskDelay(delay_ms * (NUM_INTERVALS + 1));
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT32(NUM_INTERVALS, args.cur_interval);
|
||||
for (size_t i = 0; i < NUM_INTERVALS; ++i) {
|
||||
TEST_ASSERT_INT32_WITHIN(portTICK_PERIOD_MS, (i + 1) * delay_ms, args.intervals[i]);
|
||||
}
|
||||
ets_timer_done(&timer1);
|
||||
}
|
||||
#undef NUM_INTERVALS
|
||||
|
||||
#define N 5
|
||||
|
||||
typedef struct {
|
||||
const int order[N * 3];
|
||||
size_t count;
|
||||
} test_timers_ordered_correctly_common_t;
|
||||
|
||||
typedef struct {
|
||||
int timer_index;
|
||||
const int intervals[N];
|
||||
size_t intervals_count;
|
||||
ETSTimer* timer;
|
||||
test_timers_ordered_correctly_common_t* common;
|
||||
bool pass;
|
||||
SemaphoreHandle_t done;
|
||||
} test_timers_ordered_correctly_args_t;
|
||||
|
||||
static void test_timers_ordered_correctly_timer_func(void* arg)
|
||||
{
|
||||
test_timers_ordered_correctly_args_t* p_args = (test_timers_ordered_correctly_args_t*) arg;
|
||||
// check order
|
||||
size_t count = p_args->common->count;
|
||||
int expected_index = p_args->common->order[count];
|
||||
printf("At count %d, expected timer %d, got timer %d\n",
|
||||
count, expected_index, p_args->timer_index);
|
||||
if (expected_index != p_args->timer_index) {
|
||||
p_args->pass = false;
|
||||
ets_timer_disarm(p_args->timer);
|
||||
xSemaphoreGive(p_args->done);
|
||||
return;
|
||||
}
|
||||
p_args->common->count++;
|
||||
if (++p_args->intervals_count == N) {
|
||||
ets_timer_disarm(p_args->timer);
|
||||
xSemaphoreGive(p_args->done);
|
||||
return;
|
||||
}
|
||||
int next_interval = p_args->intervals[p_args->intervals_count];
|
||||
printf("timer %d interval #%d, %d ms\n",
|
||||
p_args->timer_index, p_args->intervals_count, next_interval);
|
||||
ets_timer_arm(p_args->timer, next_interval, false);
|
||||
}
|
||||
|
||||
TEST_CASE("multiple ETSTimers are ordered correctly", "[ets_timer]")
|
||||
{
|
||||
ETSTimer timer1;
|
||||
ETSTimer timer2;
|
||||
ETSTimer timer3;
|
||||
|
||||
test_timers_ordered_correctly_common_t common = {
|
||||
.order = {1, 2, 3, 2, 1, 3, 1, 2, 1, 3, 2, 1, 3, 3, 2},
|
||||
.count = 0
|
||||
};
|
||||
|
||||
SemaphoreHandle_t done = xSemaphoreCreateCounting(3, 0);
|
||||
|
||||
test_timers_ordered_correctly_args_t args1 = {
|
||||
.timer_index = 1,
|
||||
.intervals = {10, 40, 20, 40, 30},
|
||||
.timer = &timer1,
|
||||
.common = &common,
|
||||
.pass = true,
|
||||
.done = done
|
||||
};
|
||||
|
||||
test_timers_ordered_correctly_args_t args2 = {
|
||||
.timer_index = 2,
|
||||
.intervals = {20, 20, 60, 30, 40},
|
||||
.timer = &timer2,
|
||||
.common = &common,
|
||||
.pass = true,
|
||||
.done = done
|
||||
};
|
||||
|
||||
test_timers_ordered_correctly_args_t args3 = {
|
||||
.timer_index = 3,
|
||||
.intervals = {30, 30, 60, 30, 10},
|
||||
.timer = &timer3,
|
||||
.common = &common,
|
||||
.pass = true,
|
||||
.done = done
|
||||
};
|
||||
|
||||
ets_timer_setfn(&timer1, &test_timers_ordered_correctly_timer_func, &args1);
|
||||
ets_timer_setfn(&timer2, &test_timers_ordered_correctly_timer_func, &args2);
|
||||
ets_timer_setfn(&timer3, &test_timers_ordered_correctly_timer_func, &args3);
|
||||
|
||||
ets_timer_arm(&timer1, args1.intervals[0], false);
|
||||
ets_timer_arm(&timer2, args2.intervals[0], false);
|
||||
ets_timer_arm(&timer3, args3.intervals[0], false);
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
int result = xSemaphoreTake(done, 180 / portTICK_PERIOD_MS);
|
||||
TEST_ASSERT_TRUE(result == pdPASS);
|
||||
}
|
||||
|
||||
TEST_ASSERT_TRUE(args1.pass);
|
||||
TEST_ASSERT_TRUE(args2.pass);
|
||||
TEST_ASSERT_TRUE(args3.pass);
|
||||
ets_timer_done(&timer1);
|
||||
ets_timer_done(&timer2);
|
||||
ets_timer_done(&timer3);
|
||||
}
|
||||
#undef N
|
||||
|
||||
static void IRAM_ATTR test_iram_timer_func(void* arg)
|
||||
{
|
||||
volatile bool *b = (volatile bool *)arg;
|
||||
*b = true;
|
||||
}
|
||||
|
||||
#define INTERVAL 5
|
||||
|
||||
void IRAM_ATTR test_iram_arm_disarm(void *ctx)
|
||||
{
|
||||
ETSTimer *timer1 = (ETSTimer*)ctx;
|
||||
|
||||
ets_timer_arm(timer1, INTERVAL, false);
|
||||
// redundant call is deliberate (test code path if already armed)
|
||||
ets_timer_arm(timer1, INTERVAL, false);
|
||||
ets_timer_disarm(timer1);
|
||||
}
|
||||
|
||||
void IRAM_ATTR test_iram_arm(void *ctx)
|
||||
{
|
||||
ETSTimer *timer1 = (ETSTimer*)ctx;
|
||||
|
||||
ets_timer_arm(timer1, INTERVAL, false);
|
||||
}
|
||||
|
||||
void IRAM_ATTR test_iram_disarm(void *ctx)
|
||||
{
|
||||
ETSTimer *timer1 = (ETSTimer*)ctx;
|
||||
|
||||
ets_timer_disarm(timer1);
|
||||
}
|
||||
|
||||
/* WiFi/BT coexistence will sometimes arm/disarm
|
||||
timers from an ISR where flash may be disabled. */
|
||||
IRAM_ATTR TEST_CASE("ETSTimers arm & disarm run from IRAM", "[ets_timer]")
|
||||
{
|
||||
volatile bool flag = false;
|
||||
ETSTimer timer1;
|
||||
|
||||
ets_timer_setfn(&timer1, &test_iram_timer_func, (void *)&flag);
|
||||
|
||||
/* arm a disabled timer, then disarm a live timer */
|
||||
unity_utils_run_cache_disable_stub(test_iram_arm_disarm, &timer1);
|
||||
|
||||
TEST_ASSERT_FALSE(flag); // didn't expire yet
|
||||
|
||||
/* do the same thing but wait for the timer to expire */
|
||||
unity_utils_run_cache_disable_stub(test_iram_arm, &timer1);
|
||||
|
||||
vTaskDelay(2 * INTERVAL / portTICK_PERIOD_MS);
|
||||
TEST_ASSERT_TRUE(flag);
|
||||
|
||||
unity_utils_run_cache_disable_stub(test_iram_disarm, &timer1);
|
||||
ets_timer_done(&timer1);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
# SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
from pytest_embedded_idf.utils import idf_parametrize
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@idf_parametrize(
|
||||
'config,target',
|
||||
[
|
||||
('general', 'supported_targets'),
|
||||
('release', 'supported_targets'),
|
||||
('single_core', 'esp32'),
|
||||
('freertos_compliance', 'esp32'),
|
||||
('isr_dispatch_esp32', 'esp32'),
|
||||
('isr_dispatch_esp32c3', 'esp32c3'),
|
||||
('cpu1_esp32', 'esp32'),
|
||||
('any_cpu_esp32', 'esp32'),
|
||||
('cpu1_esp32s3', 'esp32s3'),
|
||||
('any_cpu_esp32s3', 'esp32s3'),
|
||||
],
|
||||
indirect=['config', 'target'],
|
||||
)
|
||||
def test_esp_timer(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases(timeout=120)
|
||||
|
||||
|
||||
@pytest.mark.host_test
|
||||
@pytest.mark.parametrize('config', ['default'], indirect=True)
|
||||
@idf_parametrize('target', ['linux'], indirect=['target'])
|
||||
def test_esp_timer_linux(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases(timeout=180)
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.psram
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'psram',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
@idf_parametrize('target', ['esp32'], indirect=['target'])
|
||||
def test_esp_timer_psram(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases(timeout=120)
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.xtal_26mhz
|
||||
@pytest.mark.parametrize(
|
||||
'config, baud',
|
||||
[
|
||||
('26mhz_esp32c2', '74880'),
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
@idf_parametrize('target', ['esp32c2'], indirect=['target'])
|
||||
def test_esp_timer_esp32c2_xtal_26mhz(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases(timeout=120)
|
||||
|
||||
|
||||
@pytest.mark.flash_suspend
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'flash_auto_suspend',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
@idf_parametrize('target', ['esp32c3'], indirect=['target'])
|
||||
def test_esp_timer_flash_auto_suspend(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases(timeout=120)
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32c2"
|
||||
CONFIG_XTAL_FREQ_26=y
|
||||
@@ -0,0 +1,5 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD=y
|
||||
CONFIG_ESP_TIMER_SHOW_EXPERIMENTAL=y
|
||||
CONFIG_ESP_TIMER_TASK_AFFINITY_NO_AFFINITY=y
|
||||
CONFIG_ESP_TIMER_ISR_AFFINITY_NO_AFFINITY=y
|
||||
@@ -0,0 +1,5 @@
|
||||
CONFIG_IDF_TARGET="esp32s3"
|
||||
CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD=y
|
||||
CONFIG_ESP_TIMER_SHOW_EXPERIMENTAL=y
|
||||
CONFIG_ESP_TIMER_TASK_AFFINITY_NO_AFFINITY=y
|
||||
CONFIG_ESP_TIMER_ISR_AFFINITY_NO_AFFINITY=y
|
||||
@@ -0,0 +1,5 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD=y
|
||||
CONFIG_ESP_TIMER_SHOW_EXPERIMENTAL=y
|
||||
CONFIG_ESP_TIMER_TASK_AFFINITY_CPU1=y
|
||||
CONFIG_ESP_TIMER_ISR_AFFINITY_CPU1=y
|
||||
@@ -0,0 +1,5 @@
|
||||
CONFIG_IDF_TARGET="esp32s3"
|
||||
CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD=y
|
||||
CONFIG_ESP_TIMER_SHOW_EXPERIMENTAL=y
|
||||
CONFIG_ESP_TIMER_TASK_AFFINITY_CPU1=y
|
||||
CONFIG_ESP_TIMER_ISR_AFFINITY_CPU1=y
|
||||
@@ -0,0 +1 @@
|
||||
# Default CI config uses sdkconfig.defaults plus target-specific defaults.
|
||||
@@ -0,0 +1,4 @@
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_RTC_CLK_SRC_EXT_CRYS=y
|
||||
CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD=y
|
||||
@@ -0,0 +1,3 @@
|
||||
CONFIG_SPI_FLASH_AUTO_SUSPEND=y
|
||||
CONFIG_ESP_TIMER_IN_IRAM=n
|
||||
CONFIG_SPI_FLASH_FORCE_ENABLE_XMC_C_SUSPEND=y
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE=y
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD=y
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32c3"
|
||||
CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD=y
|
||||
@@ -0,0 +1,5 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_ESP_INT_WDT_TIMEOUT_MS=800
|
||||
CONFIG_SPIRAM_OCCUPY_NO_HOST=y
|
||||
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0
|
||||
@@ -0,0 +1,3 @@
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_FREERTOS_UNICORE=y
|
||||
@@ -0,0 +1,13 @@
|
||||
# General options for additional checks
|
||||
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
|
||||
CONFIG_COMPILER_WARN_WRITE_STRINGS=y
|
||||
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
|
||||
CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y
|
||||
CONFIG_COMPILER_STACK_CHECK=y
|
||||
|
||||
CONFIG_ESP_TASK_WDT_INIT=n
|
||||
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
|
||||
|
||||
CONFIG_ESP_TIMER_PROFILING=y
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=n
|
||||
CONFIG_ESP_INT_WDT=n
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
|
||||
Reference in New Issue
Block a user