diff --git a/components/driver/CMakeLists.txt b/components/driver/CMakeLists.txt index d79742aee0..07282a70fb 100644 --- a/components/driver/CMakeLists.txt +++ b/components/driver/CMakeLists.txt @@ -3,7 +3,7 @@ idf_build_get_property(target IDF_TARGET) set(srcs "gpio/gpio.c" "gpio/rtc_io.c" - "gptimer.c" + "gptimer/gptimer.c" "sdspi_crc.c" "sdspi_host.c" "sdspi_transaction.c" @@ -25,6 +25,10 @@ if(CONFIG_SOC_LEDC_SUPPORTED) list(APPEND srcs "ledc.c") endif() +if(CONFIG_SOC_TIMER_SUPPORT_ETM) + list(APPEND srcs "gptimer/gptimer_etm.c") +endif() + if(CONFIG_SOC_I2C_SUPPORTED) list(APPEND srcs "i2c.c") endif() diff --git a/components/driver/gptimer.c b/components/driver/gptimer/gptimer_core.c similarity index 91% rename from components/driver/gptimer.c rename to components/driver/gptimer/gptimer_core.c index a278ee3888..9477d0e4f4 100644 --- a/components/driver/gptimer.c +++ b/components/driver/gptimer/gptimer_core.c @@ -15,8 +15,6 @@ #include "freertos/FreeRTOS.h" #include "esp_attr.h" #include "esp_err.h" -#include "esp_heap_caps.h" -#include "esp_intr_alloc.h" #include "esp_log.h" #include "esp_check.h" #include "esp_pm.h" @@ -28,70 +26,15 @@ #include "esp_memory_utils.h" #include "esp_private/periph_ctrl.h" #include "esp_private/esp_clk.h" - -// If ISR handler is allowed to run whilst cache is disabled, -// Make sure all the code and related variables used by the handler are in the SRAM -#if CONFIG_GPTIMER_ISR_IRAM_SAFE || CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM -#define GPTIMER_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) -#else -#define GPTIMER_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT -#endif - -#if CONFIG_GPTIMER_ISR_IRAM_SAFE -#define GPTIMER_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_INTRDISABLED) -#else -#define GPTIMER_INTR_ALLOC_FLAGS ESP_INTR_FLAG_INTRDISABLED -#endif - -#define GPTIMER_PM_LOCK_NAME_LEN_MAX 16 +#include "gptimer_priv.h" static const char *TAG = "gptimer"; -typedef struct gptimer_platform_t gptimer_platform_t; -typedef struct gptimer_group_t gptimer_group_t; -typedef struct gptimer_t gptimer_t; - -struct gptimer_platform_t { +typedef struct gptimer_platform_t { _lock_t mutex; // platform level mutex lock gptimer_group_t *groups[SOC_TIMER_GROUPS]; // timer group pool int group_ref_counts[SOC_TIMER_GROUPS]; // reference count used to protect group install/uninstall -}; - -struct gptimer_group_t { - int group_id; - portMUX_TYPE spinlock; // to protect per-group register level concurrent access - gptimer_t *timers[SOC_TIMER_GROUP_TIMERS_PER_GROUP]; -}; - -typedef enum { - GPTIMER_FSM_INIT, - GPTIMER_FSM_ENABLE, -} gptimer_fsm_t; - -struct gptimer_t { - gptimer_group_t *group; - int timer_id; - uint32_t resolution_hz; - uint64_t reload_count; - uint64_t alarm_count; - gptimer_count_direction_t direction; - timer_hal_context_t hal; - gptimer_fsm_t fsm; - intr_handle_t intr; - portMUX_TYPE spinlock; // to protect per-timer resources concurrent accessed by task and ISR handler - gptimer_alarm_cb_t on_alarm; - void *user_ctx; - gptimer_clock_source_t clk_src; - esp_pm_lock_handle_t pm_lock; // power management lock -#if CONFIG_PM_ENABLE - char pm_lock_name[GPTIMER_PM_LOCK_NAME_LEN_MAX]; // pm lock name -#endif - struct { - uint32_t intr_shared: 1; - uint32_t auto_reload_on_alarm: 1; - uint32_t alarm_en: 1; - } flags; -}; +} gptimer_platform_t; // gptimer driver platform, it's always a singleton static gptimer_platform_t s_platform; @@ -239,6 +182,16 @@ esp_err_t gptimer_get_raw_count(gptimer_handle_t timer, unsigned long long *valu return ESP_OK; } +esp_err_t gptimer_get_captured_count(gptimer_handle_t timer, uint64_t *value) +{ + ESP_RETURN_ON_FALSE_ISR(timer && value, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + portENTER_CRITICAL_SAFE(&timer->spinlock); + *value = timer_ll_get_counter_value(timer->hal.dev, timer->timer_id); + portEXIT_CRITICAL_SAFE(&timer->spinlock); + return ESP_OK; +} + esp_err_t gptimer_register_event_callbacks(gptimer_handle_t timer, const gptimer_event_callbacks_t *cbs, void *user_data) { gptimer_group_t *group = NULL; diff --git a/components/driver/gptimer/gptimer_etm.c b/components/driver/gptimer/gptimer_etm.c new file mode 100644 index 0000000000..1147f91f43 --- /dev/null +++ b/components/driver/gptimer/gptimer_etm.c @@ -0,0 +1,92 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "esp_err.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_heap_caps.h" +#include "driver/gptimer.h" +#include "gptimer_priv.h" +#include "hal/timer_ll.h" +#include "esp_private/etm_interface.h" + +#define ETM_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT + +static const char *TAG = "gptimer-etm"; + +static esp_err_t gptimer_del_etm_event(esp_etm_event_t *event) +{ + free(event); + return ESP_OK; +} + +static esp_err_t gptimer_del_etm_task(esp_etm_task_t *task) +{ + free(task); + return ESP_OK; +} + +esp_err_t gptimer_new_etm_event(gptimer_handle_t timer, gptimer_etm_event_type_t event_type, esp_etm_event_handle_t *out_event) +{ + esp_etm_event_t *event = NULL; + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_FALSE(timer && out_event, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); + ESP_GOTO_ON_FALSE(event_type < GPTIMER_ETM_EVENT_MAX, ESP_ERR_INVALID_ARG, err, TAG, "invalid event type"); + 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"); + + gptimer_group_t *group = timer->group; + int group_id = group->group_id; + int timer_id = timer->timer_id; + uint32_t event_id = TIMER_LL_ETM_EVENT_TABLE(group_id, timer_id, event_type); + ESP_GOTO_ON_FALSE(event_id != 0, ESP_ERR_NOT_SUPPORTED, err, TAG, "not supported event type"); + + // fill the ETM event object + event->event_id = event_id; + event->trig_periph = ETM_TRIG_PERIPH_GPTIMER; + event->del = gptimer_del_etm_event; + *out_event = event; + return ESP_OK; + +err: + if (event) { + gptimer_del_etm_event(event); + } + return ret; +} + +esp_err_t gptimer_new_etm_task(gptimer_handle_t timer, gptimer_etm_task_type_t task_type, esp_etm_task_handle_t *out_task) +{ + esp_etm_task_t *task = NULL; + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_FALSE(timer && out_task, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); + ESP_GOTO_ON_FALSE(task_type < GPTIMER_ETM_TASK_MAX, ESP_ERR_INVALID_ARG, err, TAG, "invalid task type"); + task = heap_caps_calloc(1, sizeof(esp_etm_task_t), ETM_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(task, ESP_ERR_NO_MEM, err, TAG, "no memory for ETM task"); + + gptimer_group_t *group = timer->group; + int group_id = group->group_id; + int timer_id = timer->timer_id; + uint32_t task_id = TIMER_LL_ETM_TASK_TABLE(group_id, timer_id, task_type); + ESP_GOTO_ON_FALSE(task_id != 0, ESP_ERR_NOT_SUPPORTED, err, TAG, "not supported task type"); + + // fill the ETM task object + task->task_id = task_id; + task->trig_periph = ETM_TRIG_PERIPH_GPTIMER; + task->del = gptimer_del_etm_task; + *out_task = task; + return ESP_OK; + +err: + if (task) { + gptimer_del_etm_task(task); + } + return ret; +} diff --git a/components/driver/gptimer/gptimer_priv.h b/components/driver/gptimer/gptimer_priv.h new file mode 100644 index 0000000000..4cabb2032f --- /dev/null +++ b/components/driver/gptimer/gptimer_priv.h @@ -0,0 +1,79 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "esp_err.h" +#include "esp_intr_alloc.h" +#include "esp_heap_caps.h" +#include "esp_pm.h" +#include "soc/soc_caps.h" +#include "hal/timer_hal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// If ISR handler is allowed to run whilst cache is disabled, +// Make sure all the code and related variables used by the handler are in the SRAM +#if CONFIG_GPTIMER_ISR_IRAM_SAFE || CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM +#define GPTIMER_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) +#else +#define GPTIMER_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT +#endif + +#if CONFIG_GPTIMER_ISR_IRAM_SAFE +#define GPTIMER_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_INTRDISABLED) +#else +#define GPTIMER_INTR_ALLOC_FLAGS ESP_INTR_FLAG_INTRDISABLED +#endif + +#define GPTIMER_PM_LOCK_NAME_LEN_MAX 16 + +typedef struct gptimer_t gptimer_t; + +typedef struct gptimer_group_t { + int group_id; + portMUX_TYPE spinlock; // to protect per-group register level concurrent access + gptimer_t *timers[SOC_TIMER_GROUP_TIMERS_PER_GROUP]; +} gptimer_group_t; + +typedef enum { + GPTIMER_FSM_INIT, + GPTIMER_FSM_ENABLE, +} gptimer_fsm_t; + +struct gptimer_t { + gptimer_group_t *group; + int timer_id; + uint32_t resolution_hz; + uint64_t reload_count; + uint64_t alarm_count; + gptimer_count_direction_t direction; + timer_hal_context_t hal; + gptimer_fsm_t fsm; + intr_handle_t intr; + portMUX_TYPE spinlock; // to protect per-timer resources concurrent accessed by task and ISR handler + gptimer_alarm_cb_t on_alarm; + void *user_ctx; + gptimer_clock_source_t clk_src; + esp_pm_lock_handle_t pm_lock; // power management lock +#if CONFIG_PM_ENABLE + char pm_lock_name[GPTIMER_PM_LOCK_NAME_LEN_MAX]; // pm lock name +#endif + struct { + uint32_t intr_shared: 1; + uint32_t auto_reload_on_alarm: 1; + uint32_t alarm_en: 1; + } flags; +}; + +#ifdef __cplusplus +} +#endif diff --git a/components/driver/include/driver/gptimer.h b/components/driver/include/driver/gptimer.h index e68273443f..b24f38ab77 100644 --- a/components/driver/include/driver/gptimer.h +++ b/components/driver/include/driver/gptimer.h @@ -9,44 +9,13 @@ #include #include #include "esp_err.h" -#include "hal/timer_types.h" +#include "driver/gptimer_types.h" +#include "driver/gptimer_etm.h" #ifdef __cplusplus extern "C" { #endif -/** - * @brief Type of General Purpose Timer handle - */ -typedef struct gptimer_t *gptimer_handle_t; - -/** - * @brief GPTimer alarm event data - */ -typedef struct { - uint64_t count_value; /*!< Current count value */ - uint64_t alarm_value; /*!< Current alarm value */ -} gptimer_alarm_event_data_t; - -/** - * @brief Timer alarm callback prototype - * - * @param[in] timer Timer handle created by `gptimer_new_timer` - * @param[in] edata Alarm event data, fed by driver - * @param[in] user_ctx User data, passed from `gptimer_register_event_callbacks` - * @return Whether a high priority task has been waken up by this function - */ -typedef bool (*gptimer_alarm_cb_t) (gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx); - -/** - * @brief Group of supported GPTimer callbacks - * @note The callbacks are all running under ISR environment - * @note When CONFIG_GPTIMER_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM. - */ -typedef struct { - gptimer_alarm_cb_t on_alarm; /*!< Timer alarm callback */ -} gptimer_event_callbacks_t; - /** * @brief General Purpose Timer configuration */ @@ -60,17 +29,6 @@ typedef struct { } flags; /*!< GPTimer config flags*/ } gptimer_config_t; -/** - * @brief General Purpose Timer alarm configuration - */ -typedef struct { - uint64_t alarm_count; /*!< Alarm target count value */ - uint64_t reload_count; /*!< Alarm reload count value, effect only when `auto_reload_on_alarm` is set to true */ - struct { - uint32_t auto_reload_on_alarm: 1; /*!< Reload the count value by hardware, immediately at the alarm event */ - } flags; /*!< Alarm config flags*/ -} gptimer_alarm_config_t; - /** * @brief Create a new General Purpose Timer, and return the handle * @@ -135,6 +93,31 @@ esp_err_t gptimer_set_raw_count(gptimer_handle_t timer, uint64_t value); */ esp_err_t gptimer_get_raw_count(gptimer_handle_t timer, uint64_t *value); +/** + * @brief Get GPTimer captured count value + * + * @note The capture action can be issued either by external event or by software (see also `gptimer_get_raw_count`). + * @note This function is allowed to run within ISR context + * @note This function is allowed to be executed when Cache is disabled, by enabling `CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM` + * + * @param[in] timer Timer handle created by `gptimer_new_timer` + * @param[out] value Returned captured count value + * @return + * - ESP_OK: Get GPTimer captured count value successfully + * - ESP_ERR_INVALID_ARG: Get GPTimer captured count value failed because of invalid argument + * - ESP_FAIL: Get GPTimer captured count value failed because of other error + */ +esp_err_t gptimer_get_captured_count(gptimer_handle_t timer, uint64_t *value); + +/** + * @brief Group of supported GPTimer callbacks + * @note The callbacks are all running under ISR environment + * @note When CONFIG_GPTIMER_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM. + */ +typedef struct { + gptimer_alarm_cb_t on_alarm; /*!< Timer alarm callback */ +} gptimer_event_callbacks_t; + /** * @brief Set callbacks for GPTimer * @@ -153,6 +136,17 @@ esp_err_t gptimer_get_raw_count(gptimer_handle_t timer, uint64_t *value); */ esp_err_t gptimer_register_event_callbacks(gptimer_handle_t timer, const gptimer_event_callbacks_t *cbs, void *user_data); +/** + * @brief General Purpose Timer alarm configuration + */ +typedef struct { + uint64_t alarm_count; /*!< Alarm target count value */ + uint64_t reload_count; /*!< Alarm reload count value, effect only when `auto_reload_on_alarm` is set to true */ + struct { + uint32_t auto_reload_on_alarm: 1; /*!< Reload the count value by hardware, immediately at the alarm event */ + } flags; /*!< Alarm config flags*/ +} gptimer_alarm_config_t; + /** * @brief Set alarm event actions for GPTimer. * diff --git a/components/driver/include/driver/gptimer_etm.h b/components/driver/include/driver/gptimer_etm.h new file mode 100644 index 0000000000..506d904f07 --- /dev/null +++ b/components/driver/include/driver/gptimer_etm.h @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_err.h" +#include "esp_etm.h" +#include "driver/gptimer_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Get the ETM event for GPTimer + * + * @note The created ETM event object can be deleted later by calling `esp_etm_del_event` + * + * @param[in] timer Timer handle created by `gptimer_new_timer` + * @param[in] event_type GPTimer ETM event type + * @param[out] out_event Returned ETM event handle + * @return + * - ESP_OK: Get ETM event successfully + * - ESP_ERR_INVALID_ARG: Get ETM event failed because of invalid argument + * - ESP_FAIL: Get ETM event failed because of other error + */ +esp_err_t gptimer_new_etm_event(gptimer_handle_t timer, gptimer_etm_event_type_t event_type, esp_etm_event_handle_t *out_event); + +/** + * @brief Get the ETM task for GPTimer + * + * @note The created ETM task object can be deleted later by calling `esp_etm_del_task` + * + * @param[in] timer Timer handle created by `gptimer_new_timer` + * @param[in] task_type GPTimer ETM task type + * @param[out] out_task Returned ETM task handle + * @return + * - ESP_OK: Get ETM task successfully + * - ESP_ERR_INVALID_ARG: Get ETM task failed because of invalid argument + * - ESP_FAIL: Get ETM task failed because of other error + */ +esp_err_t gptimer_new_etm_task(gptimer_handle_t timer, gptimer_etm_task_type_t task_type, esp_etm_task_handle_t *out_task); + +#ifdef __cplusplus +} +#endif diff --git a/components/driver/include/driver/gptimer_types.h b/components/driver/include/driver/gptimer_types.h new file mode 100644 index 0000000000..4339e96275 --- /dev/null +++ b/components/driver/include/driver/gptimer_types.h @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "hal/timer_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Type of General Purpose Timer handle + */ +typedef struct gptimer_t *gptimer_handle_t; + +/** + * @brief GPTimer alarm event data + */ +typedef struct { + uint64_t count_value; /*!< Current count value */ + uint64_t alarm_value; /*!< Current alarm value */ +} gptimer_alarm_event_data_t; + +/** + * @brief Timer alarm callback prototype + * + * @param[in] timer Timer handle created by `gptimer_new_timer` + * @param[in] edata Alarm event data, fed by driver + * @param[in] user_ctx User data, passed from `gptimer_register_event_callbacks` + * @return Whether a high priority task has been waken up by this function + */ +typedef bool (*gptimer_alarm_cb_t) (gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx); + +#ifdef __cplusplus +} +#endif diff --git a/components/driver/linker.lf b/components/driver/linker.lf index 811a40948e..8492030bfe 100644 --- a/components/driver/linker.lf +++ b/components/driver/linker.lf @@ -4,6 +4,7 @@ entries: if GPTIMER_CTRL_FUNC_IN_IRAM = y: gptimer: gptimer_set_raw_count (noflash) gptimer: gptimer_get_raw_count (noflash) + gptimer: gptimer_get_captured_count (noflash) gptimer: gptimer_set_alarm_action (noflash) gptimer: gptimer_start (noflash) gptimer: gptimer_stop (noflash) 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 a502495e43..59c600d60f 100644 --- a/components/esp_hw_support/test_apps/etm/main/CMakeLists.txt +++ b/components/esp_hw_support/test_apps/etm/main/CMakeLists.txt @@ -5,6 +5,9 @@ if(CONFIG_SOC_GPIO_SUPPORT_ETM) list(APPEND srcs "test_gpio_etm.c") endif() +if(CONFIG_SOC_TIMER_SUPPORT_ETM) + list(APPEND srcs "test_gptimer_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 diff --git a/components/esp_hw_support/test_apps/etm/main/test_gptimer_etm.c b/components/esp_hw_support/test_apps/etm/main/test_gptimer_etm.c new file mode 100644 index 0000000000..c9caa57224 --- /dev/null +++ b/components/esp_hw_support/test_apps/etm/main/test_gptimer_etm.c @@ -0,0 +1,440 @@ +/* + * 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 "esp_attr.h" +#include "driver/gptimer.h" +#include "driver/gpio_etm.h" +#include "driver/gpio.h" + +static bool on_gptimer_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx) +{ + return false; +} + +TEST_CASE("gptimer_etm_alarm_event_with_interrupt_enabled", "[etm]") +{ + const uint32_t output_gpio = 1; + // GPTimer alarm ---> ETM channel A ---> GPIO toggle + printf("allocate etm channel\r\n"); + esp_etm_channel_config_t etm_config = {}; + esp_etm_channel_handle_t etm_channel_a; + 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)); + // set gpio number for the gpio etm primitives + 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("create a gptimer\r\n"); + gptimer_handle_t gptimer = NULL; + gptimer_config_t timer_config = { + .clk_src = GPTIMER_CLK_SRC_DEFAULT, + .direction = GPTIMER_COUNT_UP, + .resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us + }; + TEST_ESP_OK(gptimer_new_timer(&timer_config, &gptimer)); + + printf("get gptimer etm event handle\r\n"); + esp_etm_event_handle_t gptimer_event = NULL; + TEST_ESP_OK(gptimer_new_etm_event(gptimer, GPTIMER_ETM_EVENT_ALARM_MATCH, &gptimer_event)); + + printf("connect event and task to the channel\r\n"); + TEST_ESP_OK(esp_etm_channel_connect(etm_channel_a, gptimer_event, gpio_task)); + + printf("enable etm channel\r\n"); + TEST_ESP_OK(esp_etm_channel_enable(etm_channel_a)); + + printf("set timer alarm action\r\n"); + gptimer_alarm_config_t alarm_config = { + .reload_count = 0, + .alarm_count = 100, // 100us per alarm event + .flags.auto_reload_on_alarm = true, + }; + TEST_ESP_OK(gptimer_set_alarm_action(gptimer, &alarm_config)); + + printf("register alarm callback\r\n"); + gptimer_event_callbacks_t cbs = { + .on_alarm = on_gptimer_alarm_cb, + }; + TEST_ESP_OK(gptimer_register_event_callbacks(gptimer, &cbs, NULL)); + + printf("enable and start timer\r\n"); + TEST_ESP_OK(gptimer_enable(gptimer)); + TEST_ESP_OK(gptimer_start(gptimer)); + + // delay sometime for us to view the waveform, should see a 5KHz square waveform + vTaskDelay(pdMS_TO_TICKS(1000)); + + // delete gptimer + TEST_ESP_OK(gptimer_stop(gptimer)); + TEST_ESP_OK(gptimer_disable(gptimer)); + TEST_ESP_OK(gptimer_del_timer(gptimer)); + + // 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(gptimer_event)); + TEST_ESP_OK(esp_etm_channel_disable(etm_channel_a)); + TEST_ESP_OK(esp_etm_del_channel(etm_channel_a)); +} + +TEST_CASE("gptimer_etm_alarm_event_without_interrupt", "[etm]") +{ + const uint32_t output_gpio = 1; + // GPTimer alarm ---> ETM channel A ---> GPIO toggle + // GPTimer alarm ---> ETM channel B ---> GPTimer alarm reenable + printf("allocate etm channel\r\n"); + esp_etm_channel_config_t etm_config = {}; + esp_etm_channel_handle_t etm_channel_a, etm_channel_b; + TEST_ESP_OK(esp_etm_new_channel(&etm_config, &etm_channel_a)); + TEST_ESP_OK(esp_etm_new_channel(&etm_config, &etm_channel_b)); + + 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)); + // set gpio number for the gpio etm primitives + 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("create a gptimer\r\n"); + gptimer_handle_t gptimer = NULL; + gptimer_config_t timer_config = { + .clk_src = GPTIMER_CLK_SRC_DEFAULT, + .direction = GPTIMER_COUNT_UP, + .resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us + }; + TEST_ESP_OK(gptimer_new_timer(&timer_config, &gptimer)); + + printf("get gptimer etm event and task handle\r\n"); + esp_etm_event_handle_t gptimer_event = NULL; + TEST_ESP_OK(gptimer_new_etm_event(gptimer, GPTIMER_ETM_EVENT_ALARM_MATCH, &gptimer_event)); + esp_etm_task_handle_t gptimer_task = NULL; + TEST_ESP_OK(gptimer_new_etm_task(gptimer, GPTIMER_ETM_TASK_EN_ALARM, &gptimer_task)); + + printf("connect event and task to the channel\r\n"); + TEST_ESP_OK(esp_etm_channel_connect(etm_channel_a, gptimer_event, gpio_task)); + TEST_ESP_OK(esp_etm_channel_connect(etm_channel_b, gptimer_event, gptimer_task)); + + printf("enable etm channel\r\n"); + TEST_ESP_OK(esp_etm_channel_enable(etm_channel_a)); + TEST_ESP_OK(esp_etm_channel_enable(etm_channel_b)); + + printf("set timer alarm action\r\n"); + gptimer_alarm_config_t alarm_config = { + .reload_count = 0, + .alarm_count = 100, // 100us per alarm event + .flags.auto_reload_on_alarm = true, + }; + TEST_ESP_OK(gptimer_set_alarm_action(gptimer, &alarm_config)); + + printf("enable and start timer\r\n"); + TEST_ESP_OK(gptimer_enable(gptimer)); + TEST_ESP_OK(gptimer_start(gptimer)); + + // delay sometime for us to view the waveform, should see a 5KHz square waveform + vTaskDelay(pdMS_TO_TICKS(1000)); + + // delete gptimer + TEST_ESP_OK(gptimer_stop(gptimer)); + TEST_ESP_OK(gptimer_disable(gptimer)); + TEST_ESP_OK(gptimer_del_timer(gptimer)); + + // 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_task(gptimer_task)); + TEST_ESP_OK(esp_etm_del_event(gptimer_event)); + TEST_ESP_OK(esp_etm_channel_disable(etm_channel_a)); + TEST_ESP_OK(esp_etm_channel_disable(etm_channel_b)); + TEST_ESP_OK(esp_etm_del_channel(etm_channel_a)); + TEST_ESP_OK(esp_etm_del_channel(etm_channel_b)); +} + +TEST_CASE("gptimer_auto_reload_by_etm", "[etm]") +{ + const uint32_t output_gpio = 1; + // GPTimer alarm ---> ETM channel A ---> GPIO toggle + // GPTimer alarm ---> ETM channel B ---> GPTimer alarm reenable + // GPTimer alarm ---> ETM channel C ---> GPTimer reload + printf("allocate etm channel\r\n"); + esp_etm_channel_config_t etm_config = {}; + esp_etm_channel_handle_t etm_channel_a, etm_channel_b, etm_channel_c; + TEST_ESP_OK(esp_etm_new_channel(&etm_config, &etm_channel_a)); + TEST_ESP_OK(esp_etm_new_channel(&etm_config, &etm_channel_b)); + TEST_ESP_OK(esp_etm_new_channel(&etm_config, &etm_channel_c)); + + 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)); + // set gpio number for the gpio etm primitives + 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("create a gptimer\r\n"); + gptimer_handle_t gptimer = NULL; + gptimer_config_t timer_config = { + .clk_src = GPTIMER_CLK_SRC_DEFAULT, + .direction = GPTIMER_COUNT_UP, + .resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us + }; + TEST_ESP_OK(gptimer_new_timer(&timer_config, &gptimer)); + + printf("get gptimer etm event and task handle\r\n"); + esp_etm_event_handle_t gptimer_event_alarm = NULL; + TEST_ESP_OK(gptimer_new_etm_event(gptimer, GPTIMER_ETM_EVENT_ALARM_MATCH, &gptimer_event_alarm)); + esp_etm_task_handle_t gptimer_task_en_alarm = NULL; + TEST_ESP_OK(gptimer_new_etm_task(gptimer, GPTIMER_ETM_TASK_EN_ALARM, &gptimer_task_en_alarm)); + esp_etm_task_handle_t gptimer_task_reload = NULL; + TEST_ESP_OK(gptimer_new_etm_task(gptimer, GPTIMER_ETM_TASK_RELOAD, &gptimer_task_reload)); + + printf("connect event and task to the channel\r\n"); + TEST_ESP_OK(esp_etm_channel_connect(etm_channel_a, gptimer_event_alarm, gpio_task)); + TEST_ESP_OK(esp_etm_channel_connect(etm_channel_b, gptimer_event_alarm, gptimer_task_en_alarm)); + TEST_ESP_OK(esp_etm_channel_connect(etm_channel_c, gptimer_event_alarm, gptimer_task_reload)); + + printf("enable etm channel\r\n"); + TEST_ESP_OK(esp_etm_channel_enable(etm_channel_a)); + TEST_ESP_OK(esp_etm_channel_enable(etm_channel_b)); + TEST_ESP_OK(esp_etm_channel_enable(etm_channel_c)); + + printf("dump the etm channel usage\r\n"); + TEST_ESP_OK(esp_etm_dump(stdout)); + + printf("set timer alarm action\r\n"); + gptimer_alarm_config_t alarm_config = { + .reload_count = 0, + .alarm_count = 100, // 100us per alarm event + .flags.auto_reload_on_alarm = false, // reload will be done by ETM channel C + }; + TEST_ESP_OK(gptimer_set_alarm_action(gptimer, &alarm_config)); + + printf("enable and start timer\r\n"); + TEST_ESP_OK(gptimer_enable(gptimer)); + TEST_ESP_OK(gptimer_start(gptimer)); + + // delay sometime for us to view the waveform, should see a 5KHz square waveform + vTaskDelay(pdMS_TO_TICKS(1000)); + + // delete gptimer + TEST_ESP_OK(gptimer_stop(gptimer)); + TEST_ESP_OK(gptimer_disable(gptimer)); + TEST_ESP_OK(gptimer_del_timer(gptimer)); + + // 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_task(gptimer_task_en_alarm)); + TEST_ESP_OK(esp_etm_del_task(gptimer_task_reload)); + TEST_ESP_OK(esp_etm_del_event(gptimer_event_alarm)); + TEST_ESP_OK(esp_etm_channel_disable(etm_channel_a)); + TEST_ESP_OK(esp_etm_channel_disable(etm_channel_b)); + TEST_ESP_OK(esp_etm_channel_disable(etm_channel_c)); + TEST_ESP_OK(esp_etm_del_channel(etm_channel_a)); + TEST_ESP_OK(esp_etm_del_channel(etm_channel_b)); + TEST_ESP_OK(esp_etm_del_channel(etm_channel_c)); +} + +TEST_CASE("gptimer_etm_task_capture", "[etm]") +{ + const uint32_t input_gpio = 0; + // GPIO Posedge ---> ETM channel A ---> GPTimer capture + printf("allocate etm channel\r\n"); + esp_etm_channel_config_t etm_config = {}; + esp_etm_channel_handle_t etm_channel_a; + TEST_ESP_OK(esp_etm_new_channel(&etm_config, &etm_channel_a)); + + printf("allocate GPIO etm event\r\n"); + esp_etm_event_handle_t gpio_event = NULL; + gpio_etm_event_config_t gpio_event_config = { + .edge = GPIO_ETM_EVENT_EDGE_POS, + }; + TEST_ESP_OK(gpio_new_etm_event(&gpio_event_config, &gpio_event)); + // set gpio number for the gpio etm primitives + TEST_ESP_OK(gpio_etm_event_bind_gpio(gpio_event, input_gpio)); + + printf("initialize gpio\r\n"); + gpio_config_t task_gpio_config = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_INPUT_OUTPUT, // we want to simulate the edge signal by software, so it should be input and output + .pin_bit_mask = 1ULL << input_gpio, + }; + TEST_ESP_OK(gpio_config(&task_gpio_config)); + TEST_ESP_OK(gpio_set_level(input_gpio, 0)); + + printf("create a gptimer\r\n"); + gptimer_handle_t gptimer = NULL; + gptimer_config_t timer_config = { + .clk_src = GPTIMER_CLK_SRC_DEFAULT, + .direction = GPTIMER_COUNT_UP, + .resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us + }; + TEST_ESP_OK(gptimer_new_timer(&timer_config, &gptimer)); + + printf("get gptimer etm task handle\r\n"); + esp_etm_task_handle_t gptimer_task = NULL; + TEST_ESP_OK(gptimer_new_etm_task(gptimer, GPTIMER_ETM_TASK_CAPTURE, &gptimer_task)); + + printf("connect event and task to the channel\r\n"); + TEST_ESP_OK(esp_etm_channel_connect(etm_channel_a, gpio_event, gptimer_task)); + + printf("enable etm channel\r\n"); + TEST_ESP_OK(esp_etm_channel_enable(etm_channel_a)); + + printf("enable and start gptimer\r\n"); + TEST_ESP_OK(gptimer_enable(gptimer)); + TEST_ESP_OK(gptimer_start(gptimer)); + + vTaskDelay(pdMS_TO_TICKS(500)); + + // simulate the edge signal by software + TEST_ESP_OK(gpio_set_level(input_gpio, 1)); + TEST_ESP_OK(gpio_set_level(input_gpio, 0)); + + uint64_t capture_value = 0; + TEST_ESP_OK(gptimer_get_captured_count(gptimer, &capture_value)); + printf("capture value: %llu\r\n", capture_value); + // should be around 500us + TEST_ASSERT_UINT_WITHIN(1000, 500000, capture_value); + + // delete gptimer + TEST_ESP_OK(gptimer_stop(gptimer)); + TEST_ESP_OK(gptimer_disable(gptimer)); + TEST_ESP_OK(gptimer_del_timer(gptimer)); + + // delete etm primitives + TEST_ESP_OK(esp_etm_del_task(gptimer_task)); + TEST_ESP_OK(esp_etm_del_event(gpio_event)); + TEST_ESP_OK(esp_etm_channel_disable(etm_channel_a)); + TEST_ESP_OK(esp_etm_del_channel(etm_channel_a)); +} + +TEST_CASE("gptimer_start_stop_by_etm_task", "[etm]") +{ + const uint32_t input_gpio = 0; + // GPIO pos edge ---> ETM channel A ---> GPTimer start + // GPIO neg edge ---> ETM channel B ---> GPTimer stop + printf("allocate etm channel\r\n"); + esp_etm_channel_config_t etm_config = {}; + esp_etm_channel_handle_t etm_channel_a, etm_channel_b; + TEST_ESP_OK(esp_etm_new_channel(&etm_config, &etm_channel_a)); + TEST_ESP_OK(esp_etm_new_channel(&etm_config, &etm_channel_b)); + + printf("allocate GPIO etm events\r\n"); + esp_etm_event_handle_t gpio_event_pos, gpio_event_neg; + gpio_etm_event_config_t gpio_event_config = { + .edge = GPIO_ETM_EVENT_EDGE_POS, + }; + TEST_ESP_OK(gpio_new_etm_event(&gpio_event_config, &gpio_event_pos)); + gpio_event_config.edge = GPIO_ETM_EVENT_EDGE_NEG; + TEST_ESP_OK(gpio_new_etm_event(&gpio_event_config, &gpio_event_neg)); + + // set gpio number for the gpio etm primitives + TEST_ESP_OK(gpio_etm_event_bind_gpio(gpio_event_pos, input_gpio)); + TEST_ESP_OK(gpio_etm_event_bind_gpio(gpio_event_neg, input_gpio)); + + printf("initialize gpio\r\n"); + gpio_config_t task_gpio_config = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_INPUT_OUTPUT, // we want to simulate the edge signal by software, so it should be input and output + .pin_bit_mask = 1ULL << input_gpio, + }; + TEST_ESP_OK(gpio_config(&task_gpio_config)); + + // put the gpio into initial state + TEST_ESP_OK(gpio_set_level(input_gpio, 0)); + + printf("create a gptimer\r\n"); + gptimer_handle_t gptimer = NULL; + gptimer_config_t timer_config = { + .clk_src = GPTIMER_CLK_SRC_DEFAULT, + .direction = GPTIMER_COUNT_UP, + .resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us + }; + TEST_ESP_OK(gptimer_new_timer(&timer_config, &gptimer)); + + printf("get gptimer etm task handle\r\n"); + esp_etm_task_handle_t gptimer_task_start, gptimer_task_stop; + TEST_ESP_OK(gptimer_new_etm_task(gptimer, GPTIMER_ETM_TASK_START_COUNT, &gptimer_task_start)); + TEST_ESP_OK(gptimer_new_etm_task(gptimer, GPTIMER_ETM_TASK_STOP_COUNT, &gptimer_task_stop)); + + printf("connect event and task to the channel\r\n"); + TEST_ESP_OK(esp_etm_channel_connect(etm_channel_a, gpio_event_pos, gptimer_task_start)); + TEST_ESP_OK(esp_etm_channel_connect(etm_channel_b, gpio_event_neg, gptimer_task_stop)); + + printf("enable etm channel\r\n"); + TEST_ESP_OK(esp_etm_channel_enable(etm_channel_a)); + TEST_ESP_OK(esp_etm_channel_enable(etm_channel_b)); + + printf("enable timer\r\n"); + TEST_ESP_OK(gptimer_enable(gptimer)); + + // trigger an pos-edge, this should start the gptimer + TEST_ESP_OK(gpio_set_level(input_gpio, 1)); + vTaskDelay(pdMS_TO_TICKS(500)); + uint64_t cur_count_val = 0; + TEST_ESP_OK(gptimer_get_raw_count(gptimer, &cur_count_val)); + printf("cur_count_val: %llu\r\n", cur_count_val); + TEST_ASSERT_UINT_WITHIN(900, 500000, cur_count_val); + + // trigger an neg-edge, this should stop the gptimer + TEST_ESP_OK(gpio_set_level(input_gpio, 0)); + uint64_t count_val_0 = 0; + TEST_ESP_OK(gptimer_get_raw_count(gptimer, &count_val_0)); + vTaskDelay(pdMS_TO_TICKS(500)); + uint64_t count_val_1 = 0; + TEST_ESP_OK(gptimer_get_raw_count(gptimer, &count_val_1)); + TEST_ASSERT_EQUAL(count_val_0, count_val_1); + + // delete gptimer + TEST_ESP_OK(gptimer_disable(gptimer)); + TEST_ESP_OK(gptimer_del_timer(gptimer)); + + // delete etm primitives + TEST_ESP_OK(esp_etm_del_task(gptimer_task_start)); + TEST_ESP_OK(esp_etm_del_task(gptimer_task_stop)); + TEST_ESP_OK(esp_etm_del_event(gpio_event_pos)); + TEST_ESP_OK(esp_etm_del_event(gpio_event_neg)); + TEST_ESP_OK(esp_etm_channel_disable(etm_channel_a)); + TEST_ESP_OK(esp_etm_channel_disable(etm_channel_b)); + TEST_ESP_OK(esp_etm_del_channel(etm_channel_a)); + TEST_ESP_OK(esp_etm_del_channel(etm_channel_b)); +} diff --git a/components/hal/esp32c6/include/hal/timer_ll.h b/components/hal/esp32c6/include/hal/timer_ll.h index 96c5490a57..d76e95e2e2 100644 --- a/components/hal/esp32c6/include/hal/timer_ll.h +++ b/components/hal/esp32c6/include/hal/timer_ll.h @@ -14,6 +14,7 @@ #include "hal/timer_types.h" #include "soc/timer_group_struct.h" #include "soc/pcr_struct.h" +#include "soc/soc_etm_source.h" #ifdef __cplusplus extern "C" { @@ -23,6 +24,32 @@ extern "C" { #define TIMER_LL_GET_HW(group_id) ((group_id == 0) ? (&TIMERG0) : (&TIMERG1)) #define TIMER_LL_EVENT_ALARM(timer_id) (1 << (timer_id)) +#define TIMER_LL_ETM_TASK_TABLE(group, timer, task) \ + (uint32_t [2][1][GPTIMER_ETM_TASK_MAX]){{{ \ + [GPTIMER_ETM_TASK_START_COUNT] = TIMER0_TASK_CNT_START_TIMER0, \ + [GPTIMER_ETM_TASK_STOP_COUNT] = TIMER0_TASK_CNT_STOP_TIMER0, \ + [GPTIMER_ETM_TASK_EN_ALARM] = TIMER0_TASK_ALARM_START_TIMER0, \ + [GPTIMER_ETM_TASK_RELOAD] = TIMER0_TASK_CNT_RELOAD_TIMER0, \ + [GPTIMER_ETM_TASK_CAPTURE] = TIMER0_TASK_CNT_CAP_TIMER0, \ + }}, \ + {{ \ + [GPTIMER_ETM_TASK_START_COUNT] = TIMER1_TASK_CNT_START_TIMER0, \ + [GPTIMER_ETM_TASK_STOP_COUNT] = TIMER1_TASK_CNT_STOP_TIMER0, \ + [GPTIMER_ETM_TASK_EN_ALARM] = TIMER1_TASK_ALARM_START_TIMER0, \ + [GPTIMER_ETM_TASK_RELOAD] = TIMER1_TASK_CNT_RELOAD_TIMER0, \ + [GPTIMER_ETM_TASK_CAPTURE] = TIMER1_TASK_CNT_CAP_TIMER0, \ + }}, \ + }[group][timer][task] + +#define TIMER_LL_ETM_EVENT_TABLE(group, timer, event) \ + (uint32_t [2][1][GPTIMER_ETM_EVENT_MAX]){{{ \ + [GPTIMER_ETM_EVENT_ALARM_MATCH] = TIMER0_EVT_CNT_CMP_TIMER0, \ + }}, \ + {{ \ + [GPTIMER_ETM_EVENT_ALARM_MATCH] = TIMER1_EVT_CNT_CMP_TIMER0, \ + }}, \ + }[group][timer][event] + /** * @brief Set clock source for timer * diff --git a/components/hal/include/hal/timer_types.h b/components/hal/include/hal/timer_types.h index ea8982f33c..8c444874ae 100644 --- a/components/hal/include/hal/timer_types.h +++ b/components/hal/include/hal/timer_types.h @@ -26,6 +26,26 @@ typedef enum { GPTIMER_COUNT_UP, /*!< Increase count value */ } gptimer_count_direction_t; +/** + * @brief GPTimer specific tasks that supported by the ETM module + */ +typedef enum { + GPTIMER_ETM_TASK_START_COUNT, /*!< Start the counter */ + GPTIMER_ETM_TASK_STOP_COUNT, /*!< Stop the counter */ + GPTIMER_ETM_TASK_EN_ALARM, /*!< Enable the alarm */ + GPTIMER_ETM_TASK_RELOAD, /*!< Reload preset value into counter */ + GPTIMER_ETM_TASK_CAPTURE, /*!< Capture current count value into specific register */ + GPTIMER_ETM_TASK_MAX, /*!< Maximum number of tasks */ +} gptimer_etm_task_type_t; + +/** + * @brief GPTimer specific events that supported by the ETM module + */ +typedef enum { + GPTIMER_ETM_EVENT_ALARM_MATCH, /*!< Count value matches the alarm target value */ + GPTIMER_ETM_EVENT_MAX, /*!< Maximum number of events */ +} gptimer_etm_event_type_t; + #ifdef __cplusplus } #endif