Merge branch 'feature/sdmmc_p4_psram' into 'master'

feat(sdmmc): add support for PSRAM DMA

Closes IDF-9662

See merge request espressif/esp-idf!30044
This commit is contained in:
Ivan Grokhotkov 2024-04-17 18:10:00 +08:00
commit 8a66e059e9
9 changed files with 101 additions and 44 deletions

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -14,7 +14,6 @@
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "soc/sdmmc_periph.h"
#include "soc/soc_memory_layout.h"
#include "driver/sdmmc_types.h"
#include "driver/sdmmc_defs.h"
#include "driver/sdmmc_host.h"
@ -130,14 +129,18 @@ esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo)
if (cmdinfo->data) {
// Length should be either <4 or >=4 and =0 (mod 4).
if (cmdinfo->datalen >= 4 && cmdinfo->datalen % 4 != 0) {
ESP_LOGD(TAG, "%s: invalid size: total=%d",
ESP_LOGE(TAG, "%s: invalid size: total=%d",
__func__, cmdinfo->datalen);
ret = ESP_ERR_INVALID_SIZE;
goto out;
}
if ((intptr_t) cmdinfo->data % 4 != 0 ||
!esp_ptr_dma_capable(cmdinfo->data)) {
ESP_LOGD(TAG, "%s: buffer %p can not be used for DMA", __func__, cmdinfo->data);
esp_dma_mem_info_t dma_mem_info;
sdmmc_host_get_dma_info(slot, &dma_mem_info);
#ifdef SOC_SDMMC_PSRAM_DMA_CAPABLE
dma_mem_info.extra_heap_caps |= MALLOC_CAP_SPIRAM;
#endif
if (!esp_dma_is_buffer_alignment_satisfied(cmdinfo->data, cmdinfo->buflen, dma_mem_info)) {
ESP_LOGE(TAG, "%s: buffer %p can not be used for DMA", __func__, cmdinfo->data);
ret = ESP_ERR_INVALID_ARG;
goto out;
}

View File

@ -1,13 +1,16 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include "esp_dma_utils.h"
#include "esp_heap_caps.h"
#include "test_utils.h"
#include "sdkconfig.h"
#include "soc/soc_caps.h"
@ -17,7 +20,8 @@
#include "sdmmc_test_rw_common.h"
static void do_single_rw_perf_test(sdmmc_card_t* card, size_t start_block,
size_t block_count, size_t alignment, FILE* performance_log);
size_t block_count, size_t alignment, FILE* performance_log,
uint32_t extra_alloc_caps);
static void fill_buffer(uint32_t seed, uint8_t* dst, size_t count)
{
@ -41,13 +45,22 @@ static void check_buffer(uint32_t seed, const uint8_t* src, size_t count)
}
static void do_single_rw_perf_test(sdmmc_card_t* card, size_t start_block,
size_t block_count, size_t alignment, FILE* performance_log)
size_t block_count, size_t alignment, FILE* performance_log,
uint32_t extra_alloc_caps)
{
size_t block_size = card->csd.sector_size;
size_t total_size = block_size * block_count;
printf(" %8d | %3d | %d | %4.1f ", start_block, block_count, alignment, total_size / 1024.0f);
const char* alloc_str = (extra_alloc_caps & MALLOC_CAP_SPIRAM) ? "spiram" : " sram ";
printf(" %8d | %3d | %d | %s | %4.1f ", start_block, block_count, alignment, alloc_str, total_size / 1024.0f);
size_t actual_size = 0;
uint32_t *buffer = NULL;
esp_dma_mem_info_t dma_mem_info = {
.extra_heap_caps = extra_alloc_caps,
.dma_alignment_bytes = 64,
};
TEST_ESP_OK(esp_dma_capable_malloc(total_size + 4, &dma_mem_info, (void**) &buffer, &actual_size));
uint32_t* buffer = heap_caps_malloc(total_size + 4, MALLOC_CAP_DMA);
size_t offset = alignment % 4;
uint8_t* c_buffer = (uint8_t*) buffer + offset;
fill_buffer(start_block, c_buffer, total_size / sizeof(buffer[0]));
@ -80,10 +93,10 @@ static void do_single_rw_perf_test(sdmmc_card_t* card, size_t start_block,
static const char wr_speed_str[] = "SDMMC_WR_SPEED";
static const char rd_speed_str[] = "SDMMC_RD_SPEED";
int aligned = ((alignment % 4) == 0) ? 1 : 0;
IDF_LOG_PERFORMANCE(wr_speed_str, "%d, blk_n: %d, aligned: %d",
(int)(total_size * 1000 / time_wr), block_count, aligned);
IDF_LOG_PERFORMANCE(rd_speed_str, "%d, blk_n: %d, aligned: %d",
(int)(total_size * 1000 / time_rd), block_count, aligned);
IDF_LOG_PERFORMANCE(wr_speed_str, "%d, blk_n: %d, aligned: %d, alloc: %s",
(int)(total_size * 1000 / time_wr), block_count, aligned, alloc_str);
IDF_LOG_PERFORMANCE(rd_speed_str, "%d, blk_n: %d, aligned: %d, alloc: %s",
(int)(total_size * 1000 / time_rd), block_count, aligned, alloc_str);
stdout = old_stdout;
}
}
@ -93,7 +106,13 @@ void sdmmc_test_rw_unaligned_buffer(sdmmc_card_t* card)
const size_t buffer_size = 4096;
const size_t block_count = buffer_size / 512;
const size_t extra = 4;
uint8_t* buffer = heap_caps_malloc(buffer_size + extra, MALLOC_CAP_DMA);
const size_t total_size = buffer_size + extra;
size_t actual_size = 0;
uint8_t *buffer = NULL;
esp_dma_mem_info_t dma_mem_info = {
.dma_alignment_bytes = 64,
};
TEST_ESP_OK(esp_dma_capable_malloc(total_size + 4, &dma_mem_info, (void**) &buffer, &actual_size));
// Check read behavior: do aligned write, then unaligned read
const uint32_t seed = 0x89abcdef;
@ -116,20 +135,30 @@ void sdmmc_test_rw_unaligned_buffer(sdmmc_card_t* card)
void sdmmc_test_rw_performance(sdmmc_card_t *card, FILE *perf_log)
{
sdmmc_card_print_info(stdout, card);
printf(" sector | count | align | size(kB) | wr_time(ms) | wr_speed(MB/s) | rd_time(ms) | rd_speed(MB/s)\n");
printf(" sector | count | align | alloc | size(kB) | wr_time(ms) | wr_speed(MB/s) | rd_time(ms) | rd_speed(MB/s)\n");
const int offset = 0;
/* aligned */
do_single_rw_perf_test(card, offset, 1, 4, perf_log);
do_single_rw_perf_test(card, offset, 4, 4, perf_log);
do_single_rw_perf_test(card, offset, 8, 4, perf_log);
do_single_rw_perf_test(card, offset, 16, 4, perf_log);
do_single_rw_perf_test(card, offset, 32, 4, perf_log);
do_single_rw_perf_test(card, offset, 64, 4, perf_log);
do_single_rw_perf_test(card, offset, 128, 4, perf_log);
do_single_rw_perf_test(card, offset, 1, 4, perf_log, 0);
do_single_rw_perf_test(card, offset, 4, 4, perf_log, 0);
do_single_rw_perf_test(card, offset, 8, 4, perf_log, 0);
do_single_rw_perf_test(card, offset, 16, 4, perf_log, 0);
do_single_rw_perf_test(card, offset, 32, 4, perf_log, 0);
do_single_rw_perf_test(card, offset, 64, 4, perf_log, 0);
do_single_rw_perf_test(card, offset, 128, 4, perf_log, 0);
/* unaligned */
do_single_rw_perf_test(card, offset, 1, 1, perf_log);
do_single_rw_perf_test(card, offset, 8, 1, perf_log);
do_single_rw_perf_test(card, offset, 128, 1, perf_log);
do_single_rw_perf_test(card, offset, 1, 1, perf_log, 0);
do_single_rw_perf_test(card, offset, 8, 1, perf_log, 0);
do_single_rw_perf_test(card, offset, 128, 1, perf_log, 0);
#if CONFIG_SPIRAM && SOC_SDMMC_PSRAM_DMA_CAPABLE
/* spiram */
do_single_rw_perf_test(card, offset, 1, 4, perf_log, MALLOC_CAP_SPIRAM);
do_single_rw_perf_test(card, offset, 4, 4, perf_log, MALLOC_CAP_SPIRAM);
do_single_rw_perf_test(card, offset, 8, 4, perf_log, MALLOC_CAP_SPIRAM);
do_single_rw_perf_test(card, offset, 16, 4, perf_log, MALLOC_CAP_SPIRAM);
do_single_rw_perf_test(card, offset, 32, 4, perf_log, MALLOC_CAP_SPIRAM);
do_single_rw_perf_test(card, offset, 64, 4, perf_log, MALLOC_CAP_SPIRAM);
do_single_rw_perf_test(card, offset, 128, 4, perf_log, MALLOC_CAP_SPIRAM);
#endif
}
void sdmmc_test_rw_with_offset(sdmmc_card_t* card)
@ -137,22 +166,22 @@ void sdmmc_test_rw_with_offset(sdmmc_card_t* card)
sdmmc_card_print_info(stdout, card);
printf(" sector | count | align | size(kB) | wr_time(ms) | wr_speed(MB/s) | rd_time(ms) | rd_speed(MB/s)\n");
/* aligned */
do_single_rw_perf_test(card, 1, 16, 4, NULL);
do_single_rw_perf_test(card, 16, 32, 4, NULL);
do_single_rw_perf_test(card, 48, 64, 4, NULL);
do_single_rw_perf_test(card, 128, 128, 4, NULL);
do_single_rw_perf_test(card, card->csd.capacity - 64, 32, 4, NULL);
do_single_rw_perf_test(card, card->csd.capacity - 64, 64, 4, NULL);
do_single_rw_perf_test(card, card->csd.capacity - 8, 1, 4, NULL);
do_single_rw_perf_test(card, card->csd.capacity / 2, 1, 4, NULL);
do_single_rw_perf_test(card, card->csd.capacity / 2, 4, 4, NULL);
do_single_rw_perf_test(card, card->csd.capacity / 2, 8, 4, NULL);
do_single_rw_perf_test(card, card->csd.capacity / 2, 16, 4, NULL);
do_single_rw_perf_test(card, card->csd.capacity / 2, 32, 4, NULL);
do_single_rw_perf_test(card, card->csd.capacity / 2, 64, 4, NULL);
do_single_rw_perf_test(card, card->csd.capacity / 2, 128, 4, NULL);
do_single_rw_perf_test(card, 1, 16, 4, NULL, 0);
do_single_rw_perf_test(card, 16, 32, 4, NULL, 0);
do_single_rw_perf_test(card, 48, 64, 4, NULL, 0);
do_single_rw_perf_test(card, 128, 128, 4, NULL, 0);
do_single_rw_perf_test(card, card->csd.capacity - 64, 32, 4, NULL, 0);
do_single_rw_perf_test(card, card->csd.capacity - 64, 64, 4, NULL, 0);
do_single_rw_perf_test(card, card->csd.capacity - 8, 1, 4, NULL, 0);
do_single_rw_perf_test(card, card->csd.capacity / 2, 1, 4, NULL, 0);
do_single_rw_perf_test(card, card->csd.capacity / 2, 4, 4, NULL, 0);
do_single_rw_perf_test(card, card->csd.capacity / 2, 8, 4, NULL, 0);
do_single_rw_perf_test(card, card->csd.capacity / 2, 16, 4, NULL, 0);
do_single_rw_perf_test(card, card->csd.capacity / 2, 32, 4, NULL, 0);
do_single_rw_perf_test(card, card->csd.capacity / 2, 64, 4, NULL, 0);
do_single_rw_perf_test(card, card->csd.capacity / 2, 128, 4, NULL, 0);
/* unaligned */
do_single_rw_perf_test(card, card->csd.capacity / 2, 1, 1, NULL);
do_single_rw_perf_test(card, card->csd.capacity / 2, 8, 1, NULL);
do_single_rw_perf_test(card, card->csd.capacity / 2, 128, 1, NULL);
do_single_rw_perf_test(card, card->csd.capacity / 2, 1, 1, NULL, 0);
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);
}

