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 32ffd704725..df331a6db97 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 @@ -352,6 +352,38 @@ void sdmmc_test_rw_psram_buffer(sdmmc_card_t *card) TEST_ESP_OK(sdmmc_read_sectors(card, psram_buf, 0, block_count)); check_buffer(seed_c, psram_buf, buffer_size / sizeof(uint32_t)); + /* Test D: Write from a PSRAM buffer that is NOT cache/DMA aligned. + * + * The host (or the underlying SPI master) must transparently handle the + * unaligned PSRAM source - either by an internal aligned copy or by direct + * DMA when the hardware supports it. This exercises the fallback path of the + * SDSPI write transfer, which no longer copies the buffer itself but relies + * on the SPI master to accept PSRAM buffers (SPI_TRANS_DMA_USE_PSRAM). The + * data read back must still match. + * + * NOTE: this sub-test requires real SD-card + PSRAM hardware to be meaningful. + */ + { + const size_t misalign = 1; /* 1-byte offset breaks 4-byte/cache alignment */ + uint8_t *unaligned_psram = heap_caps_malloc(buffer_size + misalign, + MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA); + TEST_ASSERT_NOT_NULL_MESSAGE(unaligned_psram, "Failed to allocate PSRAM buffer"); + uint8_t *src = unaligned_psram + misalign; + TEST_ASSERT_MESSAGE(esp_ptr_external_ram(src), "Buffer not in PSRAM"); + + const uint32_t seed_d = 0x13572468; + for (size_t i = 0; i < buffer_size; i++) { + src[i] = (uint8_t)(seed_d + i); + } + TEST_ESP_OK(sdmmc_write_sectors(card, src, 0, block_count)); + memset(internal_buf, 0xcc, buffer_size); + TEST_ESP_OK(sdmmc_read_sectors(card, internal_buf, 0, block_count)); + for (size_t i = 0; i < buffer_size; i++) { + TEST_ASSERT_EQUAL_HEX8((uint8_t)(seed_d + i), internal_buf[i]); + } + free(unaligned_psram); + } + free(psram_buf); free(internal_buf); printf("PSRAM buffer R/W test passed\n"); diff --git a/components/esp_driver_sdspi/src/sdspi_host.c b/components/esp_driver_sdspi/src/sdspi_host.c index 99c2a0b5b5e..b8e914f3af6 100644 --- a/components/esp_driver_sdspi/src/sdspi_host.c +++ b/components/esp_driver_sdspi/src/sdspi_host.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -20,7 +20,6 @@ #include "esp_timer.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" -#include "soc/soc_memory_layout.h" #include "esp_private/esp_cache_private.h" /// Max number of transactions in flight (used in start_command_write_blocks) @@ -931,22 +930,17 @@ static esp_err_t start_command_write_blocks(slot_info_t *slot, sdspi_hw_cmd_t *c // Prepare data to be sent size_t will_send = MIN(tx_length, SDSPI_MAX_DATA_LEN); - const uint8_t* tx_data = data; - if (!esp_ptr_in_dram(tx_data)) { - // If the pointer can't be used with DMA, copy data into a new buffer - uint8_t* tmp; - ret = get_block_buf(slot, &tmp); - if (ret != ESP_OK) { - return ret; - } - memcpy(tmp, tx_data, will_send); - tx_data = tmp; - } - // Write data + // Write data. The SPI master driver handles buffers that are not + // directly DMA-usable (e.g. PSRAM, or unaligned): with + // SPI_TRANS_DMA_USE_PSRAM it transfers a suitable PSRAM buffer directly + // (no copy), and otherwise falls back to an internal aligned buffer + // itself. This avoids the redundant copy the SDSPI driver used to do. + // The flag is ignored for buffers that are not in PSRAM. spi_transaction_t t_data = { .length = will_send * 8, - .tx_buffer = tx_data, + .tx_buffer = data, + .flags = SPI_TRANS_DMA_USE_PSRAM, }; ret = spi_device_transmit(slot->spi_handle, &t_data); if (ret != ESP_OK) {