mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
gdma: support etm extension
This commit is contained in:
parent
560ea9b754
commit
356c6bb528
@ -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
components/esp_hw_support/dma/gdma_etm.c
Normal file
121
components/esp_hw_support/dma/gdma_etm.c
Normal 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
components/esp_hw_support/dma/gdma_priv.h
Normal file
84
components/esp_hw_support/dma/gdma_priv.h
Normal 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}
|
||||
|
88
components/esp_hw_support/test_apps/etm/main/test_gdma_etm.c
Normal file
88
components/esp_hw_support/test_apps/etm/main/test_gdma_etm.c
Normal 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);
|
||||
}
|
@ -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
components/hal/include/hal/gdma_types.h
Normal file
31
components/hal/include/hal/gdma_types.h
Normal 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
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user