mirror of
https://github.com/espressif/esp-idf.git
synced 2026-05-04 20:05:25 +02:00
esp32s2: add CP_DMA driver
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 );
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user