# 异步内存复制

[English]

## 概述

ESP32-H21 有一个 DMA 引擎，能够以异步方式帮助 CPU 完成内部内存复制操作。

异步 memcpy API 中封装了所有 DMA 配置和操作，`esp_async_memcpy()` 的签名与标准 C 库的 `memcpy` 函数基本相同。

DMA 允许多个内存复制请求在首个请求完成之前排队，即允许计算和内存复制的重叠。此外，通过注册事件回调函数，还可以知道内存复制请求完成的准确时间。

## 配置并安装驱动

安装异步 memcpy 驱动的方法取决于底层 DMA 引擎：

- `esp_async_memcpy_install_gdma_ahb()` 用于安装基于 AHB GDMA 引擎的异步 memcpy 驱动。

- `esp_async_memcpy_install()` 是一个通用 API，用于安装带有默认 DMA 引擎的异步 memcpy 驱动。如果 SoC 具有 CP DMA 引擎，则默认 DMA 引擎为 CP DMA，否则，默认 DMA 引擎为 AHB GDMA。

在 `async_memcpy_config_t` 中设置驱动配置：

- `backlog`：此项用于配置首个请求完成前可以排队的最大内存复制事务数量。如果将此字段设置为零，会应用默认值 4。

- `dma_burst_size`：设置单次 DMA 传输中突发数据量的大小。

- `flags`：此项可以启用一些特殊的驱动功能。

    async_memcpy_config_t config = ASYNC_MEMCPY_DEFAULT_CONFIG();
    // 更新底层 DMA 引擎支持的最大数据流
    config.backlog = 8;
    async_memcpy_handle_t driver = NULL;
    ESP_ERROR_CHECK(esp_async_memcpy_install(&config, &driver)); // 使用默认 DMA 引擎安装驱动

## 发送内存复制请求

使用 `esp_async_memcpy()` API 将内存复制请求发送到 DMA 引擎。在驱动程序成功安装后才能调用该 API。此 API 是线程安全的，因此可以从不同的任务中调用。

与 libc 版本的 `memcpy` 不同，你可以选择给 `esp_async_memcpy()` 设置一个回调函数，以便在内存复制完成时收到通知。注意，回调是在 ISR 上下文中执行的，请不要在回调中调用任何阻塞函数。

回调函数的原型是 `async_memcpy_isr_cb_t`。回调函数只有在借助 RTOS API（如 `xSemaphoreGiveFromISR()`）唤醒了高优先级任务后才能返回 true。

    // 回调实现，在 ISR 上下文中运行
    static bool my_async_memcpy_cb(async_memcpy_handle_t mcp_hdl, async_memcpy_event_t *event, void *cb_args)
    {
        SemaphoreHandle_t sem = (SemaphoreHandle_t)cb_args;
        BaseType_t high_task_wakeup = pdFALSE;
        xSemaphoreGiveFromISR(semphr, &high_task_wakeup); // 如果解锁了一些高优先级任务，则将 high_task_wakeup 设置为 pdTRUE
        return high_task_wakeup == pdTRUE;
    }

    // 创建一个信号量，在异步 memcpy 完成时进行报告
    SemaphoreHandle_t semphr = xSemaphoreCreateBinary();

    // 从用户的上下文中调用
    ESP_ERROR_CHECK(esp_async_memcpy(driver_handle, to, from, copy_len, my_async_memcpy_cb, my_semaphore));
    // 其他事项
    xSemaphoreTake(my_semaphore, portMAX_DELAY); // 等待 buffer 复制完成

对于只需等待单次复制完成的简单场景，可以使用 `esp_memcpy_blocking()`。该 API 基于异步请求路径实现，并在内部等待完成回调。

    ESP_ERROR_CHECK(esp_memcpy_blocking(driver_handle, to, from, copy_len, -1));

阻塞 API 不能在 ISR 上下文中调用。目前仅支持 `timeout_ms = -1`，表示无限期等待直到内存复制完成。

## 卸载驱动

使用 `esp_async_memcpy_uninstall()` 卸载异步 memcpy 驱动。无需在每次 memcpy 操作后手动卸载。如果你的应用程序不再需要此驱动，此 API 可以帮助回收内存和其他硬件资源。

## ETM 事件

在异步 memcpy 操作完成时会生成一个事件，此事件能够与 ETM 模块进行交互。可以调用 `esp_async_memcpy_new_etm_event()` 获取 ETM 事件句柄。

如需了解如何将此事件连接到 ETM 通道，请参考文档 ETM。

## API 参考

### Header File

- components/esp_driver_dma/include/esp_async_memcpy.h

- This header file can be included with:

  >     #include "esp_async_memcpy.h"

- This header file is a part of the API provided by the `esp_driver_dma` component. To declare that your component depends on `esp_driver_dma`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_driver_dma

  > or

  >     PRIV_REQUIRES esp_driver_dma

### Functions

