Merge branch 'feature/etm_driver' into 'master'

Event Task Matrix (ETM) driver

Closes IDF-5808, IDF-6032, and IDF-5922

See merge request espressif/esp-idf!19008
This commit is contained in:
morris 2022-11-23 17:26:59 +08:00
commit fefe6f3d75
85 changed files with 3875 additions and 537 deletions

View File

@ -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()
@ -65,6 +69,10 @@ if(CONFIG_SOC_PCNT_SUPPORTED)
list(APPEND srcs "pulse_cnt.c" "deprecated/pcnt_legacy.c")
endif()
if(CONFIG_SOC_GPIO_SUPPORT_ETM)
list(APPEND srcs "gpio/gpio_etm.c")
endif()
if(CONFIG_SOC_SDMMC_HOST_SUPPORTED)
list(APPEND srcs "sdmmc_transaction.c" "sdmmc_host.c")
endif()

View File

@ -0,0 +1,305 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <string.h>
#include <sys/cdefs.h>
#include "sdkconfig.h"
#if CONFIG_ETM_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#include "freertos/FreeRTOS.h"
#include "driver/gpio.h"
#include "driver/gpio_etm.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "esp_check.h"
#include "soc/soc_caps.h"
#include "hal/gpio_ll.h"
#include "hal/gpio_etm_ll.h"
#include "esp_private/etm_interface.h"
#define ETM_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
static const char *TAG = "gpio-etm";
typedef struct gpio_etm_task_t gpio_etm_task_t;
typedef struct gpio_etm_event_t gpio_etm_event_t;
typedef struct gpio_etm_group_t {
portMUX_TYPE spinlock;
gpio_etm_dev_t *dev;
gpio_etm_task_t *tasks[SOC_GPIO_ETM_TASKS_PER_GROUP];
gpio_etm_event_t *events[SOC_GPIO_ETM_EVENTS_PER_GROUP];
} gpio_etm_group_t;
struct gpio_etm_event_t {
esp_etm_event_t base;
int chan_id;
gpio_etm_group_t *group;
};
struct gpio_etm_task_t {
esp_etm_task_t base;
int chan_id;
gpio_etm_group_t *group;
size_t num_of_gpios; // record the number of GPIOs that are bound to the etm task
};
static gpio_etm_group_t s_gpio_etm_group = {
.dev = &GPIO_ETM,
.spinlock = portMUX_INITIALIZER_UNLOCKED,
};
static esp_err_t gpio_etm_event_register_to_group(gpio_etm_event_t *event)
{
gpio_etm_group_t *group = &s_gpio_etm_group;
int chan_id = -1;
// loop to search free one in the group
portENTER_CRITICAL(&group->spinlock);
for (int j = 0; j < SOC_GPIO_ETM_EVENTS_PER_GROUP; j++) {
if (!group->events[j]) {
chan_id = j;
group->events[j] = event;
break;
}
}
portEXIT_CRITICAL(&group->spinlock);
ESP_RETURN_ON_FALSE(chan_id != -1, ESP_ERR_NOT_FOUND, TAG, "no free event channel");
event->group = group;
event->chan_id = chan_id;
return ESP_OK;
}
static esp_err_t gpio_etm_task_register_to_group(gpio_etm_task_t *task)
{
gpio_etm_group_t *group = &s_gpio_etm_group;
int chan_id = -1;
// loop to search free one in the group
portENTER_CRITICAL(&group->spinlock);
for (int j = 0; j < SOC_GPIO_ETM_TASKS_PER_GROUP; j++) {
if (!group->tasks[j]) {
chan_id = j;
group->tasks[j] = task;
break;
}
}
portEXIT_CRITICAL(&group->spinlock);
ESP_RETURN_ON_FALSE(chan_id != -1, ESP_ERR_NOT_FOUND, TAG, "no free task channel");
task->group = group;
task->chan_id = chan_id;
return ESP_OK;
}
static void gpio_etm_event_unregister_from_group(gpio_etm_event_t *event)
{
gpio_etm_group_t *group = event->group;
int chan_id = event->chan_id;
portENTER_CRITICAL(&group->spinlock);
group->events[chan_id] = NULL;
portEXIT_CRITICAL(&group->spinlock);
}
static void gpio_etm_task_unregister_from_group(gpio_etm_task_t *task)
{
gpio_etm_group_t *group = task->group;
int chan_id = task->chan_id;
portENTER_CRITICAL(&group->spinlock);
group->tasks[chan_id] = NULL;
portEXIT_CRITICAL(&group->spinlock);
}
static esp_err_t gpio_etm_event_destroy(gpio_etm_event_t *event)
{
if (event->group) {
gpio_etm_event_unregister_from_group(event);
}
free(event);
return ESP_OK;
}
static esp_err_t gpio_etm_task_destroy(gpio_etm_task_t *task)
{
if (task->group) {
gpio_etm_task_unregister_from_group(task);
}
free(task);
return ESP_OK;
}
static esp_err_t gpio_del_etm_event(esp_etm_event_t *event)
{
gpio_etm_event_t *gpio_event = __containerof(event, gpio_etm_event_t, base);
gpio_etm_group_t *group = gpio_event->group;
// disable event channel
gpio_ll_etm_enable_event_channel(group->dev, gpio_event->chan_id, false);
gpio_etm_event_destroy(gpio_event);
return ESP_OK;
}
static esp_err_t gpio_del_etm_task(esp_etm_task_t *task)
{
gpio_etm_task_t *gpio_task = __containerof(task, gpio_etm_task_t, base);
// make sure user has called `gpio_etm_task_rm_gpio` to clean the etm task channel
ESP_RETURN_ON_FALSE(gpio_task->num_of_gpios == 0, ESP_ERR_INVALID_STATE, TAG, "some GPIO till bounded to the etm task");
gpio_etm_task_destroy(gpio_task);
return ESP_OK;
}
esp_err_t gpio_new_etm_event(const gpio_etm_event_config_t *config, esp_etm_event_handle_t *ret_event)
{
#if CONFIG_ETM_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
esp_err_t ret = ESP_OK;
gpio_etm_event_t *event = NULL;
ESP_GOTO_ON_FALSE(config && ret_event, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
event = heap_caps_calloc(1, sizeof(gpio_etm_event_t), ETM_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(event, ESP_ERR_NO_MEM, err, TAG, "no mem for event channel");
// register the event channel to the group
ESP_GOTO_ON_ERROR(gpio_etm_event_register_to_group(event), err, TAG, "register event channel to group failed");
int chan_id = event->chan_id;
uint32_t event_id = 0;
switch (config->edge) {
case GPIO_ETM_EVENT_EDGE_ANY:
event_id = GPIO_LL_ETM_EVENT_ID_ANY_EDGE(chan_id);
break;
case GPIO_ETM_EVENT_EDGE_POS:
event_id = GPIO_LL_ETM_EVENT_ID_POS_EDGE(chan_id);
break;
case GPIO_ETM_EVENT_EDGE_NEG:
event_id = GPIO_LL_ETM_EVENT_ID_NEG_EDGE(chan_id);
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "invalid edge");
}
event->base.del = gpio_del_etm_event;
event->base.event_id = event_id;
event->base.trig_periph = ETM_TRIG_PERIPH_GPIO;
ESP_LOGD(TAG, "new event @%p, event_id=%"PRIu32", chan_id=%d", event, event_id, chan_id);
*ret_event = &event->base;
return ESP_OK;
err:
if (event) {
gpio_etm_event_destroy(event);
}
return ret;
}
esp_err_t gpio_new_etm_task(const gpio_etm_task_config_t *config, esp_etm_task_handle_t *ret_task)
{
#if CONFIG_ETM_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
esp_err_t ret = ESP_OK;
gpio_etm_task_t *task = NULL;
ESP_GOTO_ON_FALSE(config && ret_task, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
task = heap_caps_calloc(1, sizeof(gpio_etm_task_t), ETM_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(task, ESP_ERR_NO_MEM, err, TAG, "no mem for task channel");
// register the task channel to the group
ESP_GOTO_ON_ERROR(gpio_etm_task_register_to_group(task), err, TAG, "register task channel to group failed");
int chan_id = task->chan_id;
uint32_t task_id = 0;
switch (config->action) {
case GPIO_ETM_TASK_ACTION_SET:
task_id = GPIO_LL_ETM_TASK_ID_SET(chan_id);
break;
case GPIO_ETM_TASK_ACTION_CLR:
task_id = GPIO_LL_ETM_TASK_ID_CLR(chan_id);
break;
case GPIO_ETM_TASK_ACTION_TOG:
task_id = GPIO_LL_ETM_TASK_ID_TOG(chan_id);
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "invalid action");
}
task->base.del = gpio_del_etm_task;
task->base.task_id = task_id;
task->base.trig_periph = ETM_TRIG_PERIPH_GPIO;
ESP_LOGD(TAG, "new task @%p, task_id=%"PRIu32", chan_id=%d", task, task_id, chan_id);
*ret_task = &task->base;
return ESP_OK;
err:
if (task) {
gpio_etm_task_destroy(task);
}
return ret;
}
esp_err_t gpio_etm_event_bind_gpio(esp_etm_event_handle_t event, int gpio_num)
{
ESP_RETURN_ON_FALSE(event, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(event->trig_periph == ETM_TRIG_PERIPH_GPIO, ESP_ERR_INVALID_ARG, TAG, "not a gpio etm event");
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "gpio is not input capable");
gpio_etm_event_t *gpio_event = __containerof(event, gpio_etm_event_t, base);
gpio_etm_group_t *group = gpio_event->group;
// disable gpio etm event channel first
gpio_ll_etm_enable_event_channel(group->dev, gpio_event->chan_id, false);
// then set the gpio number
gpio_ll_etm_event_channel_set_gpio(group->dev, gpio_event->chan_id, gpio_num);
// enable gpio etm event channel again
gpio_ll_etm_enable_event_channel(group->dev, gpio_event->chan_id, true);
return ESP_OK;
}
esp_err_t gpio_etm_task_add_gpio(esp_etm_task_handle_t task, int gpio_num)
{
ESP_RETURN_ON_FALSE(task, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(task->trig_periph == ETM_TRIG_PERIPH_GPIO, ESP_ERR_INVALID_ARG, TAG, "not a gpio etm task");
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "gpio is not output capable");
gpio_etm_task_t *gpio_task = __containerof(task, gpio_etm_task_t, base);
gpio_etm_group_t *group = gpio_task->group;
bool gpio_not_enabled = true;
// use spinlock as this function may be called with different task object in different threads
// and the gpio_num might reside in the same register
portENTER_CRITICAL(&group->spinlock);
// check if the gpio has been enabled
if (!gpio_ll_etm_is_task_gpio_enabled(group->dev, gpio_num)) {
gpio_ll_etm_gpio_set_task_channel(group->dev, gpio_num, gpio_task->chan_id);
gpio_ll_etm_enable_task_gpio(group->dev, gpio_num, true);
} else {
gpio_not_enabled = false;
}
portEXIT_CRITICAL(&group->spinlock);
ESP_RETURN_ON_FALSE(gpio_not_enabled, ESP_ERR_INVALID_STATE, TAG, "gpio already enabled by other task channel");
gpio_task->num_of_gpios++;
return ESP_OK;
}
esp_err_t gpio_etm_task_rm_gpio(esp_etm_task_handle_t task, int gpio_num)
{
ESP_RETURN_ON_FALSE(task, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "gpio is not output capable");
gpio_etm_task_t *gpio_task = __containerof(task, gpio_etm_task_t, base);
gpio_etm_group_t *group = gpio_task->group;
bool gpio_enabled_by_this_task = true;
// use spinlock as this function may be called with different task object in different threads
// and the gpio_num might reside in the same register
portENTER_CRITICAL(&group->spinlock);
// check if the gpio is managed by this etm task channel
if (gpio_ll_etm_is_task_gpio_enabled(group->dev, gpio_num) &&
(gpio_ll_etm_gpio_get_task_channel(group->dev, gpio_num) == gpio_task->chan_id)) {
gpio_ll_etm_enable_task_gpio(group->dev, gpio_num, false);
} else {
gpio_enabled_by_this_task = false;
}
portEXIT_CRITICAL(&group->spinlock);
ESP_RETURN_ON_FALSE(gpio_enabled_by_this_task, ESP_ERR_INVALID_STATE, TAG, "gpio is not enabled by this task channel");
gpio_task->num_of_gpios--;
return ESP_OK;
}

View File

@ -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;

View File

@ -0,0 +1,92 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <sys/lock.h>
#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;
}

View File

@ -0,0 +1,79 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#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

View File

@ -13,6 +13,7 @@
#include "soc/soc_caps.h"
#include "hal/gpio_types.h"
#include "esp_rom_gpio.h"
#include "driver/gpio_etm.h"
#ifdef __cplusplus
extern "C" {

View File

@ -0,0 +1,132 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "esp_etm.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief GPIO edges that can be used as ETM event
*/
typedef enum {
GPIO_ETM_EVENT_EDGE_POS, /*!< A rising edge on the GPIO will generate an ETM event signal */
GPIO_ETM_EVENT_EDGE_NEG, /*!< A falling edge on the GPIO will generate an ETM event signal */
GPIO_ETM_EVENT_EDGE_ANY, /*!< Any edge on the GPIO can generate an ETM event signal */
} gpio_etm_event_edge_t;
/**
* @brief GPIO ETM event configuration
*/
typedef struct {
gpio_etm_event_edge_t edge; /*!< Which kind of edge can trigger the ETM event module */
} gpio_etm_event_config_t;
/**
* @brief Create an ETM event object for the GPIO peripheral
*
* @note The created ETM event object can be deleted later by calling `esp_etm_del_event`
* @note The newly created ETM event object is not bind to any GPIO, you need to call `gpio_etm_event_bind_gpio` to bind the wanted GPIO
*
* @param[in] config GPIO ETM event configuration
* @param[out] ret_event Returned ETM event handle
* @return
* - ESP_OK: Create ETM event successfully
* - ESP_ERR_INVALID_ARG: Create ETM event failed because of invalid argument
* - ESP_ERR_NO_MEM: Create ETM event failed because of out of memory
* - ESP_ERR_NOT_FOUND: Create ETM event failed because all events are used up and no more free one
* - ESP_FAIL: Create ETM event failed because of other reasons
*/
esp_err_t gpio_new_etm_event(const gpio_etm_event_config_t *config, esp_etm_event_handle_t *ret_event);
/**
* @brief Bind the GPIO with the ETM event
*
* @note Calling this function multiple times with different GPIO number can override the previous setting immediately.
* @note Only GPIO ETM object can call this function
*
* @param[in] event ETM event handle that created by `gpio_new_etm_event`
* @param[in] gpio_num GPIO number that can trigger the ETM event
* @return
* - ESP_OK: Set the GPIO for ETM event successfully
* - ESP_ERR_INVALID_ARG: Set the GPIO for ETM event failed because of invalid argument, e.g. GPIO is not input capable, ETM event is not of GPIO type
* - ESP_FAIL: Set the GPIO for ETM event failed because of other reasons
*/
esp_err_t gpio_etm_event_bind_gpio(esp_etm_event_handle_t event, int gpio_num);
/**
* @brief GPIO actions that can be taken by the ETM task
*/
typedef enum {
GPIO_ETM_TASK_ACTION_SET, /*!< Set the GPIO level to high */
GPIO_ETM_TASK_ACTION_CLR, /*!< Clear the GPIO level to low */
GPIO_ETM_TASK_ACTION_TOG, /*!< Toggle the GPIO level */
} gpio_etm_task_action_t;
/**
* @brief GPIO ETM task configuration
*/
typedef struct {
gpio_etm_task_action_t action; /*!< Which action to take by the ETM task module */
} gpio_etm_task_config_t;
/**
* @brief Create an ETM task object for the GPIO peripheral
*
* @note The created ETM task object can be deleted later by calling `esp_etm_del_task`
* @note The GPIO ETM task works like a container, a newly created ETM task object doesn't have GPIO members to be managed.
* You need to call `gpio_etm_task_add_gpio` to put one or more GPIOs to the container.
*
* @param[in] config GPIO ETM task configuration
* @param[out] ret_task Returned ETM task handle
* @return
* - ESP_OK: Create ETM task successfully
* - ESP_ERR_INVALID_ARG: Create ETM task failed because of invalid argument
* - ESP_ERR_NO_MEM: Create ETM task failed because of out of memory
* - ESP_ERR_NOT_FOUND: Create ETM task failed because all tasks are used up and no more free one
* - ESP_FAIL: Create ETM task failed because of other reasons
*/
esp_err_t gpio_new_etm_task(const gpio_etm_task_config_t *config, esp_etm_task_handle_t *ret_task);
/**
* @brief Add GPIO to the ETM task.
*
* @note You can call this function multiple times to add more GPIOs
* @note Only GPIO ETM object can call this function
*
* @param[in] task ETM task handle that created by `gpio_new_etm_task`
* @param[in] gpio_num GPIO number that can be controlled by the ETM task
* @return
* - ESP_OK: Add GPIO to the ETM task successfully
* - ESP_ERR_INVALID_ARG: Add GPIO to the ETM task failed because of invalid argument, e.g. GPIO is not output capable, ETM task is not of GPIO type
* - ESP_ERR_INVALID_STATE: Add GPIO to the ETM task failed because the GPIO is used by other ETM task already
* - ESP_FAIL: Add GPIO to the ETM task failed because of other reasons
*/
esp_err_t gpio_etm_task_add_gpio(esp_etm_task_handle_t task, int gpio_num);
/**
* @brief Remove the GPIO from the ETM task
*
* @note Before deleting the ETM task, you need to remove all the GPIOs from the ETM task by this function
* @note Only GPIO ETM object can call this function
*
* @param[in] task ETM task handle that created by `gpio_new_etm_task`
* @param[in] gpio_num GPIO number that to be remove from the ETM task
* @return
* - ESP_OK: Remove the GPIO from the ETM task successfully
* - ESP_ERR_INVALID_ARG: Remove the GPIO from the ETM task failed because of invalid argument
* - ESP_ERR_INVALID_STATE: Remove the GPIO from the ETM task failed because the GPIO is not controlled by this ETM task
* - ESP_FAIL: Remove the GPIO from the ETM task failed because of other reasons
*/
esp_err_t gpio_etm_task_rm_gpio(esp_etm_task_handle_t task, int gpio_num);
#ifdef __cplusplus
}
#endif

View File

@ -9,44 +9,13 @@
#include <stdint.h>
#include <stdbool.h>
#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.
*

View File

@ -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

View File

@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#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

View File

@ -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)

