Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SD Card UHS-I SDR104 support (IDFGH-14508) #15276

Open
PickaxeHit opened this issue Jan 24, 2025 · 3 comments
Open

SD Card UHS-I SDR104 support (IDFGH-14508) #15276

PickaxeHit opened this issue Jan 24, 2025 · 3 comments
Labels
Status: Opened Issue is new Type: Feature Request Feature request for IDF

Comments

@PickaxeHit
Copy link

Is your feature request related to a problem?

No response

Describe the solution you'd like.

I noticed that there is support for UHS-I's SDR/DDR50 in ESP-IDF, but there are still many UHS-I cards that natively have higher speeds (such as SDR104, which communicates at a frequency of 208MHz). After my testing, SDR104 (or actually SDR100) itself can work, but it is just unstable. The problem only occurs when reading:

E (1494) sdmmc_req: sdmmc_host_wait_for_event returned 0x107
E (6494) sdmmc_cmd: sdmmc_read_sectors_dma: sdmmc_wait_for_idle timeout

But there is no error when writing.
I am not familiar with the SDMMC driver of IDF. I would like to understand why this happens? And whether support for SDR104 can be added in the future?

Describe alternatives you've considered.

Code here:

/* SD card and FAT filesystem example.
   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

// This example uses SDMMC peripheral to communicate with SD card.

#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_vfs_fat.h"

#include <sys/time.h>
#include "sdmmc_cmd.h"
#include "driver/sdmmc_host.h"
#include "sd_test_io.h"
#if SOC_SDMMC_IO_POWER_EXTERNAL
#include "sd_pwr_ctrl_by_on_chip_ldo.h"
#endif

#define EXAMPLE_MAX_CHAR_SIZE    64

static const char *TAG = "example";

#define MOUNT_POINT "/sdcard"
#define EXAMPLE_IS_UHS1    (CONFIG_EXAMPLE_SDMMC_SPEED_UHS_I_SDR50 || CONFIG_EXAMPLE_SDMMC_SPEED_UHS_I_DDR50)

#ifdef CONFIG_EXAMPLE_DEBUG_PIN_CONNECTIONS
const char* names[] = {"CLK", "CMD", "D0", "D1", "D2", "D3"};
const int pins[] = {CONFIG_EXAMPLE_PIN_CLK,
                    CONFIG_EXAMPLE_PIN_CMD,
                    CONFIG_EXAMPLE_PIN_D0
                    #ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
                    ,CONFIG_EXAMPLE_PIN_D1,
                    CONFIG_EXAMPLE_PIN_D2,
                    CONFIG_EXAMPLE_PIN_D3
                    #endif
                    };

const int pin_count = sizeof(pins)/sizeof(pins[0]);

#if CONFIG_EXAMPLE_ENABLE_ADC_FEATURE
const int adc_channels[] = {CONFIG_EXAMPLE_ADC_PIN_CLK,
                            CONFIG_EXAMPLE_ADC_PIN_CMD,
                            CONFIG_EXAMPLE_ADC_PIN_D0
                            #ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
                            ,CONFIG_EXAMPLE_ADC_PIN_D1,
                            CONFIG_EXAMPLE_ADC_PIN_D2,
                            CONFIG_EXAMPLE_ADC_PIN_D3
                            #endif
                            };
#endif //CONFIG_EXAMPLE_ENABLE_ADC_FEATURE

pin_configuration_t config = {
    .names = names,
    .pins = pins,
#if CONFIG_EXAMPLE_ENABLE_ADC_FEATURE
    .adc_channels = adc_channels,
#endif
};
#endif //CONFIG_EXAMPLE_DEBUG_PIN_CONNECTIONS

static esp_err_t s_example_write_file(const char *path, char *data)
{
    ESP_LOGI(TAG, "Opening file %s", path);
    FILE *f = fopen(path, "w");
    if (f == NULL) {
        ESP_LOGE(TAG, "Failed to open file for writing");
        return ESP_FAIL;
    }
    fprintf(f, data);
    fclose(f);
    ESP_LOGI(TAG, "File written");

    return ESP_OK;
}

static esp_err_t s_example_read_file(const char *path)
{
    ESP_LOGI(TAG, "Reading file %s", path);
    FILE *f = fopen(path, "r");
    if (f == NULL) {
        ESP_LOGE(TAG, "Failed to open file for reading");
        return ESP_FAIL;
    }
    char line[EXAMPLE_MAX_CHAR_SIZE];
    fgets(line, sizeof(line), f);
    fclose(f);

    // strip newline
    char *pos = strchr(line, '\n');
    if (pos) {
        *pos = '\0';
    }
    ESP_LOGI(TAG, "Read from file: '%s'", line);

    return ESP_OK;
}


#define UNIT_STRING "B"
#define UNIT_MULTIPLIER (1)

static void print_results(const char *name, double time, size_t size, int repeat_count)
{
    double average = time / repeat_count;
    double speed = (size / average) / (1024 * 1024) * (1000 * 1000);
    printf("[%-55s] (%dx) %8.3f ms %8.2f k" UNIT_STRING " %10.3f M" UNIT_STRING "/s\n",
           name, repeat_count, (float) average / 1000, (float)size * UNIT_MULTIPLIER / 1024, speed);
}


void app_main(void)
{
    esp_err_t ret;

    // Options for mounting the filesystem.
    // If format_if_mount_failed is set to true, SD card will be partitioned and
    // formatted in case when mounting fails.
    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
#ifdef CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED
        .format_if_mount_failed = true,
#else
        .format_if_mount_failed = false,
#endif // EXAMPLE_FORMAT_IF_MOUNT_FAILED
        .max_files = 5,
        .allocation_unit_size = 16 * 1024
    };
    sdmmc_card_t *card=(sdmmc_card_t *)malloc(sizeof(sdmmc_card_t));;
    const char mount_point[] = MOUNT_POINT;
    ESP_LOGI(TAG, "Initializing SD card");

    // Use settings defined above to initialize SD card and mount FAT filesystem.
    // Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions.
    // Please check its source code and implement error recovery when developing
    // production applications.

    ESP_LOGI(TAG, "Using SDMMC peripheral");

    // By default, SD card frequency is initialized to SDMMC_FREQ_DEFAULT (20MHz)
    // For setting a specific frequency, use host.max_freq_khz (range 400kHz - 40MHz for SDMMC)
    // Example: for fixed frequency of 10MHz, use host.max_freq_khz = 10000;
    sdmmc_host_t host = SDMMC_HOST_DEFAULT();
    host.input_delay_phase=SDMMC_DELAY_PHASE_2;
#if CONFIG_EXAMPLE_SDMMC_SPEED_HS
    host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
#elif CONFIG_EXAMPLE_SDMMC_SPEED_UHS_I_SDR50
    host.slot = SDMMC_HOST_SLOT_0;
    host.max_freq_khz = 208000;
    host.flags |= SDMMC_HOST_FLAG_DDR;
#elif CONFIG_EXAMPLE_SDMMC_SPEED_UHS_I_DDR50
    host.slot = SDMMC_HOST_SLOT_0;
    host.max_freq_khz = SDMMC_FREQ_DDR50;
#endif

    // For SoCs where the SD power can be supplied both via an internal or external (e.g. on-board LDO) power supply.
    // When using specific IO pins (which can be used for ultra high-speed SDMMC) to connect to the SD card
    // and the internal LDO power supply, we need to initialize the power supply first.
#if CONFIG_EXAMPLE_SD_PWR_CTRL_LDO_INTERNAL_IO
    sd_pwr_ctrl_ldo_config_t ldo_config = {
        .ldo_chan_id = CONFIG_EXAMPLE_SD_PWR_CTRL_LDO_IO_ID,
    };
    sd_pwr_ctrl_handle_t pwr_ctrl_handle = NULL;

    ret = sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &pwr_ctrl_handle);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to create a new on-chip LDO power control driver");
        return;
    }
    host.pwr_ctrl_handle = pwr_ctrl_handle;
#endif

    // This initializes the slot without card detect (CD) and write protect (WP) signals.
    // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
    sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
#if EXAMPLE_IS_UHS1
    slot_config.flags |= SDMMC_SLOT_FLAG_UHS1;
#endif

    // Set bus width to use:
#ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
    slot_config.width = 4;
#else
    slot_config.width = 1;
#endif

    // On chips where the GPIOs used for SD card can be configured, set them in
    // the slot_config structure:
#ifdef CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
    slot_config.clk = CONFIG_EXAMPLE_PIN_CLK;
    slot_config.cmd = CONFIG_EXAMPLE_PIN_CMD;
    slot_config.d0 = CONFIG_EXAMPLE_PIN_D0;
#ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
    slot_config.d1 = CONFIG_EXAMPLE_PIN_D1;
    slot_config.d2 = CONFIG_EXAMPLE_PIN_D2;
    slot_config.d3 = CONFIG_EXAMPLE_PIN_D3;
#endif  // CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
#endif  // CONFIG_SOC_SDMMC_USE_GPIO_MATRIX

    // Enable internal pullups on enabled pins. The internal pullups
    // are insufficient however, please make sure 10k external pullups are
    // connected on the bus. This is for debug / example purpose only.
    slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;

    ret = sdmmc_host_init();
    ESP_ERROR_CHECK(ret);
    ret = sdmmc_host_init_slot(host.slot, &slot_config);
    ESP_ERROR_CHECK(ret);

    sdmmc_card_init(&host, card);

//     ESP_LOGI(TAG, "Mounting filesystem");
//     ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card);

//     if (ret != ESP_OK) {
//         if (ret == ESP_FAIL) {
//             ESP_LOGE(TAG, "Failed to mount filesystem. "
//                      "If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
//         } else {
//             ESP_LOGE(TAG, "Failed to initialize the card (%s). "
//                      "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
// #ifdef CONFIG_EXAMPLE_DEBUG_PIN_CONNECTIONS
//             check_sd_card_pins(&config, pin_count);
// #endif
//         }
//         return;
//     }
//     ESP_LOGI(TAG, "Filesystem mounted");

    // Card has been initialized, print its properties
    sdmmc_card_print_info(stdout, card);

    uint32_t sector_size = card->csd.sector_size;
    size_t sector_count = 262144 / sector_size;
    size_t subsection_sector_count = sector_count / 4;

    // Best performance is achieved when the buffer is internal (not PSRAM) and aligned to RAM line size
    char *buf = (char *) heap_caps_calloc(1, sector_count * sector_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_CACHE_ALIGNED | MALLOC_CAP_DMA);
    assert(buf != NULL);

        struct timeval tv_start;
    struct timeval tv_end;
    struct timeval tv_diff;

    uint8_t repeat_count=10;

    for(int i = 0; i < 4; i++) {
        vTaskDelay(100/portTICK_PERIOD_MS);
        const size_t count = subsection_sector_count * (i + 1);
        double time = 0;

        for (int i = 0; i < repeat_count; ++i) {
            gettimeofday(&tv_start, NULL);
            ESP_ERROR_CHECK(sdmmc_write_sectors(card, buf, 0, sector_count));
            gettimeofday(&tv_end, NULL);
            timersub(&tv_end, &tv_start, &tv_diff);
            time += (tv_diff.tv_sec * 1000000 + tv_diff.tv_usec);
        }
        print_results("SD raw write", time, count * sector_size, repeat_count);

        time = 0;
        for (int i = 0; i < repeat_count; ++i) {
            gettimeofday(&tv_start, NULL);
            ESP_ERROR_CHECK(sdmmc_read_sectors(card, buf, 0, sector_count));
            gettimeofday(&tv_end, NULL);
            timersub(&tv_end, &tv_start, &tv_diff);
            time += (tv_diff.tv_sec * 1000000 + tv_diff.tv_usec);
        }
        print_results("SD raw read", time, count * sector_size, repeat_count);
    }
    free(buf);
}

Modified from example/storage/sd_card/sdmmc

Log here:

I (20) boot: ESP-IDF v5.5-dev-1428-g0f0068fff3 2nd stage bootloader
I (20) boot: compile time Jan 24 2025 10:50:42
I (21) boot: Multicore bootloader
I (22) boot: chip revision: v1.0
I (22) boot: efuse block revision: v0.1
I (22) qio_mode: Enabling QIO for flash chip WinBond
I (22) boot.esp32p4: SPI Speed      : 80MHz
I (23) boot.esp32p4: SPI Mode       : QIO
I (23) boot.esp32p4: SPI Flash Size : 32MB
I (23) boot: Enabling RNG early entropy source...
I (24) boot: Partition Table:
I (24) boot: ## Label            Usage          Type ST Offset   Length
I (24) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (25) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (25) boot:  2 factory          factory app      00 00 00010000 00100000
I (26) boot: End of partition table
I (27) esp_image: segment 0: paddr=00010020 vaddr=40020020 size=0a93ch ( 43324) map
I (34) esp_image: segment 1: paddr=0001a964 vaddr=30100000 size=00044h (    68) load
I (35) esp_image: segment 2: paddr=0001a9b0 vaddr=4ff00000 size=05668h ( 22120) load
I (39) esp_image: segment 3: paddr=00020020 vaddr=40000020 size=1ef38h (126776) map
I (56) esp_image: segment 4: paddr=0003ef60 vaddr=4ff05668 size=090e8h ( 37096) load
I (63) esp_image: segment 5: paddr=00048050 vaddr=4ff0e780 size=02038h (  8248) load
I (68) boot: Loaded app from partition at offset 0x10000
I (68) boot: Disabling RNG early entropy source...
I (69) cpu_start: Multicore app
I (79) cpu_start: Pro cpu start user code
I (79) cpu_start: cpu freq: 360000000 Hz
I (80) app_init: Application information:
I (80) app_init: Project name:     sd_card
I (80) app_init: App version:      1
I (80) app_init: Compile time:     Jan 24 2025 10:50:37
I (80) app_init: ELF file SHA256:  17d04afdc...
I (80) app_init: ESP-IDF:          v5.5-dev-1428-g0f0068fff3
I (81) efuse_init: Min chip rev:     v0.1
I (81) efuse_init: Max chip rev:     v1.99 
I (81) efuse_init: Chip rev:         v1.0
I (81) heap_init: Initializing. RAM available for dynamic allocation:
I (82) heap_init: At 4FF11EC0 len 00029100 (164 KiB): RAM
I (82) heap_init: At 4FF3AFC0 len 00004BF0 (18 KiB): RAM
I (82) heap_init: At 4FF40000 len 00060000 (384 KiB): RAM
I (82) heap_init: At 50108080 len 00007F80 (31 KiB): RTCRAM
I (83) heap_init: At 30100044 len 00001FBC (7 KiB): TCM
I (83) spi_flash: detected chip: winbond
I (83) spi_flash: flash io: qio
I (84) main_task: Started on CPU0
I (94) main_task: Calling app_main()
I (94) example: Initializing SD card
I (94) example: Using SDMMC peripheral
W (94) ldo: The voltage value 0 is out of the recommended range [500, 2700]
I (124) sdmmc_periph: host sampling edge is delayed by 2500 ps
Name: SD128
Type: SDHC
Speed: 200.00 MHz (limit: 208.00 MHz)
Size: 118900MB
CSD: ver=2, sector_size=512, capacity=243507200 read_bl_len=9
SSR: bus_width=4
[SD raw write                                           ] (10x)    6.280 ms    64.00 kB      9.952 MB/s
[SD raw read                                            ] (10x)    3.183 ms    64.00 kB     19.634 MB/s
[SD raw write                                           ] (10x)    6.292 ms   128.00 kB     19.868 MB/s
[SD raw read                                            ] (10x)    3.182 ms   128.00 kB     39.286 MB/s
[SD raw write                                           ] (10x)    6.571 ms   192.00 kB     28.532 MB/s
[SD raw read                                            ] (10x)    3.182 ms   192.00 kB     58.925 MB/s
[SD raw write                                           ] (10x)    6.546 ms   256.00 kB     38.189 MB/s
[SD raw read                                            ] (10x)    3.182 ms   256.00 kB     78.574 MB/s
I (884) main_task: Returned from app_main()

The above is the log output of a successful test by chance.

When read failed:

I (20) boot: ESP-IDF v5.5-dev-1428-g0f0068fff3 2nd stage bootloader
I (20) boot: compile time Jan 24 2025 10:50:42
I (21) boot: Multicore bootloader
I (22) boot: chip revision: v1.0
I (22) boot: efuse block revision: v0.1
I (22) qio_mode: Enabling QIO for flash chip WinBond
I (22) boot.esp32p4: SPI Speed      : 80MHz
I (23) boot.esp32p4: SPI Mode       : QIO
I (23) boot.esp32p4: SPI Flash Size : 32MB
I (23) boot: Enabling RNG early entropy source...
I (24) boot: Partition Table:
I (24) boot: ## Label            Usage          Type ST Offset   Length
I (24) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (25) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (25) boot:  2 factory          factory app      00 00 00010000 00100000
I (26) boot: End of partition table
I (27) esp_image: segment 0: paddr=00010020 vaddr=40020020 size=0a93ch ( 43324) map
I (34) esp_image: segment 1: paddr=0001a964 vaddr=30100000 size=00044h (    68) load
I (35) esp_image: segment 2: paddr=0001a9b0 vaddr=4ff00000 size=05668h ( 22120) load
I (39) esp_image: segment 3: paddr=00020020 vaddr=40000020 size=1ef38h (126776) map
I (56) esp_image: segment 4: paddr=0003ef60 vaddr=4ff05668 size=090e8h ( 37096) load
I (63) esp_image: segment 5: paddr=00048050 vaddr=4ff0e780 size=02038h (  8248) load
I (68) boot: Loaded app from partition at offset 0x10000
I (68) boot: Disabling RNG early entropy source...
I (69) cpu_start: Multicore app
I (79) cpu_start: Pro cpu start user code
I (79) cpu_start: cpu freq: 360000000 Hz
I (80) app_init: Application information:
I (80) app_init: Project name:     sd_card
I (80) app_init: App version:      1
I (80) app_init: Compile time:     Jan 24 2025 10:50:37
I (80) app_init: ELF file SHA256:  17d04afdc...
I (80) app_init: ESP-IDF:          v5.5-dev-1428-g0f0068fff3
I (81) efuse_init: Min chip rev:     v0.1
I (81) efuse_init: Max chip rev:     v1.99 
I (81) efuse_init: Chip rev:         v1.0
I (81) heap_init: Initializing. RAM available for dynamic allocation:
I (82) heap_init: At 4FF11EC0 len 00029100 (164 KiB): RAM
I (82) heap_init: At 4FF3AFC0 len 00004BF0 (18 KiB): RAM
I (82) heap_init: At 4FF40000 len 00060000 (384 KiB): RAM
I (82) heap_init: At 50108080 len 00007F80 (31 KiB): RTCRAM
I (83) heap_init: At 30100044 len 00001FBC (7 KiB): TCM
I (83) spi_flash: detected chip: winbond
I (84) spi_flash: flash io: qio
I (84) main_task: Started on CPU0
I (94) main_task: Calling app_main()
I (94) example: Initializing SD card
I (94) example: Using SDMMC peripheral
W (94) ldo: The voltage value 0 is out of the recommended range [500, 2700]
I (124) sdmmc_periph: host sampling edge is delayed by 2500 ps
Name: SD128
Type: SDHC
Speed: 200.00 MHz (limit: 208.00 MHz)
Size: 118900MB
CSD: ver=2, sector_size=512, capacity=243507200 read_bl_len=9
SSR: bus_width=4
[SD raw write                                           ] (10x)    6.220 ms    64.00 kB     10.048 MB/s
[SD raw read                                            ] (10x)    3.184 ms    64.00 kB     19.627 MB/s
[SD raw write                                           ] (10x)    7.520 ms   128.00 kB     16.621 MB/s
E (1494) sdmmc_req: sdmmc_host_wait_for_event returned 0x107
E (6494) sdmmc_cmd: sdmmc_read_sectors_dma: sdmmc_wait_for_idle timeout
ESP_ERROR_CHECK failed: esp_err_t 0x107 (ESP_ERR_TIMEOUT) at 0x40009e86

Using Kingston CANVAS GO! Plus 128GB.

Image

Additional context.

No response

@PickaxeHit PickaxeHit added the Type: Feature Request Feature request for IDF label Jan 24, 2025
@github-actions github-actions bot changed the title SD Card UHS-I SDR104 support SD Card UHS-I SDR104 support (IDFGH-14508) Jan 24, 2025
@espressif-bot espressif-bot added the Status: Opened Issue is new label Jan 24, 2025
@igrr
Copy link
Member

igrr commented Jan 24, 2025

After my testing, SDR104 (or actually SDR100) itself can work, but it is just unstable. The problem only occurs when reading:
I am not familiar with the SDMMC driver of IDF. I would like to understand why this happens? And whether support for SDR104 can be added in the future?

This is a limitation of ESP32-P4 hardware related to IO MUX and the clock tree, which we can't work around in software. The datasheet (which is to be released) will mention only SDR/DDR50 as supported modes.

I am not sure if this will be fixed in a future silicon revision of ESP32-P4, or only in some future ESP32-series chip after ESP32-P4.

@PickaxeHit
Copy link
Author

I roughly understand that the ESP32-P4 hardware IO cannot support frequencies up to 200MHz, which leads to read failures. Maybe I can try to use a logic analyzer to check what happened and compare the timing differences when successful/unsuccessful.

Do I need to close this issue?

@igrr
Copy link
Member

igrr commented Jan 25, 2025

Maybe I can try to use a logic analyzer to check what happened and compare the timing differences when successful/unsuccessful.

It's related to the signal shape, I recommend using an oscilloscope instead if you want to look into this.

I don't think there is anything to be done in software here, unfortunately, until the new chip (revision) capable of 200 MHz signals on the IO MUX comes out. So yes, we can probably close this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Opened Issue is new Type: Feature Request Feature request for IDF
Projects
None yet
Development

No branches or pull requests

3 participants