morris 3 лет назад
Родитель
Сommit
356c6bb528

+ 7 - 3
components/esp_hw_support/CMakeLists.txt

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

+ 9 - 0
components/esp_hw_support/port/async_memcpy_impl_cp_dma.c → components/esp_hw_support/dma/async_memcpy_impl_cp_dma.c

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

+ 10 - 0
components/esp_hw_support/port/async_memcpy_impl_gdma.c → components/esp_hw_support/dma/async_memcpy_impl_gdma.c

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

+ 6 - 0
components/esp_hw_support/esp_async_memcpy.c → components/esp_hw_support/dma/esp_async_memcpy.c

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

+ 3 - 67
components/esp_hw_support/gdma.c → components/esp_hw_support/dma/gdma.c

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

+ 121 - 0
components/esp_hw_support/dma/gdma_etm.c

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

+ 84 - 0
components/esp_hw_support/dma/gdma_priv.h

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

+ 27 - 2
components/esp_hw_support/include/esp_async_memcpy.h

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

+ 35 - 1
components/esp_hw_support/include/esp_private/gdma.h

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

+ 12 - 0
components/esp_hw_support/port/include/esp_async_memcpy_impl.h

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

+ 4 - 0
components/esp_hw_support/test_apps/etm/main/CMakeLists.txt

@@ -13,6 +13,10 @@ 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}

+ 88 - 0
components/esp_hw_support/test_apps/etm/main/test_gdma_etm.c

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

+ 69 - 3
components/hal/esp32c6/include/hal/gdma_ll.h

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

+ 31 - 0
components/hal/include/hal/gdma_types.h

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

+ 4 - 0
components/soc/esp32c6/include/soc/Kconfig.soc_caps.in

@@ -247,6 +247,10 @@ config SOC_GDMA_PAIRS_PER_GROUP
     int
     default 3
 
+config SOC_GDMA_SUPPORT_ETM
+    bool
+    default y
+
 config SOC_ETM_GROUPS
     int
     default 1

+ 1 - 0
components/soc/esp32c6/include/soc/gdma_channel.h

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

+ 1 - 1
components/soc/esp32c6/include/soc/soc_caps.h

@@ -137,10 +137,10 @@
     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