View File

@ -5,3 +5,7 @@ components/esp_hw_support/test_apps/dma:
- if: IDF_TARGET in ["esp32"]
temporary: false
reason: Neither GDMA nor CPDMA is supported on ESP32
components/esp_hw_support/test_apps/etm:
disable:
- if: SOC_ETM_SUPPORTED != 1

View File

@ -30,15 +30,19 @@ if(NOT BOOTLOADER_BUILD)
endif()
if(CONFIG_SOC_GDMA_SUPPORTED)
list(APPEND srcs "gdma.c" "port/async_memcpy_impl_gdma.c")
list(APPEND srcs "dma/gdma.c" "dma/async_memcpy_impl_gdma.c")
endif()
if(CONFIG_SOC_CP_DMA_SUPPORTED)
list(APPEND srcs "port/async_memcpy_impl_cp_dma.c")
list(APPEND srcs "dma/async_memcpy_impl_cp_dma.c")
endif()
if(CONFIG_SOC_GDMA_SUPPORTED OR CONFIG_SOC_CP_DMA_SUPPORTED)
list(APPEND srcs "esp_async_memcpy.c")
list(APPEND srcs "dma/esp_async_memcpy.c")
endif()
if(CONFIG_SOC_GDMA_SUPPORT_ETM)
list(APPEND srcs "dma/gdma_etm.c")
endif()
if(CONFIG_SOC_SYSTIMER_SUPPORTED)
@ -49,6 +53,10 @@ if(NOT BOOTLOADER_BUILD)
list(APPEND srcs "esp_hmac.c")
endif()
if(CONFIG_SOC_ETM_SUPPORTED)
list(APPEND srcs "esp_etm.c")
endif()
# ESP32C6-TODO
if(CONFIG_IDF_TARGET_ESP32C6)
list(REMOVE_ITEM srcs

View File

@ -161,6 +161,16 @@ menu "Hardware Settings"
so that these functions can be IRAM-safe and able to be called in the other IRAM interrupt context.
endmenu
menu "ETM Configuration"
depends on SOC_ETM_SUPPORTED
config ETM_ENABLE_DEBUG_LOG
bool "Enable debug log"
default n
help
Wether to enable the debug log message for ETM core driver.
Note that, this option only controls the ETM related driver log, won't affect other drivers.
endmenu # ETM Configuration
menu "MMU Config"
# This Config is used for configure the MMU.
# Be configured based on flash size selection.

View File

@ -1,7 +1,77 @@
## `esp_hw_support` ##
# `esp_hw_support` (G1 component)
This component contains hardware-related operations for supporting the system. These operations
are one level above that of `hal` in that these(1) use system services such as memory allocation, logging, scheduling
or (2) may be multi-step operations involving/affecting multiple parts of the SoC.
This component contains hardware-related operations for supporting the system. These operations are one level above that of `hal` in that:
1. it uses system services such as memory allocation, logging, scheduling
2. it may be multi-step operations involving/affecting multiple parts of the SoC
3. it offers a service for other components vary from multiple layers (G1, G2 and G3) of ESP-IDF
Implementations that don't fit other components cleanly, but are not worth creating a new component for (yet) may also be placed here as long as they don't pull dependencies other than the core system components.
## Event-Task Service (esp_etm)
### esp_etm driver design
`esp_etm` driver is divided into two parts:
* The **core** driver, which focuses on ETM channel allocation and offers APIs to connect the channel with ETM tasks and ETM events that come from other peripherals.
* **Peripheral** side extensions, e.g. GPTimer support generating different kinds of ETM events, and accept multiple ETM tasks. These extensions are implemented in the peripheral driver, and can be located in different components. Usually, the task and event extensions will simply inherit the interface that defined in the core driver.
See the following class diagram, we take the GPIO and GPTimer as the example to illustrate the architecture of `esp_etm` driver.
```mermaid
classDiagram
esp_etm_channel_t "1" --> "1" esp_etm_event_t : Has
esp_etm_channel_t "1" --> "1" esp_etm_task_t : Has
class esp_etm_channel_t {
-int chan_id
-esp_etm_event_t event
-esp_etm_task_t task
+enable() esp_err_t
+disable() esp_err_t
+connect(event, task) esp_err_t
+dump() esp_err_t
}
class esp_etm_event_t {
<<interface>>
#int event_id
#etm_trigger_peripheral_t trig_periph
#del() esp_err_t
}
class esp_etm_task_t {
<<interface>>
#int task_id
#etm_trigger_peripheral_t trig_periph
#del() esp_err_t
}
gpio_etm_event_t --|> esp_etm_event_t : Inheritance
class gpio_etm_event_t {
-int chan_id
+bind_gpio(gpio_num_t gpio) esp_err_t
}
gpio_etm_task_t --|> esp_etm_task_t : Inheritance
class gpio_etm_task_t {
-int chan_id
+add_gpio(gpio_num) esp_err_t
+rm_gpio(gpio_num) esp_err_t
}
gptimer_t "1" --> "1..*" gptimer_etm_event_t : Has
gptimer_t "1" --> "1..*" gptimer_etm_task_t : Has
class gptimer_t {
-gptimer_etm_event_t[] events
-gptimer_etm_task_t[] tasks
}
gptimer_etm_event_t --|> esp_etm_event_t : Inheritance
class gptimer_etm_event_t {
}
gptimer_etm_task_t --|> esp_etm_task_t : Inheritance
class gptimer_etm_task_t {
}
```

View File

@ -12,6 +12,7 @@
#include "esp_log.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_etm.h"
#include "esp_async_memcpy_impl.h"
IRAM_ATTR static void async_memcpy_impl_default_isr_handler(void *args)
@ -76,6 +77,14 @@ esp_err_t async_memcpy_impl_restart(async_memcpy_impl_t *impl)
return ESP_OK;
}
esp_err_t async_memcpy_impl_new_etm_event(async_memcpy_impl_t *impl, async_memcpy_etm_event_t event_type, esp_etm_event_handle_t *out_event)
{
(void)impl;
(void)event_type;
(void)out_event;
return ESP_ERR_NOT_SUPPORTED;
}
bool async_memcpy_impl_is_buffer_address_valid(async_memcpy_impl_t *impl, void *src, void *dst)
{
// CP_DMA can only access SRAM

View File

@ -110,6 +110,16 @@ esp_err_t async_memcpy_impl_restart(async_memcpy_impl_t *impl)
return ESP_OK;
}
esp_err_t async_memcpy_impl_new_etm_event(async_memcpy_impl_t *impl, async_memcpy_etm_event_t event_type, esp_etm_event_handle_t *out_event)
{
if (event_type == ASYNC_MEMCPY_ETM_EVENT_COPY_DONE) {
// use the RX EOF to indicate the async memcpy done event
return gdma_new_etm_event(impl->rx_channel, GDMA_ETM_EVENT_EOF, out_event);
} else {
return ESP_ERR_NOT_SUPPORTED;
}
}
bool async_memcpy_impl_is_buffer_address_valid(async_memcpy_impl_t *impl, void *src, void *dst)
{
bool valid = true;

View File

@ -117,6 +117,12 @@ err:
return ret;
}
esp_err_t esp_async_memcpy_new_etm_event(async_memcpy_t asmcp, async_memcpy_etm_event_t event_type, esp_etm_event_handle_t *out_event)
{
ESP_RETURN_ON_FALSE(asmcp, ESP_ERR_INVALID_ARG, TAG, "mcp handle can't be null");
return async_memcpy_impl_new_etm_event(&asmcp->mcp_impl, event_type, out_event);
}
static int async_memcpy_prepare_receive(async_memcpy_t asmcp, void *buffer, size_t size, dma_descriptor_t **start_desc, dma_descriptor_t **end_desc)
{
uint32_t prepared_length = 0;

View File

@ -13,42 +13,18 @@
#include "freertos/task.h"
#include "soc/soc_caps.h"
#include "soc/periph_defs.h"
#include "esp_intr_alloc.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "hal/gdma_hal.h"
#include "hal/gdma_ll.h"
#include "soc/gdma_periph.h"
#include "esp_memory_utils.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/gdma.h"
#include "gdma_priv.h"
static const char *TAG = "gdma";
#if CONFIG_GDMA_ISR_IRAM_SAFE || CONFIG_GDMA_CTRL_FUNC_IN_IRAM
#define GDMA_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define GDMA_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif
#if CONFIG_GDMA_ISR_IRAM_SAFE
#define GDMA_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_INTRDISABLED)
#else
#define GDMA_INTR_ALLOC_FLAGS ESP_INTR_FLAG_INTRDISABLED
#endif
#define GDMA_INVALID_PERIPH_TRIG (0x3F)
#define SEARCH_REQUEST_RX_CHANNEL (1 << 0)
#define SEARCH_REQUEST_TX_CHANNEL (1 << 1)
typedef struct gdma_platform_t gdma_platform_t;
typedef struct gdma_group_t gdma_group_t;
typedef struct gdma_pair_t gdma_pair_t;
typedef struct gdma_channel_t gdma_channel_t;
typedef struct gdma_tx_channel_t gdma_tx_channel_t;
typedef struct gdma_rx_channel_t gdma_rx_channel_t;
/**
* GDMA driver consists of there object class, namely: Group, Pair and Channel.
* Channel is allocated when user calls `gdma_new_channel`, its lifecycle is maintained by user.
@ -61,51 +37,11 @@ typedef struct gdma_rx_channel_t gdma_rx_channel_t;
* For pair, it has a spinlock, which is used to protect pair level stuffs, e.g. channel handle slots, occupy code.
*/
struct gdma_platform_t {
typedef struct gdma_platform_t {
portMUX_TYPE spinlock; // platform level spinlock
gdma_group_t *groups[SOC_GDMA_GROUPS]; // array of GDMA group instances
int group_ref_counts[SOC_GDMA_GROUPS]; // reference count used to protect group install/uninstall
};
struct gdma_group_t {
int group_id; // Group ID, index from 0
gdma_hal_context_t hal; // HAL instance is at group level
portMUX_TYPE spinlock; // group level spinlock
gdma_pair_t *pairs[SOC_GDMA_PAIRS_PER_GROUP]; // handles of GDMA pairs
int pair_ref_counts[SOC_GDMA_PAIRS_PER_GROUP]; // reference count used to protect pair install/uninstall
};
struct gdma_pair_t {
gdma_group_t *group; // which group the pair belongs to
int pair_id; // Pair ID, index from 0
gdma_tx_channel_t *tx_chan; // pointer of tx channel in the pair
gdma_rx_channel_t *rx_chan; // pointer of rx channel in the pair
int occupy_code; // each bit indicates which channel has been occupied (an occupied channel will be skipped during channel search)
portMUX_TYPE spinlock; // pair level spinlock
};
struct gdma_channel_t {
gdma_pair_t *pair; // which pair the channel belongs to
intr_handle_t intr; // per-channel interrupt handle
portMUX_TYPE spinlock; // channel level spinlock
gdma_channel_direction_t direction; // channel direction
int periph_id; // Peripheral instance ID, indicates which peripheral is connected to this GDMA channel
size_t sram_alignment; // alignment for memory in SRAM
size_t psram_alignment; // alignment for memory in PSRAM
esp_err_t (*del)(gdma_channel_t *channel); // channel deletion function, it's polymorphic, see `gdma_del_tx_channel` or `gdma_del_rx_channel`
};
struct gdma_tx_channel_t {
gdma_channel_t base; // GDMA channel, base class
void *user_data; // user registered DMA event data
gdma_event_callback_t on_trans_eof; // TX EOF callback
};
struct gdma_rx_channel_t {
gdma_channel_t base; // GDMA channel, base class
void *user_data; // user registered DMA event data
gdma_event_callback_t on_recv_eof; // RX EOF callback
};
} gdma_platform_t;
static gdma_group_t *gdma_acquire_group_handle(int group_id);
static gdma_pair_t *gdma_acquire_pair_handle(gdma_group_t *group, int pair_id);

View File

@ -0,0 +1,121 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#include <stdlib.h>
#include <sys/cdefs.h>
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "hal/gdma_hal.h"
#include "hal/gdma_ll.h"
#include "soc/gdma_periph.h"
#include "esp_private/gdma.h"
#include "esp_private/etm_interface.h"
#include "gdma_priv.h"
#define ETM_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
static const char *TAG = "gdma-etm";
typedef struct gdma_etm_task_t {
esp_etm_task_t base;
gdma_channel_t *chan;
} gdma_etm_task_t;
static esp_err_t gdma_del_etm_event(esp_etm_event_t *event)
{
free(event);
return ESP_OK;
}
static esp_err_t gdma_del_etm_task(esp_etm_task_t *task)
{
gdma_etm_task_t *gdma_task = __containerof(task, gdma_etm_task_t, base);
gdma_channel_t *dma_chan = gdma_task->chan;
gdma_pair_t *pair = dma_chan->pair;
gdma_group_t *group = pair->group;
if (dma_chan->direction == GDMA_CHANNEL_DIRECTION_RX) {
gdma_ll_rx_enable_etm_task(group->hal.dev, pair->pair_id, false);
} else {
gdma_ll_tx_enable_etm_task(group->hal.dev, pair->pair_id, false);
}
free(gdma_task);
return ESP_OK;
}
esp_err_t gdma_new_etm_event(gdma_channel_handle_t dma_chan, gdma_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(dma_chan && out_event, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
ESP_GOTO_ON_FALSE(event_type < GDMA_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");
gdma_pair_t *pair = dma_chan->pair;
gdma_group_t *group = pair->group;
uint32_t event_id = 0;
if (dma_chan->direction == GDMA_CHANNEL_DIRECTION_RX) {
event_id = GDMA_LL_RX_ETM_EVENT_TABLE(group->group_id, pair->pair_id, event_type);
} else {
event_id = GDMA_LL_TX_ETM_EVENT_TABLE(group->group_id, pair->pair_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_GDMA;
event->del = gdma_del_etm_event;
*out_event = event;
return ESP_OK;
err:
if (event) {
gdma_del_etm_event(event);
}
return ret;
}
esp_err_t gdma_new_etm_task(gdma_channel_handle_t dma_chan, gdma_etm_task_type_t task_type, esp_etm_task_handle_t *out_task)
{
gdma_etm_task_t *task = NULL;
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_FALSE(dma_chan && out_task, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
ESP_GOTO_ON_FALSE(task_type < GDMA_ETM_TASK_MAX, ESP_ERR_INVALID_ARG, err, TAG, "invalid task type");
task = heap_caps_calloc(1, sizeof(gdma_etm_task_t), ETM_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(task, ESP_ERR_NO_MEM, err, TAG, "no memory for ETM task");
gdma_pair_t *pair = dma_chan->pair;
gdma_group_t *group = pair->group;
uint32_t task_id = 0;
if (dma_chan->direction == GDMA_CHANNEL_DIRECTION_RX) {
task_id = GDMA_LL_RX_ETM_TASK_TABLE(group->group_id, pair->pair_id, task_type);
gdma_ll_rx_enable_etm_task(group->hal.dev, pair->pair_id, true);
} else {
task_id = GDMA_LL_TX_ETM_TASK_TABLE(group->group_id, pair->pair_id, task_type);
gdma_ll_tx_enable_etm_task(group->hal.dev, pair->pair_id, true);
}
ESP_GOTO_ON_FALSE(task_id != 0, ESP_ERR_NOT_SUPPORTED, err, TAG, "not supported task type");
// fill the ETM task object
task->chan = dma_chan;
task->base.task_id = task_id;
task->base.trig_periph = ETM_TRIG_PERIPH_GDMA;
task->base.del = gdma_del_etm_task;
*out_task = &(task->base);
return ESP_OK;
err:
if (task) {
gdma_del_etm_task(&task->base);
}
return ret;
}

View File

@ -0,0 +1,84 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "esp_err.h"
#include "esp_intr_alloc.h"
#include "esp_heap_caps.h"
#include "soc/soc_caps.h"
#include "hal/gdma_hal.h"
#include "hal/gdma_ll.h"
#include "soc/gdma_periph.h"
#include "esp_private/gdma.h"
#if CONFIG_GDMA_ISR_IRAM_SAFE || CONFIG_GDMA_CTRL_FUNC_IN_IRAM
#define GDMA_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define GDMA_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif
#if CONFIG_GDMA_ISR_IRAM_SAFE
#define GDMA_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_INTRDISABLED)
#else
#define GDMA_INTR_ALLOC_FLAGS ESP_INTR_FLAG_INTRDISABLED
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct gdma_pair_t gdma_pair_t;
typedef struct gdma_channel_t gdma_channel_t;
typedef struct gdma_tx_channel_t gdma_tx_channel_t;
typedef struct gdma_rx_channel_t gdma_rx_channel_t;
typedef struct gdma_group_t {
int group_id; // Group ID, index from 0
gdma_hal_context_t hal; // HAL instance is at group level
portMUX_TYPE spinlock; // group level spinlock
gdma_pair_t *pairs[SOC_GDMA_PAIRS_PER_GROUP]; // handles of GDMA pairs
int pair_ref_counts[SOC_GDMA_PAIRS_PER_GROUP]; // reference count used to protect pair install/uninstall
} gdma_group_t;
struct gdma_pair_t {
gdma_group_t *group; // which group the pair belongs to
int pair_id; // Pair ID, index from 0
gdma_tx_channel_t *tx_chan; // pointer of tx channel in the pair
gdma_rx_channel_t *rx_chan; // pointer of rx channel in the pair
int occupy_code; // each bit indicates which channel has been occupied (an occupied channel will be skipped during channel search)
portMUX_TYPE spinlock; // pair level spinlock
};
struct gdma_channel_t {
gdma_pair_t *pair; // which pair the channel belongs to
intr_handle_t intr; // per-channel interrupt handle
portMUX_TYPE spinlock; // channel level spinlock
gdma_channel_direction_t direction; // channel direction
int periph_id; // Peripheral instance ID, indicates which peripheral is connected to this GDMA channel
size_t sram_alignment; // alignment for memory in SRAM
size_t psram_alignment; // alignment for memory in PSRAM
esp_err_t (*del)(gdma_channel_t *channel); // channel deletion function, it's polymorphic, see `gdma_del_tx_channel` or `gdma_del_rx_channel`
};
struct gdma_tx_channel_t {
gdma_channel_t base; // GDMA channel, base class
void *user_data; // user registered DMA event data
gdma_event_callback_t on_trans_eof; // TX EOF callback
};
struct gdma_rx_channel_t {
gdma_channel_t base; // GDMA channel, base class
void *user_data; // user registered DMA event data
gdma_event_callback_t on_recv_eof; // RX EOF callback
};
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,321 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <sys/cdefs.h>
#include <sys/lock.h>
#include "sdkconfig.h"
#if CONFIG_ETM_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "soc/soc_caps.h"
#include "soc/periph_defs.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "esp_etm.h"
#include "hal/etm_hal.h"
#include "hal/etm_ll.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/etm_interface.h"
#define ETM_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
static const char *TAG = "etm";
typedef struct etm_platform_t etm_platform_t;
typedef struct etm_group_t etm_group_t;
typedef struct esp_etm_channel_t esp_etm_channel_t;
struct etm_platform_t {
_lock_t mutex; // platform level mutex lock
etm_group_t *groups[SOC_ETM_GROUPS]; // etm group pool
int group_ref_counts[SOC_ETM_GROUPS]; // reference count used to protect group install/uninstall
};
struct etm_group_t {
int group_id; // hardware group id
etm_hal_context_t hal; // hardware abstraction layer context
portMUX_TYPE spinlock; // to protect per-group register level concurrent access
esp_etm_channel_t *chans[SOC_ETM_CHANNELS_PER_GROUP];
};
typedef enum {
ETM_CHAN_FSM_INIT,
ETM_CHAN_FSM_ENABLE,
} etm_chan_fsm_t;
struct esp_etm_channel_t {
int chan_id; // Channel ID
etm_group_t *group; // which group this channel belongs to
etm_chan_fsm_t fsm; // record ETM channel's driver state
esp_etm_event_handle_t event; // which event is connect to the channel
esp_etm_task_handle_t task; // which task is connect to the channel
};
// ETM driver platform, it's always a singleton
static etm_platform_t s_platform;
static etm_group_t *etm_acquire_group_handle(int group_id)
{
bool new_group = false;
etm_group_t *group = NULL;
// prevent install ETM group concurrently
_lock_acquire(&s_platform.mutex);
if (!s_platform.groups[group_id]) {
group = heap_caps_calloc(1, sizeof(etm_group_t), ETM_MEM_ALLOC_CAPS);
if (group) {
new_group = true;
s_platform.groups[group_id] = group; // register to platform
// initialize ETM group members
group->group_id = group_id;
group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
// enable APB access ETM registers
// if we have multiple ETM groups/instances, we assume the peripheral defines are continuous
periph_module_enable(PERIPH_ETM_MODULE + group_id);
periph_module_reset(PERIPH_ETM_MODULE + group_id);
// initialize HAL context
etm_hal_init(&group->hal);
}
} else {
group = s_platform.groups[group_id];
}
if (group) {
// someone acquired the group handle means we have a new object that refer to this group
s_platform.group_ref_counts[group_id]++;
}
_lock_release(&s_platform.mutex);
if (new_group) {
ESP_LOGD(TAG, "new group (%d) at %p", group_id, group);
}
return group;
}
static void etm_release_group_handle(etm_group_t *group)
{
int group_id = group->group_id;
bool do_deinitialize = false;
_lock_acquire(&s_platform.mutex);
s_platform.group_ref_counts[group_id]--;
if (s_platform.group_ref_counts[group_id] == 0) {
assert(s_platform.groups[group_id]);
do_deinitialize = true;
s_platform.groups[group_id] = NULL; // deregister from platform
periph_module_disable(PERIPH_ETM_MODULE + group_id);
}
_lock_release(&s_platform.mutex);
if (do_deinitialize) {
free(group);
ESP_LOGD(TAG, "del group (%d)", group_id);
}
}
static esp_err_t etm_chan_register_to_group(esp_etm_channel_t *chan)
{
etm_group_t *group = NULL;
int chan_id = -1;
for (int i = 0; i < SOC_ETM_GROUPS; i++) {
group = etm_acquire_group_handle(i);
ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no mem for group (%d)", i);
// loop to search free channel in the group
portENTER_CRITICAL(&group->spinlock);
for (int j = 0; j < SOC_ETM_CHANNELS_PER_GROUP; j++) {
if (!group->chans[j]) {
chan_id = j;
group->chans[j] = chan;
break;
}
}
portEXIT_CRITICAL(&group->spinlock);
if (chan_id < 0) {
etm_release_group_handle(group);
group = NULL;
} else {
chan->chan_id = chan_id;
chan->group = group;
break;;
}
}
ESP_RETURN_ON_FALSE(chan_id != -1, ESP_ERR_NOT_FOUND, TAG, "no free channel");
return ESP_OK;
}
static void etm_chan_unregister_from_group(esp_etm_channel_t *chan)
{
etm_group_t *group = chan->group;
int chan_id = chan->chan_id;
portENTER_CRITICAL(&group->spinlock);
group->chans[chan_id] = NULL;
portEXIT_CRITICAL(&group->spinlock);
// channel has a reference on group, release it now
etm_release_group_handle(group);
}
static esp_err_t etm_chan_destroy(esp_etm_channel_t *chan)
{
if (chan->group) {
etm_chan_unregister_from_group(chan);
}
free(chan);
return ESP_OK;
}
esp_err_t esp_etm_new_channel(const esp_etm_channel_config_t *config, esp_etm_channel_handle_t *ret_chan)
{
#if CONFIG_ETM_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
esp_err_t ret = ESP_OK;
esp_etm_channel_t *chan = NULL;
ESP_GOTO_ON_FALSE(config && ret_chan, ESP_ERR_INVALID_ARG, err, TAG, "invalid args");
chan = heap_caps_calloc(1, sizeof(esp_etm_channel_t), ETM_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(chan, ESP_ERR_NO_MEM, err, TAG, "no mem for channel");
// register channel to the group, one group can have multiple channels
ESP_GOTO_ON_ERROR(etm_chan_register_to_group(chan), err, TAG, "register channel failed");
etm_group_t *group = chan->group;
int group_id = group->group_id;
int chan_id = chan->chan_id;
chan->fsm = ETM_CHAN_FSM_INIT;
ESP_LOGD(TAG, "new etm channel (%d,%d) at %p", group_id, chan_id, chan);
*ret_chan = chan;
return ESP_OK;
err:
if (chan) {
etm_chan_destroy(chan);
}
return ret;
}
esp_err_t esp_etm_del_channel(esp_etm_channel_handle_t chan)
{
ESP_RETURN_ON_FALSE(chan, ESP_ERR_INVALID_ARG, TAG, "invalid args");
ESP_RETURN_ON_FALSE(chan->fsm == ETM_CHAN_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "channel is not in init state");
etm_group_t *group = chan->group;
int group_id = group->group_id;
int chan_id = chan->chan_id;
// disconnect the channel from any event or task
etm_ll_channel_set_event(group->hal.regs, chan_id, 0);
etm_ll_channel_set_task(group->hal.regs, chan_id, 0);
ESP_LOGD(TAG, "del etm channel (%d,%d)", group_id, chan_id);
// recycle memory resource
ESP_RETURN_ON_ERROR(etm_chan_destroy(chan), TAG, "destroy etm channel failed");
return ESP_OK;
}
esp_err_t esp_etm_channel_enable(esp_etm_channel_handle_t chan)
{
ESP_RETURN_ON_FALSE(chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(chan->fsm == ETM_CHAN_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "channel is not in init state");
etm_group_t *group = chan->group;
etm_ll_enable_channel(group->hal.regs, chan->chan_id);
chan->fsm = ETM_CHAN_FSM_ENABLE;
return ESP_OK;
}
esp_err_t esp_etm_channel_disable(esp_etm_channel_handle_t chan)
{
ESP_RETURN_ON_FALSE(chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(chan->fsm == ETM_CHAN_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "channel not in enable state");
etm_group_t *group = chan->group;
etm_ll_disable_channel(group->hal.regs, chan->chan_id);
chan->fsm = ETM_CHAN_FSM_INIT;
return ESP_OK;
}
esp_err_t esp_etm_channel_connect(esp_etm_channel_handle_t chan, esp_etm_event_handle_t event, esp_etm_task_handle_t task)
{
ESP_RETURN_ON_FALSE(chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
etm_group_t *group = chan->group;
uint32_t event_id = 0;
uint32_t task_id = 0;
// if the event/task is NULL, then the channel will disconnect from the event/task
if (event) {
event_id = event->event_id;
}
if (task) {
task_id = task->task_id;
}
etm_ll_channel_set_event(group->hal.regs, chan->chan_id, event_id);
etm_ll_channel_set_task(group->hal.regs, chan->chan_id, task_id);
chan->event = event;
chan->task = task;
ESP_LOGD(TAG, "event %"PRIu32" => channel %d", event_id, chan->chan_id);
ESP_LOGD(TAG, "channel %d => task %"PRIu32, chan->chan_id, task_id);
return ESP_OK;
}
esp_err_t esp_etm_del_event(esp_etm_event_handle_t event)
{
ESP_RETURN_ON_FALSE(event, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
return event->del(event);
}
esp_err_t esp_etm_del_task(esp_etm_task_handle_t task)
{
ESP_RETURN_ON_FALSE(task, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
return task->del(task);
}
esp_err_t esp_etm_dump(FILE *out_stream)
{
etm_group_t *group = NULL;
esp_etm_channel_handle_t etm_chan = NULL;
ESP_RETURN_ON_FALSE(out_stream, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
fprintf(out_stream, "===========ETM Dump Start==========\r\n");
char line[80];
size_t len = sizeof(line);
for (int i = 0; i < SOC_ETM_GROUPS; i++) {
group = etm_acquire_group_handle(i);
ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no mem for group (%d)", i);
etm_hal_context_t *hal = &group->hal;
for (int j = 0; j < SOC_ETM_CHANNELS_PER_GROUP; j++) {
bool print_line = true;
portENTER_CRITICAL(&group->spinlock);
etm_chan = group->chans[j];
if (etm_ll_is_channel_enabled(hal->regs, j)) {
if (!etm_chan) {
// in case the etm driver is bypassed and some channel is enabled in another way (e.g. by hal driver)
snprintf(line, len, "channel %d is enabled but not recorded\r\n", j);
} else {
// print which event and task the channel is connected to
snprintf(line, len, "channel %d: event %"PRIu32" ==> task %"PRIu32"\r\n", j,
etm_chan->event ? etm_chan->event->event_id : 0,
etm_chan->task ? etm_chan->task->task_id : 0);
}
} else {
if (etm_chan) {
// channel is created, but not enabled by `esp_etm_channel_enable` yet
snprintf(line, len, "channel %d is created but not enabled\r\n", j);
} else {
// a free channel, don't print anything
print_line = false;
}
}
portEXIT_CRITICAL(&group->spinlock);
if (print_line) {
fputs(line, out_stream);
}
}
etm_release_group_handle(group);
}
fprintf(out_stream, "===========ETM Dump End============\r\n");
return ESP_OK;
}

View File

@ -13,6 +13,7 @@ extern "C" {
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#include "esp_etm.h"
/**
* @brief Type of async memcpy handle
@ -91,6 +92,8 @@ esp_err_t esp_async_memcpy_uninstall(async_memcpy_t asmcp);
/**
* @brief Send an asynchronous memory copy request
*
* @note The callback function is invoked in interrupt context, never do blocking jobs in the callback.
*
* @param[in] asmcp Handle of async memcpy driver that returned from esp_async_memcpy_install
* @param[in] dst Destination address (copy to)
* @param[in] src Source address (copy from)
@ -101,11 +104,33 @@ esp_err_t esp_async_memcpy_uninstall(async_memcpy_t asmcp);
* - ESP_OK: Send memory copy request successfully
* - ESP_ERR_INVALID_ARG: Send memory copy request failed because of invalid argument
* - ESP_FAIL: Send memory copy request failed because of other error
*
* @note The callback function is invoked in interrupt context, never do blocking jobs in the callback.
*/
esp_err_t esp_async_memcpy(async_memcpy_t asmcp, void *dst, void *src, size_t n, async_memcpy_isr_cb_t cb_isr, void *cb_args);
/**
* @brief Async memory copy specific events that supported by the ETM module
*/
typedef enum {
ASYNC_MEMCPY_ETM_EVENT_COPY_DONE, /*!< memory copy finished */
} async_memcpy_etm_event_t;
/**
* @brief Get the ETM event handle for async memcpy done signal
*
* @note The created ETM event object can be deleted later by calling `esp_etm_del_event`
*
* @param[in] asmcp Handle of async memcpy driver that returned from `esp_async_memcpy_install`
* @param[in] event_type ETM event type
* @param[out] out_event Returned ETM event handle
* @return
* @return
* - ESP_OK: Get ETM event successfully
* - ESP_ERR_INVALID_ARG: Get ETM event failed because of invalid argument
* - ESP_ERR_NOT_SUPPORTED: Get ETM event failed because the DMA hardware doesn't support ETM submodule
* - ESP_FAIL: Get ETM event failed because of other error
*/
esp_err_t esp_async_memcpy_new_etm_event(async_memcpy_t asmcp, async_memcpy_etm_event_t event_type, esp_etm_event_handle_t *out_event);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,147 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdio.h>
#include "esp_err.h"
#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;
/**
* @brief ETM channel configuration
*/
typedef struct {
} esp_etm_channel_config_t;
/**
* @brief Allocate an ETM channel
*
* @note The channel can later be freed by `esp_etm_del_channel`
*
* @param[in] config ETM channel configuration
* @param[out] ret_chan Returned ETM channel handle
* @return
* - ESP_OK: Allocate ETM channel successfully
* - ESP_ERR_INVALID_ARG: Allocate ETM channel failed because of invalid argument
* - ESP_ERR_NO_MEM: Allocate ETM channel failed because of out of memory
* - ESP_ERR_NOT_FOUND: Allocate ETM channel failed because all channels are used up and no more free one
* - ESP_FAIL: Allocate ETM channel failed because of other reasons
*/
esp_err_t esp_etm_new_channel(const esp_etm_channel_config_t *config, esp_etm_channel_handle_t *ret_chan);
/**
* @brief Delete an ETM channel
*
* @param[in] chan ETM channel handle that created by `esp_etm_new_channel`
* @return
* - ESP_OK: Delete ETM channel successfully
* - ESP_ERR_INVALID_ARG: Delete ETM channel failed because of invalid argument
* - ESP_FAIL: Delete ETM channel failed because of other reasons
*/
esp_err_t esp_etm_del_channel(esp_etm_channel_handle_t chan);
/**
* @brief Enable ETM channel
*
* @note This function will transit the channel state from init to enable.
*
* @param[in] chan ETM channel handle that created by `esp_etm_new_channel`
* @return
* - ESP_OK: Enable ETM channel successfully
* - ESP_ERR_INVALID_ARG: Enable ETM channel failed because of invalid argument
* - ESP_ERR_INVALID_STATE: Enable ETM channel failed because the channel has been enabled already
* - ESP_FAIL: Enable ETM channel failed because of other reasons
*/
esp_err_t esp_etm_channel_enable(esp_etm_channel_handle_t chan);
/**
* @brief Disable ETM channel
*
* @note This function will transit the channel state from enable to init.
*
* @param[in] chan ETM channel handle that created by `esp_etm_new_channel`
* @return
* - ESP_OK: Disable ETM channel successfully
* - ESP_ERR_INVALID_ARG: Disable ETM channel failed because of invalid argument
* - ESP_ERR_INVALID_STATE: Disable ETM channel failed because the channel is not enabled yet
* - ESP_FAIL: Disable ETM channel failed because of other reasons
*/
esp_err_t esp_etm_channel_disable(esp_etm_channel_handle_t chan);
/**
* @brief Connect an ETM event to an ETM task via a previously allocated ETM channel
*
* @note Setting the ETM event/task handle to NULL means to disconnect the channel from any event/task
*
* @param[in] chan ETM channel handle that created by `esp_etm_new_channel`
* @param[in] event ETM event handle obtained from a driver/peripheral, e.g. `xxx_new_etm_event`
* @param[in] task ETM task handle obtained from a driver/peripheral, e.g. `xxx_new_etm_task`
* @return
* - ESP_OK: Connect ETM event and task to the channel successfully
* - ESP_ERR_INVALID_ARG: Connect ETM event and task to the channel failed because of invalid argument
* - ESP_FAIL: Connect ETM event and task to the channel failed because of other reasons
*/
esp_err_t esp_etm_channel_connect(esp_etm_channel_handle_t chan, esp_etm_event_handle_t event, esp_etm_task_handle_t task);
/**
* @brief Delete ETM event
*
* @note Although the ETM event comes from various peripherals, we provide the same user API to delete the event handle seamlessly.
*
* @param[in] event ETM event handle obtained from a driver/peripheral, e.g. `xxx_new_etm_event`
* @return
* - ESP_OK: Delete ETM event successfully
* - ESP_ERR_INVALID_ARG: Delete ETM event failed because of invalid argument
* - ESP_FAIL: Delete ETM event failed because of other reasons
*/
esp_err_t esp_etm_del_event(esp_etm_event_handle_t event);
/**
* @brief Delete ETM task
*
* @note Although the ETM task comes from various peripherals, we provide the same user API to delete the task handle seamlessly.
*
* @param[in] task ETM task handle obtained from a driver/peripheral, e.g. `xxx_new_etm_task`
* @return
* - ESP_OK: Delete ETM task successfully
* - ESP_ERR_INVALID_ARG: Delete ETM task failed because of invalid argument
* - ESP_FAIL: Delete ETM task failed because of other reasons
*/
esp_err_t esp_etm_del_task(esp_etm_task_handle_t task);
/**
* @brief Dump ETM channel usages to the given IO stream
*
* @param[in] out_stream IO stream (e.g. stdout)
* @return
* - ESP_OK: Dump ETM channel usages successfully
* - ESP_ERR_INVALID_ARG: Dump ETM channel usages failed because of invalid argument
* - ESP_FAIL: Dump ETM channel usages failed because of other reasons
*/
esp_err_t esp_etm_dump(FILE *out_stream);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,71 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct esp_etm_event_t esp_etm_event_t;
typedef struct esp_etm_task_t esp_etm_task_t;
/**
* @brief List the peripherals that can trigger ETM task/event
*/
typedef enum {
ETM_TRIG_PERIPH_GPIO, /*!< ETM trigger source: GPIO */
ETM_TRIG_PERIPH_GDMA, /*!< ETM trigger source: GDMA */
ETM_TRIG_PERIPH_GPTIMER, /*!< ETM trigger source: GPTimer */
ETM_TRIG_PERIPH_SYSTIMER, /*!< ETM trigger source: Systimer */
} etm_trigger_peripheral_t;
/**
* @brief ETM event interface definition
*/
struct esp_etm_event_t {
/**
* @brief Unique event ID
*/
uint32_t event_id;
/**
* @brief ETM trigger peripheral
*/
etm_trigger_peripheral_t trig_periph;
/**
* @brief Resource destroy
*/
esp_err_t (*del)(esp_etm_event_t *event);
};
/**
* @brief ETM task interface definition
*/
struct esp_etm_task_t {
/**
* @brief Unique task ID
*/
uint32_t task_id;
/**
* @brief ETM trigger peripheral
*/
etm_trigger_peripheral_t trig_periph;
/**
* @brief Resource destroy
*/
esp_err_t (*del)(esp_etm_task_t *task);
};
#ifdef __cplusplus
}
#endif

View File

@ -5,12 +5,14 @@
*/
// DO NOT USE THESE APIS IN ANY APPLICATIONS
// GDMA driver is not public for end users, but for ESP-IDF developpers.
// GDMA driver is not public for end users, but for ESP-IDF developers.
#pragma once
#include <stdbool.h>
#include "esp_etm.h"
#include "soc/gdma_channel.h"
#include "hal/gdma_types.h"
#include "esp_err.h"
#ifdef __cplusplus
@ -325,6 +327,38 @@ esp_err_t gdma_append(gdma_channel_handle_t dma_chan);
*/
esp_err_t gdma_reset(gdma_channel_handle_t dma_chan);
/**
* @brief Get the ETM event for GDMA channel
*
* @note The created ETM event object can be deleted later by calling `esp_etm_del_event`
*
* @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
* @param[in] event_type GDMA 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_ERR_NOT_SUPPORTED: Get ETM event failed because the GDMA hardware doesn't support ETM event
* - ESP_FAIL: Get ETM event failed because of other error
*/
esp_err_t gdma_new_etm_event(gdma_channel_handle_t dma_chan, gdma_etm_event_type_t event_type, esp_etm_event_handle_t *out_event);
/**
* @brief Get the ETM task for GDMA channel
*
* @note The created ETM task object can be deleted later by calling `esp_etm_del_task`
*
* @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
* @param[in] task_type GDMA 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_ERR_NOT_SUPPORTED: Get ETM task failed because the gdma hardware doesn't support ETM task
* - ESP_FAIL: Get ETM task failed because of other error
*/
esp_err_t gdma_new_etm_task(gdma_channel_handle_t dma_chan, gdma_etm_task_type_t task_type, esp_etm_task_handle_t *out_task);
#ifdef __cplusplus
}
#endif

View File

@ -12,9 +12,11 @@ extern "C" {
#include <stdbool.h>
#include "esp_err.h"
#include "esp_intr_alloc.h"
#include "esp_etm.h"
#include "soc/soc_caps.h"
#include "hal/dma_types.h"
#include "freertos/FreeRTOS.h"
#include "esp_async_memcpy.h"
#if SOC_CP_DMA_SUPPORTED
#include "hal/cp_dma_ll.h"
@ -92,6 +94,16 @@ esp_err_t async_memcpy_impl_stop(async_memcpy_impl_t *impl);
*/
esp_err_t async_memcpy_impl_restart(async_memcpy_impl_t *impl);
/**
* @brief Get ETM Event handle
*
* @param impl async mcp implementation layer context pointer
* @param event_type ETM event type
* @param out_event Returned ETM event handle
* @return ESP_OK on success, ESP_ERR_NOT_SUPPORTED if not supported in hardware, otherwise failed
*/
esp_err_t async_memcpy_impl_new_etm_event(async_memcpy_impl_t *impl, async_memcpy_etm_event_t event_type, esp_etm_event_handle_t *out_event);
/**
* @brief check if buffer address is valid
* @note This is related to underlying target (e.g. on esp32-s2, only buffer located in SRAM is supported)

View File

@ -0,0 +1,5 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(etm_test)

View File

@ -0,0 +1,2 @@
| Supported Targets | ESP32-C6 |
| ----------------- | -------- |

View File

@ -0,0 +1,23 @@
set(srcs "test_app_main.c"
"test_etm_core.c")
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()
if(CONFIG_SOC_SYSTIMER_SUPPORT_ETM)
list(APPEND srcs "test_systimer_etm.c")
endif()
if(CONFIG_SOC_GDMA_SUPPORT_ETM)
list(APPEND srcs "test_gdma_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}
WHOLE_ARCHIVE)

View File

@ -0,0 +1,60 @@
/*
* 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"
#include "esp_newlib.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// Some resources are lazy allocated in pulse_cnt driver, the threshold is left for that case
#define TEST_MEMORY_LEAK_THRESHOLD (-300)
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)
{
/* some FreeRTOS stuff is cleaned up by idle task */
vTaskDelay(5);
/* clean up some of the newlib's lazy allocations */
esp_reent_cleanup();
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)
{
// _____ _____ __ __ _____ _
// | ____|_ _| \/ | |_ _|__ ___| |_
// | _| | | | |\/| | | |/ _ \/ __| __|
// | |___ | | | | | | | | __/\__ \ |_
// |_____| |_| |_| |_| |_|\___||___/\__|
printf(" _____ _____ __ __ _____ _\r\n");
printf("| ____|_ _| \\/ | |_ _|__ ___| |_\r\n");
printf("| _| | | | |\\/| | | |/ _ \\/ __| __|\r\n");
printf("| |___ | | | | | | | | __/\\__ \\ |_\r\n");
printf("|_____| |_| |_| |_| |_|\\___||___/\\__|\r\n");
unity_run_menu();
}

View File

@ -0,0 +1,39 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h"
#include "esp_etm.h"
#include "soc/soc_caps.h"
#include "esp_attr.h"
TEST_CASE("etm_channel_install_uninstall", "[etm]")
{
printf("install etm channels exhaustively\r\n");
esp_etm_channel_handle_t etm_chans[SOC_ETM_GROUPS][SOC_ETM_CHANNELS_PER_GROUP];
esp_etm_channel_config_t config = {};
for (int i = 0; i < SOC_ETM_GROUPS; i++) {
for (int j = 0; j < SOC_ETM_CHANNELS_PER_GROUP; j++) {
TEST_ESP_OK(esp_etm_new_channel(&config, &etm_chans[i][j]));
}
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_etm_new_channel(&config, &etm_chans[0][0]));
}
TEST_ESP_OK(esp_etm_channel_enable(etm_chans[0][0]));
TEST_ESP_OK(esp_etm_dump(stdout));
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_etm_del_channel(etm_chans[0][0]));
TEST_ESP_OK(esp_etm_channel_disable(etm_chans[0][0]));
for (int i = 0; i < SOC_ETM_GROUPS; i++) {
for (int j = 0; j < SOC_ETM_CHANNELS_PER_GROUP; j++) {
TEST_ESP_OK(esp_etm_del_channel(etm_chans[i][j]));
}
}
}

View File

@ -0,0 +1,88 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <inttypes.h>
#include "unity.h"
#include "unity_test_utils.h"
#include "freertos/FreeRTOS.h"
#include "esp_attr.h"
#include "esp_etm.h"
#include "driver/gpio_etm.h"
#include "driver/gpio.h"
#include "esp_async_memcpy.h"
TEST_CASE("async_memcpy_eof_event", "[etm]")
{
const uint32_t output_gpio = 1;
// async_memcpy done ---> 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_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("install async memcpy context\r\n");
async_memcpy_t mcp_ctx = NULL;
async_memcpy_config_t config = ASYNC_MEMCPY_DEFAULT_CONFIG();
TEST_ESP_OK(esp_async_memcpy_install(&config, &mcp_ctx));
printf("get async memcpy etm event handle\r\n");
esp_etm_event_handle_t mcp_event = NULL;
TEST_ESP_OK(esp_async_memcpy_new_etm_event(mcp_ctx, ASYNC_MEMCPY_ETM_EVENT_COPY_DONE, &mcp_event));
printf("connect event and task to the channel\r\n");
TEST_ESP_OK(esp_etm_channel_connect(etm_channel_a, mcp_event, gpio_task));
printf("enable etm channel\r\n");
TEST_ESP_OK(esp_etm_channel_enable(etm_channel_a));
TEST_ESP_OK(esp_etm_dump(stdout));
const uint32_t buffer_size = 1024;
uint8_t *src_buf = heap_caps_malloc(buffer_size, MALLOC_CAP_8BIT | MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
TEST_ASSERT_NOT_NULL(src_buf);
uint8_t *dst_buf = heap_caps_malloc(buffer_size, MALLOC_CAP_8BIT | MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
TEST_ASSERT_NOT_NULL(dst_buf);
printf("start memcpy\r\n");
for (int j = 0; j < 19; j++) {
TEST_ESP_OK(esp_async_memcpy(mcp_ctx, dst_buf, src_buf, buffer_size, NULL, NULL));
}
// simply wait for the last memcpy to finish
vTaskDelay(pdMS_TO_TICKS(1000));
// check the final GPIO level
TEST_ASSERT_EQUAL(0, gpio_get_level(output_gpio));
// delete etm primitives
TEST_ESP_OK(gpio_etm_task_rm_gpio(gpio_task, output_gpio));
TEST_ESP_OK(esp_etm_channel_disable(etm_channel_a));
TEST_ESP_OK(esp_etm_del_task(gpio_task));
TEST_ESP_OK(esp_etm_del_event(mcp_event));
TEST_ESP_OK(esp_etm_del_channel(etm_channel_a));
TEST_ESP_OK(esp_async_memcpy_uninstall(mcp_ctx));
free(src_buf);
free(dst_buf);
}

View File

@ -0,0 +1,92 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <inttypes.h>
#include "unity.h"
#include "unity_test_utils.h"
#include "esp_attr.h"
#include "driver/gpio_etm.h"
#include "driver/gpio.h"
TEST_CASE("gpio_etm_self_trigger", "[etm]")
{
// GPIO any edge ---> EMT channel ---> GPIO toggle
const uint32_t input_gpio = 0;
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 event and task\r\n");
esp_etm_task_handle_t gpio_task = NULL;
esp_etm_event_handle_t gpio_event = NULL;
gpio_etm_event_config_t gpio_event_config = {
.edge = GPIO_ETM_EVENT_EDGE_ANY,
};
TEST_ESP_OK(gpio_new_etm_event(&gpio_event_config, &gpio_event));
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 event and task
TEST_ESP_OK(gpio_etm_event_bind_gpio(gpio_event, input_gpio));
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, // we want to read the GPIO value, so it should be input and output
.pin_bit_mask = 1ULL << output_gpio,
};
TEST_ESP_OK(gpio_config(&task_gpio_config));
// set the initial level
TEST_ESP_OK(gpio_set_level(output_gpio, 0));
gpio_config_t event_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
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.pin_bit_mask = 1ULL << input_gpio,
};
TEST_ESP_OK(gpio_config(&event_gpio_config));
printf("connect event and task to the channel\r\n");
TEST_ESP_OK(esp_etm_channel_connect(etm_channel_a, gpio_event, gpio_task));
TEST_ESP_OK(esp_etm_channel_enable(etm_channel_a));
for (int i = 0; i < 10; i++) {
TEST_ESP_OK(gpio_set_level(input_gpio, i & 0x01));
vTaskDelay(pdMS_TO_TICKS(100));
}
// check the final level
TEST_ASSERT_EQUAL(1, gpio_get_level(output_gpio));
vTaskDelay(pdMS_TO_TICKS(100));
for (int i = 0; i < 5; i++) {
TEST_ESP_OK(gpio_set_level(input_gpio, i & 0x01));
vTaskDelay(pdMS_TO_TICKS(100));
}
// check the final level
TEST_ASSERT_EQUAL(0, gpio_get_level(output_gpio));
// delete gpio etm task without remove all bounded GPIOs should fail
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_etm_del_task(gpio_task));
// remove unrelated GPIO from the task should fail
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, gpio_etm_task_rm_gpio(gpio_task, 10));
// delete etm primitives
TEST_ESP_OK(gpio_etm_task_rm_gpio(gpio_task, output_gpio));
TEST_ESP_OK(esp_etm_channel_disable(etm_channel_a));
TEST_ESP_OK(esp_etm_del_task(gpio_task));
TEST_ESP_OK(esp_etm_del_event(gpio_event));
TEST_ESP_OK(esp_etm_del_channel(etm_channel_a));
}

View File

@ -0,0 +1,440 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <inttypes.h>
#include "unity.h"
#include "unity_test_utils.h"
#include "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));
}

View File

@ -0,0 +1,132 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <inttypes.h>
#include "unity.h"
#include "unity_test_utils.h"
#include "freertos/FreeRTOS.h"
#include "esp_attr.h"
#include "esp_etm.h"
#include "driver/gpio_etm.h"
#include "driver/gpio.h"
#include "esp_timer.h"
#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));
}

View File

@ -0,0 +1,20 @@
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32c6
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'release',
],
indirect=True,
)
def test_etm(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('*')
dut.expect_unity_test_output()

View File

@ -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

View File

@ -0,0 +1,2 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT=n

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -34,6 +34,7 @@
#include <stdio.h>
#include <stdbool.h>
#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

View File

@ -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;
}

View File

@ -85,6 +85,10 @@ if(NOT BOOTLOADER_BUILD)
list(APPEND srcs "emac_hal.c")
endif()
if(CONFIG_SOC_ETM_SUPPORTED)
list(APPEND srcs "etm_hal.c")
endif()
if(CONFIG_SOC_ADC_DMA_SUPPORTED)
list(APPEND srcs "adc_hal.c")
endif()

View File

@ -56,6 +56,8 @@ static inline uint32_t periph_ll_get_clk_en_mask(periph_module_t periph)
return PCR_GDMA_CLK_EN;
case PERIPH_MCPWM0_MODULE:
return PCR_PWM_CLK_EN;
case PERIPH_ETM_MODULE:
return PCR_ETM_CLK_EN;
case PERIPH_AES_MODULE:
return PCR_AES_CLK_EN;
case PERIPH_SHA_MODULE:
@ -128,6 +130,8 @@ static inline uint32_t periph_ll_get_rst_en_mask(periph_module_t periph, bool en
return PCR_GDMA_RST_EN;
case PERIPH_MCPWM0_MODULE:
return PCR_PWM_RST_EN;
case PERIPH_ETM_MODULE:
return PCR_ETM_RST_EN;
case PERIPH_ECC_MODULE:
return PCR_ECC_RST_EN;
case PERIPH_TEMPSENSOR_MODULE:
@ -224,6 +228,8 @@ static uint32_t periph_ll_get_clk_en_reg(periph_module_t periph)
return PCR_GDMA_CONF_REG;
case PERIPH_MCPWM0_MODULE:
return PCR_PWM_CONF_REG;
case PERIPH_ETM_MODULE:
return PCR_ETM_CONF_REG;
case PERIPH_AES_MODULE:
return PCR_AES_CONF_REG;
case PERIPH_SHA_MODULE:
@ -282,6 +288,8 @@ static uint32_t periph_ll_get_rst_en_reg(periph_module_t periph)
return PCR_GDMA_CONF_REG;
case PERIPH_MCPWM0_MODULE:
return PCR_PWM_CONF_REG;
case PERIPH_ETM_MODULE:
return PCR_ETM_CONF_REG;
case PERIPH_AES_MODULE:
return PCR_AES_CONF_REG;
case PERIPH_SHA_MODULE:

View File

@ -0,0 +1,103 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// Note that most of the register operations in this layer are non-atomic operations.
#pragma once
#include <stdbool.h>
#include "hal/assert.h"
#include "hal/misc.h"
#include "soc/soc_etm_struct.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Enable the clock for ETM module
*
* @param hw ETM register base address
* @param enable true to enable, false to disable
*/
static inline void etm_ll_enable_clock(soc_etm_dev_t *hw, bool enable)
{
hw->clk_en.clk_en = enable;
}
/**
* @brief Enable ETM channel
*
* @param hw ETM register base address
* @param chan Channel ID
*/
static inline void etm_ll_enable_channel(soc_etm_dev_t *hw, uint32_t chan)
{
if (chan < 32) {
hw->ch_ena_ad0_set.val = 1 << chan;
} else {
hw->ch_ena_ad1_set.val = 1 << (chan - 32);
}
}
/**
* @brief Disable ETM channel
*
* @param hw ETM register base address
* @param chan Channel ID
*/
static inline void etm_ll_disable_channel(soc_etm_dev_t *hw, uint32_t chan)
{
if (chan < 32) {
hw->ch_ena_ad0_clr.val = 1 << chan;
} else {
hw->ch_ena_ad1_clr.val = 1 << (chan - 32);
}
}
/**
* @brief Check whether the ETM channel is enabled or not
*
* @param hw ETM register base address
* @param chan Channel ID
* @return true if the channel is enabled, false otherwise
*/
static inline bool etm_ll_is_channel_enabled(soc_etm_dev_t *hw, uint32_t chan)
{
if (chan < 32) {
return hw->ch_ena_ad0.val & (1 << chan);
} else {
return hw->ch_ena_ad1.val & (1 << (chan - 32));
}
}
/**
* @brief Set the input event for the ETM channel
*
* @param hw ETM register base address
* @param chan Channel ID
* @param event Event ID
*/
static inline void etm_ll_channel_set_event(soc_etm_dev_t *hw, uint32_t chan, uint32_t event)
{
hw->channel[chan].evt_id.evt_id = event;
}
/**
* @brief Set the output task for the ETM channel
*
* @param hw ETM register base address
* @param chan Channel ID
* @param task Task ID
*/
static inline void etm_ll_channel_set_task(soc_etm_dev_t *hw, uint32_t chan, uint32_t task)
{
hw->channel[chan].task_id.task_id = task;
}
#ifdef __cplusplus
}
#endif

View File

@ -8,8 +8,10 @@
#include <stddef.h> /* Required for NULL constant */
#include <stdint.h>
#include <stdbool.h>
#include "hal/gdma_types.h"
#include "soc/gdma_struct.h"
#include "soc/gdma_reg.h"
#include "soc/soc_etm_source.h"
#ifdef __cplusplus
extern "C" {
@ -34,6 +36,50 @@ extern "C" {
#define GDMA_LL_EVENT_RX_SUC_EOF (1<<1)
#define GDMA_LL_EVENT_RX_DONE (1<<0)
#define GDMA_LL_TX_ETM_EVENT_TABLE(group, chan, event) \
(uint32_t[1][3][GDMA_ETM_EVENT_MAX]){{{ \
[GDMA_ETM_EVENT_EOF] = GDMA_EVT_OUT_EOF_CH0, \
}, \
{ \
[GDMA_ETM_EVENT_EOF] = GDMA_EVT_OUT_EOF_CH1, \
}, \
{ \
[GDMA_ETM_EVENT_EOF] = GDMA_EVT_OUT_EOF_CH2, \
}}}[group][chan][event]
#define GDMA_LL_RX_ETM_EVENT_TABLE(group, chan, event) \
(uint32_t[1][3][GDMA_ETM_EVENT_MAX]){{{ \
[GDMA_ETM_EVENT_EOF] = GDMA_EVT_IN_SUC_EOF_CH0, \
}, \
{ \
[GDMA_ETM_EVENT_EOF] = GDMA_EVT_IN_SUC_EOF_CH1, \
}, \
{ \
[GDMA_ETM_EVENT_EOF] = GDMA_EVT_IN_SUC_EOF_CH2, \
}}}[group][chan][event]
#define GDMA_LL_TX_ETM_TASK_TABLE(group, chan, task) \
(uint32_t[1][3][GDMA_ETM_TASK_MAX]){{{ \
[GDMA_ETM_TASK_START] = GDMA_TASK_OUT_START_CH0, \
}, \
{ \
[GDMA_ETM_TASK_START] = GDMA_TASK_OUT_START_CH1, \
}, \
{ \
[GDMA_ETM_TASK_START] = GDMA_TASK_OUT_START_CH2, \
}}}[group][chan][task]
#define GDMA_LL_RX_ETM_TASK_TABLE(group, chan, task) \
(uint32_t[1][3][GDMA_ETM_TASK_MAX]){{{ \
[GDMA_ETM_TASK_START] = GDMA_TASK_IN_START_CH0, \
}, \
{ \
[GDMA_ETM_TASK_START] = GDMA_TASK_IN_START_CH1, \
}, \
{ \
[GDMA_ETM_TASK_START] = GDMA_TASK_IN_START_CH2, \
}}}[group][chan][task]
///////////////////////////////////// Common /////////////////////////////////////////
/**
* @brief Enable DMA channel M2M mode (TX channel n forward data to RX channel n), disabled by default
@ -42,9 +88,9 @@ static inline void gdma_ll_enable_m2m_mode(gdma_dev_t *dev, uint32_t channel, bo
{
dev->channel[channel].in.in_conf0.mem_trans_en = enable;
if (enable) {
// to enable m2m mode, the tx chan has to be the same to rx chan, and set to a valid value
dev->channel[channel].in.in_peri_sel.peri_in_sel = 0;
dev->channel[channel].out.out_peri_sel.peri_out_sel = 0;
// to enable m2m mode, the tx chan has to be the same to rx chan, and set to a dummy value
dev->channel[channel].in.in_peri_sel.peri_in_sel = 1;
dev->channel[channel].out.out_peri_sel.peri_out_sel = 1;
}
}
@ -260,6 +306,16 @@ static inline void gdma_ll_rx_connect_to_periph(gdma_dev_t *dev, uint32_t channe
dev->channel[channel].in.in_peri_sel.peri_in_sel = periph_id;
}
/**
* @brief Whether to enable the ETM subsystem for RX channel
*
* @note When ETM_EN is 1, only ETM tasks can be used to configure the transfer direction and enable the channel.
*/
static inline void gdma_ll_rx_enable_etm_task(gdma_dev_t *dev, uint32_t channel, bool enable)
{
dev->channel[channel].in.in_conf0.in_etm_en = enable;
}
///////////////////////////////////// TX /////////////////////////////////////////
/**
* @brief Get DMA TX channel interrupt status word
@ -463,6 +519,16 @@ static inline void gdma_ll_tx_connect_to_periph(gdma_dev_t *dev, uint32_t channe
dev->channel[channel].out.out_peri_sel.peri_out_sel = periph_id;
}
/**
* @brief Whether to enable the ETM subsystem for TX channel
*
* @note When ETM_EN is 1, only ETM tasks can be used to configure the transfer direction and enable the channel.
*/
static inline void gdma_ll_tx_enable_etm_task(gdma_dev_t *dev, uint32_t channel, bool enable)
{
dev->channel[channel].out.out_conf0.out_etm_en = enable;
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,119 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// Note that most of the register operations in this layer are non-atomic operations.
#pragma once
#include <stdbool.h>
#include "hal/assert.h"
#include "hal/misc.h"
#include "soc/gpio_ext_struct.h"
#include "soc/soc_etm_source.h"
#define GPIO_LL_ETM_EVENT_ID_POS_EDGE(ch) (GPIO_EVT_CH0_RISE_EDGE + (ch))
#define GPIO_LL_ETM_EVENT_ID_NEG_EDGE(ch) (GPIO_EVT_CH0_FALL_EDGE + (ch))
#define GPIO_LL_ETM_EVENT_ID_ANY_EDGE(ch) (GPIO_EVT_CH0_ANY_EDGE + (ch))
#define GPIO_LL_ETM_TASK_ID_SET(ch) (GPIO_TASK_CH0_SET + (ch))
#define GPIO_LL_ETM_TASK_ID_CLR(ch) (GPIO_TASK_CH0_CLEAR + (ch))
#define GPIO_LL_ETM_TASK_ID_TOG(ch) (GPIO_TASK_CH0_TOGGLE + (ch))
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Set which GPIO to be bounded to the event channel
*
* @param dev Register base address
* @param chan Channel number
* @param gpio_num GPIO number
*/
static inline void gpio_ll_etm_event_channel_set_gpio(gpio_etm_dev_t *dev, uint32_t chan, uint32_t gpio_num)
{
dev->event_chn_cfg[chan].etm_ch0_event_sel = gpio_num;
}
/**
* @brief Wether to enable the event channel
*
* @param dev Register base address
* @param chan Channel number
* @param enable True to enable, false to disable
*/
static inline void gpio_ll_etm_enable_event_channel(gpio_etm_dev_t *dev, uint32_t chan, bool enable)
{
dev->event_chn_cfg[chan].etm_ch0_event_en = enable;
}
/**
* @brief Set which GPIO to be bounded to the task channel
*
* @note One channel can be bounded to multiple different GPIOs
*
* @param dev Register base address
* @param chan Channel number
* @param gpio_num GPIO number
*/
static inline void gpio_ll_etm_gpio_set_task_channel(gpio_etm_dev_t *dev, uint32_t gpio_num, uint32_t chan)
{
int g_p = gpio_num / 4;
int g_idx = gpio_num % 4;
uint32_t reg_val = dev->etm_task_pn_cfg[g_p].val;
reg_val &= ~(0x07 << (g_idx * 8 + 1));
reg_val |= ((chan & 0x07) << (g_idx * 8 + 1));
dev->etm_task_pn_cfg[g_p].val = reg_val;
}
/**
* @brief Wether to enable the GPIO to be managed by the task channel
*
* @param dev Register base address
* @param gpio_num GPIO number
* @param enable True to enable, false to disable
*/
static inline void gpio_ll_etm_enable_task_gpio(gpio_etm_dev_t *dev, uint32_t gpio_num, bool enable)
{
int g_p = gpio_num / 4;
int g_idx = gpio_num % 4;
uint32_t reg_val = dev->etm_task_pn_cfg[g_p].val;
reg_val &= ~(0x01 << (g_idx * 8));
reg_val |= ((enable & 0x01) << (g_idx * 8));
dev->etm_task_pn_cfg[g_p].val = reg_val;
}
/**
* @brief Check whether a GPIO has been enabled and managed by a task channel
*
* @param dev Register base address
* @param gpio_num GPIO number
* @return True if enabled, false otherwise
*/
static inline bool gpio_ll_etm_is_task_gpio_enabled(gpio_etm_dev_t *dev, uint32_t gpio_num)
{
int g_p = gpio_num / 4;
int g_idx = gpio_num % 4;
return dev->etm_task_pn_cfg[g_p].val & (0x01 << (g_idx * 8));
}
/**
* @brief Get the channel number that the GPIO is bounded to
*
* @param dev Register base address
* @param gpio_num GPIO number
* @return GPIO ETM Task channel number
*/
static inline uint32_t gpio_ll_etm_gpio_get_task_channel(gpio_etm_dev_t *dev, uint32_t gpio_num)
{
int g_p = gpio_num / 4;
int g_idx = gpio_num % 4;
return (dev->etm_task_pn_cfg[g_p].val >> (g_idx * 8 + 1)) & 0x07;
}
#ifdef __cplusplus
}
#endif

View File

@ -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)

View File

@ -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
*

19
components/hal/etm_hal.c Normal file
View File

@ -0,0 +1,19 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include "hal/etm_hal.h"
#include "hal/etm_ll.h"
void etm_hal_init(etm_hal_context_t *hal)
{
hal->regs = &SOC_ETM;
}
void etm_hal_deinit(etm_hal_context_t *hal)
{
hal->regs = NULL;
}

View File

@ -0,0 +1,46 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*******************************************************************************
* NOTICE
* The hal is not public api, don't use in application code.
* See readme.md in hal/include/hal/readme.md
******************************************************************************/
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct soc_etm_dev_t *etm_soc_handle_t; // ETM SOC layer handle
/**
* @brief HAL context type of ETM driver
*/
typedef struct {
etm_soc_handle_t regs; /*!< ETM Register base address */
} etm_hal_context_t;
/**
* @brief Initialize the ETM HAL driver
*
* @param hal: ETM HAL context
*/
void etm_hal_init(etm_hal_context_t *hal);
/**
* @brief Deinitialize the ETM HAL driver
*
* @param hal: ETM HAL context
*/
void etm_hal_deinit(etm_hal_context_t *hal);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,31 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief GDMA channel events that supported by the ETM module
*/
typedef enum {
GDMA_ETM_EVENT_EOF, /*!< Event that the GDMA engine meets EOF descriptor */
GDMA_ETM_EVENT_MAX, /*!< Maximum number of events */
} gdma_etm_event_type_t;
/**
* @brief GDMA channel tasks that supported by the ETM module
*/
typedef enum {
GDMA_ETM_TASK_START, /*!< Start the GDMA machine */
GDMA_ETM_TASK_MAX, /*!< Maximum number of events */
} gdma_etm_task_type_t;
#ifdef __cplusplus
}
#endif

View File

@ -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

View File

@ -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;
}

View File

@ -23,6 +23,10 @@ config SOC_TWAI_SUPPORTED
bool
default y
config SOC_ETM_SUPPORTED
bool
default y
config SOC_BT_SUPPORTED
bool
default y
@ -243,6 +247,18 @@ config SOC_GDMA_PAIRS_PER_GROUP
int
default 3
config SOC_GDMA_SUPPORT_ETM
bool
default y
config SOC_ETM_GROUPS
int
default 1
config SOC_ETM_CHANNELS_PER_GROUP
int
default 50
config SOC_GPIO_PORT
int
default 1
@ -251,6 +267,18 @@ config SOC_GPIO_PIN_COUNT
int
default 31
config SOC_GPIO_SUPPORT_ETM
bool
default y
config SOC_GPIO_ETM_EVENTS_PER_GROUP
int
default 8
config SOC_GPIO_ETM_TASKS_PER_GROUP
int
default 8
config SOC_GPIO_SUPPORT_RTC_INDEPENDENT
bool
default y
@ -675,6 +703,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

View File

@ -14,3 +14,4 @@
#define SOC_GDMA_TRIG_PERIPH_AES0 (6)
#define SOC_GDMA_TRIG_PERIPH_SHA0 (7)
#define SOC_GDMA_TRIG_PERIPH_ADC0 (8)
#define SOC_GDMA_TRIG_PERIPH_PARLIO0 (9)

View File

@ -155,314 +155,7 @@ typedef union {
uint32_t reserved_28:4;
};
uint32_t val;
} gpio_etm_task_p0_cfg_reg_t;
/** Type of etm_task_p1_cfg register
* Etm Configure Register to decide which GPIO been chosen
*/
typedef union {
struct {
/** etm_task_gpio4_en : R/W; bitpos: [0]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio4_en:1;
/** etm_task_gpio4_sel : R/W; bitpos: [3:1]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio4_sel:3;
uint32_t reserved_4:4;
/** etm_task_gpio5_en : R/W; bitpos: [8]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio5_en:1;
/** etm_task_gpio5_sel : R/W; bitpos: [11:9]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio5_sel:3;
uint32_t reserved_12:4;
/** etm_task_gpio6_en : R/W; bitpos: [16]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio6_en:1;
/** etm_task_gpio6_sel : R/W; bitpos: [19:17]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio6_sel:3;
uint32_t reserved_20:4;
/** etm_task_gpio7_en : R/W; bitpos: [24]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio7_en:1;
/** etm_task_gpio7_sel : R/W; bitpos: [27:25]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio7_sel:3;
uint32_t reserved_28:4;
};
uint32_t val;
} gpio_etm_task_p1_cfg_reg_t;
/** Type of etm_task_p2_cfg register
* Etm Configure Register to decide which GPIO been chosen
*/
typedef union {
struct {
/** etm_task_gpio8_en : R/W; bitpos: [0]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio8_en:1;
/** etm_task_gpio8_sel : R/W; bitpos: [3:1]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio8_sel:3;
uint32_t reserved_4:4;
/** etm_task_gpio9_en : R/W; bitpos: [8]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio9_en:1;
/** etm_task_gpio9_sel : R/W; bitpos: [11:9]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio9_sel:3;
uint32_t reserved_12:4;
/** etm_task_gpio10_en : R/W; bitpos: [16]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio10_en:1;
/** etm_task_gpio10_sel : R/W; bitpos: [19:17]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio10_sel:3;
uint32_t reserved_20:4;
/** etm_task_gpio11_en : R/W; bitpos: [24]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio11_en:1;
/** etm_task_gpio11_sel : R/W; bitpos: [27:25]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio11_sel:3;
uint32_t reserved_28:4;
};
uint32_t val;
} gpio_etm_task_p2_cfg_reg_t;
/** Type of etm_task_p3_cfg register
* Etm Configure Register to decide which GPIO been chosen
*/
typedef union {
struct {
/** etm_task_gpio12_en : R/W; bitpos: [0]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio12_en:1;
/** etm_task_gpio12_sel : R/W; bitpos: [3:1]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio12_sel:3;
uint32_t reserved_4:4;
/** etm_task_gpio13_en : R/W; bitpos: [8]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio13_en:1;
/** etm_task_gpio13_sel : R/W; bitpos: [11:9]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio13_sel:3;
uint32_t reserved_12:4;
/** etm_task_gpio14_en : R/W; bitpos: [16]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio14_en:1;
/** etm_task_gpio14_sel : R/W; bitpos: [19:17]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio14_sel:3;
uint32_t reserved_20:4;
/** etm_task_gpio15_en : R/W; bitpos: [24]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio15_en:1;
/** etm_task_gpio15_sel : R/W; bitpos: [27:25]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio15_sel:3;
uint32_t reserved_28:4;
};
uint32_t val;
} gpio_etm_task_p3_cfg_reg_t;
/** Type of etm_task_p4_cfg register
* Etm Configure Register to decide which GPIO been chosen
*/
typedef union {
struct {
/** etm_task_gpio16_en : R/W; bitpos: [0]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio16_en:1;
/** etm_task_gpio16_sel : R/W; bitpos: [3:1]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio16_sel:3;
uint32_t reserved_4:4;
/** etm_task_gpio17_en : R/W; bitpos: [8]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio17_en:1;
/** etm_task_gpio17_sel : R/W; bitpos: [11:9]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio17_sel:3;
uint32_t reserved_12:4;
/** etm_task_gpio18_en : R/W; bitpos: [16]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio18_en:1;
/** etm_task_gpio18_sel : R/W; bitpos: [19:17]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio18_sel:3;
uint32_t reserved_20:4;
/** etm_task_gpio19_en : R/W; bitpos: [24]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio19_en:1;
/** etm_task_gpio19_sel : R/W; bitpos: [27:25]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio19_sel:3;
uint32_t reserved_28:4;
};
uint32_t val;
} gpio_etm_task_p4_cfg_reg_t;
/** Type of etm_task_p5_cfg register
* Etm Configure Register to decide which GPIO been chosen
*/
typedef union {
struct {
/** etm_task_gpio20_en : R/W; bitpos: [0]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio20_en:1;
/** etm_task_gpio20_sel : R/W; bitpos: [3:1]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio20_sel:3;
uint32_t reserved_4:4;
/** etm_task_gpio21_en : R/W; bitpos: [8]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio21_en:1;
/** etm_task_gpio21_sel : R/W; bitpos: [11:9]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio21_sel:3;
uint32_t reserved_12:4;
/** etm_task_gpio22_en : R/W; bitpos: [16]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio22_en:1;
/** etm_task_gpio22_sel : R/W; bitpos: [19:17]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio22_sel:3;
uint32_t reserved_20:4;
/** etm_task_gpio23_en : R/W; bitpos: [24]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio23_en:1;
/** etm_task_gpio23_sel : R/W; bitpos: [27:25]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio23_sel:3;
uint32_t reserved_28:4;
};
uint32_t val;
} gpio_etm_task_p5_cfg_reg_t;
/** Type of etm_task_p6_cfg register
* Etm Configure Register to decide which GPIO been chosen
*/
typedef union {
struct {
/** etm_task_gpio24_en : R/W; bitpos: [0]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio24_en:1;
/** etm_task_gpio24_sel : R/W; bitpos: [3:1]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio24_sel:3;
uint32_t reserved_4:4;
/** etm_task_gpio25_en : R/W; bitpos: [8]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio25_en:1;
/** etm_task_gpio25_sel : R/W; bitpos: [11:9]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio25_sel:3;
uint32_t reserved_12:4;
/** etm_task_gpio26_en : R/W; bitpos: [16]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio26_en:1;
/** etm_task_gpio26_sel : R/W; bitpos: [19:17]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio26_sel:3;
uint32_t reserved_20:4;
/** etm_task_gpio27_en : R/W; bitpos: [24]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio27_en:1;
/** etm_task_gpio27_sel : R/W; bitpos: [27:25]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio27_sel:3;
uint32_t reserved_28:4;
};
uint32_t val;
} gpio_etm_task_p6_cfg_reg_t;
/** Type of etm_task_p7_cfg register
* Etm Configure Register to decide which GPIO been chosen
*/
typedef union {
struct {
/** etm_task_gpio28_en : R/W; bitpos: [0]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio28_en:1;
/** etm_task_gpio28_sel : R/W; bitpos: [3:1]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio28_sel:3;
uint32_t reserved_4:4;
/** etm_task_gpio29_en : R/W; bitpos: [8]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio29_en:1;
/** etm_task_gpio29_sel : R/W; bitpos: [11:9]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio29_sel:3;
uint32_t reserved_12:4;
/** etm_task_gpio30_en : R/W; bitpos: [16]; default: 0;
* Enable bit of GPIO response etm task.
*/
uint32_t etm_task_gpio30_en:1;
/** etm_task_gpio30_sel : R/W; bitpos: [19:17]; default: 0;
* GPIO choose a etm task channel.
*/
uint32_t etm_task_gpio30_sel:3;
uint32_t reserved_20:12;
};
uint32_t val;
} gpio_etm_task_p7_cfg_reg_t;
} gpio_etm_task_pn_cfg_reg_t;
/** Group: Version Register */
/** Type of version register
@ -491,17 +184,10 @@ typedef struct {
volatile gpio_glitch_filter_chn_reg_t glitch_filter_chn[8];
} gpio_glitch_filter_dev_t;
typedef struct {
typedef struct gpio_etm_dev_t {
volatile gpio_etm_event_chn_cfg_reg_t event_chn_cfg[8];
uint32_t reserved_080[8];
volatile gpio_etm_task_p0_cfg_reg_t etm_task_p0_cfg;
volatile gpio_etm_task_p1_cfg_reg_t etm_task_p1_cfg;
volatile gpio_etm_task_p2_cfg_reg_t etm_task_p2_cfg;
volatile gpio_etm_task_p3_cfg_reg_t etm_task_p3_cfg;
volatile gpio_etm_task_p4_cfg_reg_t etm_task_p4_cfg;
volatile gpio_etm_task_p5_cfg_reg_t etm_task_p5_cfg;
volatile gpio_etm_task_p6_cfg_reg_t etm_task_p6_cfg;
volatile gpio_etm_task_p7_cfg_reg_t etm_task_p7_cfg;
volatile gpio_etm_task_pn_cfg_reg_t etm_task_pn_cfg[8];
} gpio_etm_dev_t;
typedef struct gpio_ext_dev_t {
@ -517,6 +203,7 @@ typedef struct gpio_ext_dev_t {
extern gpio_sd_dev_t SDM;
extern gpio_glitch_filter_dev_t GLITCH_FILTER;
extern gpio_etm_dev_t GPIO_ETM;
extern gpio_ext_dev_t GPIO_EXT;
#ifndef __cplusplus
_Static_assert(sizeof(gpio_ext_dev_t) == 0x100, "Invalid size of gpio_ext_dev_t structure");

View File

@ -40,6 +40,7 @@ typedef enum {
PERIPH_DS_MODULE,
PERIPH_GDMA_MODULE,
PERIPH_MCPWM0_MODULE,
PERIPH_ETM_MODULE,
PERIPH_SYSTIMER_MODULE,
PERIPH_SARADC_MODULE,
PERIPH_TEMPSENSOR_MODULE,

View File

@ -31,6 +31,7 @@
#define SOC_PCNT_SUPPORTED 1
#define SOC_MCPWM_SUPPORTED 1
#define SOC_TWAI_SUPPORTED 1
#define SOC_ETM_SUPPORTED 1
#define SOC_BT_SUPPORTED 1
#define SOC_ASYNC_MEMCPY_SUPPORTED 1
#define SOC_USB_SERIAL_JTAG_SUPPORTED 1
@ -136,16 +137,25 @@
See TRM DS chapter for more details */
#define SOC_DS_KEY_CHECK_MAX_WAIT_US (1100)
// TODO: IDF-5319 (Copy from esp32c3, need check)
/*-------------------------- GDMA CAPS -------------------------------------*/
#define SOC_GDMA_GROUPS (1U) // Number of GDMA groups
#define SOC_GDMA_PAIRS_PER_GROUP (3) // Number of GDMA pairs in each group
#define SOC_GDMA_SUPPORT_ETM (1) // Support ETM submodule
/*-------------------------- ETM CAPS --------------------------------------*/
#define SOC_ETM_GROUPS 1U // Number of ETM groups
#define SOC_ETM_CHANNELS_PER_GROUP 50 // Number of ETM channels in the group
/*-------------------------- GPIO CAPS ---------------------------------------*/
// ESP32-C6 has 1 GPIO peripheral
#define SOC_GPIO_PORT (1U)
#define SOC_GPIO_PIN_COUNT (31)
// GPIO peripheral has the ETM extension
#define SOC_GPIO_SUPPORT_ETM 1
#define SOC_GPIO_ETM_EVENTS_PER_GROUP 8
#define SOC_GPIO_ETM_TASKS_PER_GROUP 8
// Target has the full LP IO subsystem
// On ESP32-C6, Digital IOs have their own registers to control pullup/down capability, independent of LP registers.
#define SOC_GPIO_SUPPORT_RTC_INDEPENDENT (1)
@ -339,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)

View File

@ -46,7 +46,7 @@ PROVIDE ( HMAC = 0x6008D000 );
PROVIDE ( IO_MUX = 0x60090000 );
PROVIDE ( GPIO = 0x60091000 );
PROVIDE ( GPIO_EXT = 0x60091f00 ); /*ESP32C6-TODO*/
PROVIDE ( GPIO_EXT = 0x60091f00 );
PROVIDE ( SDM = 0x60091f00 );
PROVIDE ( GLITCH_FILTER = 0x60091f30 );
PROVIDE ( GPIO_ETM = 0x60091f60 );

View File

@ -0,0 +1,22 @@
blockdiag etm_channel {
default_fontsize = 12;
node_width = 200;
node_height = 40;
event_gpio [label = "GPIO Event", shape = "beginpoint", stacked];
other_events [shape = "dots"];
event_others [label = "Periph Event", shape = "beginpoint", stacked];
channels0 [label = "Channel-N", stacked];
channels1 [label = "Channel-M", stacked];
other_channels [shape = "dots"];
task_gpio [label = "GPIO Task", shape = "endpoint", stacked];
task_others [label = "Other Tasks", shape = "endpoint", stacked];
other_tasks [shape = "dots"];
event_gpio -> channels0;
event_others -> channels1;
other_events -> other_channels;
channels0 -> task_gpio;
channels1 -> task_others;
other_channels -> other_tasks;
}

View File

@ -80,6 +80,8 @@ RMT_DOCS = ['api-reference/peripherals/rmt.rst']
DAC_DOCS = ['api-reference/peripherals/dac.rst']
ETM_DOCS = ['api-reference/peripherals/etm.rst']
TEMP_SENSOR_DOCS = ['api-reference/peripherals/temp_sensor.rst']
TOUCH_SENSOR_DOCS = ['api-reference/peripherals/touch_pad.rst']
@ -164,6 +166,7 @@ conditional_include_dict = {'SOC_BT_SUPPORTED':BT_DOCS,
'SOC_PCNT_SUPPORTED':PCNT_DOCS,
'SOC_RMT_SUPPORTED':RMT_DOCS,
'SOC_DAC_SUPPORTED':DAC_DOCS,
'SOC_ETM_SUPPORTED':ETM_DOCS,
'SOC_TOUCH_SENSOR_SUPPORTED':TOUCH_SENSOR_DOCS,
'SOC_ULP_SUPPORTED':ULP_DOCS,
'SOC_RISCV_COPROC_SUPPORTED':RISCV_COPROC_DOCS,

View File

@ -100,7 +100,6 @@ api-reference/peripherals/hmac
api-reference/peripherals/usb_device
api-reference/peripherals/sdspi_host
api-reference/peripherals/dac
api-reference/peripherals/gptimer
api-reference/peripherals/touch_element
api-reference/peripherals/lcd
api-reference/peripherals/secure_element

View File

@ -67,7 +67,10 @@ INPUT = \
$(PROJECT_PATH)/components/driver/include/driver/dac_types.h \
$(PROJECT_PATH)/components/driver/include/driver/dedic_gpio.h \
$(PROJECT_PATH)/components/driver/include/driver/gpio.h \
$(PROJECT_PATH)/components/driver/include/driver/gpio_etm.h \
$(PROJECT_PATH)/components/driver/include/driver/gptimer.h \
$(PROJECT_PATH)/components/driver/include/driver/gptimer_etm.h \
$(PROJECT_PATH)/components/driver/include/driver/gptimer_types.h \
$(PROJECT_PATH)/components/driver/include/driver/i2c.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_common.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s_pdm.h \
@ -133,6 +136,7 @@ INPUT = \
$(PROJECT_PATH)/components/esp_hw_support/include/esp_chip_info.h \
$(PROJECT_PATH)/components/esp_hw_support/include/esp_cpu.h \
$(PROJECT_PATH)/components/esp_hw_support/include/esp_crc.h \
$(PROJECT_PATH)/components/esp_hw_support/include/esp_etm.h \
$(PROJECT_PATH)/components/esp_hw_support/include/esp_hmac.h \
$(PROJECT_PATH)/components/esp_hw_support/include/esp_intr_alloc.h \
$(PROJECT_PATH)/components/esp_hw_support/include/esp_mac.h \
@ -160,6 +164,7 @@ INPUT = \
$(PROJECT_PATH)/components/esp_system/include/esp_ipc_isr.h \
$(PROJECT_PATH)/components/esp_system/include/esp_ipc.h \
$(PROJECT_PATH)/components/esp_system/include/esp_system.h \
$(PROJECT_PATH)/components/esp_system/include/esp_systick_etm.h \
$(PROJECT_PATH)/components/esp_system/include/esp_task_wdt.h \
$(PROJECT_PATH)/components/esp_timer/include/esp_timer.h \
$(PROJECT_PATH)/components/esp_wifi/include/esp_mesh.h \

View File

@ -0,0 +1,148 @@
Event Task Matrix (ETM)
=======================
Introduction
------------
Normally, if a peripheral X needs to notify peripheral Y of a particular event, this could only be done via a CPU interrupt from peripheral X (where the CPU notifies peripheral Y on behalf of peripheral X). However, in time critical applications, the latency introduced by CPU interrupts is non-negligible. The Event Task Matrix (ETM) module allows subset of peripherals to notify each other of events directly (i.e., without CPU intervention). This allows precise (and low latency) synchronization between peripherals, and lessens the CPU's work load (as the CPU no longer needs handle these events).
.. blockdiag:: /../_static/diagrams/etm/etm_channel.diag
:caption: ETM channels Overview
:align: center
The ETM module has multiple programmable channels, they're used to connect a particular **Event** to a particular **Task**. When an event is activated, the ETM channel will trigger the corresponding task automatically. Peripherals that support ETM functionality will provide their or unique set of events and tasks to be connected by the ETM. An ETM channel can connect any event to any task (even looping back an event to a task of on the same peripheral). However, an ETM channel can only connect one event to one task any time (i.e., 1 to 1 relation). If you want to use different events to trigger the same task, you can set up more ETM channels.
Typically, with the help of ETM module, you can implement features like:
- Toggle the GPIO when a timer alarm event happens
- Start an ADC conversion when a pulse edge is detected on a GPIO
Functional Overview
-------------------
The following sections of this document cover the typical steps to configure and use the ETM module.
- :ref:`etm-channel-allocation` - describes how to install and uninstall ETM channel
- :ref:`etm-event` - describes how to allocate a new ETM event handle or fetch an existing handle from various peripherals
- :ref:`etm-task` - describes how to allocate a new ETM task handle or fetch an existing handle from various peripherals
- :ref:`etm-channel-control` - describes common ETM channel control functions
- :ref:`etm-thread-safety` - lists which APIs are guaranteed to be thread safe by the driver.
- :ref:`etm-kconfig-options` - lists the supported Kconfig options that can be used to make a different effect on driver behavior
.. _etm-channel-allocation:
ETM Channel Allocation
^^^^^^^^^^^^^^^^^^^^^^
There're many identical ETM channels in {IDF_TARGET_NAME} [1]_, each channel is represented by :cpp:type:`esp_etm_channel_handle_t` in the software. The ETM core driver manages all available hardware resources in a pool, so that you don't need to care about which channel is in use and which is not. The ETM core driver will allocate a channel for you when you call :cpp:func:`esp_etm_new_channel` and delete it when you call :cpp:func:`esp_etm_del_channel`. All requirements needed for allocating a channel are provided in :cpp:type:`esp_etm_channel_config_t`.
Before deleting an ETM channel, please disable it by :cpp:func:`esp_etm_channel_disable` in advance or make sure it has not been enabled yet by :cpp:func:`esp_etm_channel_enable`.
.. _etm-event:
ETM Event
^^^^^^^^^
ETM Event abstracts the event source and is represented by :cpp:type:`esp_etm_event_handle_t` in the software. ETM event can be generated from a variety of peripherals, thus the way to get the event handle differs from peripherals. When an ETM event is no longer used, you should call :cpp:func:`esp_etm_channel_connect` with a ``NULL`` event handle to disconnect it and then call :cpp:func:`esp_etm_del_event` to free the event resource.
GPIO Events
~~~~~~~~~~~
GPIO **edge** event is the most common event type, it can be generated by any GPIO pin. You can call :cpp:func:`gpio_new_etm_event` to create a GPIO event handle, with the configurations provided in :cpp:type:`gpio_etm_event_config_t`:
- :cpp:member:`gpio_etm_event_config_t::edge` decides which edge will trigger the event, supported edge types are listed in the :cpp:type:`gpio_etm_event_edge_t`.
You need to build a connection between the GPIO ETM event handle and the GPIO number. So you should call :cpp:func:`gpio_etm_event_bind_gpio` afterwards. Please note, only the ETM event handle that created by :cpp:func:`gpio_new_etm_event` can set a GPIO number. Calling this function with other kind of ETM event will return :c:macro:`ESP_ERR_INVALID_ARG` error. Needless to say, this function won't help do the GPIO initialization, you still need to call :cpp:func:`gpio_config` to set the property like direction, pull up/down mode separately.
Other Peripheral Events
~~~~~~~~~~~~~~~~~~~~~~~
.. list::
:SOC_SYSTIMER_SUPPORT_ETM: - You can call :cpp:func:`esp_systick_new_etm_alarm_event` to get the ETM event from RTOS Systick, one per CPU core.
:SOC_SYSTIMER_SUPPORT_ETM: - Refer to :doc:`ESP Timer </api-reference/system/esp_timer>` for how to get the ETM event handle from esp_timer.
:SOC_TIMER_SUPPORT_ETM: - Refer to :doc:`GPTimer </api-reference/peripherals/gptimer>` for how to get the ETM event handle from GPTimer.
:SOC_GDMA_SUPPORT_ETM: - Refer to :doc:`Async Memory Copy </api-reference/system/async_memcpy>` for how to get the ETM event handle from async memcpy.
.. _etm-task:
ETM Task
^^^^^^^^
ETM Task abstracts the task action and is represented by :cpp:type:`esp_etm_task_handle_t` in the software. ETM task can be assigned to a variety of peripherals, thus the way to get the task handle differs from peripherals. When an ETM task is no longer used, you should call :cpp:func:`esp_etm_channel_connect` with a ``NULL`` task handle to disconnect it and then call :cpp:func:`esp_etm_del_task` to free the task resource.
GPIO Tasks
~~~~~~~~~~
GPIO task is the most common task type, one GPIO task can even manage multiple GPIOs. When tha task gets activated by the ETM channel, all managed GPIOs can set/clear/toggle at the same time. You can call :cpp:func:`gpio_new_etm_task` to create a GPIO task handle, with the configurations provided in :cpp:type:`gpio_etm_task_config_t`:
- :cpp:member:`gpio_etm_task_config_t::action` decides what the GPIO action would be taken by the ETM task. Supported actions are listed in the :cpp:type:`gpio_etm_task_action_t`.
To build a connection between the GPIO ETM task and the GPIO number, you should call :cpp:func:`gpio_etm_task_add_gpio`. You can call this function by several times if you want the task handle to manage more GPIOs. Please note, only the ETM task handle that created by :cpp:func:`gpio_new_etm_task` can manage a GPIO. Calling this function with other kind of ETM task will return :c:macro:`ESP_ERR_INVALID_ARG` error. Needless to say, this function won't help do the GPIO initialization, you still need to call :cpp:func:`gpio_config` to set the property like direction, pull up/down mode separately.
Before you call :cpp:func:`esp_etm_del_task` to delete the GPIO ETM task, make sure that all previously added GPIOs are removed by :cpp:func:`gpio_etm_task_rm_gpio` in advance.
Other Peripheral Tasks
~~~~~~~~~~~~~~~~~~~~~~
.. list::
:SOC_TIMER_SUPPORT_ETM: - Refer to :doc:`GPTimer </api-reference/peripherals/gptimer>` for how to get the ETM task handle from GPTimer.
.. _etm-channel-control:
ETM Channel Control
^^^^^^^^^^^^^^^^^^^
Connect Event and Task
~~~~~~~~~~~~~~~~~~~~~~
An ETM event has no association with an ETM task, until they're connected to the same ETM channel by calling :cpp:func:`esp_etm_channel_connect`. Specially, calling the function with a ``NULL`` task/event handle, means to disconnect the channel from any task or event. Note that, this function can be called either before or after the channel is enabled. But calling this function at runtime to change the connection can be dangerous, because the channel may be in the middle of a cycle, and the new connection may not take effect immediately.
Enable and Disable Channel
~~~~~~~~~~~~~~~~~~~~~~~~~~
You can call :cpp:func:`esp_etm_channel_enable` and :cpp:func:`esp_etm_channel_disable` to enable and disable the ETM channel from working.
ETM Channel Profiling
~~~~~~~~~~~~~~~~~~~~~
To check if the ETM channels are set with proper events and tasks, you can call :cpp:func:`esp_etm_dump` to dump all working ETM channels with their associated events and tasks. The dumping format is like:
::
===========ETM Dump Start==========
channel 0: event 48 ==> task 17
channel 1: event 48 ==> task 90
channel 2: event 48 ==> task 94
===========ETM Dump End============
The digital ID printed in the dump information is defined in the ``soc/soc_etm_source.h`` file.
.. _etm-thread-safety:
Thread Safety
^^^^^^^^^^^^^
The factory functions like :cpp:func:`esp_etm_new_channel` and :cpp:func:`gpio_new_etm_task` are guaranteed to be thread safe by the driver, which means, you can call it from different RTOS tasks without protection by extra locks.
No functions are allowed to run within ISR environment.
Other functions that take :cpp:type:`esp_etm_channel_handle_t`, :cpp:type:`esp_etm_task_handle_t` and :cpp:type:`esp_etm_event_handle_t` as the first positional parameter, are not treated as thread safe, which means you should avoid calling them from multiple tasks.
.. _etm-kconfig-options:
Kconfig Options
^^^^^^^^^^^^^^^
- :ref:`CONFIG_ETM_ENABLE_DEBUG_LOG` is used to enabled the debug log output. Enable this option will increase the firmware binary size as well.
API Reference
-------------
.. include-build-file:: inc/esp_etm.inc
.. include-build-file:: inc/gpio_etm.inc
.. include-build-file:: inc/esp_systick_etm.inc
.. [1]
Different ESP chip series might have different numbers of ETM channels. For more details, please refer to *{IDF_TARGET_NAME} Technical Reference Manual* > Chapter *Event Task Matrix (ETM)* [`PDF <{IDF_TARGET_TRM_EN_URL}#evntaskmatrix>`__]. The driver will not forbid you from applying for more channels, but it will return error when all available hardware resources are used up. Please always check the return value when doing channel allocation (i.e. :cpp:func:`esp_etm_new_channel`).

View File

@ -17,16 +17,19 @@ Functional Overview
The following sections of this document cover the typical steps to install and operate a timer:
- :ref:`gptimer-resource-allocation` - covers which parameters should be set up to get a timer handle and how to recycle the resources when GPTimer finishes working.
- :ref:`set-and-get-count-value` - covers how to force the timer counting from a start point and how to get the count value at anytime.
- :ref:`set-up-alarm-action` - covers the parameters that should be set up to enable the alarm event.
- :ref:`gptimer-register-event-callbacks` - covers how to hook user specific code to the alarm event callback function.
- :ref:`enable-and-disable-timer` - covers how to enable and disable the timer.
- :ref:`start-and-stop-timer` - shows some typical use cases that start the timer with different alarm behavior.
- :ref:`gptimer-power-management` - describes how different source clock selections can affect power consumption.
- :ref:`gptimer-iram-safe` - describes tips on how to make the timer interrupt and IO control functions work better along with a disabled cache.
- :ref:`gptimer-thread-safety` - lists which APIs are guaranteed to be thread safe by the driver.
- :ref:`gptimer-kconfig-options` - lists the supported Kconfig options that can be used to make a different effect on driver behavior.
.. list::
- :ref:`gptimer-resource-allocation` - covers which parameters should be set up to get a timer handle and how to recycle the resources when GPTimer finishes working.
- :ref:`set-and-get-count-value` - covers how to force the timer counting from a start point and how to get the count value at anytime.
- :ref:`set-up-alarm-action` - covers the parameters that should be set up to enable the alarm event.
- :ref:`gptimer-register-event-callbacks` - covers how to hook user specific code to the alarm event callback function.
- :ref:`enable-and-disable-timer` - covers how to enable and disable the timer.
- :ref:`start-and-stop-timer` - shows some typical use cases that start the timer with different alarm behavior.
:SOC_TIMER_SUPPORT_ETM: - :ref:`gptimer-etm-event-and-task` - describes what the events and tasks can be connected to the ETM channel.
- :ref:`gptimer-power-management` - describes how different source clock selections can affect power consumption.
- :ref:`gptimer-iram-safe` - describes tips on how to make the timer interrupt and IO control functions work better along with a disabled cache.
- :ref:`gptimer-thread-safety` - lists which APIs are guaranteed to be thread safe by the driver.
- :ref:`gptimer-kconfig-options` - lists the supported Kconfig options that can be used to make a different effect on driver behavior.
.. _gptimer-resource-allocation:
@ -40,7 +43,6 @@ A GPTimer instance is represented by :cpp:type:`gptimer_handle_t`. The driver be
To install a timer instance, there is a configuration structure that needs to be given in advance: :cpp:type:`gptimer_config_t`:
- :cpp:member:`gptimer_config_t::clk_src` selects the source clock for the timer. The available clocks are listed in :cpp:type:`gptimer_clock_source_t`, you can only pick one of them. For the effect on power consumption of different clock source, please refer to Section :ref:`gptimer-power-management`.
- :cpp:member:`gptimer_config_t::direction` sets the counting direction of the timer, supported directions are listed in :cpp:type:`gptimer_count_direction_t`, you can only pick one of them.
- :cpp:member:`gptimer_config_t::resolution_hz` sets the resolution of the internal counter. Each count step is equivalent to **1 / resolution_hz** seconds.
@ -256,7 +258,23 @@ Alarm value can be updated dynamically inside the ISR handler callback, by chang
ESP_ERROR_CHECK(gptimer_enable(gptimer));
ESP_ERROR_CHECK(gptimer_start(gptimer, &alarm_config));
.. _gptimer-power-management:
.. only:: SOC_TIMER_SUPPORT_ETM
.. _gptimer-etm-event-and-task:
ETM Event and Task
^^^^^^^^^^^^^^^^^^
GPTimer is able to generate various events that can interact with the :doc:`ETM </api-reference/peripherals/etm>` module. The supported events are listed in the :cpp:type:`gptimer_etm_event_type_t`. You can call :cpp:func:`gptimer_new_etm_event` to get the corresponding ETM event handle. Likewise, GPTimer exposes several tasks that can be triggered by other ETM events. The supported tasks are listed in the :cpp:type:`gptimer_etm_task_type_t`. You can call :cpp:func:`gptimer_new_etm_task` to get the corresponding ETM task handle.
For how to connect the event and task to an ETM channel, please refer to the :doc:`ETM </api-reference/peripherals/etm>` documentation.
.. _gptimer-power-management:
.. only:: not SOC_TIMER_SUPPORT_ETM
.. _gptimer-power-management:
Power Management
^^^^^^^^^^^^^^^^
@ -319,12 +337,17 @@ Kconfig Options
Application Examples
--------------------
* Typical use cases of GPTimer are listed in the example :example:`peripherals/timer_group/gptimer`.
.. list::
- Typical use cases of GPTimer are listed in the example :example:`peripherals/timer_group/gptimer`.
:SOC_TIMER_SUPPORT_ETM: - GPTimer capture external event's timestamp, with the help of ETM module: :example:`peripherals/timer_group/gptimer_capture_hc_sr04`.
API Reference
-------------
.. include-build-file:: inc/gptimer.inc
.. include-build-file:: inc/gptimer_etm.inc
.. include-build-file:: inc/gptimer_types.inc
.. include-build-file:: inc/timer_types.inc
.. [1]

View File

@ -11,6 +11,7 @@ Peripherals API
:SOC_ADC_SUPPORTED: adc_calibration
clk_tree
:SOC_DAC_SUPPORTED: dac
:SOC_ETM_SUPPORTED: etm
gpio
gptimer
:SOC_DEDICATED_GPIO_SUPPORTED: dedic_gpio

View File

@ -74,6 +74,15 @@ Uninstall driver (optional)
:cpp:func:`esp_async_memcpy_uninstall` is used to uninstall asynchronous memcpy driver. It's not necessary to uninstall the driver after each memcpy operation. If you know your application won't use this driver anymore, then this API can recycle the memory for you.
.. only:: SOC_GDMA_SUPPORT_ETM
ETM Event
---------
Async memory copy is able to generate an event when one async memcpy operation is done. This event can be used to interact with the :doc:`ETM </api-reference/peripherals/etm>` module. You can call :cpp:func:`esp_async_memcpy_new_etm_event` to get the ETM event handle.
For how to connect the event to an ETM channel, please refer to the :doc:`ETM </api-reference/peripherals/etm>` documentation.
API Reference
-------------

View File

@ -61,6 +61,15 @@ Callback functions
Timer callbacks which are processed by ``ESP_TIMER_ISR`` method should not call the context switch call - ``portYIELD_FROM_ISR()``, instead of this you should use the :cpp:func:`esp_timer_isr_dispatch_need_yield` function.
The context switch will be done after all ISR dispatch timers have been processed, if required by the system.
.. only:: SOC_SYSTIMER_SUPPORT_ETM
ETM Event
---------
The esp_timer is constructed based on a hardware timer called *systimer*, which is able to generate the alarm event and interact with the :doc:`ETM </api-reference/peripherals/etm>` module. You can call :cpp:func:`esp_timer_new_etm_alarm_event` to get the corresponding ETM event handle.
For how to connect the event to an ETM channel, please refer to the :doc:`ETM </api-reference/peripherals/etm>` documentation.
esp_timer during the light sleep
--------------------------------

View File

@ -0,0 +1 @@
.. include:: ../../../en/api-reference/peripherals/etm.rst

View File

@ -17,16 +17,19 @@
下文介绍了配置和操作定时器的常规步骤:
- :ref:`gptimer-resource-allocation` - 获取定时器句柄应设置的参数,以及如何在通用定时器完成工作时回收资源。
- :ref:`set-and-get-count-value` - 如何强制定时器从起点开始计数,以及如何随时获取计数值。
- :ref:`set-up-alarm-action` - 启动警报事件应设置的参数。
- :ref:`gptimer-register-event-callbacks` - 如何将用户的特定代码挂载到警报事件回调函数。
- :ref:`enable-and-disable-timer` - 如何使能和禁用定时器。
- :ref:`start-and-stop-timer` - 通过不同报警行为启动定时器的典型使用场景。
- :ref:`gptimer-power-management` - 选择不同的时钟源将会如何影响功耗。
- :ref:`gptimer-iram-safe` - 在 cache 禁用的情况下,如何更好地让定时器处理中断事务以及实现 IO 控制功能。
- :ref:`gptimer-thread-safety` - 驱动程序保证哪些 API 线程安全。
- :ref:`gptimer-kconfig-options` - 支持的 Kconfig 选项,这些选项会对驱动程序行为产生不同影响。
.. list::
- :ref:`gptimer-resource-allocation` - 获取定时器句柄应设置的参数,以及如何在通用定时器完成工作时回收资源。
- :ref:`set-and-get-count-value` - 如何强制定时器从起点开始计数,以及如何随时获取计数值。
- :ref:`set-up-alarm-action` - 启动警报事件应设置的参数。
- :ref:`gptimer-register-event-callbacks` - 如何将用户的特定代码挂载到警报事件回调函数。
- :ref:`enable-and-disable-timer` - 如何使能和禁用定时器。
- :ref:`start-and-stop-timer` - 通过不同报警行为启动定时器的典型使用场景。
:SOC_TIMER_SUPPORT_ETM: - :ref:`gptimer-etm-event-and-task` - 定时器提供了哪些事件和任务可以连接到 ETM 通道上。
- :ref:`gptimer-power-management` - 选择不同的时钟源将会如何影响功耗。
- :ref:`gptimer-iram-safe` - 在 cache 禁用的情况下,如何更好地让定时器处理中断事务以及实现 IO 控制功能。
- :ref:`gptimer-thread-safety` - 驱动程序保证哪些 API 线程安全。
- :ref:`gptimer-kconfig-options` - 支持的 Kconfig 选项,这些选项会对驱动程序行为产生不同影响。
.. _gptimer-resource-allocation:
@ -256,7 +259,23 @@
ESP_ERROR_CHECK(gptimer_enable(gptimer));
ESP_ERROR_CHECK(gptimer_start(gptimer, &alarm_config));
.. _gptimer-power-management:
.. only:: SOC_TIMER_SUPPORT_ETM
.. _gptimer-etm-event-and-task:
ETM 事件与任务
^^^^^^^^^^^^^^
定时器可以产生多种事件,这些事件可以连接到 :doc:`ETM </api-reference/peripherals/etm>` 模块。:cpp:type:`gptimer_etm_event_type_t` 中列出了定时器能够产生的事件类型。用户可以通过调用 :cpp:func:`gptimer_new_etm_event` 来获得相应事件的 ETM event 句柄。同样地,定时器还公开了一些可被其他事件触发然后自动执行的任务。:cpp:type:`gptimer_etm_task_type_t` 中列出了定时器能够支持的任务类型。 用户可以通过调用 :cpp:func:`gptimer_new_etm_task` 来获得相应任务的 ETM task 句柄。
关于如何将定时器事件和任务连接到 ETM 通道中,请参阅 :doc:`ETM </api-reference/peripherals/etm>` 文档。
.. _gptimer-power-management:
.. only:: not SOC_TIMER_SUPPORT_ETM
.. _gptimer-power-management:
电源管理
^^^^^^^^^^^^^^^^^
@ -319,12 +338,17 @@ Kconfig 选项
应用示例
------------------
* 示例 :example:`peripherals/timer_group/gptimer` 中列出了通用定时器的典型用例。
.. list::
- 示例 :example:`peripherals/timer_group/gptimer` 中列出了通用定时器的典型用例。
:SOC_TIMER_SUPPORT_ETM: - 示例 :example:`peripherals/timer_group/gptimer_capture_hc_sr04` 展示了如何在 ETM 模块的帮助下,用定时器捕获外部事件的时间戳。
API 参考
-------------------
.. include-build-file:: inc/gptimer.inc
.. include-build-file:: inc/gptimer_etm.inc
.. include-build-file:: inc/gptimer_types.inc
.. include-build-file:: inc/timer_types.inc
.. [1]

View File

@ -11,6 +11,7 @@
:SOC_ADC_SUPPORTED: adc_calibration
clk_tree
:SOC_DAC_SUPPORTED: dac
:SOC_ETM_SUPPORTED: etm
gpio
gptimer
:SOC_DEDICATED_GPIO_SUPPORTED: dedic_gpio

View File

@ -193,6 +193,10 @@ examples/peripherals/temp_sensor:
disable:
- if: SOC_TEMP_SENSOR_SUPPORTED != 1
examples/peripherals/timer_group/gptimer_capture_hc_sr04:
disable:
- if: SOC_TIMER_SUPPORT_ETM != 1
examples/peripherals/touch_sensor:
disable:
- if: SOC_TOUCH_SENSOR_SUPPORTED != 1

View File

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(gptimer_etm_capture)

View File

@ -0,0 +1,86 @@
| Supported Targets | ESP32-C6 |
| ----------------- | -------- |
# HC-SR04 Example based on GPTimer Capture and ETM
(See the README.md file in the upper level 'examples' directory for more information about examples.)
The general purpose timer can capture internal timer count value into dedicated registers. This feature can be used to measure the time between two events. With the help of ETM (Event Task Matrix) peripheral, we can route different events to trigger the GPTimer's capture task. In this example, we configure one GPIO to detect any edge signals and then notify the GPTimer to capture the timestamp at once, all done by hardware.
This example also shows how to decode the pulse width signals generated from a common HC-SR04 sonar sensor -- [HC-SR04](https://www.sparkfun.com/products/15569).
The signal that HC-SR04 produces (and what can be handled by this example) is a simple pulse whose width indicates the measured distance. An excitation pulse is required to send to HC-SR04 on `Trig` pin to begin a new measurement. Then the pulse described above will appear on the `Echo` pin after a while.
Typical signals:
```text
Trig +-----+
| |
| |
-----+ +-----------------------
Echo +-----+
| |
| |
-----------------+ +-----------
+--------------------------------------->
Timeline
```
## How to Use Example
### Hardware Required
* A development board with ESP SOC chip that supports ETM peripheral
* An HC-SR04 sensor module
Connection :
```text
+------+ +--------------------------------------+
+-------+ | | |
| | VCC +--------------+ 5V |
+-------+ | | |
+ Echo +----=====>----+ HC_SR04_ECHO_GPIO (internal pull up) |
| | | |
+ Trig +----<=====----+ HC_SR04_TRIG_GPIO |
+-------| | | |
| | GND +--------------+ GND |
+-------| | | |
+------+ +--------------------------------------+
```
You can change the GPIO number according to your board, by `HC_SR04_TRIG_GPIO` and `HC_SR04_ECHO_GPIO` in the [source code](main/gptimer_capture_hc_sr04.c).
### Build and Flash
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Example Output
```text
I (339) cpu_start: Starting scheduler.
I (344) example: Configure trig gpio
I (344) gpio: GPIO[0]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
I (354) example: Configure echo gpio
I (354) gpio: GPIO[1]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:3
I (364) example: Create etm event handle for echo gpio
I (374) example: Create gptimer handle
I (384) example: Get gptimer etm task handle (capture)
I (384) example: Create ETM channel then connect gpio event and gptimer task
I (394) example: Install GPIO edge interrupt
I (404) example: Enable etm channel and gptimer
I (404) example: Start gptimer
I (424) example: Measured distance: 93.40cm
I (934) example: Measured distance: 103.93cm
I (1444) example: Measured distance: 102.09cm
I (1954) example: Measured distance: 122.47cm
```
## Troubleshooting
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "gptimer_capture_hc_sr04.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,151 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gptimer.h"
#include "driver/gpio.h"
#include "driver/gpio_etm.h"
#include "esp_log.h"
static const char *TAG = "example";
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your board spec ////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define HC_SR04_TRIG_GPIO 0
#define HC_SR04_ECHO_GPIO 1
#define EXAMPLE_GPTIMER_RESOLUTION_HZ 1000000 // 1MHz, 1 tick = 1us
/**
* @brief User defined context, to be passed to GPIO ISR callback function.
*/
typedef struct {
gptimer_handle_t gptimer;
TaskHandle_t task_to_notify;
gpio_num_t echo_gpio;
} gpio_callback_user_data_t;
/**
* @brief GPIO ISR callback function, called when there's any edge detected on the GPIO.
*/
static void hc_sr04_echo_callback(void *user_data)
{
static uint64_t cap_val_end_of_sample = 0;
static uint64_t cap_val_begin_of_sample = 0;
gpio_callback_user_data_t *callback_user_data = (gpio_callback_user_data_t *)user_data;
gpio_num_t echo_gpio = callback_user_data->echo_gpio;
TaskHandle_t task_to_notify = callback_user_data->task_to_notify;
gptimer_handle_t gptimer = callback_user_data->gptimer;
if (gpio_get_level(echo_gpio) == 1) {
// store the timestamp when pos edge is detected
gptimer_get_captured_count(gptimer, &cap_val_begin_of_sample);
cap_val_end_of_sample = cap_val_begin_of_sample;
} else {
gptimer_get_captured_count(gptimer, &cap_val_end_of_sample);
uint32_t tof_ticks = (uint32_t)(cap_val_end_of_sample - cap_val_begin_of_sample);
// notify the task to calculate the distance
xTaskNotifyFromISR(task_to_notify, tof_ticks, eSetValueWithOverwrite, NULL);
}
}
/**
* @brief generate single pulse on Trig pin to start a new sample
*/
static void gen_trig_output(void)
{
gpio_set_level(HC_SR04_TRIG_GPIO, 1); // set high
esp_rom_delay_us(10);
gpio_set_level(HC_SR04_TRIG_GPIO, 0); // set low
}
void app_main(void)
{
ESP_LOGI(TAG, "Configure trig gpio");
gpio_config_t trig_io_conf = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << HC_SR04_TRIG_GPIO,
};
ESP_ERROR_CHECK(gpio_config(&trig_io_conf));
// drive low by default
ESP_ERROR_CHECK(gpio_set_level(HC_SR04_TRIG_GPIO, 0));
ESP_LOGI(TAG, "Configure echo gpio");
gpio_config_t echo_io_conf = {
.mode = GPIO_MODE_INPUT,
.intr_type = GPIO_INTR_ANYEDGE, // capture signal on both edge
.pull_up_en = true, // pull up internally
.pin_bit_mask = 1ULL << HC_SR04_ECHO_GPIO,
};
ESP_ERROR_CHECK(gpio_config(&echo_io_conf));
ESP_LOGI(TAG, "Create etm event handle for echo gpio");
esp_etm_event_handle_t gpio_event = NULL;
gpio_etm_event_config_t gpio_event_config = {
.edge = GPIO_ETM_EVENT_EDGE_ANY, // capture signal on both edge
};
ESP_ERROR_CHECK(gpio_new_etm_event(&gpio_event_config, &gpio_event));
ESP_ERROR_CHECK(gpio_etm_event_bind_gpio(gpio_event, HC_SR04_ECHO_GPIO));
ESP_LOGI(TAG, "Create gptimer handle");
gptimer_handle_t gptimer = NULL;
gptimer_config_t timer_config = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = EXAMPLE_GPTIMER_RESOLUTION_HZ,
};
ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));
ESP_LOGI(TAG, "Get gptimer etm task handle (capture)");
esp_etm_task_handle_t gptimer_capture_task = NULL;
ESP_ERROR_CHECK(gptimer_new_etm_task(gptimer, GPTIMER_ETM_TASK_CAPTURE, &gptimer_capture_task));
ESP_LOGI(TAG, "Create ETM channel then connect gpio event and gptimer task");
esp_etm_channel_handle_t etm_chan = NULL;
esp_etm_channel_config_t etm_chan_config = {};
ESP_ERROR_CHECK(esp_etm_new_channel(&etm_chan_config, &etm_chan));
// GPIO any edge ==> ETM channel ==> GPTimer capture task
ESP_ERROR_CHECK(esp_etm_channel_connect(etm_chan, gpio_event, gptimer_capture_task));
ESP_LOGI(TAG, "Install GPIO edge interrupt");
ESP_ERROR_CHECK(gpio_install_isr_service(0));
gpio_callback_user_data_t callback_user_data = {
.gptimer = gptimer,
.echo_gpio = HC_SR04_ECHO_GPIO,
.task_to_notify = xTaskGetCurrentTaskHandle(),
};
ESP_ERROR_CHECK(gpio_isr_handler_add(HC_SR04_ECHO_GPIO, hc_sr04_echo_callback, &callback_user_data));
ESP_LOGI(TAG, "Enable etm channel and gptimer");
ESP_ERROR_CHECK(esp_etm_channel_enable(etm_chan));
ESP_ERROR_CHECK(gptimer_enable(gptimer));
// Print the ETM channel usage
ESP_ERROR_CHECK(esp_etm_dump(stdout));
ESP_LOGI(TAG, "Start gptimer");
ESP_ERROR_CHECK(gptimer_start(gptimer));
uint32_t tof_ticks;
while (1) {
// trigger the sensor to start a new sample
gen_trig_output();
// wait for echo done signal
if (xTaskNotifyWait(0x00, ULONG_MAX, &tof_ticks, pdMS_TO_TICKS(1000)) == pdTRUE) {
if (tof_ticks > 35000) {
// out of range
continue;
}
// convert the pulse width into measure distance
float distance = (float) tof_ticks / 58.0f;
ESP_LOGI(TAG, "Measured distance: %.2fcm", distance);
}
vTaskDelay(pdMS_TO_TICKS(500));
}
}

View File

@ -0,0 +1,19 @@
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32c6
@pytest.mark.generic
def test_gptimer_capture(dut: Dut) -> None:
dut.expect_exact('Configure trig gpio')
dut.expect_exact('Configure echo gpio')
dut.expect_exact('Create etm event handle for echo gpio')
dut.expect_exact('Create gptimer handle')
dut.expect_exact('Get gptimer etm task handle (capture)')
dut.expect_exact('Create ETM channel then connect gpio event and gptimer task')
dut.expect_exact('Install GPIO edge interrupt')
dut.expect_exact('Enable etm channel and gptimer')
dut.expect_exact('Start gptimer')

View File

@ -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

View File

@ -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)