diff --git a/components/driver/test_apps/legacy_timer_driver/CMakeLists.txt b/components/driver/test_apps/legacy_timer_driver/CMakeLists.txt new file mode 100644 index 0000000000..f73bf63842 --- /dev/null +++ b/components/driver/test_apps/legacy_timer_driver/CMakeLists.txt @@ -0,0 +1,5 @@ +# This is the project CMakeLists.txt file for the test subproject +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(legacy_timer_driver_test) diff --git a/components/driver/test_apps/legacy_timer_driver/README.md b/components/driver/test_apps/legacy_timer_driver/README.md new file mode 100644 index 0000000000..3f7a0a04f0 --- /dev/null +++ b/components/driver/test_apps/legacy_timer_driver/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-C3 | +| ----------------- | ----- | -------- | -------- | -------- | diff --git a/components/driver/test_apps/legacy_timer_driver/app_test.py b/components/driver/test_apps/legacy_timer_driver/app_test.py new file mode 100644 index 0000000000..af6e79ba47 --- /dev/null +++ b/components/driver/test_apps/legacy_timer_driver/app_test.py @@ -0,0 +1,30 @@ +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +import glob +import os + +import ttfw_idf +from tiny_test_fw import Utility + + +@ttfw_idf.idf_component_unit_test(env_tag='COMPONENT_UT_GENERIC', target=['esp32', 'esp32s2', 'esp32s3', 'esp32c3']) +def test_component_ut_legacy_timer_driver(env, _): # type: (ttfw_idf.TinyFW.Env, None) -> None + # Get the names of all configs (sdkconfig.ci.* files) + config_files = glob.glob(os.path.join(os.path.dirname(__file__), 'sdkconfig.ci.*')) + config_names = [os.path.basename(s).replace('sdkconfig.ci.', '') for s in config_files] + + # Run test once with binaries built for each config + for name in config_names: + Utility.console_log(f'Checking config "{name}"... ', end='') + dut = env.get_dut('gptimer', 'components/driver/test_apps/legacy_timer_driver', app_config_name=name) + dut.start_app() + stdout = dut.expect('Press ENTER to see the list of tests', full_stdout=True) + dut.write('*') + stdout = dut.expect("Enter next test, or 'enter' to see menu", full_stdout=True, timeout=80) + ttfw_idf.ComponentUTResult.parse_result(stdout,ttfw_idf.TestFormat.UNITY_BASIC) + env.close_dut(dut.name) + Utility.console_log(f'Test config "{name}" done') + + +if __name__ == '__main__': + test_component_ut_legacy_timer_driver() diff --git a/components/driver/test_apps/legacy_timer_driver/main/CMakeLists.txt b/components/driver/test_apps/legacy_timer_driver/main/CMakeLists.txt new file mode 100644 index 0000000000..5dbc1ada23 --- /dev/null +++ b/components/driver/test_apps/legacy_timer_driver/main/CMakeLists.txt @@ -0,0 +1,7 @@ +set(srcs "test_app_main.c" + "test_legacy_timer.c") + +idf_component_register(SRCS ${srcs} + PRIV_REQUIRES driver unity) + +target_link_libraries(${COMPONENT_LIB} INTERFACE "-u test_app_include_legacy_timer") diff --git a/components/driver/test_apps/legacy_timer_driver/main/test_app_main.c b/components/driver/test_apps/legacy_timer_driver/main/test_app_main.c new file mode 100644 index 0000000000..183a88ae3c --- /dev/null +++ b/components/driver/test_apps/legacy_timer_driver/main/test_app_main.c @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "unity_test_runner.h" +#include "esp_heap_caps.h" + +#define TEST_MEMORY_LEAK_THRESHOLD (-600) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +static void check_leak(size_t before_free, size_t after_free, const char *type) +{ + ssize_t delta = after_free - before_free; + printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta); + TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak"); +} + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + check_leak(before_free_8bit, after_free_8bit, "8BIT"); + check_leak(before_free_32bit, after_free_32bit, "32BIT"); +} + +void app_main(void) +{ + unity_run_menu(); +} diff --git a/components/driver/test_apps/legacy_timer_driver/main/test_legacy_timer.c b/components/driver/test_apps/legacy_timer_driver/main/test_legacy_timer.c new file mode 100644 index 0000000000..f2e5eabad3 --- /dev/null +++ b/components/driver/test_apps/legacy_timer_driver/main/test_legacy_timer.c @@ -0,0 +1,1013 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "esp_system.h" +#include "unity.h" +#include "driver/timer.h" +#include "soc/rtc.h" +#include "soc/soc_caps.h" +#include "esp_rom_sys.h" + +#define TEST_TIMER_RESOLUTION_HZ 1000000 // 1MHz resolution +#define TIMER_DELTA 0.001 + +static bool alarm_flag; +static QueueHandle_t timer_queue; + +typedef struct { + timer_group_t timer_group; + timer_idx_t timer_idx; +} timer_info_t; + +typedef struct { + timer_autoreload_t type; // the type of timer's event + timer_group_t timer_group; + timer_idx_t timer_idx; + uint64_t timer_counter_value; +} timer_event_t; + +#define TIMER_INFO_INIT(TG, TID) {.timer_group = (TG), .timer_idx = (TID),} + +static timer_info_t timer_info[] = { +#if SOC_TIMER_GROUP_TOTAL_TIMERS >= 4 + TIMER_INFO_INIT(TIMER_GROUP_0, TIMER_0), + TIMER_INFO_INIT(TIMER_GROUP_0, TIMER_1), + TIMER_INFO_INIT(TIMER_GROUP_1, TIMER_0), + TIMER_INFO_INIT(TIMER_GROUP_1, TIMER_1), +#elif SOC_TIMER_GROUP_TOTAL_TIMERS >= 2 + TIMER_INFO_INIT(TIMER_GROUP_0, TIMER_0), + TIMER_INFO_INIT(TIMER_GROUP_1, TIMER_0), +#else + TIMER_INFO_INIT(TIMER_GROUP_0, TIMER_0), +#endif +}; + +static intr_handle_t timer_isr_handles[SOC_TIMER_GROUP_TOTAL_TIMERS]; + +#define GET_TIMER_INFO(TG, TID) (&timer_info[(TG)*SOC_TIMER_GROUP_TIMERS_PER_GROUP+(TID)]) + +// timer group interruption handle callback +static bool test_timer_group_isr_cb(void *arg) +{ + bool is_awoken = false; + timer_info_t *info = (timer_info_t *) arg; + const timer_group_t timer_group = info->timer_group; + const timer_idx_t timer_idx = info->timer_idx; + uint64_t timer_val; + double time; + uint64_t alarm_value; + timer_event_t evt; + alarm_flag = true; + if (timer_group_get_auto_reload_in_isr(timer_group, timer_idx)) { // For autoreload mode, the counter value has been cleared + timer_group_clr_intr_status_in_isr(timer_group, timer_idx); + esp_rom_printf("This is TG%d timer[%d] reload-timer alarm!\n", timer_group, timer_idx); + timer_get_counter_value(timer_group, timer_idx, &timer_val); + timer_get_counter_time_sec(timer_group, timer_idx, &time); + evt.type = TIMER_AUTORELOAD_EN; + } else { + timer_group_clr_intr_status_in_isr(timer_group, timer_idx); + esp_rom_printf("This is TG%d timer[%d] count-up-timer alarm!\n", timer_group, timer_idx); + timer_get_counter_value(timer_group, timer_idx, &timer_val); + timer_get_counter_time_sec(timer_group, timer_idx, &time); + timer_get_alarm_value(timer_group, timer_idx, &alarm_value); + timer_set_counter_value(timer_group, timer_idx, 0); + evt.type = TIMER_AUTORELOAD_DIS; + } + evt.timer_group = timer_group; + evt.timer_idx = timer_idx; + evt.timer_counter_value = timer_val; + if (timer_queue != NULL) { + BaseType_t awoken = pdFALSE; + BaseType_t ret = xQueueSendFromISR(timer_queue, &evt, &awoken); + TEST_ASSERT_EQUAL(pdTRUE, ret); + if (awoken) { + is_awoken = true; + } + } + return is_awoken; +} + +// timer group interruption handle +static void test_timer_group_isr(void *arg) +{ + if (test_timer_group_isr_cb(arg)) { + portYIELD_FROM_ISR(); + } +} + +// initialize all timer +static void all_timer_init(timer_config_t *config, bool expect_init) +{ + for (uint32_t tg_idx = 0; tg_idx < TIMER_GROUP_MAX; tg_idx++) { + for (uint32_t timer_idx = 0; timer_idx < TIMER_MAX; timer_idx++) { + TEST_ASSERT_EQUAL((expect_init ? ESP_OK : ESP_ERR_INVALID_ARG), timer_init(tg_idx, timer_idx, config)); + } + } + if (timer_queue == NULL) { + timer_queue = xQueueCreate(10, sizeof(timer_event_t)); + } +} + +// deinitialize all timer +static void all_timer_deinit(void) +{ + for (uint32_t tg_idx = 0; tg_idx < TIMER_GROUP_MAX; tg_idx++) { + for (uint32_t timer_idx = 0; timer_idx < TIMER_MAX; timer_idx++) { + TEST_ESP_OK(timer_deinit(tg_idx, timer_idx)); + } + } + if (timer_queue != NULL) { + vQueueDelete(timer_queue); + timer_queue = NULL; + } +} + +// start all of timer +static void all_timer_start(void) +{ + for (uint32_t tg_idx = 0; tg_idx < TIMER_GROUP_MAX; tg_idx++) { + for (uint32_t timer_idx = 0; timer_idx < TIMER_MAX; timer_idx++) { + TEST_ESP_OK(timer_start(tg_idx, timer_idx)); + } + } +} + +static void all_timer_set_counter_value(uint64_t set_cnt_val) +{ + for (uint32_t tg_idx = 0; tg_idx < TIMER_GROUP_MAX; tg_idx++) { + for (uint32_t timer_idx = 0; timer_idx < TIMER_MAX; timer_idx++) { + TEST_ESP_OK(timer_set_counter_value(tg_idx, timer_idx, set_cnt_val)); + } + } +} + +static void all_timer_pause(void) +{ + for (uint32_t tg_idx = 0; tg_idx < TIMER_GROUP_MAX; tg_idx++) { + for (uint32_t timer_idx = 0; timer_idx < TIMER_MAX; timer_idx++) { + TEST_ESP_OK(timer_pause(tg_idx, timer_idx)); + } + } +} + +static void all_timer_get_counter_value(uint64_t set_cnt_val, bool expect_equal_set_val, + uint64_t *actual_cnt_val) +{ + uint64_t current_cnt_val; + for (uint32_t tg_idx = 0; tg_idx < TIMER_GROUP_MAX; tg_idx++) { + for (uint32_t timer_idx = 0; timer_idx < TIMER_MAX; timer_idx++) { + TEST_ESP_OK(timer_get_counter_value(tg_idx, timer_idx, ¤t_cnt_val)); + if (expect_equal_set_val) { + TEST_ASSERT_EQUAL(set_cnt_val, current_cnt_val); + } else { + TEST_ASSERT_NOT_EQUAL(set_cnt_val, current_cnt_val); + if (actual_cnt_val != NULL) { + actual_cnt_val[tg_idx * SOC_TIMER_GROUP_TIMERS_PER_GROUP + timer_idx] = current_cnt_val; + } + } + } + } +} + +static void all_timer_get_counter_time_sec(int expect_time) +{ + double time; + for (uint32_t tg_idx = 0; tg_idx < TIMER_GROUP_MAX; tg_idx++) { + for (uint32_t timer_idx = 0; timer_idx < TIMER_MAX; timer_idx++) { + TEST_ESP_OK(timer_get_counter_time_sec(tg_idx, timer_idx, &time)); + TEST_ASSERT_FLOAT_WITHIN(TIMER_DELTA, expect_time, time); + } + } +} + +static void all_timer_set_counter_mode(timer_count_dir_t counter_dir) +{ + for (uint32_t tg_idx = 0; tg_idx < TIMER_GROUP_MAX; tg_idx++) { + for (uint32_t timer_idx = 0; timer_idx < TIMER_MAX; timer_idx++) { + TEST_ESP_OK(timer_set_counter_mode(tg_idx, timer_idx, counter_dir)); + } + } +} + +static void all_timer_set_divider(uint32_t divider) +{ + for (uint32_t tg_idx = 0; tg_idx < TIMER_GROUP_MAX; tg_idx++) { + for (uint32_t timer_idx = 0; timer_idx < TIMER_MAX; timer_idx++) { + TEST_ESP_OK(timer_set_divider(tg_idx, timer_idx, divider)); + } + } +} + +static void all_timer_set_alarm_value(uint64_t alarm_cnt_val) +{ + for (uint32_t tg_idx = 0; tg_idx < TIMER_GROUP_MAX; tg_idx++) { + for (uint32_t timer_idx = 0; timer_idx < TIMER_MAX; timer_idx++) { + TEST_ESP_OK(timer_set_alarm_value(tg_idx, timer_idx, alarm_cnt_val)); + } + } +} + +static void all_timer_get_alarm_value(uint64_t *alarm_vals) +{ + for (uint32_t tg_idx = 0; tg_idx < TIMER_GROUP_MAX; tg_idx++) { + for (uint32_t timer_idx = 0; timer_idx < TIMER_MAX; timer_idx++) { + TEST_ESP_OK(timer_get_alarm_value(tg_idx, timer_idx, &alarm_vals[tg_idx * SOC_TIMER_GROUP_TIMERS_PER_GROUP + timer_idx])); + } + } +} + +static void all_timer_isr_reg(void) +{ + for (uint32_t tg_idx = 0; tg_idx < TIMER_GROUP_MAX; tg_idx++) { + for (uint32_t timer_idx = 0; timer_idx < TIMER_MAX; timer_idx++) { + TEST_ESP_OK(timer_isr_register(tg_idx, timer_idx, test_timer_group_isr, + GET_TIMER_INFO(tg_idx, timer_idx), ESP_INTR_FLAG_LOWMED, &timer_isr_handles[tg_idx * SOC_TIMER_GROUP_TIMERS_PER_GROUP + timer_idx])); + } + } +} + +static void all_timer_isr_unreg(void) +{ + for (uint32_t tg_idx = 0; tg_idx < TIMER_GROUP_MAX; tg_idx++) { + for (uint32_t timer_idx = 0; timer_idx < TIMER_MAX; timer_idx++) { + TEST_ESP_OK(esp_intr_free(timer_isr_handles[tg_idx * SOC_TIMER_GROUP_TIMERS_PER_GROUP + timer_idx])); + } + } +} + +// enable interrupt and start timer +static void timer_intr_enable_and_start(int timer_group, int timer_idx, double alarm_time) +{ + TEST_ESP_OK(timer_pause(timer_group, timer_idx)); + TEST_ESP_OK(timer_set_counter_value(timer_group, timer_idx, 0x0)); + TEST_ESP_OK(timer_set_alarm_value(timer_group, timer_idx, alarm_time * TEST_TIMER_RESOLUTION_HZ)); + TEST_ESP_OK(timer_set_alarm(timer_group, timer_idx, TIMER_ALARM_EN)); + TEST_ESP_OK(timer_enable_intr(timer_group, timer_idx)); + TEST_ESP_OK(timer_start(timer_group, timer_idx)); +} + +static void timer_isr_check(timer_group_t group_num, timer_idx_t timer_num, timer_autoreload_t autoreload, uint64_t alarm_cnt_val) +{ + timer_event_t evt; + TEST_ASSERT_EQUAL(pdTRUE, xQueueReceive(timer_queue, &evt, 3000 / portTICK_PERIOD_MS)); + TEST_ASSERT_EQUAL(autoreload, evt.type); + TEST_ASSERT_EQUAL(group_num, evt.timer_group); + TEST_ASSERT_EQUAL(timer_num, evt.timer_idx); + TEST_ASSERT_EQUAL((uint32_t)(alarm_cnt_val >> 32), (uint32_t)(evt.timer_counter_value >> 32)); + TEST_ASSERT_UINT32_WITHIN(1000, (uint32_t)(alarm_cnt_val), (uint32_t)(evt.timer_counter_value)); +} + +static void timer_intr_enable_disable_test(timer_group_t group_num, timer_idx_t timer_num, uint64_t alarm_cnt_val) +{ + alarm_flag = false; + TEST_ESP_OK(timer_set_counter_value(group_num, timer_num, 0)); + TEST_ESP_OK(timer_set_alarm(group_num, timer_num, TIMER_ALARM_EN)); + TEST_ESP_OK(timer_enable_intr(group_num, timer_num)); + TEST_ESP_OK(timer_start(group_num, timer_num)); + timer_isr_check(group_num, timer_num, TIMER_AUTORELOAD_DIS, alarm_cnt_val); + TEST_ASSERT_EQUAL(true, alarm_flag); + + // disable interrupt of tg0_timer0 + alarm_flag = false; + TEST_ESP_OK(timer_pause(group_num, timer_num)); + TEST_ESP_OK(timer_set_counter_value(group_num, timer_num, 0)); + TEST_ESP_OK(timer_disable_intr(group_num, timer_num)); + TEST_ESP_OK(timer_start(group_num, timer_num)); + vTaskDelay(2000 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL(false, alarm_flag); +} + +TEST_CASE("Timer_init", "[hw_timer]") +{ + // Test init 1:config parameter + // empty parameter + timer_config_t config0 = { }; + all_timer_init(&config0, false); + + // only one parameter + timer_config_t config1 = { + .auto_reload = TIMER_AUTORELOAD_EN + }; + all_timer_init(&config1, false); + + // lack one parameter + timer_config_t config2 = { + .clk_src = TIMER_SRC_CLK_APB, + .divider = APB_CLK_FREQ / TEST_TIMER_RESOLUTION_HZ, + .auto_reload = TIMER_AUTORELOAD_EN, + .counter_dir = TIMER_COUNT_UP, + .counter_en = TIMER_START, + .intr_type = TIMER_INTR_LEVEL + }; + all_timer_init(&config2, true); + + config2.counter_en = TIMER_PAUSE; + all_timer_init(&config2, true); + + // Test init 2: init + uint64_t set_timer_val = 0x0; + timer_config_t config = { + .clk_src = TIMER_SRC_CLK_APB, + .divider = APB_CLK_FREQ / TEST_TIMER_RESOLUTION_HZ, + .alarm_en = TIMER_ALARM_DIS, + .auto_reload = TIMER_AUTORELOAD_EN, + .counter_dir = TIMER_COUNT_UP, + .counter_en = TIMER_START, + .intr_type = TIMER_INTR_LEVEL + }; + + // judge get config parameters + timer_config_t get_config; + TEST_ESP_OK(timer_init(TIMER_GROUP_0, TIMER_0, &config)); + TEST_ESP_OK(timer_get_config(TIMER_GROUP_0, TIMER_0, &get_config)); + TEST_ASSERT_EQUAL(config.alarm_en, get_config.alarm_en); + TEST_ASSERT_EQUAL(config.auto_reload, get_config.auto_reload); + TEST_ASSERT_EQUAL(config.counter_dir, get_config.counter_dir); + TEST_ASSERT_EQUAL(config.counter_en, get_config.counter_en); + TEST_ASSERT_EQUAL(config.intr_type, get_config.intr_type); + TEST_ASSERT_EQUAL(config.divider, get_config.divider); + + all_timer_init(&config, true); + all_timer_pause(); + all_timer_set_counter_value(set_timer_val); + all_timer_start(); + all_timer_get_counter_value(set_timer_val, false, NULL); + + // Test init 3: wrong parameter + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, timer_init(-1, TIMER_0, &config)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, timer_init(TIMER_GROUP_0, 2, &config)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, timer_init(TIMER_GROUP_0, -1, &config)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, timer_init(2, TIMER_0, &config)); + all_timer_deinit(); +} + +/** + * read count case: + * 1. start timer compare value + * 2. pause timer compare value + * 3. delay some time */ +TEST_CASE("Timer_read_counter_value", "[hw_timer]") +{ + timer_config_t config = { + .clk_src = TIMER_SRC_CLK_APB, + .divider = APB_CLK_FREQ / TEST_TIMER_RESOLUTION_HZ, + .alarm_en = TIMER_ALARM_EN, + .auto_reload = TIMER_AUTORELOAD_EN, + .counter_dir = TIMER_COUNT_UP, + .counter_en = TIMER_START, + .intr_type = TIMER_INTR_LEVEL + }; + uint64_t set_timer_val = 0x0; + + all_timer_init(&config, true); + + // Test read value 1: start timer get counter value + all_timer_set_counter_value(set_timer_val); + all_timer_start(); + all_timer_get_counter_value(set_timer_val, false, NULL); + + // Test read value 2: pause timer get counter value + all_timer_pause(); + set_timer_val = 0x30405000ULL; + all_timer_set_counter_value(set_timer_val); + all_timer_get_counter_value(set_timer_val, true, NULL); + + // Test read value 3:delay 1s get counter value + set_timer_val = 0x0; + all_timer_set_counter_value(set_timer_val); + all_timer_start(); + vTaskDelay(1000 / portTICK_PERIOD_MS); + all_timer_get_counter_time_sec(1); + all_timer_deinit(); +} + +/** + * start timer case: + * 1. normal start + * 2. error start parameter + * */ +TEST_CASE("Timer_start", "[hw_timer]") +{ + timer_config_t config = { + .clk_src = TIMER_SRC_CLK_APB, + .divider = APB_CLK_FREQ / TEST_TIMER_RESOLUTION_HZ, + .alarm_en = TIMER_ALARM_EN, + .auto_reload = TIMER_AUTORELOAD_EN, + .counter_dir = TIMER_COUNT_UP, + .counter_en = TIMER_START, + .intr_type = TIMER_INTR_LEVEL + }; + uint64_t set_timer_val = 0x0; + all_timer_init(&config, true); + + //Test start 1: normal start + all_timer_start(); + all_timer_set_counter_value(set_timer_val); + all_timer_get_counter_value(set_timer_val, false, NULL); + + //Test start 2:wrong parameter + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, timer_start(2, TIMER_0)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, timer_start(-1, TIMER_0)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, timer_start(TIMER_GROUP_0, 2)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, timer_start(TIMER_GROUP_0, -1)); + all_timer_deinit(); +} + +/** + * pause timer case: + * 1. normal pause, read value + * 2. error pause error + */ +TEST_CASE("Timer_pause", "[hw_timer]") +{ + timer_config_t config = { + .clk_src = TIMER_SRC_CLK_APB, + .divider = APB_CLK_FREQ / TEST_TIMER_RESOLUTION_HZ, + .alarm_en = TIMER_ALARM_EN, + .auto_reload = TIMER_AUTORELOAD_EN, + .counter_dir = TIMER_COUNT_UP, + .counter_en = TIMER_START, + .intr_type = TIMER_INTR_LEVEL + }; + uint64_t set_timer_val = 0x0; + all_timer_init(&config, true); + + //Test pause 1: right parameter + all_timer_pause(); + all_timer_set_counter_value(set_timer_val); + all_timer_get_counter_value(set_timer_val, true, NULL); + + //Test pause 2: wrong parameter + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, timer_pause(-1, TIMER_0)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, timer_pause(TIMER_GROUP_0, -1)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, timer_pause(2, TIMER_0)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, timer_pause(TIMER_GROUP_0, 2)); + all_timer_deinit(); +} + +// positive mode and negative mode +TEST_CASE("Timer_counter_direction", "[hw_timer]") +{ + timer_config_t config = { + .clk_src = TIMER_SRC_CLK_APB, + .divider = APB_CLK_FREQ / TEST_TIMER_RESOLUTION_HZ, + .alarm_en = TIMER_ALARM_EN, + .auto_reload = TIMER_AUTORELOAD_EN, + .counter_dir = TIMER_COUNT_UP, + .counter_en = TIMER_START, + .intr_type = TIMER_INTR_LEVEL + }; + uint64_t set_timer_val = 0x0; + all_timer_init(&config, true); + all_timer_pause(); + + // Test counter mode 1: TIMER_COUNT_UP + all_timer_set_counter_mode(TIMER_COUNT_UP); + all_timer_set_counter_value(set_timer_val); + all_timer_start(); + vTaskDelay(1000 / portTICK_PERIOD_MS); + all_timer_get_counter_time_sec(1); + + // Test counter mode 2: TIMER_COUNT_DOWN + all_timer_pause(); + set_timer_val = TEST_TIMER_RESOLUTION_HZ * 3; // 3s clock counter value + all_timer_set_counter_mode(TIMER_COUNT_DOWN); + all_timer_set_counter_value(set_timer_val); + all_timer_start(); + vTaskDelay(1000 / portTICK_PERIOD_MS); + all_timer_get_counter_time_sec(2); + + // Test counter mode 3 : wrong parameter + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, timer_set_counter_mode(TIMER_GROUP_0, TIMER_0, -1)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, timer_set_counter_mode(TIMER_GROUP_0, TIMER_0, 2)); + all_timer_deinit(); +} + +TEST_CASE("Timer_divider", "[hw_timer]") +{ + timer_config_t config = { + .clk_src = TIMER_SRC_CLK_APB, + .divider = APB_CLK_FREQ / TEST_TIMER_RESOLUTION_HZ, + .alarm_en = TIMER_ALARM_EN, + .auto_reload = TIMER_AUTORELOAD_EN, + .counter_dir = TIMER_COUNT_UP, + .counter_en = TIMER_START, + .intr_type = TIMER_INTR_LEVEL + }; + uint64_t set_timer_val = 0; + uint64_t time_val[TIMER_GROUP_MAX * TIMER_MAX]; + uint64_t comp_time_val[TIMER_GROUP_MAX * TIMER_MAX]; + all_timer_init(&config, true); + + all_timer_pause(); + all_timer_set_counter_value(set_timer_val); + + all_timer_start(); + vTaskDelay(1000 / portTICK_PERIOD_MS); + all_timer_get_counter_value(set_timer_val, false, time_val); + + all_timer_pause(); + all_timer_set_divider(config.divider / 2); // half of original divider + all_timer_set_counter_value(set_timer_val); + + all_timer_start(); + vTaskDelay(1000 / portTICK_PERIOD_MS); //delay the same time + all_timer_get_counter_value(set_timer_val, false, comp_time_val); + for (int i = 0; i < TIMER_GROUP_MAX * TIMER_MAX; i++) { + TEST_ASSERT_INT_WITHIN(2000, 1000000, time_val[i]); + TEST_ASSERT_INT_WITHIN(2000, 2000000, comp_time_val[i]); + } + + all_timer_pause(); + all_timer_set_divider(256); + all_timer_set_counter_value(set_timer_val); + all_timer_start(); + vTaskDelay(1000 / portTICK_PERIOD_MS); //delay the same time + all_timer_get_counter_value(set_timer_val, false, comp_time_val); + for (int i = 0; i < TIMER_GROUP_MAX * TIMER_MAX; i++) { + TEST_ASSERT_INT_WITHIN(100, APB_CLK_FREQ / 256, comp_time_val[i]); + } + + all_timer_pause(); + all_timer_set_divider(2); + all_timer_set_counter_value(set_timer_val); + all_timer_start(); + vTaskDelay(1000 / portTICK_PERIOD_MS); + all_timer_get_counter_value(set_timer_val, false, comp_time_val); + for (int i = 0; i < TIMER_GROUP_MAX * TIMER_MAX; i++) { + TEST_ASSERT_INT_WITHIN(5000, APB_CLK_FREQ / 2, comp_time_val[i]); + } + + all_timer_pause(); + all_timer_set_divider(65536); + all_timer_set_counter_value(set_timer_val); + all_timer_start(); + vTaskDelay(1000 / portTICK_PERIOD_MS); //delay the same time + all_timer_get_counter_value(set_timer_val, false, comp_time_val); + for (int i = 0; i < TIMER_GROUP_MAX * TIMER_MAX; i++) { + TEST_ASSERT_INT_WITHIN(10, APB_CLK_FREQ / 65536, comp_time_val[i]); + } + + all_timer_pause(); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, timer_set_divider(TIMER_GROUP_0, TIMER_0, 1)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, timer_set_divider(TIMER_GROUP_0, TIMER_0, 65537)); + all_timer_deinit(); +} + +/** + * enable alarm case: + * 1. enable alarm ,set alarm value and get value + * 2. disable alarm ,set alarm value and get value + */ +TEST_CASE("Timer_enable_alarm", "[hw_timer]") +{ + timer_config_t config_test = { + .clk_src = TIMER_SRC_CLK_APB, + .divider = APB_CLK_FREQ / TEST_TIMER_RESOLUTION_HZ, + .alarm_en = TIMER_ALARM_DIS, + .auto_reload = TIMER_AUTORELOAD_DIS, + .counter_dir = TIMER_COUNT_UP, + .counter_en = TIMER_PAUSE, + .intr_type = TIMER_INTR_LEVEL + }; + all_timer_init(&config_test, true); + all_timer_isr_reg(); + + // enable alarm of tg0_timer1 + alarm_flag = false; + TEST_ESP_OK(timer_set_alarm(TIMER_GROUP_0, TIMER_0, TIMER_ALARM_EN)); + timer_intr_enable_and_start(TIMER_GROUP_0, TIMER_0, 1.2); + timer_isr_check(TIMER_GROUP_0, TIMER_0, TIMER_AUTORELOAD_DIS, 1.2 * TEST_TIMER_RESOLUTION_HZ); + TEST_ASSERT_EQUAL(true, alarm_flag); + + // disable alarm of tg0_timer1 + alarm_flag = false; + timer_intr_enable_and_start(TIMER_GROUP_0, TIMER_0, 1.2); + TEST_ESP_OK(timer_set_alarm(TIMER_GROUP_0, TIMER_0, TIMER_ALARM_DIS)); + vTaskDelay(2000 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL(false, alarm_flag); + +#if SOC_TIMER_GROUPS > 1 + // enable alarm of tg1_timer0 + alarm_flag = false; + TEST_ESP_OK(timer_set_alarm(TIMER_GROUP_1, TIMER_0, TIMER_ALARM_EN)); + timer_intr_enable_and_start(TIMER_GROUP_1, TIMER_0, 1.2); + timer_isr_check(TIMER_GROUP_1, TIMER_0, TIMER_AUTORELOAD_DIS, 1.2 * TEST_TIMER_RESOLUTION_HZ); + TEST_ASSERT_EQUAL(true, alarm_flag); + + // disable alarm of tg1_timer0 + alarm_flag = false; + timer_intr_enable_and_start(TIMER_GROUP_1, TIMER_0, 1.2); + TEST_ESP_OK(timer_set_alarm(TIMER_GROUP_1, TIMER_0, TIMER_ALARM_DIS)); + vTaskDelay(2000 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL(false, alarm_flag); +#endif + all_timer_isr_unreg(); + all_timer_deinit(); +} + +/** + * alarm value case: + * 1. set alarm value and get value + * 2. interrupt test time + */ +TEST_CASE("Timer_set_alarm_value", "[hw_timer]") +{ + uint64_t alarm_val[SOC_TIMER_GROUP_TOTAL_TIMERS]; + timer_config_t config = { + .clk_src = TIMER_SRC_CLK_APB, + .divider = APB_CLK_FREQ / TEST_TIMER_RESOLUTION_HZ, + .alarm_en = TIMER_ALARM_EN, + .auto_reload = TIMER_AUTORELOAD_DIS, + .counter_dir = TIMER_COUNT_UP, + .counter_en = TIMER_PAUSE, + .intr_type = TIMER_INTR_LEVEL + }; + all_timer_init(&config, true); + all_timer_isr_reg(); + + // set and get alarm value + all_timer_set_alarm_value(3 * TEST_TIMER_RESOLUTION_HZ); + all_timer_get_alarm_value(alarm_val); + for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { + TEST_ASSERT_EQUAL_UINT32(3 * TEST_TIMER_RESOLUTION_HZ, (uint32_t)alarm_val[i]); + } + + // set interrupt read alarm value + timer_intr_enable_and_start(TIMER_GROUP_0, TIMER_0, 2.4); + timer_isr_check(TIMER_GROUP_0, TIMER_0, TIMER_AUTORELOAD_DIS, 2.4 * TEST_TIMER_RESOLUTION_HZ); +#if SOC_TIMER_GROUPS > 1 + timer_intr_enable_and_start(TIMER_GROUP_1, TIMER_0, 1.4); + timer_isr_check(TIMER_GROUP_1, TIMER_0, TIMER_AUTORELOAD_DIS, 1.4 * TEST_TIMER_RESOLUTION_HZ); +#endif + all_timer_isr_unreg(); + all_timer_deinit(); +} + +/** + * auto reload case: + * 1. no reload + * 2. auto reload + */ +TEST_CASE("Timer_auto_reload", "[hw_timer]") +{ + timer_config_t config = { + .clk_src = TIMER_SRC_CLK_APB, + .divider = APB_CLK_FREQ / TEST_TIMER_RESOLUTION_HZ, + .alarm_en = TIMER_ALARM_EN, + .auto_reload = TIMER_AUTORELOAD_DIS, + .counter_dir = TIMER_COUNT_UP, + .counter_en = TIMER_PAUSE, + .intr_type = TIMER_INTR_LEVEL + }; + all_timer_init(&config, true); + all_timer_isr_reg(); + + // test disable auto_reload + timer_intr_enable_and_start(TIMER_GROUP_0, TIMER_0, 1.14); + timer_isr_check(TIMER_GROUP_0, TIMER_0, TIMER_AUTORELOAD_DIS, 1.14 * TEST_TIMER_RESOLUTION_HZ); +#if SOC_TIMER_GROUPS > 1 + timer_intr_enable_and_start(TIMER_GROUP_1, TIMER_0, 1.14); + timer_isr_check(TIMER_GROUP_1, TIMER_0, TIMER_AUTORELOAD_DIS, 1.14 * TEST_TIMER_RESOLUTION_HZ); +#endif + + //test enable auto_reload + TEST_ESP_OK(timer_set_auto_reload(TIMER_GROUP_0, TIMER_0, TIMER_AUTORELOAD_EN)); + timer_intr_enable_and_start(TIMER_GROUP_0, TIMER_0, 1.4); + timer_isr_check(TIMER_GROUP_0, TIMER_0, TIMER_AUTORELOAD_EN, 0); +#if SOC_TIMER_GROUPS > 1 + TEST_ESP_OK(timer_set_auto_reload(TIMER_GROUP_1, TIMER_0, TIMER_AUTORELOAD_EN)); + timer_intr_enable_and_start(TIMER_GROUP_1, TIMER_0, 1.4); + timer_isr_check(TIMER_GROUP_1, TIMER_0, TIMER_AUTORELOAD_EN, 0); +#endif + all_timer_isr_unreg(); + all_timer_deinit(); +} + +/** + * timer_enable_intr case: + * 1. enable timer_intr + * 2. disable timer_intr + */ +TEST_CASE("Timer_enable_timer_interrupt", "[hw_timer]") +{ + timer_config_t config = { + .clk_src = TIMER_SRC_CLK_APB, + .divider = APB_CLK_FREQ / TEST_TIMER_RESOLUTION_HZ, + .alarm_en = TIMER_ALARM_DIS, + .counter_dir = TIMER_COUNT_UP, + .auto_reload = TIMER_AUTORELOAD_DIS, + .counter_en = TIMER_PAUSE, + .intr_type = TIMER_INTR_LEVEL + }; + + all_timer_init(&config, true); + all_timer_pause(); + all_timer_set_alarm_value(1.2 * TEST_TIMER_RESOLUTION_HZ); + all_timer_set_counter_value(0); + all_timer_isr_reg(); + timer_intr_enable_disable_test(TIMER_GROUP_0, TIMER_0, 1.2 * TEST_TIMER_RESOLUTION_HZ); +#if SOC_TIMER_GROUPS > 1 + timer_intr_enable_disable_test(TIMER_GROUP_1, TIMER_0, 1.2 * TEST_TIMER_RESOLUTION_HZ); +#endif + + // enable interrupt of tg0_timer0 again + alarm_flag = false; + TEST_ESP_OK(timer_pause(TIMER_GROUP_0, TIMER_0)); + TEST_ESP_OK(timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0)); + TEST_ESP_OK(timer_set_alarm(TIMER_GROUP_0, TIMER_0, TIMER_ALARM_EN)); + TEST_ESP_OK(timer_enable_intr(TIMER_GROUP_0, TIMER_0)); + TEST_ESP_OK(timer_start(TIMER_GROUP_0, TIMER_0)); + timer_isr_check(TIMER_GROUP_0, TIMER_0, TIMER_AUTORELOAD_DIS, 1.2 * TEST_TIMER_RESOLUTION_HZ); + TEST_ASSERT_EQUAL(true, alarm_flag); + all_timer_isr_unreg(); + all_timer_deinit(); +} + +/** + * enable timer group case: + * 1. enable timer group + * 2. disable timer group + */ +TEST_CASE("Timer_enable_timer_group_interrupt", "[hw_timer][ignore]") +{ + intr_handle_t isr_handle = NULL; + alarm_flag = false; + timer_config_t config = { + .clk_src = TIMER_SRC_CLK_APB, + .divider = APB_CLK_FREQ / TEST_TIMER_RESOLUTION_HZ, + .alarm_en = TIMER_ALARM_EN, + .auto_reload = TIMER_AUTORELOAD_DIS, + .counter_dir = TIMER_COUNT_UP, + .counter_en = TIMER_PAUSE, + .intr_type = TIMER_INTR_LEVEL + }; + uint64_t set_timer_val = 0x0; + all_timer_init(&config, true); + all_timer_pause(); + all_timer_set_counter_value(set_timer_val); + all_timer_set_alarm_value(1.2 * TEST_TIMER_RESOLUTION_HZ); + + // enable interrupt of tg0_timer0 + TEST_ESP_OK(timer_group_intr_enable(TIMER_GROUP_0, TIMER_INTR_T0)); + TEST_ESP_OK(timer_isr_register(TIMER_GROUP_0, TIMER_0, test_timer_group_isr, + GET_TIMER_INFO(TIMER_GROUP_0, TIMER_0), ESP_INTR_FLAG_LOWMED, &isr_handle)); + TEST_ESP_OK(timer_start(TIMER_GROUP_0, TIMER_0)); + timer_isr_check(TIMER_GROUP_0, TIMER_0, TIMER_AUTORELOAD_DIS, 1.2 * TEST_TIMER_RESOLUTION_HZ); + TEST_ASSERT_EQUAL(true, alarm_flag); + + // disable interrupt of tg0_timer0 + alarm_flag = false; + TEST_ESP_OK(timer_set_counter_value(TIMER_GROUP_0, TIMER_0, set_timer_val)); + TEST_ESP_OK(timer_group_intr_disable(TIMER_GROUP_0, TIMER_INTR_T0)); + TEST_ESP_OK(timer_start(TIMER_GROUP_0, TIMER_0)); + vTaskDelay(2000 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL(false, alarm_flag); + esp_intr_free(isr_handle); +} + +/** + * isr_register case: + * Cycle register 15 times, compare the heap size to ensure no memory leaks + */ +TEST_CASE("Timer_interrupt_register", "[hw_timer]") +{ + timer_config_t config = { + .clk_src = TIMER_SRC_CLK_APB, + .divider = APB_CLK_FREQ / TEST_TIMER_RESOLUTION_HZ, + .alarm_en = TIMER_ALARM_DIS, + .auto_reload = TIMER_AUTORELOAD_DIS, + .counter_dir = TIMER_COUNT_UP, + .counter_en = TIMER_PAUSE, + .intr_type = TIMER_INTR_LEVEL + }; + + for (int i = 0; i < 15; i++) { + all_timer_init(&config, true); + timer_isr_handle_t timer_isr_handle[TIMER_GROUP_MAX * TIMER_MAX]; + for (uint32_t tg_idx = 0; tg_idx < TIMER_GROUP_MAX; tg_idx++) { + for (uint32_t timer_idx = 0; timer_idx < TIMER_MAX; timer_idx++) { + TEST_ESP_OK(timer_isr_register(tg_idx, timer_idx, test_timer_group_isr, + GET_TIMER_INFO(tg_idx, timer_idx), ESP_INTR_FLAG_LOWMED, &timer_isr_handle[tg_idx * SOC_TIMER_GROUP_TIMERS_PER_GROUP + timer_idx])); + } + } + + TEST_ESP_OK(timer_set_alarm(TIMER_GROUP_0, TIMER_0, TIMER_ALARM_EN)); + timer_intr_enable_and_start(TIMER_GROUP_0, TIMER_0, 0.54); +#if SOC_TIMER_GROUPS > 1 + TEST_ESP_OK(timer_set_alarm(TIMER_GROUP_1, TIMER_0, TIMER_ALARM_EN)); + timer_intr_enable_and_start(TIMER_GROUP_1, TIMER_0, 0.34); +#endif + + TEST_ESP_OK(timer_set_auto_reload(TIMER_GROUP_0, TIMER_0, TIMER_AUTORELOAD_EN)); + TEST_ESP_OK(timer_set_alarm(TIMER_GROUP_0, TIMER_0, TIMER_ALARM_EN)); + timer_intr_enable_and_start(TIMER_GROUP_0, TIMER_0, 0.4); +#if SOC_TIMER_GROUPS > 1 + TEST_ESP_OK(timer_set_auto_reload(TIMER_GROUP_1, TIMER_0, TIMER_AUTORELOAD_EN)); + TEST_ESP_OK(timer_set_alarm(TIMER_GROUP_1, TIMER_0, TIMER_ALARM_EN)); + timer_intr_enable_and_start(TIMER_GROUP_1, TIMER_0, 0.6); +#endif + vTaskDelay(1000 / portTICK_PERIOD_MS); + + // ISR hanlde function should be free before next ISR register. + for (uint32_t tg_idx = 0; tg_idx < TIMER_GROUP_MAX; tg_idx++) { + for (uint32_t timer_idx = 0; timer_idx < TIMER_MAX; timer_idx++) { + TEST_ESP_OK(esp_intr_free(timer_isr_handle[tg_idx * SOC_TIMER_GROUP_TIMERS_PER_GROUP + timer_idx])); + } + } + all_timer_deinit(); + } +} + +#if SOC_TIMER_GROUP_SUPPORT_XTAL +/** + * Timer clock source: + * 1. configure clock source as APB clock, and enable timer interrupt + * 2. configure clock source as XTAL clock, adn enable timer interrupt + */ +TEST_CASE("Timer_clock_source", "[hw_timer]") +{ + // configure clock source as APB clock + timer_config_t config = { + .clk_src = TIMER_SRC_CLK_APB, + .divider = APB_CLK_FREQ / TEST_TIMER_RESOLUTION_HZ, + .alarm_en = TIMER_ALARM_DIS, + .auto_reload = TIMER_AUTORELOAD_DIS, + .counter_dir = TIMER_COUNT_UP, + .counter_en = TIMER_PAUSE, + .intr_type = TIMER_INTR_LEVEL, + }; + all_timer_init(&config, true); + all_timer_pause(); + all_timer_set_alarm_value(1.2 * TEST_TIMER_RESOLUTION_HZ); + all_timer_set_counter_value(0); + all_timer_isr_reg(); + + timer_intr_enable_disable_test(TIMER_GROUP_0, TIMER_0, 1.2 * TEST_TIMER_RESOLUTION_HZ); +#if SOC_TIMER_GROUPS > 1 + timer_intr_enable_disable_test(TIMER_GROUP_1, TIMER_0, 1.2 * TEST_TIMER_RESOLUTION_HZ); +#endif + + // configure clock source as XTAL clock + all_timer_pause(); + config.clk_src = TIMER_SRC_CLK_XTAL; + config.divider = rtc_clk_xtal_freq_get() * 1000000 / TEST_TIMER_RESOLUTION_HZ; + all_timer_init(&config, true); + all_timer_set_alarm_value(1.2 * TEST_TIMER_RESOLUTION_HZ); + + timer_intr_enable_disable_test(TIMER_GROUP_0, TIMER_0, 1.2 * TEST_TIMER_RESOLUTION_HZ); +#if SOC_TIMER_GROUPS > 1 + timer_intr_enable_disable_test(TIMER_GROUP_1, TIMER_0, 1.2 * TEST_TIMER_RESOLUTION_HZ); +#endif + + all_timer_isr_unreg(); + all_timer_deinit(); +} +#endif + +/** + * Timer ISR callback test + */ +TEST_CASE("Timer_ISR_callback", "[hw_timer]") +{ + alarm_flag = false; + timer_config_t config = { + .clk_src = TIMER_SRC_CLK_APB, + .divider = APB_CLK_FREQ / TEST_TIMER_RESOLUTION_HZ, + .alarm_en = TIMER_ALARM_EN, + .auto_reload = TIMER_AUTORELOAD_DIS, + .counter_dir = TIMER_COUNT_UP, + .counter_en = TIMER_PAUSE, + .intr_type = TIMER_INTR_LEVEL, + }; + uint64_t alarm_cnt_val = 1.2 * TEST_TIMER_RESOLUTION_HZ; + uint64_t set_timer_val = 0x0; + all_timer_init(&config, true); + all_timer_pause(); + all_timer_set_alarm_value(alarm_cnt_val); + all_timer_set_counter_value(set_timer_val); + + // add isr callback for tg0_timer0 + TEST_ESP_OK(timer_isr_callback_add(TIMER_GROUP_0, TIMER_0, test_timer_group_isr_cb, + GET_TIMER_INFO(TIMER_GROUP_0, TIMER_0), ESP_INTR_FLAG_LOWMED)); + TEST_ESP_OK(timer_set_counter_value(TIMER_GROUP_0, TIMER_0, set_timer_val)); + TEST_ESP_OK(timer_start(TIMER_GROUP_0, TIMER_0)); + timer_isr_check(TIMER_GROUP_0, TIMER_0, TIMER_AUTORELOAD_DIS, alarm_cnt_val); + TEST_ASSERT_EQUAL(true, alarm_flag); + + // remove isr callback for tg0_timer0 + TEST_ESP_OK(timer_pause(TIMER_GROUP_0, TIMER_0)); + TEST_ESP_OK(timer_isr_callback_remove(TIMER_GROUP_0, TIMER_0)); + alarm_flag = false; + TEST_ESP_OK(timer_set_counter_value(TIMER_GROUP_0, TIMER_0, set_timer_val)); + TEST_ESP_OK(timer_start(TIMER_GROUP_0, TIMER_0)); + vTaskDelay(2000 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL(false, alarm_flag); + +#if SOC_TIMER_GROUPS > 1 + // add isr callback for tg1_timer0 + TEST_ESP_OK(timer_pause(TIMER_GROUP_1, TIMER_0)); + TEST_ESP_OK(timer_isr_callback_add(TIMER_GROUP_1, TIMER_0, test_timer_group_isr_cb, + GET_TIMER_INFO(TIMER_GROUP_1, TIMER_0), ESP_INTR_FLAG_LOWMED)); + TEST_ESP_OK(timer_set_counter_value(TIMER_GROUP_1, TIMER_0, set_timer_val)); + TEST_ESP_OK(timer_start(TIMER_GROUP_1, TIMER_0)); + timer_isr_check(TIMER_GROUP_1, TIMER_0, TIMER_AUTORELOAD_DIS, alarm_cnt_val); + TEST_ASSERT_EQUAL(true, alarm_flag); + + // remove isr callback for tg1_timer0 + TEST_ESP_OK(timer_pause(TIMER_GROUP_1, TIMER_0)); + TEST_ESP_OK(timer_isr_callback_remove(TIMER_GROUP_1, TIMER_0)); + alarm_flag = false; + TEST_ESP_OK(timer_set_counter_value(TIMER_GROUP_1, TIMER_0, set_timer_val)); + TEST_ESP_OK(timer_start(TIMER_GROUP_1, TIMER_0)); + vTaskDelay(2000 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL(false, alarm_flag); +#endif + all_timer_deinit(); +} + +/** + * Timer memory test + */ +TEST_CASE("Timer_init_deinit_stress_test", "[hw_timer]") +{ + timer_config_t config = { + .clk_src = TIMER_SRC_CLK_APB, + .divider = APB_CLK_FREQ / TEST_TIMER_RESOLUTION_HZ, + .alarm_en = TIMER_ALARM_EN, + .auto_reload = TIMER_AUTORELOAD_EN, + .counter_dir = TIMER_COUNT_UP, + .counter_en = TIMER_PAUSE, + .intr_type = TIMER_INTR_LEVEL, + }; + for (uint32_t i = 0; i < 100; i++) { + all_timer_init(&config, true); + all_timer_deinit(); + } +} + +// The following test cases are used to check if the timer_group fix works. +// Some applications use a software reset, at the reset time, timer_group happens to generate an interrupt. +// but software reset does not clear interrupt status, this is not safe for application when enable the interrupt of timer_group. +// This case will check under this fix, whether the interrupt status is cleared after timer_group initialization. +static void timer_group_test_init(void) +{ + static const uint32_t time_ms = 100; // Alarm value 100ms. + static const uint32_t ste_val = time_ms * TEST_TIMER_RESOLUTION_HZ / 1000; + timer_config_t config = { + .clk_src = TIMER_SRC_CLK_APB, + .divider = APB_CLK_FREQ / TEST_TIMER_RESOLUTION_HZ, + .counter_dir = TIMER_COUNT_UP, + .counter_en = TIMER_PAUSE, + .alarm_en = TIMER_ALARM_EN, + .intr_type = TIMER_INTR_LEVEL, + .auto_reload = TIMER_AUTORELOAD_EN, + }; + TEST_ESP_OK(timer_init(TIMER_GROUP_0, TIMER_0, &config)); + TEST_ESP_OK(timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0x00000000ULL)); + TEST_ESP_OK(timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, ste_val)); + //Now the timer is ready. + //We only need to check the interrupt status and don't have to register a interrupt routine. +} + +// +// Timer check reinitialization sequence +// +TEST_CASE("Timer_check_reinitialization_sequence", "[hw_timer]") +{ + // 1. step - install driver + timer_group_test_init(); + // 2 - register interrupt and start timer + TEST_ESP_OK(timer_enable_intr(TIMER_GROUP_0, TIMER_0)); + TEST_ESP_OK(timer_start(TIMER_GROUP_0, TIMER_0)); + // Do some work + vTaskDelay(80 / portTICK_PERIOD_MS); + // 3 - deinit timer driver + TEST_ESP_OK(timer_deinit(TIMER_GROUP_0, TIMER_0)); + timer_config_t config = { + .clk_src = TIMER_SRC_CLK_APB, + .divider = APB_CLK_FREQ / TEST_TIMER_RESOLUTION_HZ, + .counter_dir = TIMER_COUNT_UP, + .counter_en = TIMER_START, + .alarm_en = TIMER_ALARM_EN, + .intr_type = TIMER_INTR_LEVEL, + .auto_reload = TIMER_AUTORELOAD_EN, + }; + // 4 - reinstall driver + TEST_ESP_OK(timer_init(TIMER_GROUP_0, TIMER_0, &config)); + // 5 - enable interrupt + TEST_ESP_OK(timer_enable_intr(TIMER_GROUP_0, TIMER_0)); + vTaskDelay(30 / portTICK_PERIOD_MS); + // The pending timer interrupt should not be triggered + TEST_ASSERT_EQUAL(0, timer_group_get_intr_status_in_isr(TIMER_GROUP_0) & TIMER_INTR_T0); +} + +void test_app_include_legacy_timer(void) +{ +} diff --git a/components/driver/test_apps/legacy_timer_driver/sdkconfig.ci.release b/components/driver/test_apps/legacy_timer_driver/sdkconfig.ci.release new file mode 100644 index 0000000000..91d93f163e --- /dev/null +++ b/components/driver/test_apps/legacy_timer_driver/sdkconfig.ci.release @@ -0,0 +1,5 @@ +CONFIG_PM_ENABLE=y +CONFIG_FREERTOS_USE_TICKLESS_IDLE=y +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y diff --git a/components/driver/test_apps/legacy_timer_driver/sdkconfig.defaults b/components/driver/test_apps/legacy_timer_driver/sdkconfig.defaults new file mode 100644 index 0000000000..daa206f8c7 --- /dev/null +++ b/components/driver/test_apps/legacy_timer_driver/sdkconfig.defaults @@ -0,0 +1,3 @@ +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP_TASK_WDT=n +CONFIG_GPTIMER_SUPPRESS_DEPRECATE_WARN=y