ESP32-S3 - esp_async_memcpy not working with PSRAM using GDMA
Posted: Wed Nov 15, 2023 7:59 am
I'm struggling to get the esp_async_memcpy API to copy memory between external PSRAM using GDMA. The ESP32-S3 Technical Reference Manual says it is possible with GDMA and the esp-camera library does it using the GDMA library directly. I cannot find any actual complete example code using esp_async_memcpy, so I put this code together from the API documentation to try and make it work.
I can successfully transfer data within IRAM using the MALLOC_CAP_DMA. If I try to word align (32bit align) the memory it doesn't work saying the memory is not aligned. Even though the memory addresses and lengths look aligned to me.
Finally, I noticed GDMA functionality for esp_async_memcpy is only available on the Master branch of the ESP-IDF and that it doesn't even exist in IDF 5.1.1.
Can anyone tell me what I'm doing wrong? Or is there a bug in the implementation of the esp_async_memcpy API?
This is the log when I use IRAM and everything works
This is the log when I attempt to use external PSRAM
I can successfully transfer data within IRAM using the MALLOC_CAP_DMA. If I try to word align (32bit align) the memory it doesn't work saying the memory is not aligned. Even though the memory addresses and lengths look aligned to me.
Finally, I noticed GDMA functionality for esp_async_memcpy is only available on the Master branch of the ESP-IDF and that it doesn't even exist in IDF 5.1.1.
Can anyone tell me what I'm doing wrong? Or is there a bug in the implementation of the esp_async_memcpy API?
Code: Untitled.c Select all
/*
* Attempts to use the async_memcpy driver to copy data from one buffer to another
*
* Works for MALLOC_CAP_DMA but not if we try to align the buffers to 32 bits
* Does not work for MALLOC_CAP_SPIRAM no matter what I try.
* Reporting "invalid argument" on the call to esp_async_memcpy
*
* Using an ESP32-S3-WROOM-1U-N8R8 module
* Using the master branch of ESP-IDF cloned on 15/11/2023
*
*/
#include <inttypes.h>
#include <string.h>
#include "malloc.h"
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "esp_async_memcpy.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
static const char *TAG = "async_memcpy";
/// @brief The source buffer
void* _source;
/// @brief The destination buffer
void* _dest;
/// @brief Async Memory copy callback implementation, running in ISR context
/// @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
static IRAM_ATTR bool my_async_memcpy_cb(async_memcpy_t mcp_hdl, async_memcpy_event_t *event, void *cb_args)
{
// Get the semaphore handle from the callback arguments
SemaphoreHandle_t sem = (SemaphoreHandle_t)cb_args;
// Give the semaphore so the CPU can continue execution when its ready
// high_task_wakeup set to pdTRUE if we want to yield to a high priority task
BaseType_t high_task_wakeup = pdTRUE;
xSemaphoreGiveFromISR(sem, &high_task_wakeup);
// Return whether a high priority task was woken up so the ISR knows if it needs to yield
return high_task_wakeup == pdTRUE;
}
/// @brief Main application entry point
void app_main(void)
{
// Hello world
ESP_LOGI(TAG, "\n\nasync_memcpy!\n");
// Allocate some RAM for the source and destination buffers
ESP_LOGI(TAG, "Allocating memory for buffers...");
uint32_t size = 100000;
// ** This works **
_source = heap_caps_malloc(size, MALLOC_CAP_DMA);
_dest = heap_caps_malloc(size, MALLOC_CAP_DMA);
// ** This DOES NOT work **
// _source = heap_caps_malloc(size, MALLOC_CAP_DMA | MALLOC_CAP_32BIT);
// _dest = heap_caps_malloc(size, MALLOC_CAP_DMA | MALLOC_CAP_32BIT);
// ** This DOES NOT work **
// _source = heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA | MALLOC_CAP_32BIT);
// _dest = heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA | MALLOC_CAP_32BIT);
// ** This DOES NOT work **
//_source = heap_caps_malloc(size, MALLOC_CAP_SPIRAM);
//_dest = heap_caps_malloc(size, MALLOC_CAP_SPIRAM);
// Initialize the source buffer
ESP_LOGI(TAG, "Initializing source buffer...");
//memset(_source, 0x77, size);
// Install the Async memcpy driver. Using the defaults + a larger backlog.
ESP_LOGI(TAG, "Installing async_memcpy driver...");
async_memcpy_config_t config = ASYNC_MEMCPY_DEFAULT_CONFIG();
//uint32_t backlog = 8;
uint32_t backlog = (size > 2500) ? size/2500 : 4; // I think 2500 is approx the size of DMA transfer?
config.backlog = backlog;
// config.sram_trans_align = 32; // These don't work
// config.psram_trans_align = 32;
async_memcpy_handle_t driver = NULL;
//ESP_ERROR_CHECK(esp_async_memcpy_install(&config, &driver));
ESP_ERROR_CHECK(esp_async_memcpy_install_gdma_ahb(&config, &driver));
// Create a semaphore so we can wait for the async memcpy to complete
ESP_LOGI(TAG, "Creating semaphore...");
SemaphoreHandle_t semaphore = xSemaphoreCreateBinary();
// Start the async memcpy
ESP_LOGI(TAG, "Starting the async_memcpy to transfer %" PRIu32 " bytes...", size);
ESP_ERROR_CHECK(esp_async_memcpy(driver, _dest, _source, size, my_async_memcpy_cb, semaphore));
// Wait for the async memcpy to complete
ESP_LOGI(TAG, "Waiting for the DMA operation to complete...");
xSemaphoreTake(semaphore, portMAX_DELAY);
// Success. All done
ESP_LOGI(TAG, "async_memcpy complete! Transferred %" PRIu32 " bytes", size);
}
Code: Untitled.txt Select all
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce3810,len:0x178c
load:0x403c9700,len:0x4
load:0x403c9704,len:0xcb8
load:0x403cc700,len:0x2d84
entry 0x403c9914
I (26) boot: ESP-IDF v5.3-dev-277-gc8243465e4 2nd stage bootloader
I (27) boot: compile time Nov 15 2023 20:47:33
I (27) boot: Multicore bootloader
I (31) boot: chip revision: v0.1
I (35) boot.esp32s3: Boot SPI Speed : 80MHz
I (40) boot.esp32s3: SPI Mode : DIO
I (45) boot.esp32s3: SPI Flash Size : 8MB
I (49) boot: Enabling RNG early entropy source...
I (55) boot: Partition Table:
I (58) boot: ## Label Usage Type ST Offset Length
I (66) boot: 0 nvs WiFi data 01 02 00009000 00006000
I (73) boot: 1 phy_init RF data 01 01 0000f000 00001000
I (81) boot: 2 factory factory app 00 00 00010000 00100000
I (88) boot: End of partition table
I (92) esp_image: segment 0: paddr=00010020 vaddr=3c020020 size=0dc44h ( 56388) map
I (111) esp_image: segment 1: paddr=0001dc6c vaddr=3fc92500 size=023ach ( 9132) load
I (113) esp_image: segment 2: paddr=00020020 vaddr=42000020 size=1a66ch (108140) map
I (137) esp_image: segment 3: paddr=0003a694 vaddr=3fc948ac size=00608h ( 1544) load
I (138) esp_image: segment 4: paddr=0003aca4 vaddr=40374000 size=0e4b4h ( 58548) load
I (161) boot: Loaded app from partition at offset 0x10000
I (162) boot: Disabling RNG early entropy source...
I (173) cpu_start: Multicore app
I (182) cpu_start: Pro cpu start user code
I (182) cpu_start: cpu freq: 160000000 Hz
I (183) cpu_start: Application information:
I (185) cpu_start: Project name: async_memcpy
I (191) cpu_start: App version: d6b7f7a-dirty
I (196) cpu_start: Compile time: Nov 15 2023 20:47:26
I (202) cpu_start: ELF file SHA256: 97614a826...
I (208) cpu_start: ESP-IDF: v5.3-dev-277-gc8243465e4
I (214) cpu_start: Min chip rev: v0.0
I (219) cpu_start: Max chip rev: v0.99
I (224) cpu_start: Chip rev: v0.1
I (228) heap_init: Initializing. RAM available for dynamic allocation:
I (236) heap_init: At 3FC95790 len 00053F80 (335 KiB): RAM
I (242) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM
I (248) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM
I (254) heap_init: At 600FE010 len 00001FD8 (7 KiB): RTCRAM
I (261) spi_flash: detected chip: generic
I (265) spi_flash: flash io: dio
I (269) sleep: Configure to isolate all GPIO pins in sleep state
I (276) sleep: Enable automatic switching of GPIO sleep configuration
I (283) main_task: Started on CPU0
I (293) main_task: Calling app_main()
I (293) async_memcpy:
async_memcpy!
I (293) async_memcpy: Allocating memory for buffers...
I (303) async_memcpy: Initializing source buffer...
I (303) async_memcpy: Installing async_memcpy driver...
D (313) gdma: new group (0) at 0x3fccbd8c
D (313) gdma: new pair (0,0) at 0x3fccbe14
D (323) gdma: new tx channel (0,0) at 0x3fccbd54
D (323) gdma: new rx channel (0,0) at 0x3fccbe34
D (333) gdma: tx channel (0,0), (0:32) bytes aligned, burst enabled
D (333) gdma: rx channel (0,0), (0:32) bytes aligned, burst disabled
D (343) gdma: install interrupt service for rx channel (0,0)
I (353) async_memcpy: Creating semaphore...
I (353) async_memcpy: Starting the async_memcpy to transfer 100000 bytes...
I (363) async_memcpy: Waiting for the DMA operation to complete...
I (373) async_memcpy: async_memcpy complete! Transferred 100000 bytes
I (373) main_task: Returned from app_main()
Code: Untitled.txt Select all
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce3810,len:0x178c
load:0x403c9700,len:0x4
load:0x403c9704,len:0xcb8
load:0x403cc700,len:0x2d84
entry 0x403c9914
I (26) boot: ESP-IDF v5.3-dev-277-gc8243465e4 2nd stage bootloader
I (26) boot: compile time Nov 15 2023 20:47:33
I (27) boot: Multicore bootloader
I (31) boot: chip revision: v0.1
I (35) boot.esp32s3: Boot SPI Speed : 80MHz
I (39) boot.esp32s3: SPI Mode : DIO
I (44) boot.esp32s3: SPI Flash Size : 8MB
I (49) boot: Enabling RNG early entropy source...
I (54) boot: Partition Table:
I (58) boot: ## Label Usage Type ST Offset Length
I (65) boot: 0 nvs WiFi data 01 02 00009000 00006000
I (73) boot: 1 phy_init RF data 01 01 0000f000 00001000
I (80) boot: 2 factory factory app 00 00 00010000 00100000
I (88) boot: End of partition table
I (92) esp_image: segment 0: paddr=00010020 vaddr=3c020020 size=0dc44h ( 56388) map
I (110) esp_image: segment 1: paddr=0001dc6c vaddr=3fc92500 size=023ach ( 9132) load
I (113) esp_image: segment 2: paddr=00020020 vaddr=42000020 size=1a66ch (108140) map
I (136) esp_image: segment 3: paddr=0003a694 vaddr=3fc948ac size=00608h ( 1544) load
I (137) esp_image: segment 4: paddr=0003aca4 vaddr=40374000 size=0e4b4h ( 58548) load
I (161) boot: Loaded app from partition at offset 0x10000
I (161) boot: Disabling RNG early entropy source...
I (172) cpu_start: Multicore app
I (182) cpu_start: Pro cpu start user code
I (182) cpu_start: cpu freq: 160000000 Hz
I (182) cpu_start: Application information:
I (185) cpu_start: Project name: async_memcpy
I (190) cpu_start: App version: d6b7f7a-dirty
I (196) cpu_start: Compile time: Nov 15 2023 20:47:26
I (202) cpu_start: ELF file SHA256: 7c7112055...
I (207) cpu_start: ESP-IDF: v5.3-dev-277-gc8243465e4
I (214) cpu_start: Min chip rev: v0.0
I (218) cpu_start: Max chip rev: v0.99
I (223) cpu_start: Chip rev: v0.1
I (228) heap_init: Initializing. RAM available for dynamic allocation:
I (235) heap_init: At 3FC95790 len 00053F80 (335 KiB): RAM
I (241) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM
I (247) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM
I (253) heap_init: At 600FE010 len 00001FD8 (7 KiB): RTCRAM
I (261) spi_flash: detected chip: generic
I (264) spi_flash: flash io: dio
I (269) sleep: Configure to isolate all GPIO pins in sleep state
I (275) sleep: Enable automatic switching of GPIO sleep configuration
I (283) main_task: Started on CPU0
I (293) main_task: Calling app_main()
I (293) async_memcpy:
async_memcpy!
I (293) async_memcpy: Allocating memory for buffers...
I (303) async_memcpy: Initializing source buffer...
I (303) async_memcpy: Installing async_memcpy driver...
D (313) gdma: new group (0) at 0x3fc9b05c
D (313) gdma: new pair (0,0) at 0x3fc9b0e4
D (323) gdma: new tx channel (0,0) at 0x3fc9b024
D (323) gdma: new rx channel (0,0) at 0x3fc9b104
D (333) gdma: tx channel (0,0), (0:32) bytes aligned, burst enabled
D (333) gdma: rx channel (0,0), (0:32) bytes aligned, burst disabled
D (343) gdma: install interrupt service for rx channel (0,0)
I (353) async_memcpy: Creating semaphore...
I (353) async_memcpy: Starting the async_memcpy to transfer 100000 bytes...
E (363) async_mcp: esp_async_memcpy(21): invalid argument
ESP_ERROR_CHECK failed: esp_err_t 0x102 (ESP_ERR_INVALID_ARG) at 0x420084c1
0x420084c1: app_main at D:/Ferret/Dev/esp32/Tests/Async_memcpy/main/main.c:102 (discriminator 1)
file: "./main/main.c" line 102
func: app_main
expression: esp_async_memcpy(driver, _dest, _source, size, my_async_memcpy_cb, semaphore)
abort() was called at PC 0x40379ca7 on core 0
ex40379ca7: _esp_error_check_failed at C:/esp32/esp-idf_master/esp-idf/components/esp_system/esp_err.cN:o50n
Backtrace: 0x403758a6:0x3fc99340 0x40379cb1:0x3fc99360 0x40380441:0x3fc99380 0x40379ca7:0x3fc993f0 0x420084c1:0x3fc99420 0x42019813:0x3fc99460 0x4037a515:0x3fc99490
Waiting for the device to reconnect0x403758a6: panic_abort at C:/esp32/esp-idf_master/esp-idf/components/esp_system/panic.c:472
0x40379cb1: esp_system_abort at C:/esp32/esp-idf_master/esp-idf/components/esp_system/port/esp_system_chip.c:93
0x40380441: abort at C:/esp32/esp-idf_master/esp-idf/components/newlib/abort.c:38
0x40379ca7: _esp_error_check_failed at C:/esp32/esp-idf_master/esp-idf/components/esp_system/esp_err.c:50
0x420084c1: app_main at D:/Ferret/Dev/esp32/Tests/Async_memcpy/main/main.c:102 (discriminator 1)
0x42019813: main_task at C:/esp32/esp-idf_master/esp-idf/components/freertos/app_startup.c:208
0x4037a515: vPortTaskWrapper at C:/esp32/esp-idf_master/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:137
ELF file SHA256: 7c7112055
Rebooting...
ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0xc (RTC_SW_CPU_RST),boot:0xf (SPI_FAST_FLASH_BOOT)
Saved PC:0x4037581c
0x4037581c: esp_restart_noos at C:/esp32/esp-idf_master/esp-idf/components/esp_system/port/soc/esp32s3/system_internal.c:158
