gdma: support etm extension

This commit is contained in:
morris
2022-11-23 15:32:34 +08:00
parent 560ea9b754
commit 356c6bb528
17 changed files with 512 additions and 77 deletions
+7 -3
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)
@@ -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
@@ -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;
@@ -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;
@@ -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
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;
}
+84
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
@@ -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
@@ -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,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)
@@ -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}
@@ -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);
}