diff --git a/components/esp_hw_support/test_apps/dma/main/test_gdma.c b/components/esp_hw_support/test_apps/dma/main/test_gdma.c index 39e89a6cc4..469cbd7580 100644 --- a/components/esp_hw_support/test_apps/dma/main/test_gdma.c +++ b/components/esp_hw_support/test_apps/dma/main/test_gdma.c @@ -521,3 +521,193 @@ TEST_CASE("GDMA M2M Unaligned RX Buffer Test", "[GDMA][M2M]") free(sbuf); free(dbuf); } + +[[maybe_unused]] static void test_gdma_memcpy_from_to_psram(gdma_channel_handle_t tx_chan, gdma_channel_handle_t rx_chan) +{ +#define COPY_SIZE (40*1024) + SemaphoreHandle_t done_sem = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(done_sem); + gdma_rx_event_callbacks_t rx_cbs = { + .on_recv_eof = test_gdma_m2m_rx_eof_callback, + }; + TEST_ESP_OK(gdma_register_rx_event_callbacks(rx_chan, &rx_cbs, done_sem)); + + gdma_strategy_config_t strategy = { + .auto_update_desc = true, + .owner_check = true, + .eof_till_data_popped = true, + }; + TEST_ESP_OK(gdma_apply_strategy(tx_chan, &strategy)); + TEST_ESP_OK(gdma_apply_strategy(rx_chan, &strategy)); + + gdma_transfer_config_t transfer_cfg = { + .max_data_burst_size = 32, + .access_ext_mem = true, // allow to do memory copy from/to external memory + }; + TEST_ESP_OK(gdma_config_transfer(tx_chan, &transfer_cfg)); + TEST_ESP_OK(gdma_config_transfer(rx_chan, &transfer_cfg)); + + gdma_trigger_t m2m_trigger = GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_M2M, 0); + // get a free DMA trigger ID for memory copy + uint32_t free_m2m_id_mask = 0; + gdma_get_free_m2m_trig_id_mask(tx_chan, &free_m2m_id_mask); + m2m_trigger.instance_id = __builtin_ctz(free_m2m_id_mask); + TEST_ESP_OK(gdma_connect(tx_chan, m2m_trigger)); + TEST_ESP_OK(gdma_connect(rx_chan, m2m_trigger)); + + gdma_link_list_handle_t tx_link_list = NULL; + gdma_link_list_handle_t rx_link_list = NULL; + // create DMA link list for TX channel (a singly link with 3 nodes) + gdma_link_list_config_t tx_link_list_config = { + .buffer_alignment = 32, + .item_alignment = 8, // 8-byte alignment required by the AXI-GDMA + .num_items = 20, + .flags = { + .items_in_ext_mem = false, + } + }; + TEST_ESP_OK(gdma_new_link_list(&tx_link_list_config, &tx_link_list)); + // create DMA link list for RX channel + gdma_link_list_config_t rx_link_list_config = { + .buffer_alignment = 32, + .item_alignment = 8, // 8-byte alignment required by the AXI-GDMA + .num_items = 20, + .flags = { + .items_in_ext_mem = false, + }, + }; + TEST_ESP_OK(gdma_new_link_list(&rx_link_list_config, &rx_link_list)); + + // allocate the source buffer from SRAM + uint8_t *src_data = heap_caps_aligned_calloc(32, 1, COPY_SIZE, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + TEST_ASSERT_NOT_NULL(src_data); + TEST_ASSERT_TRUE(esp_ptr_internal(src_data)); + // prepare the source data + for (int i = 0; i < COPY_SIZE; i++) { + src_data[i] = i; + } + size_t sram_cache_line_sz = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA); + size_t psram_cache_line_sz = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA); + // do cache sync if necessary + if (sram_cache_line_sz) { + TEST_ESP_OK(esp_cache_msync(src_data, COPY_SIZE, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE)); + } + + // allocate the destination buffer from PSRAM + uint8_t *dst_data = heap_caps_aligned_calloc(32, 1, COPY_SIZE, MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + TEST_ASSERT_NOT_NULL(dst_data); + TEST_ASSERT_TRUE(esp_ptr_external_ram(dst_data)); + if (psram_cache_line_sz) { + TEST_ESP_OK(esp_cache_msync(dst_data, COPY_SIZE, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE)); + } + + gdma_buffer_mount_config_t tx_buf_mount_config = { + .buffer = src_data, + .length = COPY_SIZE, + .flags = { + .mark_eof = true, + .mark_final = true, // using singly list, so terminate the link here + } + }; + TEST_ESP_OK(gdma_link_mount_buffers(tx_link_list, 0, &tx_buf_mount_config, 1, NULL)); + + gdma_buffer_mount_config_t rx_buf_mount_config = { + .buffer = dst_data, + .length = COPY_SIZE, + .flags = { + .mark_final = true, // using singly list, so terminate the link here + } + }; + TEST_ESP_OK(gdma_link_mount_buffers(rx_link_list, 0, &rx_buf_mount_config, 1, NULL)); + + TEST_ESP_OK(gdma_start(rx_chan, gdma_link_get_head_addr(rx_link_list))); + TEST_ESP_OK(gdma_start(tx_chan, gdma_link_get_head_addr(tx_link_list))); + + xSemaphoreTake(done_sem, pdMS_TO_TICKS(1000)); + + /// let the DMA to copy the data back to the source buffer again + /// clear the "src_data" because now we want to use it as the destination buffer + memset(src_data, 0, COPY_SIZE); + // do cache sync if necessary + if (sram_cache_line_sz) { + TEST_ESP_OK(esp_cache_msync(src_data, COPY_SIZE, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE)); + } + + tx_buf_mount_config.buffer = dst_data; + TEST_ESP_OK(gdma_link_mount_buffers(tx_link_list, 0, &tx_buf_mount_config, 1, NULL)); + rx_buf_mount_config.buffer = src_data; + TEST_ESP_OK(gdma_link_mount_buffers(rx_link_list, 0, &rx_buf_mount_config, 1, NULL)); + + TEST_ESP_OK(gdma_start(rx_chan, gdma_link_get_head_addr(rx_link_list))); + TEST_ESP_OK(gdma_start(tx_chan, gdma_link_get_head_addr(tx_link_list))); + + xSemaphoreTake(done_sem, pdMS_TO_TICKS(1000)); + + bool compare_result = true; + for (int i = 0; i < COPY_SIZE; i++) { + if (src_data[i] != i % 256) { + printf("miss match! src_data[%d]=%d, should be %d\n", i, src_data[i], i % 256); + compare_result = false; + } + if (dst_data[i] != i % 256) { + printf("miss match! dst_data[%d]=%d, should be %d\n", i, dst_data[i], i % 256); + compare_result = false; + } + } + TEST_ASSERT_TRUE(compare_result); + + free(src_data); + free(dst_data); + TEST_ESP_OK(gdma_del_link_list(tx_link_list)); + TEST_ESP_OK(gdma_del_link_list(rx_link_list)); + vSemaphoreDelete(done_sem); +#undef COPY_SIZE +} + +#if SOC_SPIRAM_SUPPORTED +TEST_CASE("GDMA memory copy SRAM->PSRAM->SRAM", "[GDMA][M2M]") +{ + [[maybe_unused]] gdma_channel_handle_t tx_chan = NULL; + [[maybe_unused]] gdma_channel_handle_t rx_chan = NULL; + [[maybe_unused]] gdma_channel_alloc_config_t tx_chan_alloc_config = {}; + [[maybe_unused]] gdma_channel_alloc_config_t rx_chan_alloc_config = {}; + +#if SOC_AHB_GDMA_SUPPORTED && SOC_AHB_GDMA_SUPPORT_PSRAM + printf("Testing AHB-GDMA memory copy SRAM->PSRAM->SRAM\n"); + tx_chan_alloc_config = (gdma_channel_alloc_config_t) { + .direction = GDMA_CHANNEL_DIRECTION_TX, + .flags.reserve_sibling = true, + }; + TEST_ESP_OK(gdma_new_ahb_channel(&tx_chan_alloc_config, &tx_chan)); + rx_chan_alloc_config = (gdma_channel_alloc_config_t) { + .direction = GDMA_CHANNEL_DIRECTION_RX, + .sibling_chan = tx_chan, + }; + TEST_ESP_OK(gdma_new_ahb_channel(&rx_chan_alloc_config, &rx_chan)); + + test_gdma_memcpy_from_to_psram(tx_chan, rx_chan); + + TEST_ESP_OK(gdma_del_channel(tx_chan)); + TEST_ESP_OK(gdma_del_channel(rx_chan)); +#endif + +#if SOC_AXI_GDMA_SUPPORTED && SOC_AXI_GDMA_SUPPORT_PSRAM + printf("Testing AXI-GDMA memory copy SRAM->PSRAM->SRAM\n"); + tx_chan_alloc_config = (gdma_channel_alloc_config_t) { + .direction = GDMA_CHANNEL_DIRECTION_TX, + .flags.reserve_sibling = true, + }; + TEST_ESP_OK(gdma_new_axi_channel(&tx_chan_alloc_config, &tx_chan)); + rx_chan_alloc_config = (gdma_channel_alloc_config_t) { + .direction = GDMA_CHANNEL_DIRECTION_RX, + .sibling_chan = tx_chan, + }; + TEST_ESP_OK(gdma_new_axi_channel(&rx_chan_alloc_config, &rx_chan)); + + test_gdma_memcpy_from_to_psram(tx_chan, rx_chan); + + TEST_ESP_OK(gdma_del_channel(tx_chan)); + TEST_ESP_OK(gdma_del_channel(rx_chan)); +#endif +} +#endif // SOC_SPIRAM_SUPPORTED