From 4bd2322993e2d8e4d47112b274dab2a7597a7d0d Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 21 Feb 2025 16:04:01 +0100 Subject: [PATCH 1/2] fix(sdmmc): move DMA descriptor refilling into the ISR Previously, as DMA descriptors were processed, the task performing SDMMC transfer would get woken up and would refill the descriptors. This design didn't work correctly when higher priority tasks occupied the CPU for too long, resulting in SDMMC transfer timing out. This change moves DMA descriptor refilling into SDMMC ISR. Now the "DMA done" interrupt is delivered back to task context only when the entire transfer is completed. Closes https://github.com/espressif/esp-idf/issues/13934 --- components/esp_driver_sdmmc/src/sdmmc_host.c | 102 +++++++++++++++++- .../esp_driver_sdmmc/src/sdmmc_internal.h | 2 +- .../esp_driver_sdmmc/src/sdmmc_transaction.c | 100 +---------------- components/hal/esp32/include/hal/sdmmc_ll.h | 8 +- components/hal/esp32p4/include/hal/sdmmc_ll.h | 8 +- components/hal/esp32s3/include/hal/sdmmc_ll.h | 8 +- 6 files changed, 123 insertions(+), 105 deletions(-) diff --git a/components/esp_driver_sdmmc/src/sdmmc_host.c b/components/esp_driver_sdmmc/src/sdmmc_host.c index ac8f00b7df..9a480cbbfb 100644 --- a/components/esp_driver_sdmmc/src/sdmmc_host.c +++ b/components/esp_driver_sdmmc/src/sdmmc_host.c @@ -18,6 +18,7 @@ #include "esp_rom_sys.h" #include "driver/gpio.h" #include "driver/sdmmc_host.h" +#include "esp_cache.h" #include "esp_private/esp_clk_tree_common.h" #include "esp_private/periph_ctrl.h" #include "sdmmc_internal.h" @@ -33,6 +34,12 @@ #define SDMMC_EVENT_QUEUE_LENGTH 32 +/* Number of DMA descriptors used for transfer. + * Increasing this value above 4 doesn't improve performance for the usual case + * of SD memory cards (most data transfers are multiples of 512 bytes). + */ +#define SDMMC_DMA_DESC_CNT 4 + #define SDMMC_FREQ_SDR104 208000 /*!< MMC 208MHz speed */ #if !SOC_RCC_IS_INDEPENDENT @@ -93,6 +100,10 @@ typedef struct host_ctx_t { uint8_t num_of_init_slots; int8_t active_slot_num; #endif + uint8_t* data_ptr; + size_t size_remaining; + size_t next_desc; + size_t desc_remaining; } host_ctx_t; #if SOC_SDMMC_NUM_SLOTS >= 2 @@ -100,6 +111,7 @@ static host_ctx_t s_host_ctx = {.active_slot_num = -1}; #else static host_ctx_t s_host_ctx = {0}; #endif +DRAM_DMA_ALIGNED_ATTR static sdmmc_desc_t s_dma_desc[SDMMC_DMA_DESC_CNT]; static void sdmmc_isr(void *arg); static esp_err_t sdmmc_host_pullup_en_internal(int slot, int width); @@ -1022,17 +1034,89 @@ void sdmmc_host_enable_clk_cmd11(int slot, bool enable) sdmmc_ll_enable_1v8_mode(s_host_ctx.hal.dev, slot, enable); } +static size_t get_free_descriptors_count(void) +{ + const size_t next = s_host_ctx.next_desc; + size_t count = 0; + /* Starting with the current DMA descriptor, count the number of + * descriptors which have 'owned_by_idmac' set to 0. These are the + * descriptors already processed by the DMA engine. + */ + for (size_t i = 0; i < SDMMC_DMA_DESC_CNT; ++i) { + sdmmc_desc_t* desc = &s_dma_desc[(next + i) % SDMMC_DMA_DESC_CNT]; +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE + esp_err_t ret = esp_cache_msync((void *)desc, sizeof(sdmmc_desc_t), ESP_CACHE_MSYNC_FLAG_DIR_M2C); + assert(ret == ESP_OK); +#endif + if (desc->owned_by_idmac) { + break; + } + ++count; + if (desc->next_desc_ptr == NULL) { + /* final descriptor in the chain */ + break; + } + } + return count; +} + +static void fill_dma_descriptors(size_t num_desc) +{ + for (size_t i = 0; i < num_desc; ++i) { + if (s_host_ctx.size_remaining == 0) { + return; + } + const size_t next = s_host_ctx.next_desc; + sdmmc_desc_t* desc = &s_dma_desc[next]; + assert(!desc->owned_by_idmac); + size_t size_to_fill = + (s_host_ctx.size_remaining < SDMMC_DMA_MAX_BUF_LEN) ? + s_host_ctx.size_remaining : SDMMC_DMA_MAX_BUF_LEN; + bool last = size_to_fill == s_host_ctx.size_remaining; + desc->last_descriptor = last; + desc->second_address_chained = 1; + desc->owned_by_idmac = 1; + desc->buffer1_ptr = s_host_ctx.data_ptr; + desc->next_desc_ptr = (last) ? NULL : &s_dma_desc[(next + 1) % SDMMC_DMA_DESC_CNT]; + assert(size_to_fill < 4 || size_to_fill % 4 == 0); + desc->buffer1_size = (size_to_fill + 3) & (~3); + + s_host_ctx.size_remaining -= size_to_fill; + s_host_ctx.data_ptr += size_to_fill; + s_host_ctx.next_desc = (s_host_ctx.next_desc + 1) % SDMMC_DMA_DESC_CNT; + ESP_EARLY_LOGV(TAG, "fill %d desc=%d rem=%d next=%d last=%d sz=%d", + num_desc, next, s_host_ctx.size_remaining, + s_host_ctx.next_desc, desc->last_descriptor, desc->buffer1_size); +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE + esp_err_t ret = esp_cache_msync((void *)desc, sizeof(sdmmc_desc_t), ESP_CACHE_MSYNC_FLAG_DIR_C2M); + assert(ret == ESP_OK); +#endif + } +} + void sdmmc_host_dma_stop(void) { sdmmc_ll_stop_dma(s_host_ctx.hal.dev); } -void sdmmc_host_dma_prepare(sdmmc_desc_t *desc, size_t block_size, size_t data_size) +void sdmmc_host_dma_prepare(void* data_ptr, size_t data_size, size_t block_size) { + // this clears "owned by IDMAC" bits + memset(s_dma_desc, 0, sizeof(s_dma_desc)); + // initialize first descriptor + s_dma_desc[0].first_descriptor = 1; + // save transfer info + s_host_ctx.data_ptr = (uint8_t*) data_ptr; + s_host_ctx.size_remaining = data_size; + s_host_ctx.next_desc = 0; + s_host_ctx.desc_remaining = (data_size + SDMMC_DMA_MAX_BUF_LEN - 1) / SDMMC_DMA_MAX_BUF_LEN; + // prepare descriptors + fill_dma_descriptors(SDMMC_DMA_DESC_CNT); + // Set size of data and DMA descriptor pointer sdmmc_ll_set_data_transfer_len(s_host_ctx.hal.dev, data_size); sdmmc_ll_set_block_size(s_host_ctx.hal.dev, block_size); - sdmmc_ll_set_desc_addr(s_host_ctx.hal.dev, (uint32_t)desc); + sdmmc_ll_set_desc_addr(s_host_ctx.hal.dev, (uint32_t)&s_dma_desc[0]); // Enable everything needed to use DMA sdmmc_ll_enable_dma(s_host_ctx.hal.dev, true); @@ -1124,7 +1208,19 @@ static void sdmmc_isr(void *arg) uint32_t dma_pending = sdmmc_ll_get_idsts_interrupt_raw(s_host_ctx.hal.dev); sdmmc_ll_clear_idsts_interrupt(s_host_ctx.hal.dev, dma_pending); - event.dma_status = dma_pending & 0x1f; + + if (dma_pending & SDMMC_LL_EVENT_DMA_NI) { + // refill DMA descriptors + size_t free_desc = get_free_descriptors_count(); + if (free_desc > 0) { + fill_dma_descriptors(free_desc); + sdmmc_host_dma_resume(); + } + //NI, logic OR of TI and RI. This is a sticky bit and must be cleared each time TI or RI is cleared. + dma_pending &= ~(SDMMC_LL_EVENT_DMA_NI | SDMMC_LL_EVENT_DMA_TI | SDMMC_LL_EVENT_DMA_RI); + } + + event.dma_status = dma_pending & SDMMC_LL_EVENT_DMA_MASK; if (pending != 0 || dma_pending != 0) { xQueueSendFromISR(queue, &event, &higher_priority_task_awoken); diff --git a/components/esp_driver_sdmmc/src/sdmmc_internal.h b/components/esp_driver_sdmmc/src/sdmmc_internal.h index 20ad83289a..f0e7ec3e63 100644 --- a/components/esp_driver_sdmmc/src/sdmmc_internal.h +++ b/components/esp_driver_sdmmc/src/sdmmc_internal.h @@ -28,7 +28,7 @@ esp_err_t sdmmc_host_start_command(int slot, sdmmc_hw_cmd_t cmd, uint32_t arg); esp_err_t sdmmc_host_wait_for_event(int tick_count, sdmmc_event_t* out_event); -void sdmmc_host_dma_prepare(sdmmc_desc_t* desc, size_t block_size, size_t data_size); +void sdmmc_host_dma_prepare(void* data_ptr, size_t data_size, size_t block_size); void sdmmc_host_dma_stop(void); diff --git a/components/esp_driver_sdmmc/src/sdmmc_transaction.c b/components/esp_driver_sdmmc/src/sdmmc_transaction.c index 55cf55a402..d8cc8909dd 100644 --- a/components/esp_driver_sdmmc/src/sdmmc_transaction.c +++ b/components/esp_driver_sdmmc/src/sdmmc_transaction.c @@ -24,12 +24,6 @@ #include "soc/soc_caps.h" #include "hal/sdmmc_ll.h" -/* Number of DMA descriptors used for transfer. - * Increasing this value above 4 doesn't improve performance for the usual case - * of SD memory cards (most data transfers are multiples of 512 bytes). - */ -#define SDMMC_DMA_DESC_CNT 4 - #define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) static const char* TAG = "sdmmc_req"; @@ -43,13 +37,6 @@ typedef enum { SDMMC_WAITING_VOLTAGE_SWITCH, } sdmmc_req_state_t; -typedef struct { - uint8_t* ptr; - size_t size_remaining; - size_t next_desc; - size_t desc_remaining; -} sdmmc_transfer_state_t; - const uint32_t SDMMC_DATA_ERR_MASK = SDMMC_INTMASK_DTO | SDMMC_INTMASK_DCRC | SDMMC_INTMASK_HTO | SDMMC_INTMASK_SBE | @@ -64,8 +51,6 @@ const uint32_t SDMMC_CMD_ERR_MASK = SDMMC_INTMASK_RCRC | SDMMC_INTMASK_RESP_ERR; -DRAM_DMA_ALIGNED_ATTR static sdmmc_desc_t s_dma_desc[SDMMC_DMA_DESC_CNT]; -static sdmmc_transfer_state_t s_cur_transfer = { 0 }; static QueueHandle_t s_request_mutex; static bool s_is_app_cmd; // This flag is set if the next command is an APP command #ifdef CONFIG_PM_ENABLE @@ -79,8 +64,6 @@ static esp_err_t handle_event(int slot, sdmmc_command_t* cmd, sdmmc_req_state_t* static esp_err_t process_events(int slot, sdmmc_event_t evt, sdmmc_command_t* cmd, sdmmc_req_state_t* pstate, sdmmc_event_t* unhandled_events); static void process_command_response(uint32_t status, sdmmc_command_t* cmd); -static void fill_dma_descriptors(size_t num_desc); -static size_t get_free_descriptors_count(void); static bool wait_for_busy_cleared(uint32_t timeout_ms); static void handle_voltage_switch_stage1(int slot, sdmmc_command_t* cmd); static void handle_voltage_switch_stage2(int slot, sdmmc_command_t* cmd); @@ -165,19 +148,8 @@ esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo) goto out; } #endif - // this clears "owned by IDMAC" bits - memset(s_dma_desc, 0, sizeof(s_dma_desc)); - // initialize first descriptor - s_dma_desc[0].first_descriptor = 1; - // save transfer info - s_cur_transfer.ptr = (uint8_t*) cmdinfo->data; - s_cur_transfer.size_remaining = cmdinfo->datalen; - s_cur_transfer.next_desc = 0; - s_cur_transfer.desc_remaining = (cmdinfo->datalen + SDMMC_DMA_MAX_BUF_LEN - 1) / SDMMC_DMA_MAX_BUF_LEN; - // prepare descriptors - fill_dma_descriptors(SDMMC_DMA_DESC_CNT); // write transfer info into hardware - sdmmc_host_dma_prepare(&s_dma_desc[0], cmdinfo->blklen, cmdinfo->datalen); + sdmmc_host_dma_prepare(cmdinfo->data, cmdinfo->datalen, cmdinfo->blklen); } // write command into hardware, this also sends the command to the card ret = sdmmc_host_start_command(slot, hw_cmd, cmdinfo->arg); @@ -221,66 +193,6 @@ out: return ret; } -static size_t get_free_descriptors_count(void) -{ - const size_t next = s_cur_transfer.next_desc; - size_t count = 0; - /* Starting with the current DMA descriptor, count the number of - * descriptors which have 'owned_by_idmac' set to 0. These are the - * descriptors already processed by the DMA engine. - */ - for (size_t i = 0; i < SDMMC_DMA_DESC_CNT; ++i) { - sdmmc_desc_t* desc = &s_dma_desc[(next + i) % SDMMC_DMA_DESC_CNT]; -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - esp_err_t ret = esp_cache_msync((void *)desc, sizeof(sdmmc_desc_t), ESP_CACHE_MSYNC_FLAG_DIR_M2C); - assert(ret == ESP_OK); -#endif - if (desc->owned_by_idmac) { - break; - } - ++count; - if (desc->next_desc_ptr == NULL) { - /* final descriptor in the chain */ - break; - } - } - return count; -} - -static void fill_dma_descriptors(size_t num_desc) -{ - for (size_t i = 0; i < num_desc; ++i) { - if (s_cur_transfer.size_remaining == 0) { - return; - } - const size_t next = s_cur_transfer.next_desc; - sdmmc_desc_t* desc = &s_dma_desc[next]; - assert(!desc->owned_by_idmac); - size_t size_to_fill = - (s_cur_transfer.size_remaining < SDMMC_DMA_MAX_BUF_LEN) ? - s_cur_transfer.size_remaining : SDMMC_DMA_MAX_BUF_LEN; - bool last = size_to_fill == s_cur_transfer.size_remaining; - desc->last_descriptor = last; - desc->second_address_chained = 1; - desc->owned_by_idmac = 1; - desc->buffer1_ptr = s_cur_transfer.ptr; - desc->next_desc_ptr = (last) ? NULL : &s_dma_desc[(next + 1) % SDMMC_DMA_DESC_CNT]; - assert(size_to_fill < 4 || size_to_fill % 4 == 0); - desc->buffer1_size = (size_to_fill + 3) & (~3); - - s_cur_transfer.size_remaining -= size_to_fill; - s_cur_transfer.ptr += size_to_fill; - s_cur_transfer.next_desc = (s_cur_transfer.next_desc + 1) % SDMMC_DMA_DESC_CNT; - ESP_LOGV(TAG, "fill %d desc=%d rem=%d next=%d last=%d sz=%d", - num_desc, next, s_cur_transfer.size_remaining, - s_cur_transfer.next_desc, desc->last_descriptor, desc->buffer1_size); -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - esp_err_t ret = esp_cache_msync((void *)desc, sizeof(sdmmc_desc_t), ESP_CACHE_MSYNC_FLAG_DIR_C2M); - assert(ret == ESP_OK); -#endif - } -} - static esp_err_t handle_idle_state_events(void) { /* Handle any events which have happened in between transfers. @@ -514,15 +426,7 @@ static esp_err_t process_events(int slot, sdmmc_event_t evt, sdmmc_command_t* cm sdmmc_host_dma_stop(); } if (mask_check_and_clear(&evt.dma_status, SDMMC_DMA_DONE_MASK)) { - s_cur_transfer.desc_remaining--; - if (s_cur_transfer.size_remaining) { - int desc_to_fill = get_free_descriptors_count(); - fill_dma_descriptors(desc_to_fill); - sdmmc_host_dma_resume(); - } - if (s_cur_transfer.desc_remaining == 0) { - next_state = SDMMC_BUSY; - } + next_state = SDMMC_BUSY; } if (orig_evt.sdmmc_status & (SDMMC_INTMASK_SBE | SDMMC_INTMASK_DATA_OVER)) { // On start bit error, DATA_DONE interrupt will not be generated diff --git a/components/hal/esp32/include/hal/sdmmc_ll.h b/components/hal/esp32/include/hal/sdmmc_ll.h index 0f5a62ea37..a509637723 100644 --- a/components/hal/esp32/include/hal/sdmmc_ll.h +++ b/components/hal/esp32/include/hal/sdmmc_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -74,6 +74,12 @@ extern "C" { SDMMC_LL_EVENT_SBE | SDMMC_LL_EVENT_ACD |\ SDMMC_LL_EVENT_EBE) +// DMA interrupts (idsts register) +#define SDMMC_LL_EVENT_DMA_TI SDMMC_IDMAC_INTMASK_TI +#define SDMMC_LL_EVENT_DMA_RI SDMMC_IDMAC_INTMASK_RI +#define SDMMC_LL_EVENT_DMA_NI SDMMC_IDMAC_INTMASK_NI +#define SDMMC_LL_EVENT_DMA_MASK 0x1f //NI and AI will be indicated by TI/RI and FBE/DU respectively + /** * SDMMC capabilities */ diff --git a/components/hal/esp32p4/include/hal/sdmmc_ll.h b/components/hal/esp32p4/include/hal/sdmmc_ll.h index 9f168b2620..2710b0d778 100644 --- a/components/hal/esp32p4/include/hal/sdmmc_ll.h +++ b/components/hal/esp32p4/include/hal/sdmmc_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -77,6 +77,12 @@ extern "C" { SDMMC_LL_EVENT_SBE | SDMMC_LL_EVENT_ACD |\ SDMMC_LL_EVENT_EBE) +// DMA interrupts (idsts register) +#define SDMMC_LL_EVENT_DMA_TI SDMMC_IDMAC_INTMASK_TI +#define SDMMC_LL_EVENT_DMA_RI SDMMC_IDMAC_INTMASK_RI +#define SDMMC_LL_EVENT_DMA_NI SDMMC_IDMAC_INTMASK_NI +#define SDMMC_LL_EVENT_DMA_MASK 0x1f //NI and AI will be indicated by TI/RI and FBE/DU respectively + /** * SDMMC capabilities */ diff --git a/components/hal/esp32s3/include/hal/sdmmc_ll.h b/components/hal/esp32s3/include/hal/sdmmc_ll.h index 2747905931..d5d450fa26 100644 --- a/components/hal/esp32s3/include/hal/sdmmc_ll.h +++ b/components/hal/esp32s3/include/hal/sdmmc_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -74,6 +74,12 @@ extern "C" { SDMMC_LL_EVENT_SBE | SDMMC_LL_EVENT_ACD |\ SDMMC_LL_EVENT_EBE) +// DMA interrupts (idsts register) +#define SDMMC_LL_EVENT_DMA_TI SDMMC_IDMAC_INTMASK_TI +#define SDMMC_LL_EVENT_DMA_RI SDMMC_IDMAC_INTMASK_RI +#define SDMMC_LL_EVENT_DMA_NI SDMMC_IDMAC_INTMASK_NI +#define SDMMC_LL_EVENT_DMA_MASK 0x1f //NI and AI will be indicated by TI/RI and FBE/DU respectively + /** * SDMMC capabilities */ From c27683eb98b815d32a82343df952ca29c545e10f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 21 Feb 2025 15:28:19 +0100 Subject: [PATCH 2/2] test(sdmmc): add test for high-prio task busy while writing Related to https://github.com/espressif/esp-idf/issues/13934 --- .../common_test_flows/CMakeLists.txt | 2 +- .../include/sdmmc_test_rw_common.h | 8 +++- .../common_test_flows/sdmmc_test_rw_common.c | 41 +++++++++++++++++++ .../components/sdmmc_tests/sdmmc_test_rw_sd.c | 24 ++++++++++- 4 files changed, 72 insertions(+), 3 deletions(-) diff --git a/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/CMakeLists.txt b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/CMakeLists.txt index 649cb95a54..bd957d11b1 100644 --- a/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/CMakeLists.txt +++ b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/CMakeLists.txt @@ -5,5 +5,5 @@ set(public_include "include") idf_component_register( SRCS ${srcs} INCLUDE_DIRS ${public_include} - PRIV_REQUIRES sdmmc unity test_utils + PRIV_REQUIRES sdmmc unity test_utils esp_timer ) diff --git a/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/include/sdmmc_test_rw_common.h b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/include/sdmmc_test_rw_common.h index 3398c79f02..b678631a55 100644 --- a/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/include/sdmmc_test_rw_common.h +++ b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/include/sdmmc_test_rw_common.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -50,6 +50,12 @@ void sdmmc_test_rw_unaligned_buffer(sdmmc_card_t* card); */ void sdmmc_test_rw_with_offset(sdmmc_card_t* card); +/** + * @brief Test read/write with higher priority tasks running concurrently + * @param card Pointer to the card object, must be initialized before calling this function. + */ +void sdmmc_test_rw_highprio_task(sdmmc_card_t* card); + #ifdef __cplusplus }; #endif diff --git a/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/sdmmc_test_rw_common.c b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/sdmmc_test_rw_common.c index b883e00435..077f7ee29e 100644 --- a/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/sdmmc_test_rw_common.c +++ b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/sdmmc_test_rw_common.c @@ -11,6 +11,7 @@ #include #include "esp_dma_utils.h" #include "esp_heap_caps.h" +#include "esp_timer.h" #include "test_utils.h" #include "sdkconfig.h" #include "soc/soc_caps.h" @@ -185,3 +186,43 @@ void sdmmc_test_rw_with_offset(sdmmc_card_t* card) do_single_rw_perf_test(card, card->csd.capacity / 2, 8, 1, NULL, 0); do_single_rw_perf_test(card, card->csd.capacity / 2, 128, 1, NULL, 0); } + +typedef struct { + SemaphoreHandle_t stop; + SemaphoreHandle_t done; + uint32_t busy_time_us; +} highprio_busy_task_args_t; + +static void highprio_busy_task(void* varg) +{ + highprio_busy_task_args_t* args = (highprio_busy_task_args_t*) varg; + while (xSemaphoreTake(args->stop, 0) != pdTRUE) { + vTaskDelay(1); + int64_t start = esp_timer_get_time(); + while (esp_timer_get_time() - start < args->busy_time_us) { + usleep(100); + } + } + xSemaphoreGive(args->done); + vTaskDelete(NULL); +} + +void sdmmc_test_rw_highprio_task(sdmmc_card_t* card) +{ + highprio_busy_task_args_t args = { + .stop = xSemaphoreCreateBinary(), + .done = xSemaphoreCreateBinary(), + .busy_time_us = 250000, + }; + + TEST_ASSERT(xTaskCreate(highprio_busy_task, "highprio_busy_task", 4096, &args, 20, NULL)); + + for (int i = 0; i < 4; ++i) { + do_single_rw_perf_test(card, 0, 64, 0, NULL, 0); + } + + xSemaphoreGive(args.stop); + xSemaphoreTake(args.done, portMAX_DELAY); + vSemaphoreDelete(args.stop); + vSemaphoreDelete(args.done); +} diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_rw_sd.c b/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_rw_sd.c index 3a07056213..8c1a5c36a7 100644 --- a/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_rw_sd.c +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_rw_sd.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -106,3 +106,25 @@ TEST_CASE("sdmmc read/write using unaligned buffer, slot 1, 4-bit", "[sdmmc]") { do_one_sdmmc_rw_test_unaligned_buffer(SLOT_1, 4, SDMMC_FREQ_DEFAULT, 0); } + +/* ========== Read/write tests with higher priority tasks running concurrently ========== */ + +static void do_one_sdmmc_rw_test_highprio_task(int slot, int width) +{ + sdmmc_card_t card; + sdmmc_test_sd_skip_if_board_incompatible(slot, width, SDMMC_FREQ_DEFAULT, NO_DDR, NO_EMMC); + sdmmc_test_sd_begin(slot, width, SDMMC_FREQ_DEFAULT, NO_DDR, &card); + sdmmc_card_print_info(stdout, &card); + sdmmc_test_rw_highprio_task(&card); + sdmmc_test_sd_end(&card); +} + +TEST_CASE("sdmmc read/write with concurrent high-prio task, slot 0, 4-bit", "[sdmmc]") +{ + do_one_sdmmc_rw_test_highprio_task(0, 4); +} + +TEST_CASE("sdmmc read/write with concurrent high-prio task, slot 1, 4-bit", "[sdmmc]") +{ + do_one_sdmmc_rw_test_highprio_task(1, 4); +}