From ea5f98fe63e87a6b6878e6740198468957938888 Mon Sep 17 00:00:00 2001 From: Marius Vikhammer Date: Tue, 12 May 2026 11:53:27 +0800 Subject: [PATCH] test(rtc_timer): add rtc-timer test-app --- .../test_apps/.build-test-rules.yml | 6 + .../test_apps/CMakeLists.txt | 6 + .../esp_hal_rtc_timer/test_apps/README.md | 12 ++ .../test_apps/main/CMakeLists.txt | 8 + .../test_apps/main/test_app_main.c | 29 ++++ .../test_apps/main/test_rtc_timer.c | 141 ++++++++++++++++++ .../test_apps/pytest_rtc_timer.py | 13 ++ .../test_apps/sdkconfig.ci.default | 0 .../test_apps/sdkconfig.defaults | 1 + 9 files changed, 216 insertions(+) create mode 100644 components/esp_hal_rtc_timer/test_apps/.build-test-rules.yml create mode 100644 components/esp_hal_rtc_timer/test_apps/CMakeLists.txt create mode 100644 components/esp_hal_rtc_timer/test_apps/README.md create mode 100644 components/esp_hal_rtc_timer/test_apps/main/CMakeLists.txt create mode 100644 components/esp_hal_rtc_timer/test_apps/main/test_app_main.c create mode 100644 components/esp_hal_rtc_timer/test_apps/main/test_rtc_timer.c create mode 100644 components/esp_hal_rtc_timer/test_apps/pytest_rtc_timer.py create mode 100644 components/esp_hal_rtc_timer/test_apps/sdkconfig.ci.default create mode 100644 components/esp_hal_rtc_timer/test_apps/sdkconfig.defaults diff --git a/components/esp_hal_rtc_timer/test_apps/.build-test-rules.yml b/components/esp_hal_rtc_timer/test_apps/.build-test-rules.yml new file mode 100644 index 00000000000..612d33ff31f --- /dev/null +++ b/components/esp_hal_rtc_timer/test_apps/.build-test-rules.yml @@ -0,0 +1,6 @@ +components/esp_hal_rtc_timer/test_apps: + enable: + - if: SOC_RTC_TIMER_SUPPORTED == 1 + depends_components: + - esp_hal_rtc_timer + - soc diff --git a/components/esp_hal_rtc_timer/test_apps/CMakeLists.txt b/components/esp_hal_rtc_timer/test_apps/CMakeLists.txt new file mode 100644 index 00000000000..5ed95ed7771 --- /dev/null +++ b/components/esp_hal_rtc_timer/test_apps/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.22) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(test_rtc_timer) diff --git a/components/esp_hal_rtc_timer/test_apps/README.md b/components/esp_hal_rtc_timer/test_apps/README.md new file mode 100644 index 00000000000..39362ea91b9 --- /dev/null +++ b/components/esp_hal_rtc_timer/test_apps/README.md @@ -0,0 +1,12 @@ +| 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 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- | + +# RTC timer HAL tests + +This test app validates the `esp_hal_rtc_timer` component directly. + +It focuses on: + +1. Reading RTC timer counters. +2. Converting RTC slow-clock ticks into elapsed time and checking the result against a measured delay. +3. Exercising alarm interrupt raise/clear behavior on targets with the newer multi-target RTC timer hardware. diff --git a/components/esp_hal_rtc_timer/test_apps/main/CMakeLists.txt b/components/esp_hal_rtc_timer/test_apps/main/CMakeLists.txt new file mode 100644 index 00000000000..40e2209e8df --- /dev/null +++ b/components/esp_hal_rtc_timer/test_apps/main/CMakeLists.txt @@ -0,0 +1,8 @@ +set(srcs "test_app_main.c" + "test_rtc_timer.c") + +# In order for the cases defined by `TEST_CASE` to be linked into the final elf, +# the component can be registered as WHOLE_ARCHIVE +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS "." + WHOLE_ARCHIVE) diff --git a/components/esp_hal_rtc_timer/test_apps/main/test_app_main.c b/components/esp_hal_rtc_timer/test_apps/main/test_app_main.c new file mode 100644 index 00000000000..13c468cc9b0 --- /dev/null +++ b/components/esp_hal_rtc_timer/test_apps/main/test_app_main.c @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "unity_test_runner.h" +#include "esp_heap_caps.h" +#include "esp_newlib.h" +#include "unity_test_utils.h" + +#define TEST_MEMORY_LEAK_THRESHOLD (0) + +void setUp(void) +{ + unity_utils_record_free_mem(); +} + +void tearDown(void) +{ + esp_reent_cleanup(); + unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD); +} + +void app_main(void) +{ + unity_run_menu(); +} diff --git a/components/esp_hal_rtc_timer/test_apps/main/test_rtc_timer.c b/components/esp_hal_rtc_timer/test_apps/main/test_rtc_timer.c new file mode 100644 index 00000000000..0906a9523d7 --- /dev/null +++ b/components/esp_hal_rtc_timer/test_apps/main/test_rtc_timer.c @@ -0,0 +1,141 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "sdkconfig.h" +#include "unity.h" +#include "esp_rom_sys.h" +#include "esp_timer.h" +#include "esp_private/esp_clk.h" +#include "hal/rtc_timer_hal.h" +#include "hal/rtc_timer_ll.h" +#include "soc/rtc.h" +#include "soc/soc_caps.h" +#include "soc/rtc_cntl_periph.h" + +#if SOC_RTC_TIMER_SUPPORTED + +#define RTC_TIMER_SAMPLE_DELAY_US 50000 +#define RTC_TIMER_CONVERSION_MIN_TOLERANCE_US 5000 +#define RTC_TIMER_ALARM_DELAY_US 4000 +#define RTC_TIMER_ALARM_TIMEOUT_US 100000 + +static uint64_t max_u64(uint64_t lhs, uint64_t rhs) +{ + return (lhs > rhs) ? lhs : rhs; +} + +static uint64_t abs_diff_u64(uint64_t lhs, uint64_t rhs) +{ + return (lhs > rhs) ? (lhs - rhs) : (rhs - lhs); +} + +static uint32_t get_rtc_timer_period(void) +{ + uint32_t period = esp_clk_slowclk_cal_get(); + + TEST_ASSERT_NOT_EQUAL_UINT32(0, period); + return period; +} + +static void assert_counter_elapsed_matches_time(uint8_t timer_id) +{ + const uint32_t period = get_rtc_timer_period(); + const int64_t start_host_us = esp_timer_get_time(); + const uint64_t start_cycles = rtc_timer_hal_get_cycle_count(timer_id); + + esp_rom_delay_us(RTC_TIMER_SAMPLE_DELAY_US); + + const uint64_t end_cycles = rtc_timer_hal_get_cycle_count(timer_id); + const int64_t end_host_us = esp_timer_get_time(); + const uint64_t host_elapsed_us = end_host_us - start_host_us; + const uint64_t timer_elapsed_us = rtc_time_slowclk_to_us(end_cycles - start_cycles, period); + const uint64_t tolerance_us = max_u64(host_elapsed_us / 4, RTC_TIMER_CONVERSION_MIN_TOLERANCE_US); + const uint64_t delta_us = abs_diff_u64(timer_elapsed_us, host_elapsed_us); + char message[160]; + + printf("timer%" PRIu8 " counter %" PRIu64 " -> %" PRIu64 " cycles, host=%" PRIu64 "us timer=%" PRIu64 "us tol=%" PRIu64 "us\n", + timer_id, start_cycles, end_cycles, host_elapsed_us, timer_elapsed_us, tolerance_us); + + snprintf(message, sizeof(message), "timer%" PRIu8 " counter did not increase: %" PRIu64 " -> %" PRIu64, + timer_id, start_cycles, end_cycles); + TEST_ASSERT_TRUE_MESSAGE(end_cycles > start_cycles, message); + + snprintf(message, sizeof(message), "timer%" PRIu8 " elapsed mismatch: host=%" PRIu64 "us timer=%" PRIu64 "us delta=%" PRIu64 "us tol=%" PRIu64 "us", + timer_id, host_elapsed_us, timer_elapsed_us, delta_us, tolerance_us); + TEST_ASSERT_TRUE_MESSAGE(delta_us <= tolerance_us, message); +} + +TEST_CASE("rtc timer counter can be read and converted to elapsed time", "[rtc_timer]") +{ + assert_counter_elapsed_matches_time(0); +} + +#if SOC_RTC_TIMER_V2 || SOC_RTC_TIMER_V3 +static uint32_t get_alarm_intr_mask(uint8_t timer_id) +{ + if (timer_id == 0) { +#if SOC_RTC_TIMER_V3 + return RTC_TIMER_SOC_WAKEUP_INT_RAW; +#else + return LP_TIMER_SOC_WAKEUP_INT_RAW; +#endif + } + +#if defined(RTC_TIMER_MAIN_TIMER_LP_INT_RAW) + return RTC_TIMER_MAIN_TIMER_LP_INT_RAW; +#elif defined(LP_TIMER_MAIN_TIMER_LP_INT_RAW) + return LP_TIMER_MAIN_TIMER_LP_INT_RAW; +#else + return 0; +#endif +} + +static void assert_alarm_interrupt_round_trip(uint8_t timer_id) +{ + const uint32_t period = get_rtc_timer_period(); + uint64_t delay_ticks = rtc_time_us_to_slowclk(RTC_TIMER_ALARM_DELAY_US, period); + const uint32_t raw_mask = get_alarm_intr_mask(timer_id); + + rtc_timer_ll_set_target_enable(&LP_TIMER, timer_id, false); + rtc_timer_ll_alarm_intr_enable(&LP_TIMER, timer_id, false); + rtc_timer_ll_clear_alarm_intr_status(&LP_TIMER, timer_id); + TEST_ASSERT_EQUAL_HEX32(0, rtc_timer_ll_get_intr_raw(&LP_TIMER, timer_id) & raw_mask); + + const uint64_t start_cycles = rtc_timer_hal_get_cycle_count(timer_id); + rtc_timer_hal_set_wakeup_time(timer_id, start_cycles + delay_ticks); + + bool fired = false; + const int64_t deadline = esp_timer_get_time() + RTC_TIMER_ALARM_TIMEOUT_US; + while (esp_timer_get_time() < deadline) { + if (rtc_timer_ll_get_intr_raw(&LP_TIMER, timer_id) & raw_mask) { + fired = true; + break; + } + } + + printf("timer%" PRIu8 " alarm raw=0x%" PRIx32 " mask=0x%" PRIx32 "\n", + timer_id, rtc_timer_ll_get_intr_raw(&LP_TIMER, timer_id), raw_mask); + TEST_ASSERT_TRUE_MESSAGE(fired, "RTC timer alarm did not raise its raw interrupt status"); + + rtc_timer_ll_set_target_enable(&LP_TIMER, timer_id, false); + rtc_timer_ll_alarm_intr_enable(&LP_TIMER, timer_id, false); + rtc_timer_ll_clear_alarm_intr_status(&LP_TIMER, timer_id); + TEST_ASSERT_EQUAL_HEX32(0, rtc_timer_ll_get_intr_raw(&LP_TIMER, timer_id) & raw_mask); +} + +TEST_CASE("rtc timer alarm interrupts can be raised and cleared", "[rtc_timer]") +{ + assert_alarm_interrupt_round_trip(0); + if (get_alarm_intr_mask(1) != 0) { + assert_alarm_interrupt_round_trip(1); + } +} +#endif // SOC_RTC_TIMER_V2 || SOC_RTC_TIMER_V3 + +#endif // SOC_RTC_TIMER_SUPPORTED diff --git a/components/esp_hal_rtc_timer/test_apps/pytest_rtc_timer.py b/components/esp_hal_rtc_timer/test_apps/pytest_rtc_timer.py new file mode 100644 index 00000000000..09fa5985525 --- /dev/null +++ b/components/esp_hal_rtc_timer/test_apps/pytest_rtc_timer.py @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize + + +@pytest.mark.generic +@pytest.mark.parametrize('config', ['default'], indirect=True) +@idf_parametrize('target', ['supported_targets'], indirect=['target']) +def test_rtc_timer(dut: Dut) -> None: + dut.run_all_single_board_cases() diff --git a/components/esp_hal_rtc_timer/test_apps/sdkconfig.ci.default b/components/esp_hal_rtc_timer/test_apps/sdkconfig.ci.default new file mode 100644 index 00000000000..e69de29bb2d diff --git a/components/esp_hal_rtc_timer/test_apps/sdkconfig.defaults b/components/esp_hal_rtc_timer/test_apps/sdkconfig.defaults new file mode 100644 index 00000000000..e4bfc208a55 --- /dev/null +++ b/components/esp_hal_rtc_timer/test_apps/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_ESP_TASK_WDT_EN=n