diff --git a/components/esp_hw_support/test_apps/etm/main/CMakeLists.txt b/components/esp_hw_support/test_apps/etm/main/CMakeLists.txt index 59c600d60f..d4bd70187e 100644 --- a/components/esp_hw_support/test_apps/etm/main/CMakeLists.txt +++ b/components/esp_hw_support/test_apps/etm/main/CMakeLists.txt @@ -9,6 +9,10 @@ if(CONFIG_SOC_TIMER_SUPPORT_ETM) list(APPEND srcs "test_gptimer_etm.c") endif() +if(CONFIG_SOC_SYSTIMER_SUPPORT_ETM) + list(APPEND srcs "test_systimer_etm.c") +endif() + # 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} diff --git a/components/esp_hw_support/test_apps/etm/main/test_systimer_etm.c b/components/esp_hw_support/test_apps/etm/main/test_systimer_etm.c new file mode 100644 index 0000000000..69bf008e00 --- /dev/null +++ b/components/esp_hw_support/test_apps/etm/main/test_systimer_etm.c @@ -0,0 +1,132 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#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" +#include "esp_systick_etm.h" + +TEST_CASE("rtos_systick_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_TOG, + }; + 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_OUTPUT, + .pin_bit_mask = 1ULL << output_gpio, + }; + TEST_ESP_OK(gpio_config(&task_gpio_config)); + + printf("acquire systick etm event\r\n"); + esp_etm_event_handle_t systick_event = NULL; + TEST_ESP_OK(esp_systick_new_etm_alarm_event(0, &systick_event)); + + printf("connect event and task to the channel\r\n"); + TEST_ESP_OK(esp_etm_channel_connect(etm_channel_a, systick_event, gpio_task)); + + TEST_ESP_OK(esp_etm_channel_enable(etm_channel_a)); + + // should see a 500Hz square wave on the GPIO (if RTOS systick is set to 1000Hz) + vTaskDelay(pdMS_TO_TICKS(1000)); + + // 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(systick_event)); + TEST_ESP_OK(esp_etm_channel_disable(etm_channel_a)); + TEST_ESP_OK(esp_etm_del_channel(etm_channel_a)); +} + +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_TOG, + }; + 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\b"); + 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)); + + // should see a 1Hz square wave on the GPIO + vTaskDelay(pdMS_TO_TICKS(1200)); + + // check the final GPIO level + TEST_ASSERT_EQUAL(1, 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)); +} diff --git a/components/esp_rom/esp32c6/ld/esp32c6.rom.ld b/components/esp_rom/esp32c6/ld/esp32c6.rom.ld index 88adb38f83..5b5cf26a05 100644 --- a/components/esp_rom/esp32c6/ld/esp32c6.rom.ld +++ b/components/esp_rom/esp32c6/ld/esp32c6.rom.ld @@ -192,8 +192,10 @@ wdt_hal_is_enabled = 0x400003bc; ***************************************/ /* Functions */ -systimer_hal_init = 0x400003c0; -systimer_hal_deinit = 0x400003c4; +/* The following ROM functions are commented out because they're patched in the esp_rom_systimer.c */ +/* systimer_hal_init = 0x400003c0; */ +/* systimer_hal_deinit = 0x400003c4; */ + systimer_hal_set_tick_rate_ops = 0x400003c8; systimer_hal_get_counter_value = 0x400003cc; systimer_hal_get_time = 0x400003d0; diff --git a/components/esp_rom/patches/esp_rom_systimer.c b/components/esp_rom/patches/esp_rom_systimer.c index 87cad1e313..bf930139a1 100644 --- a/components/esp_rom/patches/esp_rom_systimer.c +++ b/components/esp_rom/patches/esp_rom_systimer.c @@ -64,4 +64,20 @@ void systimer_hal_counter_value_advance(systimer_hal_context_t *hal, uint32_t co } #endif // CONFIG_IDF_TARGET_ESP32C2 +#if CONFIG_IDF_TARGET_ESP32C6 +void systimer_hal_init(systimer_hal_context_t *hal) +{ + hal->dev = &SYSTIMER; + systimer_ll_enable_clock(hal->dev, true); + systimer_ll_enable_etm(&SYSTIMER, true); +} + +void systimer_hal_deinit(systimer_hal_context_t *hal) +{ + systimer_ll_enable_etm(&SYSTIMER, false); + systimer_ll_enable_clock(hal->dev, false); + hal->dev = NULL; +} +#endif // CONFIG_IDF_TARGET_ESP32C6 + #endif // CONFIG_HAL_SYSTIMER_USE_ROM_IMPL diff --git a/components/esp_system/CMakeLists.txt b/components/esp_system/CMakeLists.txt index d8f6072f5f..d362187683 100644 --- a/components/esp_system/CMakeLists.txt +++ b/components/esp_system/CMakeLists.txt @@ -49,6 +49,10 @@ else() list(APPEND srcs "eh_frame_parser.c") endif() + if(CONFIG_SOC_SYSTIMER_SUPPORT_ETM) + list(APPEND srcs "systick_etm.c") + endif() + idf_component_register(SRCS "${srcs}" INCLUDE_DIRS include PRIV_REQUIRES spi_flash esp_timer diff --git a/components/esp_system/include/esp_systick_etm.h b/components/esp_system/include/esp_systick_etm.h new file mode 100644 index 0000000000..e593fbc8e0 --- /dev/null +++ b/components/esp_system/include/esp_systick_etm.h @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_err.h" +#include "esp_etm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Get the ETM event handle of systick hardware's alarm/heartbeat event + * + * @note The created ETM event object can be deleted later by calling `esp_etm_del_event` + * + * @param[in] core_id CPU core ID + * @param[out] out_event Returned ETM event handle + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t esp_systick_new_etm_alarm_event(int core_id, esp_etm_event_handle_t *out_event); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_system/systick_etm.c b/components/esp_system/systick_etm.c new file mode 100644 index 0000000000..45327eae9e --- /dev/null +++ b/components/esp_system/systick_etm.c @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_log.h" +#include "esp_check.h" +#include "esp_heap_caps.h" +#include "esp_systick_etm.h" +#include "soc/soc_caps.h" +#include "soc/soc_etm_source.h" +#include "hal/systimer_ll.h" +#include "esp_private/etm_interface.h" + +#define ETM_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT + +static const char *TAG = "systick-etm"; + +static esp_err_t systick_etm_event_del(esp_etm_event_t *event) +{ + free(event); + return ESP_OK; +} + +esp_err_t esp_systick_new_etm_alarm_event(int core_id, esp_etm_event_handle_t *out_event) +{ + esp_etm_event_t *event = NULL; + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_FALSE(out_event && core_id < SOC_CPU_CORES_NUM, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); + event = heap_caps_calloc(1, sizeof(esp_etm_event_t), ETM_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(event, ESP_ERR_NO_MEM, err, TAG, "no memory for ETM event"); + + // fill the ETM event object + uint32_t event_id = SYSTIMER_EVT_CNT_CMP0 + SYSTIMER_LL_ALARM_OS_TICK_CORE0 + core_id; + event->event_id = event_id; + event->trig_periph = ETM_TRIG_PERIPH_SYSTIMER; + event->del = systick_etm_event_del; + *out_event = event; + return ESP_OK; + +err: + if (event) { + systick_etm_event_del(event); + } + return ret; +} diff --git a/components/esp_timer/CMakeLists.txt b/components/esp_timer/CMakeLists.txt index 02a93c13dc..f644d63f43 100644 --- a/components/esp_timer/CMakeLists.txt +++ b/components/esp_timer/CMakeLists.txt @@ -10,6 +10,10 @@ elseif(CONFIG_ESP_TIMER_IMPL_SYSTIMER) list(APPEND srcs "src/esp_timer_impl_systimer.c") endif() +if(CONFIG_SOC_SYSTIMER_SUPPORT_ETM) + list(APPEND srcs "src/esp_timer_etm.c") +endif() + idf_component_register(SRCS "${srcs}" INCLUDE_DIRS include PRIV_INCLUDE_DIRS private_include diff --git a/components/esp_timer/include/esp_timer.h b/components/esp_timer/include/esp_timer.h index 1fd8e049c4..7ee2c21b91 100644 --- a/components/esp_timer/include/esp_timer.h +++ b/components/esp_timer/include/esp_timer.h @@ -34,6 +34,7 @@ #include #include #include "esp_err.h" +#include "esp_etm.h" #include "sdkconfig.h" #ifdef __cplusplus @@ -311,6 +312,21 @@ void esp_timer_isr_dispatch_need_yield(void); */ bool esp_timer_is_active(esp_timer_handle_t timer); +/** + * @brief Get the ETM event handle of esp_timer underlying alarm event + * + * @note The created ETM event object can be deleted later by calling `esp_etm_del_event` + * + * @note The ETM event is generated by the underlying hardware -- systimer, + * therefore, if the esp_timer is not clocked by systimer, then no ETM event will be generated. + * + * @param[out] out_event Returned ETM event handle + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t esp_timer_new_etm_alarm_event(esp_etm_event_handle_t *out_event); + #ifdef __cplusplus } #endif diff --git a/components/esp_timer/src/esp_timer_etm.c b/components/esp_timer/src/esp_timer_etm.c new file mode 100644 index 0000000000..3803821582 --- /dev/null +++ b/components/esp_timer/src/esp_timer_etm.c @@ -0,0 +1,46 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_log.h" +#include "esp_check.h" +#include "esp_heap_caps.h" +#include "esp_timer.h" +#include "soc/soc_etm_source.h" +#include "hal/systimer_ll.h" +#include "esp_private/etm_interface.h" + +#define ETM_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT + +static const char *TAG = "esptimer-etm"; + +static esp_err_t esp_timer_etm_event_del(esp_etm_event_t *event) +{ + free(event); + return ESP_OK; +} + +esp_err_t esp_timer_new_etm_alarm_event(esp_etm_event_handle_t *out_event) +{ + esp_etm_event_t *event = NULL; + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_FALSE(out_event, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); + event = heap_caps_calloc(1, sizeof(esp_etm_event_t), ETM_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(event, ESP_ERR_NO_MEM, err, TAG, "no memory for ETM event"); + + // fill the ETM event object + uint32_t event_id = SYSTIMER_EVT_CNT_CMP0 + SYSTIMER_LL_ALARM_CLOCK; + event->event_id = event_id; + event->trig_periph = ETM_TRIG_PERIPH_SYSTIMER; + event->del = esp_timer_etm_event_del; + *out_event = event; + return ESP_OK; + +err: + if (event) { + esp_timer_etm_event_del(event); + } + return ret; +} diff --git a/components/hal/esp32c6/include/hal/systimer_ll.h b/components/hal/esp32c6/include/hal/systimer_ll.h index 632a8c1191..618787be5d 100644 --- a/components/hal/esp32c6/include/hal/systimer_ll.h +++ b/components/hal/esp32c6/include/hal/systimer_ll.h @@ -42,6 +42,13 @@ static inline soc_periph_systimer_clk_src_t systimer_ll_get_clock_source(void) return (PCR.systimer_func_clk_conf.systimer_func_clk_sel == 1) ? SYSTIMER_CLK_SRC_RC_FAST : SYSTIMER_CLK_SRC_XTAL; } +/********************** ETM *****************************/ + +__attribute__((always_inline)) static inline void systimer_ll_enable_etm(systimer_dev_t *dev, bool en) +{ + dev->conf.etm_en = en; +} + /******************* Counter *************************/ __attribute__((always_inline)) static inline void systimer_ll_enable_counter(systimer_dev_t *dev, uint32_t counter_id, bool en) diff --git a/components/hal/systimer_hal.c b/components/hal/systimer_hal.c index b3b071d413..3ef3337f26 100644 --- a/components/hal/systimer_hal.c +++ b/components/hal/systimer_hal.c @@ -16,10 +16,16 @@ void systimer_hal_init(systimer_hal_context_t *hal) { hal->dev = &SYSTIMER; systimer_ll_enable_clock(hal->dev, true); +#if SOC_SYSTIMER_SUPPORT_ETM + systimer_ll_enable_etm(&SYSTIMER, true); +#endif } void systimer_hal_deinit(systimer_hal_context_t *hal) { +#if SOC_SYSTIMER_SUPPORT_ETM + systimer_ll_enable_etm(&SYSTIMER, false); +#endif systimer_ll_enable_clock(hal->dev, false); hal->dev = NULL; } diff --git a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in index 2fc9611a6c..483bc56d12 100644 --- a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in @@ -699,6 +699,10 @@ config SOC_SYSTIMER_ALARM_MISS_COMPENSATE bool default y +config SOC_SYSTIMER_SUPPORT_ETM + bool + default y + config SOC_TIMER_GROUPS int default 2 diff --git a/components/soc/esp32c6/include/soc/soc_caps.h b/components/soc/esp32c6/include/soc/soc_caps.h index 1ea341d408..af3829b083 100644 --- a/components/soc/esp32c6/include/soc/soc_caps.h +++ b/components/soc/esp32c6/include/soc/soc_caps.h @@ -349,6 +349,7 @@ #define SOC_SYSTIMER_SUPPORT_RC_FAST 1 // Systimer can use RC_FAST clock source #define SOC_SYSTIMER_INT_LEVEL 1 // Systimer peripheral uses level interrupt #define SOC_SYSTIMER_ALARM_MISS_COMPENSATE 1 // Systimer peripheral can generate interrupt immediately if t(target) > t(current) +#define SOC_SYSTIMER_SUPPORT_ETM 1 // Systimer comparator can generate ETM event /*--------------------------- TIMER GROUP CAPS ---------------------------------------*/ #define SOC_TIMER_GROUPS (2) diff --git a/tools/mocks/esp_hw_support/include/esp_etm.h b/tools/mocks/esp_hw_support/include/esp_etm.h new file mode 100644 index 0000000000..38963482db --- /dev/null +++ b/tools/mocks/esp_hw_support/include/esp_etm.h @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * NOTE: this is not the original header file from the esp_hw_support component. + * It is a stripped-down copy to support mocking. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ETM channel handle + */ +typedef struct esp_etm_channel_t *esp_etm_channel_handle_t; + +/** + * @brief ETM event handle + */ +typedef struct esp_etm_event_t *esp_etm_event_handle_t; + +/** + * @brief ETM task handle + */ +typedef struct esp_etm_task_t *esp_etm_task_handle_t; + +#ifdef __cplusplus +} +#endif diff --git a/tools/mocks/esp_timer/CMakeLists.txt b/tools/mocks/esp_timer/CMakeLists.txt index c9dac893d2..39fe5a78f4 100644 --- a/tools/mocks/esp_timer/CMakeLists.txt +++ b/tools/mocks/esp_timer/CMakeLists.txt @@ -4,6 +4,10 @@ message(STATUS "building ESP TIMER MOCKS") idf_component_get_property(original_esp_timer_dir esp_timer COMPONENT_OVERRIDEN_DIR) -idf_component_mock(INCLUDE_DIRS "${original_esp_timer_dir}/include" +set(include_dirs + "${original_esp_timer_dir}/include" + "${CMAKE_CURRENT_SOURCE_DIR}/../esp_hw_support/include") + +idf_component_mock(INCLUDE_DIRS ${include_dirs} REQUIRES esp_common MOCK_HEADER_FILES ${original_esp_timer_dir}/include/esp_timer.h)