From 9b0d75f2dff05020b4470c4a6a2b5947cac1c5d1 Mon Sep 17 00:00:00 2001 From: Armando Date: Mon, 27 Nov 2023 19:55:53 +0800 Subject: [PATCH] refactor(sdmmc): added component pytest cases and enabled them on CI --- .gitlab/ci/target-test.yml | 27 +- .../test_apps/.build-test-rules.yml | 6 + .../common_test_flows/CMakeLists.txt | 9 + .../include/sdmmc_test_cd_wp_common.h | 37 ++ .../include/sdmmc_test_rw_common.h | 55 +++ .../sdmmc_test_cd_wp_common.c | 75 ++++ .../common_test_flows/sdmmc_test_rw_common.c | 158 +++++++ .../sdmmc_test_boards/CMakeLists.txt | 3 + .../sdmmc_test_boards/Kconfig.projbuild | 138 +++++++ .../components/sdmmc_test_boards/README.md | 8 + .../include/sdmmc_test_board.h | 129 ++++++ .../sdmmc_test_boards/sdmmc_test_board.c | 115 ++++++ .../sdmmc_test_boards/sdmmc_test_board_defs.c | 391 ++++++++++++++++++ .../test_apps/sdmmc/CMakeLists.txt | 9 + .../test_apps/sdmmc/README.md | 108 +++++ .../test_apps/sdmmc/main/CMakeLists.txt | 13 + .../test_apps/sdmmc/main/test_app_main.c | 43 ++ .../test_apps/sdmmc/pytest_sdmmc.py | 12 + .../test_apps/sdmmc/sdkconfig.defaults | 2 + .../test_apps/sdmmc_tests/CMakeLists.txt | 20 + .../sdmmc_tests/sdmmc_test_begin_end.h | 53 +++ .../sdmmc_tests/sdmmc_test_begin_end_sd.c | 118 ++++++ .../sdmmc_tests/sdmmc_test_cd_wp_sd.c | 48 +++ .../sdmmc_tests/sdmmc_test_probe_sd.c | 66 +++ .../test_apps/sdmmc_tests/sdmmc_test_rw_sd.c | 103 +++++ tools/ci/idf_pytest/constants.py | 3 +- 26 files changed, 1739 insertions(+), 10 deletions(-) create mode 100644 components/esp_driver_sdmmc/test_apps/.build-test-rules.yml create mode 100644 components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/CMakeLists.txt create mode 100644 components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/include/sdmmc_test_cd_wp_common.h create mode 100644 components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/include/sdmmc_test_rw_common.h create mode 100644 components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/sdmmc_test_cd_wp_common.c create mode 100644 components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/sdmmc_test_rw_common.c create mode 100644 components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/CMakeLists.txt create mode 100644 components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/Kconfig.projbuild create mode 100644 components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/README.md create mode 100644 components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/include/sdmmc_test_board.h create mode 100644 components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/sdmmc_test_board.c create mode 100644 components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/sdmmc_test_board_defs.c create mode 100644 components/esp_driver_sdmmc/test_apps/sdmmc/CMakeLists.txt create mode 100644 components/esp_driver_sdmmc/test_apps/sdmmc/README.md create mode 100644 components/esp_driver_sdmmc/test_apps/sdmmc/main/CMakeLists.txt create mode 100644 components/esp_driver_sdmmc/test_apps/sdmmc/main/test_app_main.c create mode 100644 components/esp_driver_sdmmc/test_apps/sdmmc/pytest_sdmmc.py create mode 100644 components/esp_driver_sdmmc/test_apps/sdmmc/sdkconfig.defaults create mode 100644 components/esp_driver_sdmmc/test_apps/sdmmc_tests/CMakeLists.txt create mode 100644 components/esp_driver_sdmmc/test_apps/sdmmc_tests/sdmmc_test_begin_end.h create mode 100644 components/esp_driver_sdmmc/test_apps/sdmmc_tests/sdmmc_test_begin_end_sd.c create mode 100644 components/esp_driver_sdmmc/test_apps/sdmmc_tests/sdmmc_test_cd_wp_sd.c create mode 100644 components/esp_driver_sdmmc/test_apps/sdmmc_tests/sdmmc_test_probe_sd.c create mode 100644 components/esp_driver_sdmmc/test_apps/sdmmc_tests/sdmmc_test_rw_sd.c diff --git a/.gitlab/ci/target-test.yml b/.gitlab/ci/target-test.yml index 36c49b7028..9f15c4c960 100644 --- a/.gitlab/ci/target-test.yml +++ b/.gitlab/ci/target-test.yml @@ -715,6 +715,24 @@ pytest_components_esp32_adc: artifacts: false tags: [ esp32, adc ] +pytest_components_esp32_sdmmc: + extends: + - .pytest_components_dir_template + - .rules:test:component_ut-esp32 + needs: + - job: build_pytest_components_esp32 + artifacts: false + tags: [ esp32, sdcard ] + +pytest_components_esp32s3_sdmmc: + extends: + - .pytest_components_dir_template + - .rules:test:component_ut-esp32s3 + needs: + - job: build_pytest_components_esp32s3 + artifacts: false + tags: [ esp32s3, sdcard ] + pytest_components_esp32_sdio: extends: - .pytest_components_dir_template @@ -1171,15 +1189,6 @@ pytest_components_esp32c3_flash_multi: artifacts: false tags: [ esp32c3, flash_multi ] -pytest_components_esp32_sdmmc: - extends: - - .pytest_components_dir_template - - .rules:test:component_ut-esp32 - needs: - - job: build_pytest_components_esp32 - artifacts: false - tags: [ esp32, sdcard_sdmode ] - pytest_components_esp32_sdspi: extends: - .pytest_components_dir_template diff --git a/components/esp_driver_sdmmc/test_apps/.build-test-rules.yml b/components/esp_driver_sdmmc/test_apps/.build-test-rules.yml new file mode 100644 index 0000000000..7a4947815c --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/.build-test-rules.yml @@ -0,0 +1,6 @@ +components/esp_driver_sdmmc/test_apps/sdmmc: + disable: + - if: SOC_SDMMC_HOST_SUPPORTED != 1 + depends_components: + - sdmmc + - esp_driver_sdmmc 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 new file mode 100644 index 0000000000..6fc914d3ff --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/CMakeLists.txt @@ -0,0 +1,9 @@ +set(srcs "sdmmc_test_cd_wp_common.c" "sdmmc_test_rw_common.c") + +set(public_include "include") + +idf_component_register( + SRCS ${srcs} + INCLUDE_DIRS ${public_include} + PRIV_REQUIRES sdmmc unity test_utils +) diff --git a/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/include/sdmmc_test_cd_wp_common.h b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/include/sdmmc_test_cd_wp_common.h new file mode 100644 index 0000000000..f1f61a7657 --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/include/sdmmc_test_cd_wp_common.h @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "sd_protocol_types.h" +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Test card detect functionality + * + * Common part of CD test, works with both SDMMC and SDSPI hosts. + * Called from sdmmc_test_cd_wp_sd.c and sdmmc_test_cd_wp_spi.c. + * + * @param gpio_cd_num GPIO number used for the test. Must not be connected to anything on the board. + * @param config Pointer to the host configuration structure. + */ +void sdmmc_test_cd_input(int gpio_cd_num, const sdmmc_host_t* config); + +/** + * @brief Test write protect functionality + * + * Common part of WP test, works with both SDMMC and SDSPI hosts. + * Called from sdmmc_test_cd_wp_sd.c and sdmmc_test_cd_wp_spi.c. + * + * @param gpio_wp_num GPIO number used for the test. Must not be connected to anything on the board. + * @param config Pointer to the host configuration structure. + */ +void sdmmc_test_wp_input(int gpio_wp_num, const sdmmc_host_t* config); + +#ifdef __cplusplus +}; +#endif 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 new file mode 100644 index 0000000000..3398c79f02 --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/include/sdmmc_test_rw_common.h @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "sd_protocol_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Test read/write performance of the card + * + * This function writes a buffer to the card, then reads it back. + * The time taken for each operation is measured, and the throughput is calculated. + * The process is repeated for different buffer sizes and card offsets. + * In this test, data is always written and read at the beginning of the card. + * + * This test function works both with SDMMC and SDSPI hosts. + * + * @param card Pointer to the card object, must be initialized before calling this function. + * @param perf_log If not null, additional "performance log" lines are written to this file. + * These lines are in a format recognized by pytest-embedded. + */ +void sdmmc_test_rw_performance(sdmmc_card_t *card, FILE *perf_log); + +/** + * @brief Test read/write with unaligned buffer + * + * This function verifies that the driver deals with unaligned source/destination buffers correctly. + * + * 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_unaligned_buffer(sdmmc_card_t* card); + +/** + * @brief Test read/write with offset + * + * Similar to sdmmc_test_rw_performance, but this time data is written at different + * offsets on the card. + * + * 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_with_offset(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_cd_wp_common.c b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/sdmmc_test_cd_wp_common.c new file mode 100644 index 0000000000..3eb9ab984a --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/sdmmc_test_cd_wp_common.c @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "unity.h" +#include "esp_rom_gpio.h" +#include "hal/gpio_hal.h" +#include "sdmmc_cmd.h" +#include "sdmmc_test_cd_wp_common.h" + +void sdmmc_test_cd_input(int gpio_cd_num, const sdmmc_host_t* config) +{ + sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t)); + TEST_ASSERT_NOT_NULL(card); + + // SDMMC host should have configured CD as input. + // Enable output as well (not using the driver, to avoid touching input + // enable bits). + gpio_hal_context_t gpio_hal = { + .dev = GPIO_HAL_GET_HW(GPIO_PORT_0) + }; + esp_rom_gpio_connect_out_signal(gpio_cd_num, SIG_GPIO_OUT_IDX, false, false); + gpio_hal_output_enable(&gpio_hal, gpio_cd_num); + + // Check that card initialization fails if CD is high + gpio_hal_set_level(&gpio_hal, gpio_cd_num, 1); + usleep(1000); + TEST_ESP_ERR(ESP_ERR_NOT_FOUND, sdmmc_card_init(config, card)); + + // Check that card initialization succeeds if CD is low + gpio_hal_set_level(&gpio_hal, gpio_cd_num, 0); + usleep(1000); + TEST_ESP_OK(sdmmc_card_init(config, card)); + + free(card); +} + +void sdmmc_test_wp_input(int gpio_wp_num, const sdmmc_host_t* config) +{ + sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t)); + TEST_ASSERT_NOT_NULL(card); + + // SDMMC host should have configured WP as input. + // Enable output as well (not using the driver, to avoid touching input + // enable bits). + gpio_hal_context_t gpio_hal = { + .dev = GPIO_HAL_GET_HW(GPIO_PORT_0) + }; + esp_rom_gpio_connect_out_signal(gpio_wp_num, SIG_GPIO_OUT_IDX, false, false); + gpio_hal_output_enable(&gpio_hal, gpio_wp_num); + + // Check that the card can be initialized with WP low + gpio_hal_set_level(&gpio_hal, gpio_wp_num, 0); + TEST_ESP_OK(sdmmc_card_init(config, card)); + + uint32_t* data = heap_caps_calloc(1, 512, MALLOC_CAP_DMA); + + // Check that card write succeeds if WP is high + gpio_hal_set_level(&gpio_hal, gpio_wp_num, 1); + usleep(1000); + TEST_ESP_OK(sdmmc_write_sectors(card, &data, 0, 1)); + + // Check that write fails if WP is low + gpio_hal_set_level(&gpio_hal, gpio_wp_num, 0); + usleep(1000); + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, sdmmc_write_sectors(card, &data, 0, 1)); + // ...but reads still work + TEST_ESP_OK(sdmmc_read_sectors(card, &data, 0, 1)); + + free(data); + free(card); +} 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 new file mode 100644 index 0000000000..625f9d0786 --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/sdmmc_test_rw_common.c @@ -0,0 +1,158 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include +#include "test_utils.h" +#include "sdkconfig.h" +#include "soc/soc_caps.h" +#include "unity.h" +#include "sd_protocol_defs.h" +#include "sdmmc_cmd.h" +#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); + +static void fill_buffer(uint32_t seed, uint8_t* dst, size_t count) +{ + srand(seed); + for (size_t i = 0; i < count; ++i) { + uint32_t val = rand(); + memcpy(dst + i * sizeof(uint32_t), &val, sizeof(val)); + } +} + +// Check if the buffer pointed to by 'dst' contains 'count' 32-bit +// ints generated from 'rand' with the starting value of 'seed' +static void check_buffer(uint32_t seed, const uint8_t* src, size_t count) +{ + srand(seed); + for (size_t i = 0; i < count; ++i) { + uint32_t val; + memcpy(&val, src + i * sizeof(uint32_t), sizeof(val)); + TEST_ASSERT_EQUAL_HEX32(rand(), val); + } +} + +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_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); + + 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])); + + struct timeval t_start_wr; + gettimeofday(&t_start_wr, NULL); + TEST_ESP_OK(sdmmc_write_sectors(card, c_buffer, start_block, block_count)); + struct timeval t_stop_wr; + gettimeofday(&t_stop_wr, NULL); + float time_wr = 1e3f * (t_stop_wr.tv_sec - t_start_wr.tv_sec) + 1e-3f * (t_stop_wr.tv_usec - t_start_wr.tv_usec); + + memset(buffer, 0xbb, total_size + 4); + + struct timeval t_start_rd; + gettimeofday(&t_start_rd, NULL); + TEST_ESP_OK(sdmmc_read_sectors(card, c_buffer, start_block, block_count)); + struct timeval t_stop_rd; + gettimeofday(&t_stop_rd, NULL); + float time_rd = 1e3f * (t_stop_rd.tv_sec - t_start_rd.tv_sec) + 1e-3f * (t_stop_rd.tv_usec - t_start_rd.tv_usec); + + printf(" | %6.2f | %5.2f | %6.2f | %5.2f\n", + time_wr, total_size / (time_wr / 1000) / (1024 * 1024), + time_rd, total_size / (time_rd / 1000) / (1024 * 1024)); + check_buffer(start_block, c_buffer, total_size / sizeof(buffer[0])); + free(buffer); + + if (performance_log) { + FILE* old_stdout = stdout; + stdout = performance_log; + 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); + stdout = old_stdout; + } +} + +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); + + // Check read behavior: do aligned write, then unaligned read + const uint32_t seed = 0x89abcdef; + fill_buffer(seed, buffer, buffer_size / sizeof(uint32_t)); + TEST_ESP_OK(sdmmc_write_sectors(card, buffer, 0, block_count)); + memset(buffer, 0xcc, buffer_size + extra); + TEST_ESP_OK(sdmmc_read_sectors(card, buffer + 1, 0, block_count)); + check_buffer(seed, buffer + 1, buffer_size / sizeof(uint32_t)); + + // Check write behavior: do unaligned write, then aligned read + fill_buffer(seed, buffer + 1, buffer_size / sizeof(uint32_t)); + TEST_ESP_OK(sdmmc_write_sectors(card, buffer + 1, 8, block_count)); + memset(buffer, 0xcc, buffer_size + extra); + TEST_ESP_OK(sdmmc_read_sectors(card, buffer, 8, block_count)); + check_buffer(seed, buffer, buffer_size / sizeof(uint32_t)); + + free(buffer); +} + +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"); + 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); + /* 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); +} + +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); + /* 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); +} diff --git a/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/CMakeLists.txt b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/CMakeLists.txt new file mode 100644 index 0000000000..8bed4b6fec --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS sdmmc_test_board.c sdmmc_test_board_defs.c + INCLUDE_DIRS include + REQUIRES esp_driver_sdmmc esp_driver_sdspi esp_driver_gpio) diff --git a/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/Kconfig.projbuild b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/Kconfig.projbuild new file mode 100644 index 0000000000..b29fdda466 --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/Kconfig.projbuild @@ -0,0 +1,138 @@ +menu "SDMMC Test Board Configuration" + + choice SDMMC_BOARD + prompt "Board type" + help + Select development board used for running the test. + + config SDMMC_BOARD_ESP32_WROVER_KIT + bool "ESP32 WROVER-KIT" + depends on IDF_TARGET_ESP32 + + config SDMMC_BOARD_ESP32_EMMC_TEST + bool "ESP32 eMMC Test Board v1" + depends on IDF_TARGET_ESP32 + + config SDMMC_BOARD_ESP32S2_S3_USB_OTG + bool "ESP32-S2/S3 USB-OTG" + depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + + config SDMMC_BOARD_ESP32S3_EMMC_TEST + bool "ESP32-S3 eMMC Test Board v1" + depends on IDF_TARGET_ESP32S3 + + config SDMMC_BOARD_ESP32S3_EYE + bool "ESP32-S3 EYE v2.1" + depends on IDF_TARGET_ESP32S3 + + config SDMMC_BOARD_ESP32C3_BREAKOUT + bool "ESP32-C3 breakout board" + depends on IDF_TARGET_ESP32C3 + + config SDMMC_BOARD_CUSTOM_SD + depends on SOC_SDMMC_HOST_SUPPORTED + bool "Custom SD (choose pins)" + + config SDMMC_BOARD_CUSTOM_SPI + bool "Custom SPI (choose pins)" + + menu "Custom pins" + if SDMMC_BOARD_CUSTOM_SD + + # just a dummy option to prevent the Kconfig checker from complaining + # about the option name prefix + config SDMMC_BOARD_CUSTOM_DUMMY + int + + choice SDMMC_BOARD_CUSTOM_BUS_WIDTH + prompt "Bus width" + + config SDMMC_BOARD_CUSTOM_4BIT + bool "4-bit" + + config SDMMC_BOARD_CUSTOM_1BIT + bool "1-bit" + endchoice + + config SDMMC_BOARD_CUSTOM_BUS_WIDTH + int + default 4 if SDMMC_BOARD_CUSTOM_4BIT + default 1 + + if SOC_SDMMC_USE_GPIO_MATRIX + + config SDMMC_BOARD_CUSTOM_CMD + int "CMD GPIO" + default 1 + + config SDMMC_BOARD_CUSTOM_CLK + int "CLK GPIO" + default 2 + + config SDMMC_BOARD_CUSTOM_D0 + int "D0 GPIO" + default 3 + + if SDMMC_BOARD_CUSTOM_4BIT + + config SDMMC_BOARD_CUSTOM_D1 + int + prompt "D1 GPIO" if SDMMC_BOARD_CUSTOM_4BIT + default 4 if SDMMC_BOARD_CUSTOM_4BIT + default -1 + + config SDMMC_BOARD_CUSTOM_D2 + int + prompt "D2 GPIO" if SDMMC_BOARD_CUSTOM_4BIT + default 5 if SDMMC_BOARD_CUSTOM_4BIT + default -1 + + config SDMMC_BOARD_CUSTOM_D3 + int + prompt "D3 GPIO" if SDMMC_BOARD_CUSTOM_4BIT + default 6 if SDMMC_BOARD_CUSTOM_4BIT + default -1 + + endif # SDMMC_BOARD_CUSTOM_4BIT + + endif # SOC_SDMMC_USE_GPIO_MATRIX + + endif # SDMMC_BOARD_CUSTOM_SD + + if SDMMC_BOARD_CUSTOM_SPI + + config SDMMC_BOARD_CUSTOM_MISO + int "MISO GPIO" + default 1 + + config SDMMC_BOARD_CUSTOM_MOSI + int "MOSI GPIO" + default 2 + + config SDMMC_BOARD_CUSTOM_SCK + int "SCK GPIO" + default 3 + + config SDMMC_BOARD_CUSTOM_CS + int "CS GPIO" + default 4 + + endif # SDMMC_BOARD_CUSTOM_SPI + + config SDMMC_BOARD_CUSTOM_CD + int "Card Detect GPIO" + default -1 + + config SDMMC_BOARD_CUSTOM_WP + int "Write Protect GPIO" + default -1 + + config SDMMC_BOARD_CUSTOM_UNUSED + int "GPIO not routed on the board" + default -1 + + endmenu + + endchoice # SDMMC_BOARD + +endmenu diff --git a/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/README.md b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/README.md new file mode 100644 index 0000000000..192e7e6e47 --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/README.md @@ -0,0 +1,8 @@ +# SDMMC Test Boards + +This component is a test utility component for test board info: +- SDMMC test boards (e.g. ESP32_WROVER_KIT, ESP32S3_USB_OTG, etc.) +- eMMC test boards +- SDMMC with SD breakout adapter board +- SDSPI with SD breakout adapter board +- ... diff --git a/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/include/sdmmc_test_board.h b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/include/sdmmc_test_board.h new file mode 100644 index 0000000000..1417f36d82 --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/include/sdmmc_test_board.h @@ -0,0 +1,129 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "sdkconfig.h" +#include "soc/soc_caps.h" +#include "driver/sdspi_host.h" +#if SOC_SDMMC_HOST_SUPPORTED +#include "driver/sdmmc_host.h" +#endif +#include "driver/gpio.h" +#include "driver/spi_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file sdmmc_test.h + * This file contains APIs to get the board specific information (pin mappings) for SDMMC tests. + */ + +/** + * @brief Information about a slot on the test board + */ +typedef struct { + bool slot_exists; /*< Whether the slot exists on the dev board */ + bool is_emmc; /*< True if this slot is connected to eMMC */ + int bus_width; /*< SD interface width (1, 4 or 8) */ + int max_freq_khz; /*< Max frequency (kHz) of SD interface, supported by the board */ + gpio_num_t clk; /*< CLK pin number */ + gpio_num_t cmd_mosi; /*< CMD pin number (SD mode) or MOSI (SPI mode) */ + gpio_num_t d0_miso; /*< D0 pin number (SD mode) or MISO (SPI mode) */ + gpio_num_t d1; /*< D1 pin number */ + gpio_num_t d2; /*< D2 pin number */ + gpio_num_t d3_cs; /*< D3 pin number (SD mode) or CS (SPI mode) */ + gpio_num_t d4; /*< D4 pin number */ + gpio_num_t d5; /*< D5 pin number */ + gpio_num_t d6; /*< D6 pin number */ + gpio_num_t d7; /*< D7 pin number */ + gpio_num_t cd; /*< Card detect pin number */ + gpio_num_t wp; /*< Write protect pin number */ + gpio_num_t unused_pin; /*< Pin not routed on the board, for CD/WP loopback tests */ +} sdmmc_test_board_slot_info_t; + +#define SDMMC_TEST_BOARD_MAX_SLOTS 2 /*< Number of slots we need to support on one test board */ + +typedef struct { + const char* name; /*< name of the board */ + sdmmc_test_board_slot_info_t slot[SDMMC_TEST_BOARD_MAX_SLOTS]; /*< array of information about slots */ + void (*card_power_set)(bool); /*< function to turn card power on or off */ +} sdmmc_test_board_info_t; + +/** + * @brief Get information about the test board + * @return Pointer to the board information structure + */ +const sdmmc_test_board_info_t* sdmmc_test_board_get_info(void); + +/** + * @brief Get information about a slot on the test board + * @param slot_index Index of the slot (0 to SDMMC_TEST_BOARD_MAX_SLOTS-1) + * @return Pointer to the slot information structure + */ +const sdmmc_test_board_slot_info_t* sdmmc_test_board_get_slot_info(int slot_index); + +#if SOC_SDMMC_HOST_SUPPORTED +/** + * @brief Fill SDMMC host and slot configuration structures with information about a slot on the test board + * @note Before calling this function, initialize the host/slot configuration structures using default initializer macros. + * @param slot_index Index of the slot (0 to SDMMC_TEST_BOARD_MAX_SLOTS-1) + * @param[out] out_host_config Output, pointer to the host configuration structure to be filled. + * @param[out] out_slot_config Output, pointer to the slot configuration structure to be filled. + */ +void sdmmc_test_board_get_config_sdmmc(int slot_index, sdmmc_host_t *out_host_config, sdmmc_slot_config_t *out_slot_config); +#endif + +/** + * @brief Fill SDSPI host, bus and device configuration structures with information about a slot on the test board + * @note Before calling this function, initialize the these configuration structures using default initializer macros. + * @param slot_index Index of the slot (0 to SDMMC_TEST_BOARD_MAX_SLOTS-1) + * @param out_host_config Output, pointer to the host configuration structure to be filled. + * @param out_spi_bus_config Output, pointer to the SPI bus configuration structure to be filled. + * @param out_dev_config Output, pointer to the device configuration structure to be filled. + */ +void sdmmc_test_board_get_config_sdspi(int slot_index, sdmmc_host_t *out_host_config, + spi_bus_config_t *out_spi_bus_config, sdspi_device_config_t *out_dev_config); + +/** + * @brief Set card power on or off + * For boards with card power control circuit, this function allows powering the card up or down. + * @param enable True to turn power on, false to turn power off + */ +void sdmmc_test_board_card_power_set(bool enable); + +/** + * @brief Check if a slot exists on the test board + * "Exists" here means that something (card slot or eMMC) is connected to that slot. + * @param slot_index Index of the slot (0 to SDMMC_TEST_BOARD_MAX_SLOTS-1) + * @return True if the slot exists on the board + */ +bool sdmmc_test_board_has_slot(int slot_index); + +/** + * @brief Check if a slot is connected to eMMC + * + * Note that this function simply returns what the board definitions in sdmmc_test_board_defs.c say. + * It does not check if the card is actually eMMC. + * The main purpose of this function is to prevent SPI-related tests + * from being run on slots which are known to be connected to eMMC. + * + * @param slot_index Index of the slot (0 to SDMMC_TEST_BOARD_MAX_SLOTS-1) + * @return True if the slot is connected to eMMC + */ +bool sdmmc_test_board_slot_is_emmc(int slot_index); + +/** + * @brief Print information about the test board (pin mapping) + */ +void sdmmc_test_board_print_info(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/sdmmc_test_board.c b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/sdmmc_test_board.c new file mode 100644 index 0000000000..01b85ffdcf --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/sdmmc_test_board.c @@ -0,0 +1,115 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sdmmc_test_board.h" +#include "sdkconfig.h" +#include "soc/soc_caps.h" + +const sdmmc_test_board_slot_info_t* sdmmc_test_board_get_slot_info(int slot_index) +{ + assert(slot_index >= 0 && slot_index < SDMMC_TEST_BOARD_MAX_SLOTS); + return &sdmmc_test_board_get_info()->slot[slot_index]; +} + +#if SOC_SDMMC_HOST_SUPPORTED + +void sdmmc_test_board_get_config_sdmmc(int slot_index, sdmmc_host_t *out_host_config, sdmmc_slot_config_t *out_slot_config) +{ + const sdmmc_test_board_slot_info_t* slot = sdmmc_test_board_get_slot_info(slot_index); + assert(slot->slot_exists); + + out_host_config->slot = slot_index; + if (slot->bus_width < 8) { + out_host_config->flags &= ~SDMMC_HOST_FLAG_8BIT; + } + if (slot->bus_width < 4) { + out_host_config->flags &= ~SDMMC_HOST_FLAG_4BIT; + } + if (slot->max_freq_khz > 0) { + out_host_config->max_freq_khz = slot->max_freq_khz; + } + +#if SOC_SDMMC_USE_GPIO_MATRIX + out_slot_config->clk = slot->clk; + out_slot_config->cmd = slot->cmd_mosi; + out_slot_config->d0 = slot->d0_miso; + out_slot_config->d1 = slot->d1; + out_slot_config->d2 = slot->d2; + out_slot_config->d3 = slot->d3_cs; + out_slot_config->d4 = slot->d4; + out_slot_config->d5 = slot->d5; + out_slot_config->d6 = slot->d6; + out_slot_config->d7 = slot->d7; +#endif // SOC_SDMMC_USE_GPIO_MATRIX + out_slot_config->wp = slot->wp; + out_slot_config->cd = slot->cd; + out_slot_config->width = slot->bus_width; +} + +#endif // SOC_SDMMC_HOST_SUPPORTED + +void sdmmc_test_board_get_config_sdspi(int slot_index, sdmmc_host_t *out_host_config, spi_bus_config_t *out_spi_bus_config, sdspi_device_config_t *out_dev_config) +{ + const sdmmc_test_board_slot_info_t* slot = sdmmc_test_board_get_slot_info(slot_index); + assert(slot->slot_exists); + + if (slot->max_freq_khz > 0) { + out_host_config->max_freq_khz = slot->max_freq_khz; + } + + out_spi_bus_config->mosi_io_num = slot->cmd_mosi; + out_spi_bus_config->miso_io_num = slot->d0_miso; + out_spi_bus_config->sclk_io_num = slot->clk; + out_dev_config->gpio_cs = slot->d3_cs; + out_dev_config->gpio_cd = slot->cd; + out_dev_config->gpio_wp = slot->wp; +} + +void sdmmc_test_board_card_power_set(bool enable) +{ + const sdmmc_test_board_info_t* board = sdmmc_test_board_get_info(); + if (board->card_power_set != NULL) { + board->card_power_set(enable); + } +} + +bool sdmmc_test_board_has_slot(int slot_index) +{ + return sdmmc_test_board_get_slot_info(slot_index)->slot_exists; +} + +bool sdmmc_test_board_slot_is_emmc(int slot_index) +{ + return sdmmc_test_board_get_slot_info(slot_index)->is_emmc; +} + +void sdmmc_test_board_print_info() +{ + const sdmmc_test_board_info_t *board = sdmmc_test_board_get_info(); + printf("\nTest app built for %s\n", board->name); + for (int slot_index = 0; slot_index < SDMMC_TEST_BOARD_MAX_SLOTS; ++slot_index) { + const sdmmc_test_board_slot_info_t *slot = sdmmc_test_board_get_slot_info(slot_index); + if (!slot->slot_exists) { + printf("- Slot %d: not present\n", slot_index); + continue; + } + printf("- Slot %d: %d-bit", slot_index, slot->bus_width); + if (slot->max_freq_khz > 0) { + printf(", freq limit: %d kHz", slot->max_freq_khz); + } + if (slot->is_emmc > 0) { + printf(", eMMC"); + } + printf("\n"); + printf(" CMD/MOSI: %2d\n CLK: %2d\n D0/MISO: %2d\n", slot->cmd_mosi, slot->clk, slot->d0_miso); + if (slot->bus_width > 1 || slot->d3_cs) { + printf(" D1: %2d\n D2: %2d\n D3/CS: %2d\n", slot->d1, slot->d2, slot->d3_cs); + } + if (slot->bus_width > 4) { + printf(" D4: %2d\n D5: %2d\n D6: %2d\n D7: %2d\n", slot->d4, slot->d5, slot->d6, slot->d7); + } + } +} diff --git a/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/sdmmc_test_board_defs.c b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/sdmmc_test_board_defs.c new file mode 100644 index 0000000000..2610e1a78b --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/sdmmc_test_board_defs.c @@ -0,0 +1,391 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "sdmmc_test_board.h" +#include "sdkconfig.h" +#include "soc/soc_caps.h" +#include "driver/gpio.h" +#if SOC_SDMMC_HOST_SUPPORTED +#include "soc/sdmmc_pins.h" +#endif + +#if CONFIG_SDMMC_BOARD_ESP32_WROVER_KIT + +static const sdmmc_test_board_info_t s_board_info = { + .name = "ESP32-WROVER-KIT", + .slot = { + { + .slot_exists = false + }, + { + .slot_exists = true, + .bus_width = 4, + .clk = SDMMC_SLOT1_IOMUX_PIN_NUM_CLK, + .cmd_mosi = SDMMC_SLOT1_IOMUX_PIN_NUM_CMD, + .d0_miso = SDMMC_SLOT1_IOMUX_PIN_NUM_D0, + .d1 = SDMMC_SLOT1_IOMUX_PIN_NUM_D1, + .d2 = SDMMC_SLOT1_IOMUX_PIN_NUM_D2, + .d3_cs = SDMMC_SLOT1_IOMUX_PIN_NUM_D3, + .d4 = GPIO_NUM_NC, + .d5 = GPIO_NUM_NC, + .d6 = GPIO_NUM_NC, + .d7 = GPIO_NUM_NC, + .cd = GPIO_NUM_NC, + .wp = GPIO_NUM_NC, + .unused_pin = 18, + } + }, +}; + +#elif CONFIG_SDMMC_BOARD_ESP32_EMMC_TEST + +#define SD_TEST_BOARD_VSEL_GPIO 26 +#define SD_TEST_BOARD_VSEL_3V3 1 +#define SD_TEST_BOARD_VSEL_1V8 0 +#define SD_TEST_BOARD_EN_GPIO 27 +#define SD_TEST_BOARD_EN_LEVEL 1 +#define SD_TEST_BOARD_PWR_RST_DELAY_MS 5 +#define SD_TEST_BOARD_PWR_ON_DELAY_MS 50 + +static void card_power_set_esp32_emmc(bool en) +{ + if (en) { + /* set voltage */ + gpio_set_direction(SD_TEST_BOARD_VSEL_GPIO, GPIO_MODE_OUTPUT); + gpio_set_level(SD_TEST_BOARD_VSEL_GPIO, SD_TEST_BOARD_VSEL_3V3); + /* power off to make sure card is reset */ + gpio_set_direction(SD_TEST_BOARD_EN_GPIO, GPIO_MODE_OUTPUT); + gpio_set_level(SD_TEST_BOARD_EN_GPIO, !SD_TEST_BOARD_EN_LEVEL); + usleep(SD_TEST_BOARD_PWR_RST_DELAY_MS * 1000); + /* power on */ + gpio_set_level(SD_TEST_BOARD_EN_GPIO, SD_TEST_BOARD_EN_LEVEL); + usleep(SD_TEST_BOARD_PWR_ON_DELAY_MS * 1000); + } else { + gpio_set_level(SD_TEST_BOARD_EN_GPIO, !SD_TEST_BOARD_EN_LEVEL); + gpio_set_level(SD_TEST_BOARD_VSEL_GPIO, 0); + gpio_set_direction(SD_TEST_BOARD_VSEL_GPIO, GPIO_MODE_INPUT); + gpio_set_direction(SD_TEST_BOARD_EN_GPIO, GPIO_MODE_INPUT); + } +} + +static const sdmmc_test_board_info_t s_board_info = { + .name = "ESP32 eMMC test board v1", + .slot = { + { + .slot_exists = true, + .is_emmc = true, + .bus_width = 8, + .clk = SDMMC_SLOT0_IOMUX_PIN_NUM_CLK, + .cmd_mosi = SDMMC_SLOT0_IOMUX_PIN_NUM_CMD, + .d0_miso = SDMMC_SLOT0_IOMUX_PIN_NUM_D0, + .d1 = SDMMC_SLOT0_IOMUX_PIN_NUM_D1, + .d2 = SDMMC_SLOT0_IOMUX_PIN_NUM_D2, + .d3_cs = SDMMC_SLOT0_IOMUX_PIN_NUM_D3, + .d4 = SDMMC_SLOT0_IOMUX_PIN_NUM_D4, + .d5 = SDMMC_SLOT0_IOMUX_PIN_NUM_D5, + .d6 = SDMMC_SLOT0_IOMUX_PIN_NUM_D6, + .d7 = SDMMC_SLOT0_IOMUX_PIN_NUM_D7, + .cd = GPIO_NUM_NC, + .wp = GPIO_NUM_NC, + .unused_pin = 18, + }, + { + .slot_exists = true, + .bus_width = 4, + .clk = SDMMC_SLOT1_IOMUX_PIN_NUM_CLK, + .cmd_mosi = SDMMC_SLOT1_IOMUX_PIN_NUM_CMD, + .d0_miso = SDMMC_SLOT1_IOMUX_PIN_NUM_D0, + .d1 = SDMMC_SLOT1_IOMUX_PIN_NUM_D1, + .d2 = SDMMC_SLOT1_IOMUX_PIN_NUM_D2, + .d3_cs = SDMMC_SLOT1_IOMUX_PIN_NUM_D3, + .d4 = GPIO_NUM_NC, + .d5 = GPIO_NUM_NC, + .d6 = GPIO_NUM_NC, + .d7 = GPIO_NUM_NC, + .cd = GPIO_NUM_NC, + .wp = GPIO_NUM_NC, + .unused_pin = 18, + } + }, + .card_power_set = card_power_set_esp32_emmc +}; +#elif CONFIG_SDMMC_BOARD_ESP32S2_S3_USB_OTG + +static const sdmmc_test_board_info_t s_board_info = { + .name = "ESP32-S2/ESP32-S3 USB_OTG", + .slot = { + { + .slot_exists = false + }, + { + .slot_exists = true, + .bus_width = 4, + .clk = 36, + .cmd_mosi = 35, + .d0_miso = 37, + .d1 = 38, + .d2 = 33, + .d3_cs = 34, + .d4 = GPIO_NUM_NC, + .d5 = GPIO_NUM_NC, + .d6 = GPIO_NUM_NC, + .d7 = GPIO_NUM_NC, + .cd = GPIO_NUM_NC, + .wp = GPIO_NUM_NC, + .unused_pin = 2, + } + }, +}; + +#elif CONFIG_SDMMC_BOARD_ESP32S3_EMMC_TEST + +#define SD_TEST_BOARD_EN_GPIO 47 +#define SD_TEST_BOARD_EN_LEVEL 0 +// Pin pulled down to discharge VDD_SDIO capacitors. CMD pin used here. +#define SD_TEST_BOARD_DISCHARGE_GPIO 4 +#define SD_TEST_BOARD_PWR_RST_DELAY_MS 100 +#define SD_TEST_BOARD_PWR_ON_DELAY_MS 100 + +static void card_power_set_esp32s3_emmc(bool en) +{ + if (en) { + /* power off to make sure the card is reset */ + gpio_reset_pin(SD_TEST_BOARD_EN_GPIO); + gpio_set_direction(SD_TEST_BOARD_EN_GPIO, GPIO_MODE_OUTPUT); + gpio_set_level(SD_TEST_BOARD_EN_GPIO, !SD_TEST_BOARD_EN_LEVEL); + /* discharge capacitors on VDD_SDIO */ + gpio_reset_pin(SD_TEST_BOARD_DISCHARGE_GPIO); + gpio_set_direction(SD_TEST_BOARD_DISCHARGE_GPIO, GPIO_MODE_OUTPUT); + gpio_set_level(SD_TEST_BOARD_DISCHARGE_GPIO, 0); + usleep(SD_TEST_BOARD_PWR_RST_DELAY_MS * 1000); + /* power on */ + gpio_reset_pin(SD_TEST_BOARD_DISCHARGE_GPIO); + gpio_set_level(SD_TEST_BOARD_EN_GPIO, SD_TEST_BOARD_EN_LEVEL); + usleep(SD_TEST_BOARD_PWR_ON_DELAY_MS * 1000); + } else { + /* power off the card */ + gpio_set_level(SD_TEST_BOARD_EN_GPIO, !SD_TEST_BOARD_EN_LEVEL); + gpio_set_direction(SD_TEST_BOARD_EN_GPIO, GPIO_MODE_INPUT); + /* discharge capacitors on VDD_SDIO */ + gpio_reset_pin(SD_TEST_BOARD_DISCHARGE_GPIO); + gpio_set_direction(SD_TEST_BOARD_DISCHARGE_GPIO, GPIO_MODE_OUTPUT); + gpio_set_level(SD_TEST_BOARD_DISCHARGE_GPIO, 0); + usleep(SD_TEST_BOARD_PWR_RST_DELAY_MS * 1000); + /* reset the pin but leaving it floating so that VDD_SDIO won't be charged again */ + gpio_reset_pin(SD_TEST_BOARD_DISCHARGE_GPIO); + gpio_pullup_dis(SD_TEST_BOARD_DISCHARGE_GPIO); + } +} + +static const sdmmc_test_board_info_t s_board_info = { + .name = "ESP32-S3 eMMC test board v1", + .slot = { + { + .slot_exists = true, + .is_emmc = true, + .bus_width = 8, + .clk = 34, + .cmd_mosi = 33, + .d0_miso = 37, + .d1 = 38, + .d2 = 39, + .d3_cs = 36, + .d4 = 35, + .d5 = 40, + .d6 = 42, + .d7 = 41, + .cd = GPIO_NUM_NC, + .wp = GPIO_NUM_NC, + .unused_pin = 1, + }, + { + .slot_exists = true, + .bus_width = 4, + .clk = 2, + .cmd_mosi = 4, + .d0_miso = 5, + .d1 = 6, + .d2 = 7, + .d3_cs = 8, + .d4 = GPIO_NUM_NC, + .d5 = GPIO_NUM_NC, + .d6 = GPIO_NUM_NC, + .d7 = GPIO_NUM_NC, + .cd = 21, + .wp = GPIO_NUM_NC, + .unused_pin = 1, + } + }, + .card_power_set = card_power_set_esp32s3_emmc +}; + +#elif CONFIG_SDMMC_BOARD_ESP32S3_EYE + +static const sdmmc_test_board_info_t s_board_info = { + .name = "ESP32-S3-EYE", + .slot = { + { + .slot_exists = false + }, + { + .slot_exists = true, + .bus_width = 1, + .clk = 39, + .cmd_mosi = 38, + .d0_miso = 40, + .d1 = GPIO_NUM_NC, + .d2 = GPIO_NUM_NC, + .d3_cs = GPIO_NUM_NC, + .d4 = GPIO_NUM_NC, + .d5 = GPIO_NUM_NC, + .d6 = GPIO_NUM_NC, + .d7 = GPIO_NUM_NC, + .cd = GPIO_NUM_NC, + .wp = GPIO_NUM_NC, + .unused_pin = 1, + } + }, +}; + +#elif CONFIG_SDMMC_BOARD_ESP32C3_BREAKOUT + +#define SD_BREAKOUT_BOARD_EN_GPIO 10 +#define SD_BREAKOUT_BOARD_EN_LEVEL 0 +// Pin pulled down to discharge VDD_SDIO capacitors. CMD pin used here. +#define SD_TEST_BOARD_DISCHARGE_GPIO 4 +#define SD_BREAKOUT_BOARD_PWR_RST_DELAY_MS 100 +#define SD_BREAKOUT_BOARD_PWR_ON_DELAY_MS 100 + +static void card_power_set_esp32c3_breakout(bool en) +{ + if (en) { + /* power off to make sure card is reset */ + gpio_reset_pin(SD_BREAKOUT_BOARD_EN_GPIO); + gpio_set_direction(SD_BREAKOUT_BOARD_EN_GPIO, GPIO_MODE_OUTPUT); + gpio_set_level(SD_BREAKOUT_BOARD_EN_GPIO, !SD_BREAKOUT_BOARD_EN_LEVEL); + /* discharge capacitors on VDD_SDIO */ + gpio_reset_pin(SD_TEST_BOARD_DISCHARGE_GPIO); + gpio_set_direction(SD_TEST_BOARD_DISCHARGE_GPIO, GPIO_MODE_OUTPUT); + gpio_set_level(SD_TEST_BOARD_DISCHARGE_GPIO, 0); + usleep(SD_BREAKOUT_BOARD_PWR_RST_DELAY_MS * 1000); + /* power on */ + gpio_reset_pin(SD_TEST_BOARD_DISCHARGE_GPIO); + gpio_set_level(SD_BREAKOUT_BOARD_EN_GPIO, SD_BREAKOUT_BOARD_EN_LEVEL); + usleep(SD_BREAKOUT_BOARD_PWR_ON_DELAY_MS * 1000); + } else { + /* power off the card */ + gpio_set_level(SD_BREAKOUT_BOARD_EN_GPIO, !SD_BREAKOUT_BOARD_EN_LEVEL); + gpio_set_direction(SD_BREAKOUT_BOARD_EN_GPIO, GPIO_MODE_INPUT); + /* set CMD low to discharge capacitors on VDD_SDIO */ + gpio_reset_pin(SD_TEST_BOARD_DISCHARGE_GPIO); + gpio_set_direction(SD_TEST_BOARD_DISCHARGE_GPIO, GPIO_MODE_OUTPUT); + gpio_set_level(SD_TEST_BOARD_DISCHARGE_GPIO, 0); + usleep(SD_BREAKOUT_BOARD_PWR_RST_DELAY_MS * 1000); + /* reset the pin but leaving it floating so that VDD_SDIO won't be charged again */ + gpio_reset_pin(SD_TEST_BOARD_DISCHARGE_GPIO); + gpio_reset_pin(SD_TEST_BOARD_DISCHARGE_GPIO); + } +} + +static const sdmmc_test_board_info_t s_board_info = { + .name = "ESP32-C3 breakout board", + .slot = { + { + .slot_exists = false + }, + { + .slot_exists = true, + .bus_width = 1, + .clk = 5, + .cmd_mosi = 4, + .d0_miso = 6, + .d1 = GPIO_NUM_NC, + .d2 = GPIO_NUM_NC, + .d3_cs = 1, + .d4 = GPIO_NUM_NC, + .d5 = GPIO_NUM_NC, + .d6 = GPIO_NUM_NC, + .d7 = GPIO_NUM_NC, + .cd = GPIO_NUM_NC, + .wp = GPIO_NUM_NC, + .unused_pin = 2, + } + }, + .card_power_set = card_power_set_esp32c3_breakout +}; + +#elif CONFIG_SDMMC_BOARD_CUSTOM_SD + +static const sdmmc_test_board_info_t s_board_info = { + .name = "Custom defined board (SD)", + .slot = { + { + .slot_exists = false + }, + { + .slot_exists = true, + .bus_width = CONFIG_SDMMC_BOARD_CUSTOM_BUS_WIDTH, +#ifdef SOC_SDMMC_USE_GPIO_MATRIX + .clk = CONFIG_SDMMC_BOARD_CUSTOM_CLK, + .cmd_mosi = CONFIG_SDMMC_BOARD_CUSTOM_CMD, + .d0_miso = CONFIG_SDMMC_BOARD_CUSTOM_D0, + .d1 = CONFIG_SDMMC_BOARD_CUSTOM_D1, + .d2 = CONFIG_SDMMC_BOARD_CUSTOM_D2, + .d3_cs = CONFIG_SDMMC_BOARD_CUSTOM_D3, +#else // SOC_SDMMC_USE_GPIO_MATRIX + .clk = SDMMC_SLOT1_IOMUX_PIN_NUM_CLK, + .cmd_mosi = SDMMC_SLOT1_IOMUX_PIN_NUM_CMD, + .d0_miso = SDMMC_SLOT1_IOMUX_PIN_NUM_D0, + .d1 = SDMMC_SLOT1_IOMUX_PIN_NUM_D1, + .d2 = SDMMC_SLOT1_IOMUX_PIN_NUM_D2, + .d3_cs = SDMMC_SLOT1_IOMUX_PIN_NUM_D3, +#endif // SOC_SDMMC_USE_GPIO_MATRIX + .d4 = GPIO_NUM_NC, + .d5 = GPIO_NUM_NC, + .d6 = GPIO_NUM_NC, + .d7 = GPIO_NUM_NC, + .cd = CONFIG_SDMMC_BOARD_CUSTOM_CD, + .wp = CONFIG_SDMMC_BOARD_CUSTOM_WP, + .unused_pin = CONFIG_SDMMC_BOARD_CUSTOM_UNUSED, + } + }, +}; + +#elif CONFIG_SDMMC_BOARD_CUSTOM_SPI + +static const sdmmc_test_board_info_t s_board_info = { + .name = "Custom defined board (SPI)", + .slot = { + { + .slot_exists = false + }, + { + .slot_exists = true, + .bus_width = 1, + .clk = CONFIG_SDMMC_BOARD_CUSTOM_SCK, + .cmd_mosi = CONFIG_SDMMC_BOARD_CUSTOM_MOSI, + .d0_miso = CONFIG_SDMMC_BOARD_CUSTOM_MISO, + .d1 = GPIO_NUM_NC, + .d2 = GPIO_NUM_NC, + .d3_cs = CONFIG_SDMMC_BOARD_CUSTOM_CS, + .d4 = GPIO_NUM_NC, + .d5 = GPIO_NUM_NC, + .d6 = GPIO_NUM_NC, + .d7 = GPIO_NUM_NC, + .cd = CONFIG_SDMMC_BOARD_CUSTOM_CD, + .wp = CONFIG_SDMMC_BOARD_CUSTOM_WP, + .unused_pin = CONFIG_SDMMC_BOARD_CUSTOM_UNUSED, + } + }, +}; + +#endif // CONFIG_SDMMC_BOARD_* + +const sdmmc_test_board_info_t* sdmmc_test_board_get_info(void) +{ + return &s_board_info; +} diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/CMakeLists.txt b/components/esp_driver_sdmmc/test_apps/sdmmc/CMakeLists.txt new file mode 100644 index 0000000000..d51133d37f --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.16) + +list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components") +list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/components/esp_driver_sdmmc/test_apps/sd_test_utils/components") +list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/components/esp_driver_sdmmc/test_apps/sdmmc_tests") +set(COMPONENTS main) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(sdmmc_test_console) diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/README.md b/components/esp_driver_sdmmc/test_apps/sdmmc/README.md new file mode 100644 index 0000000000..c656113da2 --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/README.md @@ -0,0 +1,108 @@ +| Supported Targets | ESP32 | ESP32-P4 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | + +# SDMMC Test Application + +This app is used to test the SDMMC protocol layer (in `sdmmc` component), as well as SDMMC and SDSPI drivers (in `driver` component). + +The app serves two purposes: + +1. It allows various Unity test cases for SDMMC protocol layer features: probing, data transfer, etc. +2. It also allows running lower level operations from the console, which makes it useful as a debugging/hacking/experimentation tool when investigating SDMMC issues. + - Initializing the SDMMC host in various modes + - Probing the card + - (more to come: sending commands, reading/writing data, etc.) + +## SDMMC test app code overview + +The app consists of several components: +- `main` — small amount of code to initialize the console and register all the commands. +- `components/cmd_unity` — console command to run Unity tests. +- `components/cmd_sdmmc` — console commands for SDMMC low-level operations. +- `components/sdmmc_test_board` — contains pin mappings for various development boards, and APIs for tests to access them. +- `components/sdmmc_test` — contains the actual test cases. + +## Supported boards + +* ESP32-WROVER-KIT +* ESP32 eMMC test board (black board with two SD card slots and eMMC) +* ESP32-S3 USB_OTG v1. (Also works with an older ESP32-S2 variant of the board.) +* ESP32-S3 eMMC test board (white board with two SD card slots and eMMC) +* ESP32-S3-EYE +* Breakout board for ESP32-C3 DevKitM-1 +* Custom board definition: specify the pin mapping in `menuconfig`. + +## Using the app + +### Select the target, configure, build and flash the app + +1. Choose the chip target, and open menuconfig: + ```bash + idf.py set-target esp32 + idf.py menuconfig + ``` +2. Select the development board in `SDMMC Test Board Configuration` menu. This will select the correct pins for the SD card slot on the board. +3. Save the configuration and exit menuconfig. +4. Build the app: + ```bash + idf.py build + ``` +5. Flash and monitor: + ```bash + idf.py flash monitor + ``` + +### Advanced: multiple build configurations side-by-side + +It is often useful to verify changes in `sdmmc` component on multiple chips or development boards. To do this, it is possible to build the app multiple times, with different configurations, keeping the builds in separate directories. + +1. Build the app for the first target. This command does multiple things: selects the target, sets the build directory to `build_esp32`, and puts the sdkconfig file into the build directory: + ```bash + idf.py -D IDF_TARGET=esp32 -B build_esp32 -D SDKCONFIG=build_esp32/sdkconfig build + ``` +2. Flash and monitor. Note that the build directory has to be specified with `-B` option: + ```bash + idf.py -B build_esp32 flash monitor + ``` +3. Now you can build the app for the second target in another build directory: + ```bash + idf.py -D IDF_TARGET=esp32s3 -B build_esp32s3 -D SDKCONFIG=build_esp32s3/sdkconfig build + ``` +4. Flash and monitor, again specifying the build directory: + ```bash + idf.py -B build_esp32s3 flash monitor + ``` + +Compared to the `idf.py set-target` approach, this method allows keeping multiple build configurations side-by-side, and switching between them easily. If you have multiple terminal windows open, you can use one window per board. Set ESPPORT environment variable to the correct serial port in each window, and flash and monitor the app in each window with the corresponding build directory (`-B`) argument. + +### Console commands + +The app supports the following console commands: + +- Common system commands: `help`, `restart`, `version`, `free`, `heap` +- Log control: `log_level ` — dynamically change the log level for a given tag. For example, `log_level sdmmc_req debug` will enable debug logging in sdmmc_transaction.c +- Running unit tests: `test [index|name|tag|*]`. + - If no argument is given, prints the list of available tests. + - If a test index is given, runs the test with that index. + - If a test name is given, runs the test with that name. + - If a test tag is given, runs all tests with that tag. You can negate the tag by prefixing it with `!`, for example `test ![sdspi]` will run all tests except those tagged with `[sdspi]`. +- SDMMC low-level commands: `sdmmc_host_init`, `sdmmc_host_deinit`, `card_init`, `card_info`. Refer to the `help` output for more details. + +As most other IDF console applications, the app supports line editing, commands history and tab completion. + +### Running test cases + +When running the tests, keep in mind that once an SD card has been initialized in SPI mode, it cannot be re-initalized in SD mode without first powering it off. This means that on boards without an SD card power control circuit, it is not possible to run all the tests in one go with `test *` command. Run SDMMC tests first, then SDSPI tests: `test [sdmmc]` and then `test [sdspi]`. If you need to run SDMMC test again, power cycle the card first. + +On chips without an SDMMC host controller only SDSPI tests are compiled. In this case, `test *` can be used to run all tests. + +### Skipped tests + +To make the app compatible with various development boards without having lots of ifdefs in test cases, the tests will be ignored if the board is not compatible with the test case. For example, on boards with just one SD card slot (Slot 1), every test which uses Slot 0 is skipped. + +For example, when running sdspi tests on ESP32-WROVER-KIT you will see something like this: +``` +12 Tests 0 Failures 5 Ignored +``` + +This means that the 5 tests which use Slot 0 were skipped. diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/main/CMakeLists.txt b/components/esp_driver_sdmmc/test_apps/sdmmc/main/CMakeLists.txt new file mode 100644 index 0000000000..03cd59aaed --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/main/CMakeLists.txt @@ -0,0 +1,13 @@ +set(srcs "test_app_main.c") + +set(priv_requires + # tests reside in this component, also available for `sdmmc_console` + sdmmc_tests + # general + unity +) + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS "." + PRIV_REQUIRES ${priv_requires} + WHOLE_ARCHIVE TRUE) diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/main/test_app_main.c b/components/esp_driver_sdmmc/test_apps/sdmmc/main/test_app_main.c new file mode 100644 index 0000000000..e6e17ee73e --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/main/test_app_main.c @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "unity.h" +#include "unity_test_utils.h" +#include "esp_heap_caps.h" +#include "sdkconfig.h" + +#define TEST_MEMORY_LEAK_THRESHOLD (300) + +void setUp(void) +{ + unity_utils_record_free_mem(); +} + +void tearDown(void) +{ + unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD); +} + +void app_main(void) +{ + /* + _____ _ ______________ ______ ________ + |_ _| | | / ___| _ \ \/ || \/ / __ \ + | | ___ ___| |_ \ `--.| | | | . . || . . | / \/ + | |/ _ \/ __| __| `--. \ | | | |\/| || |\/| | | + | | __/\__ \ |_ /\__/ / |/ /| | | || | | | \__/\ + \_/\___||___/\__| \____/|___/ \_| |_/\_| |_/\____/ + */ + + printf(" _____ _ ______________ ______ ________\n"); + printf("|_ _| | | / ___| _ \\ \\/ || \\/ / __ \\ \n"); + printf(" | | ___ ___| |_ \\ `--.| | | | . . || . . | / \\/\n"); + printf(" | |/ _ \\/ __| __| `--. \\ | | | |\\/| || |\\/| | |\n"); + printf(" | | __/\\__ \\ |_ /\\__/ / |/ /| | | || | | | \\__/\\\n"); + printf(" \\_/\\___||___/\\__| \\____/|___/ \\_| |_/\\_| |_/\\____/\n"); + + unity_run_menu(); +} diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/pytest_sdmmc.py b/components/esp_driver_sdmmc/test_apps/sdmmc/pytest_sdmmc.py new file mode 100644 index 0000000000..6d4e712930 --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/pytest_sdmmc.py @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import pytest +from pytest_embedded_idf import IdfDut + + +@pytest.mark.esp32 +@pytest.mark.esp32s3 +@pytest.mark.sdcard +def test_sdmmc(dut: IdfDut) -> None: + dut.run_all_single_board_cases() diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/sdkconfig.defaults b/components/esp_driver_sdmmc/test_apps/sdmmc/sdkconfig.defaults new file mode 100644 index 0000000000..fa8ac618b9 --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP_TASK_WDT_EN=n diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc_tests/CMakeLists.txt b/components/esp_driver_sdmmc/test_apps/sdmmc_tests/CMakeLists.txt new file mode 100644 index 0000000000..c406967c6b --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sdmmc_tests/CMakeLists.txt @@ -0,0 +1,20 @@ +set(srcs) + + +if(CONFIG_SOC_SDMMC_HOST_SUPPORTED) + list(APPEND srcs "sdmmc_test_begin_end_sd.c" + "sdmmc_test_cd_wp_sd.c" + "sdmmc_test_probe_sd.c" + "sdmmc_test_rw_sd.c") +endif() + +set(priv_requires "sdmmc" + "esp_driver_sdmmc" + "sdmmc_test_boards" + "common_test_flows" + "unity" +) + +idf_component_register(SRCS ${srcs} + PRIV_REQUIRES ${priv_requires} + WHOLE_ARCHIVE TRUE) diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc_tests/sdmmc_test_begin_end.h b/components/esp_driver_sdmmc/test_apps/sdmmc_tests/sdmmc_test_begin_end.h new file mode 100644 index 0000000000..1120781185 --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sdmmc_tests/sdmmc_test_begin_end.h @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "sd_protocol_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Defines for readability */ +#define SLOT_0 0 +#define SLOT_1 1 +#define NO_DDR 0 +#define WITH_DDR 1 + +/* Helper functions to initialize/deinintalize the host (SDMMC/SDSPI) inside the test */ + +#if SOC_SDMMC_HOST_SUPPORTED +/** + * @brief Skip the test if the board is incompatible with the given slot, width, frequency and DDR mode + * This compares the requested values with the information from the board config, and skips the test + * (basically, exits it using TEST_IGNORE/longjmp) if the board is incompatible. + * @param slot Slot index (SLOT_0 or SLOT_1) + * @param width Slot width (1, 4 or 8) + * @param freq_khz Bus frequency in kHz + * @param ddr Whether to use DDR mode (NO_DDR or WITH_DDR) + */ +void sdmmc_test_sd_skip_if_board_incompatible(int slot, int width, int freq_khz, int ddr); + +/** + * @brief Helper function to initialize the SDMMC host and slot for the test using the given settings + * @param slot Slot index (SLOT_0 or SLOT_1) + * @param width Slot width (1, 4 or 8) + * @param freq_khz Bus frequency in kHz + * @param ddr Whether to use DDR mode (NO_DDR or WITH_DDR) + * @param[out] out_card Output, pointer to the card structure to be filled by this function + */ +void sdmmc_test_sd_begin(int slot, int width, int freq_khz, int ddr, sdmmc_card_t *out_card); + +/** + * @brief Helper function to deinitialize the SDMMC host and slot after the test + * @param card Pointer to the card structure to be deinitialized + */ +void sdmmc_test_sd_end(sdmmc_card_t *card); +#endif + +#ifdef __cplusplus +}; +#endif diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc_tests/sdmmc_test_begin_end_sd.c b/components/esp_driver_sdmmc/test_apps/sdmmc_tests/sdmmc_test_begin_end_sd.c new file mode 100644 index 0000000000..cff9c66d83 --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sdmmc_tests/sdmmc_test_begin_end_sd.c @@ -0,0 +1,118 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include "esp_log.h" +#include "sdkconfig.h" +#include "soc/soc_caps.h" +#include "unity.h" +#include "sdmmc_test_board.h" +#include "driver/sdmmc_host.h" +#include "sd_protocol_defs.h" +#include "sdmmc_cmd.h" +#include "sdmmc_test_begin_end.h" +#include "hal/gpio_hal.h" + +void sdmmc_test_sd_skip_if_board_incompatible(int slot, int width, int freq_khz, int ddr) +{ + sdmmc_host_t config = SDMMC_HOST_DEFAULT(); + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + + if (!sdmmc_test_board_has_slot(slot)) { + TEST_IGNORE_MESSAGE("Board doesn't have the required slot"); + } + sdmmc_test_board_get_config_sdmmc(slot, &config, &slot_config); + int board_max_freq_khz = sdmmc_test_board_get_slot_info(slot)->max_freq_khz; + if (board_max_freq_khz > 0 && board_max_freq_khz < freq_khz) { + TEST_IGNORE_MESSAGE("Board doesn't support required max_freq_khz"); + } + if (slot_config.width < width) { + TEST_IGNORE_MESSAGE("Board doesn't support required bus width"); + } +} +void sdmmc_test_sd_begin(int slot, int width, int freq_khz, int ddr, sdmmc_card_t *out_card) +{ + sdmmc_host_t config = SDMMC_HOST_DEFAULT(); + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + + /* Similar to the checks in sdmmc_test_sd_skip_if_board_incompatible, but + * we fail the test if we somehow got to this point with an incompatible board. + */ + if (!sdmmc_test_board_has_slot(slot)) { + TEST_FAIL_MESSAGE("Board doesn't have the required slot"); + } + sdmmc_test_board_get_config_sdmmc(slot, &config, &slot_config); + int board_max_freq_khz = sdmmc_test_board_get_slot_info(slot)->max_freq_khz; + if (board_max_freq_khz > 0 && board_max_freq_khz < freq_khz) { + TEST_FAIL_MESSAGE("Board doesn't support required max_freq_khz"); + } + if (slot_config.width < width) { + TEST_FAIL_MESSAGE("Board doesn't support required bus width"); + } + + config.max_freq_khz = freq_khz; + + if (width == 1) { + config.flags = SDMMC_HOST_FLAG_1BIT; + slot_config.width = 1; + } else if (width == 4) { + config.flags = SDMMC_HOST_FLAG_4BIT; + slot_config.width = 4; + } else { + config.flags = SDMMC_HOST_FLAG_8BIT; + slot_config.width = 8; + assert(!ddr && "host driver does not support 8-line DDR mode yet"); + } + + /* Note, not checking sdmmc_test_board_slot_is_emmc here, + * to let us test DDR mode with eMMC breakout boards in SD slots. + */ + if (ddr) { + config.flags |= SDMMC_HOST_FLAG_DDR; + } + + sdmmc_test_board_card_power_set(true); + TEST_ESP_OK(sdmmc_host_init()); + TEST_ESP_OK(sdmmc_host_init_slot(slot, &slot_config)); + TEST_ESP_OK(sdmmc_card_init(&config, out_card)); +} + +void sdmmc_test_sd_end(sdmmc_card_t *card) +{ + TEST_ESP_OK(sdmmc_host_deinit()); + + // Reset all GPIOs to their default states + int slot = card->host.slot; + const sdmmc_test_board_slot_info_t *slot_info = sdmmc_test_board_get_slot_info(slot); + const int pins[] = { + slot_info->clk, + slot_info->cmd_mosi, + slot_info->d0_miso, + slot_info->d1, + slot_info->d2, + slot_info->d3_cs, + slot_info->d4, + slot_info->d5, + slot_info->d6, + slot_info->d7, + slot_info->cd, + slot_info->wp, + }; + const int num_pins = sizeof(pins) / sizeof(pins[0]); + // Silence logging in gpio_reset_pin, which logs at INFO level + esp_log_level_t old_level = esp_log_level_get("gpio"); + esp_log_level_set("gpio", ESP_LOG_WARN); + for (int i = 0; i < num_pins; i++) { + if (pins[i] >= 0) { + gpio_reset_pin(pins[i]); + gpio_pullup_dis(pins[i]); + } + } + esp_log_level_set("gpio", old_level); + + //Need to reset GPIO first, otherrwise cannot discharge VDD of card completely. + sdmmc_test_board_card_power_set(false); +} diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc_tests/sdmmc_test_cd_wp_sd.c b/components/esp_driver_sdmmc/test_apps/sdmmc_tests/sdmmc_test_cd_wp_sd.c new file mode 100644 index 0000000000..2db5ffe921 --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sdmmc_tests/sdmmc_test_cd_wp_sd.c @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "unity.h" +#include "sdmmc_cmd.h" +#include "driver/sdmmc_host.h" +#include "sdmmc_test_cd_wp_common.h" +#include "sdmmc_test_board.h" + +//TODO: IDF-8734 +#if !CONFIG_IDF_TARGET_ESP32 && !CONFIG_IDF_TARGET_ESP32S3 +TEST_CASE("CD input works in SD mode", "[sdmmc]") +{ + sdmmc_host_t config = SDMMC_HOST_DEFAULT(); + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + sdmmc_test_board_get_config_sdmmc(SDMMC_HOST_SLOT_1, &config, &slot_config); + const int test_gpio = sdmmc_test_board_get_slot_info(SDMMC_HOST_SLOT_1)->unused_pin; + slot_config.gpio_cd = test_gpio; + sdmmc_test_board_card_power_set(true); + TEST_ESP_OK(sdmmc_host_init()); + TEST_ESP_OK(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config)); + + sdmmc_test_cd_input(test_gpio, &config); + + TEST_ESP_OK(sdmmc_host_deinit()); + sdmmc_test_board_card_power_set(false); +} + +TEST_CASE("WP input works in SD mode", "[sdmmc]") +{ + sdmmc_test_board_card_power_set(true); + sdmmc_host_t config = SDMMC_HOST_DEFAULT(); + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + sdmmc_test_board_get_config_sdmmc(SDMMC_HOST_SLOT_1, &config, &slot_config); + const int test_gpio = sdmmc_test_board_get_slot_info(SDMMC_HOST_SLOT_1)->unused_pin; + slot_config.gpio_wp = test_gpio; + TEST_ESP_OK(sdmmc_host_init()); + TEST_ESP_OK(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config)); + + sdmmc_test_wp_input(test_gpio, &config); + + TEST_ESP_OK(sdmmc_host_deinit()); + sdmmc_test_board_card_power_set(false); +} +#endif diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc_tests/sdmmc_test_probe_sd.c b/components/esp_driver_sdmmc/test_apps/sdmmc_tests/sdmmc_test_probe_sd.c new file mode 100644 index 0000000000..2984b8401d --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sdmmc_tests/sdmmc_test_probe_sd.c @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "unity.h" +#include "sdmmc_cmd.h" +#include "sdmmc_test_begin_end.h" + +static void do_one_sdmmc_probe_test(int slot, int width, int freq_khz, int ddr) +{ + sdmmc_card_t card; + sdmmc_test_sd_skip_if_board_incompatible(slot, width, freq_khz, ddr); + sdmmc_test_sd_begin(slot, width, freq_khz, ddr, &card); + sdmmc_card_print_info(stdout, &card); + uint8_t* buffer = heap_caps_calloc(512, 1, MALLOC_CAP_DMA); + TEST_ESP_OK(sdmmc_read_sectors(&card, buffer, 0, 1)); + free(buffer); + sdmmc_test_sd_end(&card); +} + +TEST_CASE("sdmmc probe, slot 0, 1-bit", "[sdmmc]") +{ + do_one_sdmmc_probe_test(SLOT_0, 1, SDMMC_FREQ_PROBING, NO_DDR); + do_one_sdmmc_probe_test(SLOT_0, 1, SDMMC_FREQ_DEFAULT, NO_DDR); + do_one_sdmmc_probe_test(SLOT_0, 1, SDMMC_FREQ_HIGHSPEED, NO_DDR); +} + +TEST_CASE("sdmmc probe, slot 0, 4-bit", "[sdmmc]") +{ + do_one_sdmmc_probe_test(SLOT_0, 4, SDMMC_FREQ_PROBING, NO_DDR); + do_one_sdmmc_probe_test(SLOT_0, 4, SDMMC_FREQ_DEFAULT, NO_DDR); + do_one_sdmmc_probe_test(SLOT_0, 4, SDMMC_FREQ_HIGHSPEED, NO_DDR); +} + +TEST_CASE("sdmmc probe, slot 0, 4-bit DDR", "[sdmmc]") +{ + do_one_sdmmc_probe_test(SLOT_0, 4, SDMMC_FREQ_HIGHSPEED, WITH_DDR); +} + +TEST_CASE("sdmmc probe, slot 0, 8-bit", "[sdmmc]") +{ + do_one_sdmmc_probe_test(SLOT_0, 8, SDMMC_FREQ_PROBING, NO_DDR); + do_one_sdmmc_probe_test(SLOT_0, 8, SDMMC_FREQ_DEFAULT, NO_DDR); + do_one_sdmmc_probe_test(SLOT_0, 8, SDMMC_FREQ_HIGHSPEED, NO_DDR); +} + +TEST_CASE("sdmmc probe, slot 1, 1-bit", "[sdmmc]") +{ + do_one_sdmmc_probe_test(SLOT_1, 1, SDMMC_FREQ_PROBING, NO_DDR); + do_one_sdmmc_probe_test(SLOT_1, 1, SDMMC_FREQ_DEFAULT, NO_DDR); + do_one_sdmmc_probe_test(SLOT_1, 1, SDMMC_FREQ_HIGHSPEED, NO_DDR); +} + +TEST_CASE("sdmmc probe, slot 1, 4-bit", "[sdmmc]") +{ + do_one_sdmmc_probe_test(SLOT_1, 4, SDMMC_FREQ_PROBING, NO_DDR); + do_one_sdmmc_probe_test(SLOT_1, 4, SDMMC_FREQ_DEFAULT, NO_DDR); + do_one_sdmmc_probe_test(SLOT_1, 4, SDMMC_FREQ_HIGHSPEED, NO_DDR); +} + +TEST_CASE("sdmmc probe, slot 1, 4-bit DDR", "[sdmmc]") +{ + do_one_sdmmc_probe_test(SLOT_1, 4, SDMMC_FREQ_HIGHSPEED, WITH_DDR); +} diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc_tests/sdmmc_test_rw_sd.c b/components/esp_driver_sdmmc/test_apps/sdmmc_tests/sdmmc_test_rw_sd.c new file mode 100644 index 0000000000..9ed0c245ef --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sdmmc_tests/sdmmc_test_rw_sd.c @@ -0,0 +1,103 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include "unity.h" +#include "sdmmc_cmd.h" +#include "sdmmc_test_begin_end.h" +#include "sdmmc_test_rw_common.h" + +/* ========== Read/write performance tests, SD ========== */ + +static void do_one_sdmmc_perf_test(int slot, int width, int freq_khz, int ddr, FILE* perf_log) +{ + sdmmc_card_t card; + sdmmc_test_sd_skip_if_board_incompatible(slot, width, freq_khz, ddr); + sdmmc_test_sd_begin(slot, width, freq_khz, ddr, &card); + sdmmc_card_print_info(stdout, &card); + sdmmc_test_rw_performance(&card, perf_log); + sdmmc_test_sd_end(&card); +} + +TEST_CASE("sdmmc read/write performance, slot 0, 4-bit", "[sdmmc]") +{ + do_one_sdmmc_perf_test(SLOT_0, 4, SDMMC_FREQ_HIGHSPEED, NO_DDR, NULL); +} + +TEST_CASE("sdmmc read/write performance, slot 0, 4-bit DDR", "[sdmmc]") +{ + do_one_sdmmc_perf_test(SLOT_0, 4, SDMMC_FREQ_HIGHSPEED, WITH_DDR, NULL); +} + +TEST_CASE("sdmmc read/write performance, slot 0, 8-bit", "[sdmmc]") +{ + do_one_sdmmc_perf_test(SLOT_0, 8, SDMMC_FREQ_HIGHSPEED, NO_DDR, NULL); +} + +TEST_CASE("sdmmc read/write performance, slot 1, 4-bit", "[sdmmc]") +{ + /* Set up in-memory file for collecting performance logs */ + char *perf_log_buf = NULL; + size_t perf_log_size = 0; + FILE* perf_log = open_memstream(&perf_log_buf, &perf_log_size); + + /* Run the actual tests */ + do_one_sdmmc_perf_test(SLOT_1, 4, SDMMC_FREQ_HIGHSPEED, NO_DDR, perf_log); + + /* Dump the contents of the performance log to stdout */ + fclose(perf_log); + fwrite(perf_log_buf, perf_log_size, 1, stdout); + free(perf_log_buf); +} + +TEST_CASE("sdmmc read/write performance, slot 1, 4-bit DDR", "[sdmmc]") +{ + do_one_sdmmc_perf_test(SLOT_1, 4, SDMMC_FREQ_HIGHSPEED, WITH_DDR, NULL); +} + +/* ========== Read/write tests with offset, SD ========== */ + +static void do_one_sdmmc_rw_test_with_offset(int slot, int width, int freq_khz, int ddr) +{ + sdmmc_card_t card; + sdmmc_test_sd_skip_if_board_incompatible(slot, width, freq_khz, ddr); + sdmmc_test_sd_begin(slot, width, freq_khz, ddr, &card); + sdmmc_card_print_info(stdout, &card); + sdmmc_test_rw_with_offset(&card); + sdmmc_test_sd_end(&card); +} + +TEST_CASE("sdmmc read/write performance with offset, slot 0, 4-bit", "[sdmmc]") +{ + do_one_sdmmc_rw_test_with_offset(SLOT_0, 4, SDMMC_FREQ_HIGHSPEED, 0); +} + +TEST_CASE("sdmmc read/write performance with offset, slot 1, 4-bit", "[sdmmc]") +{ + do_one_sdmmc_rw_test_with_offset(SLOT_1, 4, SDMMC_FREQ_HIGHSPEED, 0); +} + +/* ========== Read/write tests with unaligned source/destination buffer, SD ========== */ + +static void do_one_sdmmc_rw_test_unaligned_buffer(int slot, int width, int freq_khz, int ddr) +{ + sdmmc_card_t card; + sdmmc_test_sd_skip_if_board_incompatible(slot, width, freq_khz, ddr); + sdmmc_test_sd_begin(slot, width, freq_khz, ddr, &card); + sdmmc_card_print_info(stdout, &card); + sdmmc_test_rw_unaligned_buffer(&card); + sdmmc_test_sd_end(&card); +} + +TEST_CASE("sdmmc read/write using unaligned buffer, slot 0, 4-bit", "[sdmmc]") +{ + do_one_sdmmc_rw_test_unaligned_buffer(SLOT_0, 4, SDMMC_FREQ_DEFAULT, 0); +} + +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); +} diff --git a/tools/ci/idf_pytest/constants.py b/tools/ci/idf_pytest/constants.py index 0d20705e1b..cddb18808c 100644 --- a/tools/ci/idf_pytest/constants.py +++ b/tools/ci/idf_pytest/constants.py @@ -77,9 +77,10 @@ ENV_MARKERS = { 'xtal_26mhz': 'runner with 26MHz xtal on board', 'xtal_40mhz': 'runner with 40MHz xtal on board', 'external_flash': 'external flash memory connected via VSPI (FSPI)', - 'sdcard_sdmode': 'sdcard running in SD mode', + 'sdcard_sdmode': 'sdcard running in SD mode, to be removed after test migration', 'sdcard_spimode': 'sdcard running in SPI mode', 'emmc': 'eMMC card', + 'sdcard': 'sdcard runner', 'MSPI_F8R8': 'runner with Octal Flash and Octal PSRAM', 'MSPI_F4R8': 'runner with Quad Flash and Octal PSRAM', 'MSPI_F4R4': 'runner with Quad Flash and Quad PSRAM',