esp32s2: add CP_DMA driver

This commit is contained in:
morris
2020-07-28 20:15:13 +08:00
parent 6434c1e2bd
commit b30bd7a2ef
19 changed files with 2511 additions and 746 deletions
+1
View File
@@ -14,6 +14,7 @@ else()
set(srcs "cache_err_int.c"
"memprot.c"
"clk.c"
"cp_dma.c"
"crosscore_int.c"
"dport_access.c"
"hw_random.c"
+235
View File
@@ -0,0 +1,235 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <sys/cdefs.h>
#include <stdatomic.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "esp_compiler.h"
#include "esp_intr_alloc.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "soc/soc_caps.h"
#include "soc/cp_dma_caps.h"
#include "hal/cp_dma_hal.h"
#include "hal/cp_dma_ll.h"
#include "cp_dma.h"
#include "soc/periph_defs.h"
static const char *TAG = "cp_dma";
#define CP_DMA_CHECK(a, msg, tag, ret, ...) \
do { \
if (unlikely(!(a))) { \
ESP_LOGE(TAG, "%s(%d): " msg, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
ret_code = ret; \
goto tag; \
} \
} while (0)
/**
* @brief Stream is high level abstraction over descriptor.
* It combines the descriptor used by DMA and the callback function registered by user.
* The benifit is, we can converter the descriptor address into stream handle.
*/
typedef struct {
cp_dma_descriptor_t tx_desc;
cp_dma_isr_cb_t cb;
void *cb_args;
} cp_dma_out_stream_t;
typedef struct {
cp_dma_descriptor_t rx_desc;
cp_dma_isr_cb_t cb;
void *cb_args;
} cp_dma_in_stream_t;
typedef struct cp_dma_driver_context_s {
uint32_t max_out_stream;
uint32_t max_in_stream;
uint32_t flags;
cp_dma_hal_context_t hal; // HAL context
intr_handle_t intr_hdl; // interrupt handle
portMUX_TYPE spin_lock;
cp_dma_out_stream_t *out_streams; // pointer to the first out stream
cp_dma_in_stream_t *in_streams; // pointer to the first in stream
uint8_t streams[0]; // stream buffer (out streams + in streams), the size if configured by user
} cp_dma_driver_context_t;
static void cp_dma_isr_default_handler(void *arg) IRAM_ATTR;
esp_err_t cp_dma_driver_install(const cp_dma_config_t *config, cp_dma_driver_t *drv_hdl)
{
esp_err_t ret_code = ESP_OK;
cp_dma_driver_context_t *cp_dma_driver = NULL;
CP_DMA_CHECK(config, "configuration can't be null", err, ESP_ERR_INVALID_ARG);
CP_DMA_CHECK(drv_hdl, "driver handle can't be null", err, ESP_ERR_INVALID_ARG);
size_t total_malloc_size = sizeof(cp_dma_driver_context_t) + sizeof(cp_dma_out_stream_t) * config->max_out_stream + sizeof(cp_dma_in_stream_t) * config->max_in_stream;
if (config->flags & CP_DMA_FLAGS_WORK_WITH_CACHE_DISABLED) {
// to work when cache is disabled, make sure to put driver handle in DRAM
cp_dma_driver = heap_caps_calloc(1, total_malloc_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
} else {
cp_dma_driver = calloc(1, total_malloc_size);
}
CP_DMA_CHECK(cp_dma_driver, "allocate driver memory failed", err, ESP_ERR_NO_MEM);
int int_flags = 0;
if (config->flags & CP_DMA_FLAGS_WORK_WITH_CACHE_DISABLED) {
int_flags |= ESP_INTR_FLAG_IRAM; // make sure interrupt can still work when cache is disabled
}
ret_code = esp_intr_alloc(ETS_DMA_COPY_INTR_SOURCE, int_flags, cp_dma_isr_default_handler, cp_dma_driver, &cp_dma_driver->intr_hdl);
CP_DMA_CHECK(ret_code == ESP_OK, "allocate intr failed", err, ret_code);
cp_dma_driver->out_streams = (cp_dma_out_stream_t *)cp_dma_driver->streams;
cp_dma_driver->in_streams = (cp_dma_in_stream_t *)(cp_dma_driver->streams + config->max_out_stream * sizeof(cp_dma_out_stream_t));
// HAL layer has no idea about "data stream" but TX/RX descriptors
// We put all descritprs' addresses into an array, HAL driver will make it a loop during initialization
{
cp_dma_descriptor_t *tx_descriptors[config->max_out_stream];
cp_dma_descriptor_t *rx_descriptors[config->max_in_stream];
for (int i = 0; i < config->max_out_stream; i++) {
tx_descriptors[i] = &cp_dma_driver->out_streams[i].tx_desc;
}
for (int i = 0; i < config->max_in_stream; i++) {
rx_descriptors[i] = &cp_dma_driver->in_streams[i].rx_desc;
}
cp_dma_hal_init(&cp_dma_driver->hal, tx_descriptors, config->max_out_stream, rx_descriptors, config->max_in_stream);
} // limit the scope of tx_descriptors and rx_descriptors so that goto can jump after this code block
cp_dma_driver->spin_lock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
cp_dma_driver->max_in_stream = config->max_in_stream;
cp_dma_driver->max_out_stream = config->max_out_stream;
*drv_hdl = cp_dma_driver;
cp_dma_hal_start(&cp_dma_driver->hal); // enable DMA and interrupt
return ESP_OK;
err:
if (cp_dma_driver) {
if (cp_dma_driver->intr_hdl) {
esp_intr_free(cp_dma_driver->intr_hdl);
}
free(cp_dma_driver);
}
if (drv_hdl) {
*drv_hdl = NULL;
}
return ret_code;
}
esp_err_t cp_dma_driver_uninstall(cp_dma_driver_t drv_hdl)
{
esp_err_t ret_code = ESP_OK;
CP_DMA_CHECK(drv_hdl, "driver handle can't be null", err, ESP_ERR_INVALID_ARG);
esp_intr_free(drv_hdl->intr_hdl);
cp_dma_hal_stop(&drv_hdl->hal);
cp_dma_hal_deinit(&drv_hdl->hal);
free(drv_hdl);
return ESP_OK;
err:
return ret_code;
}
esp_err_t cp_dma_memcpy(cp_dma_driver_t drv_hdl, void *dst, void *src, size_t n, cp_dma_isr_cb_t cb_isr, void *cb_args)
{
esp_err_t ret_code = ESP_OK;
cp_dma_descriptor_t *rx_start_desc = NULL;
cp_dma_descriptor_t *rx_end_desc = NULL;
cp_dma_descriptor_t *tx_start_desc = NULL;
cp_dma_descriptor_t *tx_end_desc = NULL;
int rx_prepared_size = 0;
int tx_prepared_size = 0;
CP_DMA_CHECK(drv_hdl, "driver handle can't be null", err, ESP_ERR_INVALID_ARG);
// CP_DMA can only access SRAM
CP_DMA_CHECK(esp_ptr_internal(src) && esp_ptr_internal(dst), "address not in SRAM", err, ESP_ERR_INVALID_ARG);
CP_DMA_CHECK(n <= SOC_CP_DMA_MAX_BUFFER_SIZE * drv_hdl->max_out_stream, "exceed max num of tx stream", err, ESP_ERR_INVALID_ARG);
CP_DMA_CHECK(n <= SOC_CP_DMA_MAX_BUFFER_SIZE * drv_hdl->max_in_stream, "exceed max num of rx stream", err, ESP_ERR_INVALID_ARG);
if (cb_isr && (drv_hdl->flags & CP_DMA_FLAGS_WORK_WITH_CACHE_DISABLED)) {
CP_DMA_CHECK(esp_ptr_in_iram(cb_isr), "callback(%p) not in IRAM", err, ESP_ERR_INVALID_ARG, cb_isr);
}
// Prepare TX and RX descriptor
portENTER_CRITICAL_SAFE(&drv_hdl->spin_lock);
// prepare functions will not change internal status of HAL until cp_dma_hal_restart_* are called
rx_prepared_size = cp_dma_hal_prepare_receive(&drv_hdl->hal, dst, n, &rx_start_desc, &rx_end_desc);
tx_prepared_size = cp_dma_hal_prepare_transmit(&drv_hdl->hal, src, n, &tx_start_desc, &tx_end_desc);
if ((rx_prepared_size == n) && (tx_prepared_size == n)) {
// register user callback to the end-of-frame descriptor (must before we restart RX)
cp_dma_in_stream_t *data_stream_rx = __containerof(rx_end_desc, cp_dma_in_stream_t, rx_desc);
data_stream_rx->cb = cb_isr;
data_stream_rx->cb_args = cb_args;
// The restart should be called with the exact returned start and end desc from previous successful prepare calls
cp_dma_hal_restart_rx(&drv_hdl->hal, rx_start_desc, rx_end_desc);
cp_dma_hal_restart_tx(&drv_hdl->hal, tx_start_desc, tx_end_desc);
}
portEXIT_CRITICAL_SAFE(&drv_hdl->spin_lock);
CP_DMA_CHECK(rx_prepared_size == n, "out of rx descriptor", err, ESP_FAIL);
// It's unlikely that we have space for rx descriptor but no space for tx descriptor
// Because in CP_DMA, both tx and rx descriptor should move in the same pace
CP_DMA_CHECK(tx_prepared_size == n, "out of tx descriptor", err, ESP_FAIL);
return ESP_OK;
err:
return ret_code;
}
/**
* @brief Default ISR handler provided by ESP-IDF
*/
static void cp_dma_isr_default_handler(void *args)
{
cp_dma_driver_context_t *cp_dma_driver = (cp_dma_driver_context_t *)args;
cp_dma_in_stream_t *in_stream = NULL;
cp_dma_descriptor_t *next_desc = NULL;
bool need_yield = false;
bool to_continue = false;
portENTER_CRITICAL_ISR(&cp_dma_driver->spin_lock);
uint32_t status = cp_dma_hal_get_intr_status(&cp_dma_driver->hal);
cp_dma_hal_clear_intr_status(&cp_dma_driver->hal, status);
portEXIT_CRITICAL_ISR(&cp_dma_driver->spin_lock);
ESP_EARLY_LOGD(TAG, "intr status=0x%x", status);
// End-Of-Frame on RX side
if (status & CP_DMA_LL_EVENT_RX_EOF) {
cp_dma_descriptor_t *eof = (cp_dma_descriptor_t *)cp_dma_ll_get_rx_eof_descriptor_address(cp_dma_driver->hal.dev);
// traversal all unchecked descriptors
do {
portENTER_CRITICAL_ISR(&cp_dma_driver->spin_lock);
// There is an assumption that the usage of rx descriptors are in the same pace as tx descriptors (this is determined by CP DMA working mechanism)
// And once the rx descriptor is recycled, the corresponding tx desc is guaranteed to be returned by DMA
to_continue = cp_dma_hal_get_next_rx_descriptor(&cp_dma_driver->hal, eof, &next_desc);
portEXIT_CRITICAL_ISR(&cp_dma_driver->spin_lock);
if (next_desc) {
in_stream = __containerof(next_desc, cp_dma_in_stream_t, rx_desc);
// invoke user registered callback if available
if (in_stream->cb) {
cp_dma_event_t e = {.id = CP_DMA_EVENT_M2M_DONE};
if (in_stream->cb(cp_dma_driver, &e, in_stream->cb_args)) {
need_yield = true;
}
in_stream->cb = NULL;
in_stream->cb_args = NULL;
}
}
} while (to_continue);
}
if (need_yield) {
portYIELD_FROM_ISR();
}
}
+126
View File
@@ -0,0 +1,126 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include "esp_err.h"
/**
* @brief Handle of CP_DMA driver
*
*/
typedef struct cp_dma_driver_context_s *cp_dma_driver_t;
/**
* @brief CP_DMA event ID
*
*/
typedef enum {
CP_DMA_EVENT_M2M_DONE, /*!< One or more memory copy transactions are done */
} cp_dma_event_id_t;
/**
* @brief Type defined for CP_DMA event object (including event ID, event data)
*
*/
typedef struct {
cp_dma_event_id_t id; /*!< Event ID */
void *data; /*!< Event data */
} cp_dma_event_t;
/**
* @brief Type defined for cp_dma ISR callback function
*
* @param drv_hdl Handle of CP_DMA driver
* @param event Event object, which contains the event ID, event data, and so on
* @param cb_args User defined arguments for the callback function. It's passed in cp_dma_memcpy function
* @return Whether a high priority task is woken up by the callback function
*
*/
typedef bool (*cp_dma_isr_cb_t)(cp_dma_driver_t drv_hdl, cp_dma_event_t *event, void *cb_args);
/**
* @brief Type defined for configuration of CP_DMA driver
*
*/
typedef struct {
uint32_t max_out_stream; /*!< maximum number of out link streams that can work simultaneously */
uint32_t max_in_stream; /*!< maximum number of in link streams that can work simultaneously */
uint32_t flags; /*!< Extra flags to control some special behaviour of CP_DMA, OR'ed of CP_DMA_FLAGS_xxx macros */
} cp_dma_config_t;
#define CP_DMA_FLAGS_WORK_WITH_CACHE_DISABLED (1 << 0) /*!< CP_DMA can work even when cache is diabled */
/**
* @brief Default configuration for CP_DMA driver
*
*/
#define CP_DMA_DEFAULT_CONFIG() \
{ \
.max_out_stream = 8, \
.max_in_stream = 8, \
.flags = 0, \
}
/**
* @brief Install CP_DMA driver
*
* @param[in] config Configuration of CP_DMA driver
* @param[out] drv_hdl Returned handle of CP_DMA driver or NULL if driver installation failed
* @return
* - ESP_OK: Install CP_DMA driver successfully
* - ESP_ERR_INVALID_ARG: Install CP_DMA driver failed because of some invalid argument
* - ESP_ERR_NO_MEM: Install CP_DMA driver failed because there's no enough capable memory
* - ESP_FAIL: Install CP_DMA driver failed because of other error
*/
esp_err_t cp_dma_driver_install(const cp_dma_config_t *config, cp_dma_driver_t *drv_hdl);
/**
* @brief Uninstall CP_DMA driver
*
* @param[in] drv_hdl Handle of CP_DMA driver that returned from cp_dma_driver_install
* @return
* - ESP_OK: Uninstall CP_DMA driver successfully
* - ESP_ERR_INVALID_ARG: Uninstall CP_DMA driver failed because of some invalid argument
* - ESP_FAIL: Uninstall CP_DMA driver failed because of other error
*/
esp_err_t cp_dma_driver_uninstall(cp_dma_driver_t drv_hdl);
/**
* @brief Send an asynchronous memory copy request
*
* @param[in] drv_hdl Handle of CP_DMA driver that returned from cp_dma_driver_install
* @param[in] dst Destination address (copy to)
* @param[in] src Source address (copy from)
* @param[in] n Number of bytes to copy
* @param[in] cb_isr Callback function, which got invoked in ISR context. A NULL pointer here can bypass the callback.
* @param[in] cb_args User defined argument to be passed to the callback function
* @return
* - ESP_OK: Send memcopy request successfully
* - ESP_ERR_INVALID_ARG: Send memcopy request failed because of some invalid argument
* - ESP_FAIL: Send memcopy request failed because of other error
*
* @note The callback function is invoked in ISR context, please never handle heavy load in the callback.
* The default ISR handler is placed in IRAM, please place callback function in IRAM as well by applying IRAM_ATTR to it.
*/
esp_err_t cp_dma_memcpy(cp_dma_driver_t drv_hdl, void *dst, void *src, size_t n, cp_dma_isr_cb_t cb_isr, void *cb_args);
#ifdef __cplusplus
}
#endif
+1 -1
View File
@@ -17,7 +17,7 @@ PROVIDE ( RMTMEM = 0x3f416400 );
PROVIDE ( PCNT = 0x3f417000 );
PROVIDE ( SLC = 0x3f418000 );
PROVIDE ( LEDC = 0x3f419000 );
PROVIDE ( MCP = 0x3f4c3000 );
PROVIDE ( CP_DMA = 0x3f4c3000 );
PROVIDE ( TIMERG0 = 0x3f41F000 );
PROVIDE ( TIMERG1 = 0x3f420000 );
PROVIDE ( GPSPI2 = 0x3f424000 );
+184
View File
@@ -0,0 +1,184 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include "esp_heap_caps.h"
#include "esp_rom_sys.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "unity.h"
#include "test_utils.h"
#include "ccomp_timer.h"
#include "soc/cp_dma_caps.h"
#include "cp_dma.h"
#define ALIGN_UP(addr, align) (((addr) + (align)-1) & ~((align)-1))
static void cp_dma_setup_testbench(uint32_t seed, uint32_t *buffer_size, uint8_t **src_buf, uint8_t **dst_buf, uint8_t **from_addr, uint8_t **to_addr, uint32_t align)
{
srand(seed);
printf("allocating memory buffer...\r\n");
// memory copy from/to PSRAM is not allowed
*src_buf = heap_caps_malloc(*buffer_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
*dst_buf = heap_caps_calloc(1, *buffer_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
TEST_ASSERT_NOT_NULL_MESSAGE(*src_buf, "allocate source buffer failed");
TEST_ASSERT_NOT_NULL_MESSAGE(*dst_buf, "allocate destination buffer failed");
*from_addr = (uint8_t *)ALIGN_UP((uint32_t)(*src_buf), 4);
*to_addr = (uint8_t *)ALIGN_UP((uint32_t)(*dst_buf), 4);
uint8_t gap = MAX(*from_addr - *src_buf, *to_addr - *dst_buf);
*buffer_size -= gap;
*from_addr += align;
*to_addr += align;
*buffer_size -= align;
printf("...size %d Bytes, src@%p, dst@%p\r\n", *buffer_size, *from_addr, *to_addr);
printf("fill src buffer with random data\r\n");
for (int i = 0; i < *buffer_size; i++) {
(*from_addr)[i] = rand() % 256;
}
}
static void cp_dma_verify_and_clear_testbench(uint32_t seed, uint32_t buffer_size, uint8_t *src_buf, uint8_t *dst_buf, uint8_t *from_addr, uint8_t *to_addr)
{
srand(seed);
for (int i = 0; i < buffer_size; i++) {
// check if source date has been copied to destination and source data not broken
TEST_ASSERT_EQUAL_MESSAGE(rand() % 256, to_addr[i], "destination data doesn't match generator data");
}
srand(seed);
for (int i = 0; i < buffer_size; i++) {
// check if source data has been copied to destination
TEST_ASSERT_EQUAL_MESSAGE(rand() % 256, to_addr[i], "destination data doesn't match source data");
}
free(src_buf);
free(dst_buf);
}
TEST_CASE("memory copy by DMA one by one", "[CP_DMA]")
{
cp_dma_config_t config = CP_DMA_DEFAULT_CONFIG();
config.max_in_stream = 4;
config.max_out_stream = 4;
cp_dma_driver_t driver = NULL;
TEST_ESP_OK(cp_dma_driver_install(&config, &driver));
uint32_t test_buffer_len[] = {256, 512, 1024, 2048, 4096, 5011};
uint8_t *sbuf = NULL;
uint8_t *dbuf = NULL;
uint8_t *from = NULL;
uint8_t *to = NULL;
for (int i = 0; i < sizeof(test_buffer_len) / sizeof(test_buffer_len[0]); i++) {
// Test different align edge
for (int align = 0; align < 4; align++) {
cp_dma_setup_testbench(i, &test_buffer_len[i], &sbuf, &dbuf, &from, &to, align);
TEST_ESP_OK(cp_dma_memcpy(driver, to, from, test_buffer_len[i], NULL, NULL));
cp_dma_verify_and_clear_testbench(i, test_buffer_len[i], sbuf, dbuf, from, to);
vTaskDelay(pdMS_TO_TICKS(100));
}
}
TEST_ESP_OK(cp_dma_driver_uninstall(driver));
}
TEST_CASE("memory copy by DMA on the fly", "[CP_DMA]")
{
cp_dma_config_t config = CP_DMA_DEFAULT_CONFIG();
config.max_in_stream = 4;
config.max_out_stream = 4;
cp_dma_driver_t driver = NULL;
TEST_ESP_OK(cp_dma_driver_install(&config, &driver));
uint32_t test_buffer_len[] = {512, 1024, 2048, 4096, 5011};
uint8_t *sbufs[] = {0, 0, 0, 0, 0};
uint8_t *dbufs[] = {0, 0, 0, 0, 0};
uint8_t *froms[] = {0, 0, 0, 0, 0};
uint8_t *tos[] = {0, 0, 0, 0, 0};
// Aligned case
for (int i = 0; i < sizeof(sbufs) / sizeof(sbufs[0]); i++) {
cp_dma_setup_testbench(i, &test_buffer_len[i], &sbufs[i], &dbufs[i], &froms[i], &tos[i], 0);
}
for (int i = 0; i < sizeof(test_buffer_len) / sizeof(test_buffer_len[0]); i++) {
TEST_ESP_OK(cp_dma_memcpy(driver, tos[i], froms[i], test_buffer_len[i], NULL, NULL));
}
for (int i = 0; i < sizeof(sbufs) / sizeof(sbufs[0]); i++) {
cp_dma_verify_and_clear_testbench(i, test_buffer_len[i], sbufs[i], dbufs[i], froms[i], tos[i]);
}
// Non-aligned case
for (int i = 0; i < sizeof(sbufs) / sizeof(sbufs[0]); i++) {
cp_dma_setup_testbench(i, &test_buffer_len[i], &sbufs[i], &dbufs[i], &froms[i], &tos[i], 3);
}
for (int i = 0; i < sizeof(test_buffer_len) / sizeof(test_buffer_len[0]); i++) {
TEST_ESP_OK(cp_dma_memcpy(driver, tos[i], froms[i], test_buffer_len[i], NULL, NULL));
}
for (int i = 0; i < sizeof(sbufs) / sizeof(sbufs[0]); i++) {
cp_dma_verify_and_clear_testbench(i, test_buffer_len[i], sbufs[i], dbufs[i], froms[i], tos[i]);
}
TEST_ESP_OK(cp_dma_driver_uninstall(driver));
}
#define TEST_CP_DMA_MECP_DMAY_BENCH_COUNTS (16)
static uint32_t test_cp_dma_memcpy_bench_len = 4096;
static int count = 0;
static IRAM_ATTR bool test_cp_dma_memcpy_cb(cp_dma_driver_t drv_hdl, cp_dma_event_t *event, void *cb_args)
{
SemaphoreHandle_t sem = (SemaphoreHandle_t)cb_args;
BaseType_t high_task_wakeup = pdFALSE;
switch (event->id) {
case CP_DMA_EVENT_M2M_DONE:
count++;
if (count == TEST_CP_DMA_MECP_DMAY_BENCH_COUNTS) {
xSemaphoreGiveFromISR(sem, &high_task_wakeup);
}
break;
default:
break;
}
return high_task_wakeup == pdTRUE;
}
TEST_CASE("memory copy by DMA with callback", "[CP_DMA][performance]")
{
SemaphoreHandle_t sem = xSemaphoreCreateBinary();
cp_dma_config_t config = CP_DMA_DEFAULT_CONFIG();
cp_dma_driver_t driver = NULL;
TEST_ESP_OK(cp_dma_driver_install(&config, &driver));
uint8_t *sbuf = NULL;
uint8_t *dbuf = NULL;
uint8_t *from = NULL;
uint8_t *to = NULL;
cp_dma_setup_testbench(0, &test_cp_dma_memcpy_bench_len, &sbuf, &dbuf, &from, &to, 0);
count = 0;
ccomp_timer_start();
for (int i = 0; i < TEST_CP_DMA_MECP_DMAY_BENCH_COUNTS; i++) {
TEST_ESP_OK(cp_dma_memcpy(driver, to, from, test_cp_dma_memcpy_bench_len, test_cp_dma_memcpy_cb, sem));
}
// wait for done semaphore
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(sem, pdMS_TO_TICKS(1000)));
esp_rom_printf("memcpy %d Bytes data by HW costs %lldus\r\n", test_cp_dma_memcpy_bench_len, ccomp_timer_stop() / TEST_CP_DMA_MECP_DMAY_BENCH_COUNTS);
ccomp_timer_start();
for (int i = 0; i < TEST_CP_DMA_MECP_DMAY_BENCH_COUNTS; i++) {
memcpy(to, from, test_cp_dma_memcpy_bench_len);
}
esp_rom_printf("memcpy %d Bytes data by SW costs %lldus\r\n", test_cp_dma_memcpy_bench_len, ccomp_timer_stop() / TEST_CP_DMA_MECP_DMAY_BENCH_COUNTS);
cp_dma_verify_and_clear_testbench(0, test_cp_dma_memcpy_bench_len, sbuf, dbuf, from, to);
TEST_ESP_OK(cp_dma_driver_uninstall(driver));
vSemaphoreDelete(sem);
}