View File

@ -5,6 +5,8 @@ set(priv_requires
sdmmc_tests
# general
unity
# for PSRAM tests
esp_psram
)
idf_component_register(SRCS ${srcs}

View File

@ -0,0 +1,5 @@
CONFIG_SDMMC_BOARD_ESP32P4_EV_BOARD=y
CONFIG_SPIRAM=y
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
CONFIG_SPIRAM_SPEED_200M=y

View File

@ -405,6 +405,9 @@ esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src,
size_t block_size = card->csd.sector_size;
esp_dma_mem_info_t dma_mem_info;
card->host.get_dma_info(card->host.slot, &dma_mem_info);
#ifdef SOC_SDMMC_PSRAM_DMA_CAPABLE
dma_mem_info.extra_heap_caps |= MALLOC_CAP_SPIRAM;
#endif
if (esp_dma_is_buffer_alignment_satisfied(src, block_size * block_count, dma_mem_info)) {
err = sdmmc_write_sectors_dma(card, src, start_block, block_count, block_size * block_count);
} else {
@ -413,6 +416,9 @@ esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src,
// DMA-capable buffer.
void *tmp_buf = NULL;
size_t actual_size = 0;
// Clear the SPIRAM flag. We don't want to force the allocation into SPIRAM, the allocator
// will decide based on the buffer size and memory availability.
dma_mem_info.extra_heap_caps &= ~MALLOC_CAP_SPIRAM;
err = esp_dma_capable_malloc(block_size, &dma_mem_info, &tmp_buf, &actual_size);
if (err != ESP_OK) {
return err;

View File

@ -1027,6 +1027,10 @@ config SOC_SDMMC_IO_POWER_EXTERNAL
bool
default y
config SOC_SDMMC_PSRAM_DMA_CAPABLE
bool
default y
config SOC_SHA_DMA_MAX_BUFFER_SIZE
int
default 3968

View File

@ -415,6 +415,7 @@
/* Supported host clock delay phase number */
#define SOC_SDMMC_DELAY_PHASE_NUM 4
#define SOC_SDMMC_IO_POWER_EXTERNAL 1 ///< SDMMC IO power controlled by external power supply
#define SOC_SDMMC_PSRAM_DMA_CAPABLE 1 ///< SDMMC peripheral can do DMA transfer to/from PSRAM
// TODO: IDF-5353 (Copy from esp32c3, need check)
/*--------------------------- SHA CAPS ---------------------------------------*/

View File

@ -7,6 +7,8 @@ set(requires
sdmmc_test_boards sdmmc_tests sdspi_tests
# various console commands
cmd_unity cmd_system cmd_sdmmc
# for PSRAM-related tests
esp_psram
)
idf_component_register(SRCS sdmmc_console_main.c

View File

@ -0,0 +1,5 @@
CONFIG_SDMMC_BOARD_ESP32P4_EV_BOARD=y
CONFIG_SPIRAM=y
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
CONFIG_SPIRAM_SPEED_200M=y