mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
81 lines
4.1 KiB
ReStructuredText
81 lines
4.1 KiB
ReStructuredText
|
The Async memcpy API
|
||
|
====================
|
||
|
|
||
|
Overview
|
||
|
--------
|
||
|
|
||
|
ESP32-S2 features a dedicated DMA (a.k.a `CP_DMA`) which aims to offload internal memory copy operations from the CPU. When using 160MHz CPU, copying 4KB of data via memcpy() takes 14us, copying via cp_dma_memcpy can complete in 7us.
|
||
|
|
||
|
The async memcpy API wraps all DMA configurations and operations, the signature of :cpp:func:`cp_dma_memcpy` is almost the same to the standard libc one.
|
||
|
|
||
|
Thanks to the benefit of the DMA, we don't have to wait for each memory copy to be done before we issue another memcpy request. By providing a user defined callback, it's still possible to know when memcpy has finished.
|
||
|
|
||
|
.. note::
|
||
|
Memory copy with external PSRAM is not supported on ESP32-S2, :cpp:func:`cp_dma_memcpy` will abort returning an error if memory address does not reside in SRAM.
|
||
|
|
||
|
Configure and Install driver
|
||
|
----------------------------
|
||
|
|
||
|
:cpp:func:`cp_dma_driver_install` is used to install `CP_DMA` driver with user's configuration. Please note that async memcpy has to be called with the handle returned by :cpp:func:`cp_dma_driver_install`.
|
||
|
|
||
|
Driver configuration is described in :cpp:type:`cp_dma_config_t`:
|
||
|
:cpp:member:`max_out_stream` and :cpp:member:`max_in_stream`: You can increase/decrease the number if you want to support more/less memcpy operations to be pending in background.
|
||
|
:cpp:member:`flags`: Control special behavior of `CP_DMA`. If `CP_DMA_FLAGS_WORK_WITH_CACHE_DISABLE` is set in the flags, then `CP_DMA` driver can work even when cache is disabled. Please note, it would increase the consumption of SRAM.
|
||
|
|
||
|
:c:macro:`CP_DMA_DEFAULT_CONFIG` provides a default configuration, which specifies the maximum data streams used by underlay DMA engine to 8.
|
||
|
|
||
|
.. highlight:: c
|
||
|
|
||
|
::
|
||
|
|
||
|
cp_dma_config_t config = CP_DMA_DEFAULT_CONFIG();
|
||
|
config.max_in_stream = 4; // update the maximum data stream supported by DMA
|
||
|
config.max_out_stream = 4;
|
||
|
config.flags = CP_DMA_FLAGS_WORK_WITH_CACHE_DISABLE; // the driver can work even when cache is disabled
|
||
|
cp_dma_driver_t driver = NULL;
|
||
|
ESP_ERROR_CHECK(cp_dma_driver_install(&config, &driver)); // install driver, return driver handle
|
||
|
|
||
|
Send memory copy request
|
||
|
------------------------
|
||
|
|
||
|
:cpp:func:`cp_dma_memcpy` is the API to send memory copy request to DMA engine. It must be called after `CP_DMA` driver is installed successfully. This API is thread safe, so it can be called from different tasks.
|
||
|
|
||
|
Different from the libc version of `memcpy`, user can pass a callback to :cpp:func:`cp_dma_memcpy` when it's necessary. The callback is executed in the ISR context, make sure you won't violate the the restriction applied to ISR handler.
|
||
|
|
||
|
Besides that, the callback function should reside in IRAM space by applying `IRAM_ATTR` attribute. The prototype of the callback function is :cpp:type:`cp_dma_isr_cb_t`, please note that, the callback function should return true if there's a high priority task woken up due to any operations done in the callback.
|
||
|
|
||
|
.. highlight:: c
|
||
|
|
||
|
::
|
||
|
|
||
|
Semphr_Handle_t semphr; //already initialized in somewhere
|
||
|
|
||
|
// Callback implementation, running in ISR context
|
||
|
static IRAM_ATTR bool memcpy_cb(cp_dma_driver_t drv_hdl, cp_dma_event_t *event, void *cb_args)
|
||
|
{
|
||
|
BaseType_t high_task_wakeup = pdFALSE;
|
||
|
switch (event->id) {
|
||
|
case CP_DMA_EVENT_M2M_DONE:
|
||
|
SemphrGiveInISR(semphr, &high_task_wakeup); // high_task_wakeup set to pdTRUE if some high priority task unblocked
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
return high_task_wakeup == pdTRUE;
|
||
|
}
|
||
|
|
||
|
// Called from user's context
|
||
|
ESP_ERROR_CHECK(cp_dma_memcpy(driver, to, from, copy_len, memcpy_cb, cb_args));
|
||
|
//Do something else here
|
||
|
SemphrTake(semphr, ...); //wait until the buffer copy is done
|
||
|
|
||
|
Uninstall driver (optional)
|
||
|
---------------------------
|
||
|
|
||
|
:cpp:func:`cp_dma_driver_uninstall` is used to uninstall `CP_DMA` driver. It's not necessary to uninstall the driver after each memcpy operation. If your application won't use `CP_DMA` anymore, then this API can recycle the memory used by driver.
|
||
|
|
||
|
API Reference
|
||
|
-------------
|
||
|
|
||
|
.. include-build-file:: inc/cp_dma.inc
|