diff --git a/components/esp_hw_support/CMakeLists.txt b/components/esp_hw_support/CMakeLists.txt index ceab3d741a..8bf80f7fa0 100644 --- a/components/esp_hw_support/CMakeLists.txt +++ b/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) diff --git a/components/esp_hw_support/port/async_memcpy_impl_cp_dma.c b/components/esp_hw_support/dma/async_memcpy_impl_cp_dma.c similarity index 90% rename from components/esp_hw_support/port/async_memcpy_impl_cp_dma.c rename to components/esp_hw_support/dma/async_memcpy_impl_cp_dma.c index 8d268a20b6..de74a9d5a0 100644 --- a/components/esp_hw_support/port/async_memcpy_impl_cp_dma.c +++ b/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 diff --git a/components/esp_hw_support/port/async_memcpy_impl_gdma.c b/components/esp_hw_support/dma/async_memcpy_impl_gdma.c similarity index 90% rename from components/esp_hw_support/port/async_memcpy_impl_gdma.c rename to components/esp_hw_support/dma/async_memcpy_impl_gdma.c index ee56d174a9..4bf879206d 100644 --- a/components/esp_hw_support/port/async_memcpy_impl_gdma.c +++ b/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; diff --git a/components/esp_hw_support/esp_async_memcpy.c b/components/esp_hw_support/dma/esp_async_memcpy.c similarity index 97% rename from components/esp_hw_support/esp_async_memcpy.c rename to components/esp_hw_support/dma/esp_async_memcpy.c index 6b245f2d7e..cd0001b5ec 100644 --- a/components/esp_hw_support/esp_async_memcpy.c +++ b/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; diff --git a/components/esp_hw_support/gdma.c b/components/esp_hw_support/dma/gdma.c similarity index 91% rename from components/esp_hw_support/gdma.c rename to components/esp_hw_support/dma/gdma.c index 6ffa28fc8b..10be324835 100644 --- a/components/esp_hw_support/gdma.c +++ b/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); diff --git a/components/esp_hw_support/dma/gdma_etm.c b/components/esp_hw_support/dma/gdma_etm.c new file mode 100644 index 0000000000..d959c8193e --- /dev/null +++ b/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 +#include +#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; +} diff --git a/components/esp_hw_support/dma/gdma_priv.h b/components/esp_hw_support/dma/gdma_priv.h new file mode 100644 index 0000000000..22c41b4c57 --- /dev/null +++ b/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 +#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 diff --git a/components/esp_hw_support/include/esp_async_memcpy.h b/components/esp_hw_support/include/esp_async_memcpy.h index c45c61b2d1..6d1f06571b 100644 --- a/components/esp_hw_support/include/esp_async_memcpy.h +++ b/components/esp_hw_support/include/esp_async_memcpy.h @@ -13,6 +13,7 @@ extern "C" { #include #include #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 diff --git a/components/esp_hw_support/include/esp_private/gdma.h b/components/esp_hw_support/include/esp_private/gdma.h index f888cd5bc9..1d678550b0 100644 --- a/components/esp_hw_support/include/esp_private/gdma.h +++ b/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 +#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 diff --git a/components/esp_hw_support/port/include/esp_async_memcpy_impl.h b/components/esp_hw_support/port/include/esp_async_memcpy_impl.h index ce9c1fb88c..9d1c76a41d 100644 --- a/components/esp_hw_support/port/include/esp_async_memcpy_impl.h +++ b/components/esp_hw_support/port/include/esp_async_memcpy_impl.h @@ -12,9 +12,11 @@ extern "C" { #include #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) diff --git a/components/esp_hw_support/test_apps/etm/main/CMakeLists.txt b/components/esp_hw_support/test_apps/etm/main/CMakeLists.txt index d4bd70187e..fb291cc16a 100644 --- a/components/esp_hw_support/test_apps/etm/main/CMakeLists.txt +++ b/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} diff --git a/components/esp_hw_support/test_apps/etm/main/test_gdma_etm.c b/components/esp_hw_support/test_apps/etm/main/test_gdma_etm.c new file mode 100644 index 0000000000..a550de0192 --- /dev/null +++ b/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 +#include +#include "unity.h" +#include "unity_test_utils.h" +#include "freertos/FreeRTOS.h" +#include "esp_attr.h" +#include "esp_etm.h" +#include "driver/gpio_etm.h" +#include "driver/gpio.h" +#include "esp_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); +} diff --git a/components/hal/esp32c6/include/hal/gdma_ll.h b/components/hal/esp32c6/include/hal/gdma_ll.h index f938c1600f..719c16f68f 100644 --- a/components/hal/esp32c6/include/hal/gdma_ll.h +++ b/components/hal/esp32c6/include/hal/gdma_ll.h @@ -8,8 +8,10 @@ #include /* Required for NULL constant */ #include #include +#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 diff --git a/components/hal/include/hal/gdma_types.h b/components/hal/include/hal/gdma_types.h new file mode 100644 index 0000000000..cd3c9f39c5 --- /dev/null +++ b/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 diff --git a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in index 483bc56d12..c3d9c9e1bc 100644 --- a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in +++ b/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 diff --git a/components/soc/esp32c6/include/soc/gdma_channel.h b/components/soc/esp32c6/include/soc/gdma_channel.h index de9e2dd659..6ae11185f8 100644 --- a/components/soc/esp32c6/include/soc/gdma_channel.h +++ b/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) diff --git a/components/soc/esp32c6/include/soc/soc_caps.h b/components/soc/esp32c6/include/soc/soc_caps.h index af3829b083..d1ed6f241f 100644 --- a/components/soc/esp32c6/include/soc/soc_caps.h +++ b/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