gdma: support etm extension

This commit is contained in:
morris 2022-09-13 18:47:08 +08:00
parent 560ea9b754
commit 356c6bb528
17 changed files with 512 additions and 77 deletions

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)

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

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

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

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

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

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

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

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

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