mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
fix(sdmmc): Add a better handling of devices accessing PSRAM through DMA
Closes https://github.com/espressif/esp-idf/issues/18412
This commit is contained in:
@@ -536,6 +536,16 @@ bool sd_host_check_buffer_alignment(sd_host_sdmmc_slot_t *slot, const void *buf,
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !SOC_SDMMC_PSRAM_DMA_CAPABLE
|
||||
// The SDMMC peripheral's DMA cannot reach PSRAM on this target, so a PSRAM
|
||||
// buffer can never be used directly regardless of its alignment. Reporting
|
||||
// it as not directly usable makes the protocol layer fall back to an
|
||||
// internal DMA-capable buffer.
|
||||
if (esp_ptr_external_ram(buf)) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
int cache_flags = 0;
|
||||
size_t cache_alignment_bytes = 0;
|
||||
|
||||
+25
@@ -73,6 +73,31 @@ void sdmmc_test_rw_highprio_task(sdmmc_card_t* card);
|
||||
*/
|
||||
void sdmmc_test_rw_unaligned_buffer_multiblock(sdmmc_card_t* card, size_t chunk_size);
|
||||
|
||||
/**
|
||||
* @brief Test read/write with PSRAM-allocated buffers
|
||||
*
|
||||
* This function verifies that the driver correctly handles buffers allocated in PSRAM.
|
||||
* When the host reports the PSRAM buffer as directly usable (via check_buffer_alignment),
|
||||
* data is transferred directly without intermediate copying. Otherwise, the driver uses
|
||||
* double-buffering through internal RAM.
|
||||
*
|
||||
* The test covers:
|
||||
* - Writing from PSRAM, reading to internal RAM
|
||||
* - Writing from internal RAM, reading to PSRAM
|
||||
* - Both writing and reading from PSRAM
|
||||
*
|
||||
* If PSRAM is not enabled (CONFIG_SPIRAM), this function is a no-op. Callers
|
||||
* must skip the test (TEST_IGNORE) BEFORE initializing the slot/controller, so
|
||||
* that this function is not relied upon to abort the test. Doing the skip here
|
||||
* would longjmp out of the test before the caller's cleanup runs and leak the
|
||||
* SD slot.
|
||||
*
|
||||
* This test function works both with SDMMC and SDSPI hosts.
|
||||
*
|
||||
* @param card Pointer to the card object, must be initialized before calling this function.
|
||||
*/
|
||||
void sdmmc_test_rw_psram_buffer(sdmmc_card_t *card);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
+62
@@ -14,6 +14,7 @@
|
||||
#include "test_utils.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "unity.h"
|
||||
#include "sd_protocol_defs.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
@@ -295,3 +296,64 @@ void sdmmc_test_rw_unaligned_buffer_multiblock(sdmmc_card_t* card, size_t chunk_
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
void sdmmc_test_rw_psram_buffer(sdmmc_card_t *card)
|
||||
{
|
||||
#if !CONFIG_SPIRAM
|
||||
/* The PSRAM-disabled skip is intentionally NOT done here via TEST_IGNORE.
|
||||
* This function runs after the slot/controller has been initialized, and
|
||||
* TEST_IGNORE would longjmp out before the caller's *_end() cleanup runs,
|
||||
* leaking the slot. Callers must skip before initializing the hardware.
|
||||
*/
|
||||
(void)card;
|
||||
#else
|
||||
const size_t block_size = card->csd.sector_size;
|
||||
const size_t block_count = 8;
|
||||
const size_t buffer_size = block_size * block_count;
|
||||
|
||||
/* Allocate buffer in PSRAM */
|
||||
uint8_t *psram_buf = heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA | MALLOC_CAP_CACHE_ALIGNED);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(psram_buf, "Failed to allocate PSRAM buffer; is PSRAM enabled?");
|
||||
TEST_ASSERT_MESSAGE(esp_ptr_external_ram(psram_buf), "Buffer not in PSRAM");
|
||||
|
||||
/* Also allocate a reference buffer in internal RAM for comparison */
|
||||
uint8_t *internal_buf = heap_caps_malloc(buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_CACHE_ALIGNED);
|
||||
TEST_ASSERT_NOT_NULL(internal_buf);
|
||||
|
||||
/* A PSRAM buffer can be transferred directly only if the host reports it as
|
||||
* usable; otherwise the protocol layer transparently bounce-buffers it. This
|
||||
* test verifies correctness for both cases. */
|
||||
bool host_can_use_psram_directly =
|
||||
card->host.check_buffer_alignment(card->host.slot, psram_buf, buffer_size);
|
||||
printf("Testing PSRAM buffer R/W: %d blocks, host can use PSRAM directly=%s\n",
|
||||
(int)block_count, host_can_use_psram_directly ? "true" : "false");
|
||||
|
||||
/* Test A: Write from PSRAM buffer, read back to internal buffer, verify */
|
||||
const uint32_t seed_a = 0xABCD1234;
|
||||
fill_buffer(seed_a, psram_buf, buffer_size / sizeof(uint32_t));
|
||||
TEST_ESP_OK(sdmmc_write_sectors(card, psram_buf, 0, block_count));
|
||||
memset(internal_buf, 0xcc, buffer_size);
|
||||
TEST_ESP_OK(sdmmc_read_sectors(card, internal_buf, 0, block_count));
|
||||
check_buffer(seed_a, internal_buf, buffer_size / sizeof(uint32_t));
|
||||
|
||||
/* Test B: Write from internal buffer, read back to PSRAM buffer, verify */
|
||||
const uint32_t seed_b = 0x5678EFAB;
|
||||
fill_buffer(seed_b, internal_buf, buffer_size / sizeof(uint32_t));
|
||||
TEST_ESP_OK(sdmmc_write_sectors(card, internal_buf, 0, block_count));
|
||||
memset(psram_buf, 0xcc, buffer_size);
|
||||
TEST_ESP_OK(sdmmc_read_sectors(card, psram_buf, 0, block_count));
|
||||
check_buffer(seed_b, psram_buf, buffer_size / sizeof(uint32_t));
|
||||
|
||||
/* Test C: Both write and read from PSRAM buffer */
|
||||
const uint32_t seed_c = 0xDEAD9876;
|
||||
fill_buffer(seed_c, psram_buf, buffer_size / sizeof(uint32_t));
|
||||
TEST_ESP_OK(sdmmc_write_sectors(card, psram_buf, 0, block_count));
|
||||
memset(psram_buf, 0xcc, buffer_size);
|
||||
TEST_ESP_OK(sdmmc_read_sectors(card, psram_buf, 0, block_count));
|
||||
check_buffer(seed_c, psram_buf, buffer_size / sizeof(uint32_t));
|
||||
|
||||
free(psram_buf);
|
||||
free(internal_buf);
|
||||
printf("PSRAM buffer R/W test passed\n");
|
||||
#endif // CONFIG_SPIRAM
|
||||
}
|
||||
|
||||
+24
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -128,3 +128,26 @@ TEST_CASE("sdmmc read/write with concurrent high-prio task, slot 1, 4-bit", "[sd
|
||||
{
|
||||
do_one_sdmmc_rw_test_highprio_task(1, 4);
|
||||
}
|
||||
|
||||
static void do_one_sdmmc_rw_test_psram_dma_buffer(int slot, int width)
|
||||
{
|
||||
sdmmc_card_t card;
|
||||
int freq_khz = SDMMC_FREQ_HIGHSPEED;
|
||||
#if !CONFIG_SPIRAM
|
||||
TEST_IGNORE_MESSAGE("PSRAM is not enabled");
|
||||
#endif
|
||||
sdmmc_test_sd_skip_if_board_incompatible(slot, width, freq_khz, NO_DDR, NO_EMMC);
|
||||
sdmmc_test_sd_begin(slot, width, freq_khz, 0, &card);
|
||||
sdmmc_test_rw_psram_buffer(&card);
|
||||
sdmmc_test_sd_end(&card);
|
||||
}
|
||||
|
||||
TEST_CASE("sdmmc read/write using PSRAM DMA accessible buffer, slot 0, 4-bit", "[sdmmc]")
|
||||
{
|
||||
do_one_sdmmc_rw_test_psram_dma_buffer(0, 4);
|
||||
}
|
||||
|
||||
TEST_CASE("sdmmc read/write using PSRAM DMA accessible buffer, slot 1, 4-bit", "[sdmmc]")
|
||||
{
|
||||
do_one_sdmmc_rw_test_psram_dma_buffer(1, 4);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user