esp_err_t esp_async_memcpy_install(const async_memcpy_config_t *config, async_memcpy_handle_t *mcp)

  Install async memcpy driver with the default DMA backend.

  备注

  On chip with CPDMA support, CPDMA is the default choice. On chip with AHB-GDMA support, AHB-GDMA is the default choice.

  参数:

    - config -- [in] Configuration of async memcpy

    - mcp -- [out] Returned driver handle

  返回:

    - ESP_OK: Install async memcpy driver successfully

    - ESP_ERR_INVALID_ARG: Install async memcpy driver failed because of invalid argument

    - ESP_ERR_NO_MEM: Install async memcpy driver failed because out of memory

    - ESP_FAIL: Install async memcpy driver failed because of other error

esp_err_t esp_async_memcpy_uninstall(async_memcpy_handle_t mcp)

  Uninstall async memcpy driver.

  参数:

    mcp -- [in] Handle of async memcpy driver that returned from `esp_async_memcpy_install`

  返回:

    - ESP_OK: Uninstall async memcpy driver successfully

    - ESP_ERR_INVALID_ARG: Uninstall async memcpy driver failed because of invalid argument

    - ESP_FAIL: Uninstall async memcpy driver failed because of other error

esp_err_t esp_async_memcpy(async_memcpy_handle_t mcp, void *dst, void *src, size_t n, async_memcpy_isr_cb_t cb_isr, void *cb_args)

  Send an asynchronous memory copy request.

  备注

  The callback function is invoked in interrupt context, never do blocking jobs in the callback.

  参数:

    - mcp -- [in] Handle of async memcpy driver that returned from `esp_async_memcpy_install`

    - dst -- [in] Destination address (copy to)

    - src -- [in] Source address (copy from)

    - n -- [in] Number of bytes to copy

    - cb_isr -- [in] Callback function, which got invoked in interrupt context. Set to NULL can bypass the callback.

    - cb_args -- [in] User defined argument to be passed to the callback function

  返回:

    - 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

esp_err_t esp_memcpy_blocking(async_memcpy_handle_t mcp, void *dst, void *src, size_t n, int32_t timeout_ms)

  Blocking memory copy function with timeout.

  备注

  This function is blocking and should not be called from interrupt context.

  备注

  Only `timeout_ms=-1` is supported, which means waiting indefinitely.

  参数:

    - mcp -- [in] Handle of async memcpy driver that returned from `esp_async_memcpy_install`

    - dst -- [in] Destination address (copy to)

    - src -- [in] Source address (copy from)

    - n -- [in] Number of bytes to copy

    - timeout_ms -- [in] Timeout in milliseconds. Only -1 is supported.

  返回:

    - ESP_OK: Copy memory successfully

    - ESP_ERR_INVALID_ARG: Copy memory failed because of invalid argument

    - ESP_ERR_INVALID_STATE: Function called from ISR context or driver in invalid state

    - ESP_FAIL: Copy memory failed because of other error

esp_err_t esp_async_memcpy_new_etm_event(async_memcpy_handle_t mcp, async_memcpy_etm_event_t event_type, esp_etm_event_handle_t *out_event)

  Get the ETM event handle for async memcpy done signal.

  备注

  The created ETM event object can be deleted later by calling `esp_etm_del_event`

  参数:

    - mcp -- [in] Handle of async memcpy driver that returned from `esp_async_memcpy_install`

    - event_type -- [in] ETM event type

    - out_event -- [out] Returned ETM event handle

  返回:

    - 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

### Structures

struct async_memcpy_event_t

  Async memory copy event data.

  Public Members

  void *data

    Event data

struct async_memcpy_config_t

  Type of async memcpy configuration.

  Public Members

  uint32_t backlog

    Maximum number of transactions that can be prepared in the background

  uint32_t weight

    Weight of async memcpy dma channel, higher weight means higher average bandwidth

  size_t dma_burst_size

    DMA transfer burst size, in bytes

  uint32_t flags

    Extra flags to control async memcpy feature

### Macros

ASYNC_MEMCPY_DEFAULT_CONFIG()

  Default configuration for async memcpy.

### Type Definitions

typedef struct async_memcpy_context_t *async_memcpy_handle_t

  Async memory copy driver handle.

typedef bool (*async_memcpy_isr_cb_t)(async_memcpy_handle_t mcp_hdl, async_memcpy_event_t *event, void *cb_args)

  Type of async memcpy interrupt callback function.

  备注

  User can call OS primitives (semaphore, mutex, etc) in the callback function. Keep in mind, if any OS primitive wakes high priority task up, the callback should return true.

  Param mcp_hdl:

    Handle of async memcpy

  Param event:

    Event object, which contains related data, reserved for future

  Param cb_args:

    User defined arguments, passed from esp_async_memcpy function

  Return:

    Whether a high priority task is woken up by the callback function

### Enumerations

enum async_memcpy_etm_event_t

  Async memory copy specific events that supported by the ETM module.

  Values:

  enumerator ASYNC_MEMCPY_ETM_EVENT_COPY_DONE

    memory copy finished

此文档对您有帮助吗？

- 反馈已收到，谢谢！
- 如果您有其他意见，欢迎填写乐鑫文档反馈表。

- 我们重视您的反馈。
- 您可以填写乐鑫文档反馈表告诉我们如何改进该文档。