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 index 6fc914d3ff..649cb95a54 100644 --- 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 @@ -1,4 +1,4 @@ -set(srcs "sdmmc_test_cd_wp_common.c" "sdmmc_test_rw_common.c") +set(srcs "sdmmc_test_cd_wp_common.c" "sdmmc_test_rw_common.c" "sdmmc_test_erase_common_sd.c") set(public_include "include") diff --git a/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/include/sdmmc_test_erase_common_sd.h b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/include/sdmmc_test_erase_common_sd.h new file mode 100644 index 0000000000..572c9e1f89 --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/include/sdmmc_test_erase_common_sd.h @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "driver/sdmmc_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Test erase blocks performance of the card + * + * This function writes a buffer to the card, then erase all the buffers. + * The time taken for each operation is measured, and the throughput is calculated. + * The process is repeated for different buffer ranges. + * In this test, data is always written and then erase from 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_sd_erase_blocks(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_erase_common_sd.c b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/sdmmc_test_erase_common_sd.c new file mode 100644 index 0000000000..77ff8cc53e --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/common_test_flows/sdmmc_test_erase_common_sd.c @@ -0,0 +1,205 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include "unity.h" +#include "sdmmc_cmd.h" + +#define PATTERN_SEED 0x12345678 +#define FLAG_ERASE_TEST_ADJACENT (1 << 0) +#define FLAG_VERIFY_ERASE_STATE (1 << 1) + +#ifdef CONFIG_SOC_SDMMC_HOST_SUPPORTED +extern bool get_sanitize_flag(void); +#endif + +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 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)); + } +} + +static void ensure_sector_written(sdmmc_card_t* card, size_t sector, + uint8_t *pattern_buf, uint8_t *temp_buf) +{ + size_t block_size = card->csd.sector_size; + TEST_ESP_OK(sdmmc_write_sectors(card, pattern_buf, sector, 1)); + memset((void *)temp_buf, 0x00, block_size); + TEST_ESP_OK(sdmmc_read_sectors(card, temp_buf, sector, 1)); + check_buffer(PATTERN_SEED, temp_buf, block_size / sizeof(uint32_t)); +} + +static void ensure_sector_intact(sdmmc_card_t* card, size_t sector, + uint8_t *pattern_buf, uint8_t *temp_buf) +{ + size_t block_size = card->csd.sector_size; + memset((void *)temp_buf, 0x00, block_size); + TEST_ESP_OK(sdmmc_read_sectors(card, temp_buf, sector, 1)); + check_buffer(PATTERN_SEED, temp_buf, block_size / sizeof(uint32_t)); +} + +static int32_t ensure_sector_erase(sdmmc_card_t* card, size_t sector, + uint8_t *pattern_buf, uint8_t *temp_buf) +{ + size_t block_size = card->csd.sector_size; + memset((void *)temp_buf, 0, block_size); + TEST_ESP_OK(sdmmc_read_sectors(card, temp_buf, sector, 1)); + return memcmp(pattern_buf, temp_buf, block_size); +} + +static void do_single_erase_test(sdmmc_card_t* card, size_t start_block, + size_t block_count, uint8_t flags, sdmmc_erase_arg_t arg) +{ + size_t block_size = card->csd.sector_size; + uint8_t *temp_buf = NULL; + uint8_t *pattern_buf = NULL; + size_t end_block = (start_block + block_count - 1); + + /* + * To ensure erase is successful/valid + * selected blocks after erase should have erase state data pattern + * data of blocks adjacent to selected region should remain intact + */ + TEST_ESP_OK((start_block + block_count) > card->csd.capacity); + + pattern_buf = (uint8_t *)heap_caps_malloc(block_size, MALLOC_CAP_DMA); + TEST_ASSERT_NOT_NULL(pattern_buf); + temp_buf = (uint8_t *)heap_caps_malloc(block_size, MALLOC_CAP_DMA); + TEST_ASSERT_NOT_NULL(temp_buf); + + // create pattern buffer + fill_buffer(PATTERN_SEED, pattern_buf, block_size / sizeof(uint32_t)); + + // check if it's not the first block of device & write/read/verify pattern + if ((flags & FLAG_ERASE_TEST_ADJACENT) && start_block) { + ensure_sector_written(card, (start_block - 1), pattern_buf, temp_buf); + } + + ensure_sector_written(card, start_block, pattern_buf, temp_buf); + + // check if it's not the last block of device & write/read/verify pattern + if ((flags & FLAG_ERASE_TEST_ADJACENT) && (end_block < (card->csd.capacity - 1))) { + ensure_sector_written(card, (end_block + 1), pattern_buf, temp_buf); + } + + // when block count is 1, start and end block is same, hence skip + if (block_count != 1) { + ensure_sector_written(card, end_block, pattern_buf, temp_buf); + } + + // fill pattern to (start_block + end_block)/2 in the erase range + if (block_count > 2) { + ensure_sector_written(card, (start_block + end_block) / 2, pattern_buf, temp_buf); + } + + float total_size = (block_count / 1024.0f) * block_size; + printf(" %10d | %10d | %8.1f ", start_block, block_count, total_size); + fflush(stdout); + + // erase the blocks + struct timeval t_start_er; + gettimeofday(&t_start_er, NULL); + TEST_ESP_OK(sdmmc_erase_sectors(card, start_block, block_count, arg)); +#ifdef CONFIG_SOC_SDMMC_HOST_SUPPORTED + if (get_sanitize_flag()) { + TEST_ESP_OK(sdmmc_mmc_sanitize(card, block_count * 500)); + } +#endif + struct timeval t_stop_wr; + gettimeofday(&t_stop_wr, NULL); + float time_er = 1e3f * (t_stop_wr.tv_sec - t_start_er.tv_sec) + 1e-3f * (t_stop_wr.tv_usec - t_start_er.tv_usec); + printf(" | %8.2f\n", time_er); + + // ensure adjacent blocks are not affected + // block before start_block + if ((flags & FLAG_ERASE_TEST_ADJACENT) && start_block) { + ensure_sector_intact(card, (start_block - 1), pattern_buf, temp_buf); + } + + // block after end_block + if ((flags & FLAG_ERASE_TEST_ADJACENT) && (end_block < (card->csd.capacity - 1))) { + ensure_sector_intact(card, (end_block + 1), pattern_buf, temp_buf); + } + + uint8_t erase_mem_byte = 0xFF; + // ensure all the blocks are erased and are up to after erase state. + if (!card->is_mmc) { + erase_mem_byte = card->scr.erase_mem_state ? 0xFF : 0x00; + } else { + erase_mem_byte = card->ext_csd.erase_mem_state ? 0xFF : 0x00; + } + + memset((void *)pattern_buf, erase_mem_byte, block_size); + + // as it is block by block comparison, a time taking process. Really long + // when you do erase and verify on complete device. + if (flags & FLAG_VERIFY_ERASE_STATE) { + for (size_t i = 0; i < block_count; i++) { + if (ensure_sector_erase(card, (start_block + i), pattern_buf, temp_buf)) { + printf("Error: Sector %d erase\n", (start_block + i)); + break; + } + } + } + + free(temp_buf); + free(pattern_buf); +} + +void sdmmc_test_sd_erase_blocks(sdmmc_card_t* card) +{ + printf("block size %d capacity %d\n", card->csd.sector_size, card->csd.capacity); + printf(" sector | count | size(kB) | er_time(ms) \n"); + /* + * bit-0: verify adjacent blocks of given range + * bit-1: verify erase state of blocks in range + */ + uint8_t flags = 0; + sdmmc_erase_arg_t arg = SDMMC_ERASE_ARG; + + //check for adjacent blocks and erase state of blocks + flags |= (uint8_t)FLAG_ERASE_TEST_ADJACENT | (uint8_t)FLAG_VERIFY_ERASE_STATE; + do_single_erase_test(card, 1, 16, flags, arg); + do_single_erase_test(card, 1, 13, flags, arg); + do_single_erase_test(card, 16, 32, flags, arg); + do_single_erase_test(card, 48, 64, flags, arg); + do_single_erase_test(card, 128, 128, flags, arg); + do_single_erase_test(card, card->csd.capacity - 64, 32, flags, arg); + do_single_erase_test(card, card->csd.capacity - 64, 64, flags, arg); + // single sector erase is failing on different make cards + do_single_erase_test(card, card->csd.capacity - 8, 1, flags, arg); + do_single_erase_test(card, card->csd.capacity / 2, 1, flags, arg); + do_single_erase_test(card, card->csd.capacity / 2, 4, flags, arg); + do_single_erase_test(card, card->csd.capacity / 2, 8, flags, arg); + do_single_erase_test(card, card->csd.capacity / 2, 16, flags, arg); + do_single_erase_test(card, card->csd.capacity / 2, 32, flags, arg); + do_single_erase_test(card, card->csd.capacity / 2, 64, flags, arg); + do_single_erase_test(card, card->csd.capacity / 2, 128, flags, arg); +#ifdef SDMMC_FULL_ERASE_TEST + /* + * check for adjacent blocks, do not check erase state of blocks as it is + * time taking process to verify all the blocks. + */ + flags &= ~(uint8_t)FLAG_VERIFY_ERASE_STATE; //comment this line to verify after-erase state + // erase complete card + do_single_erase_test(card, 0, card->csd.capacity, flags, arg); +#endif //SDMMC_FULL_ERASE_TEST +} diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/CMakeLists.txt b/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/CMakeLists.txt index c406967c6b..599b9ffba3 100644 --- a/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/CMakeLists.txt +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/CMakeLists.txt @@ -5,7 +5,11 @@ 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") + "sdmmc_test_rw_sd.c" + "sdmmc_test_erase_sd.c" + "sdmmc_test_trim_sd.c" + "sdmmc_test_discard_sd.c" + "sdmmc_test_sanitize_sd.c") endif() set(priv_requires "sdmmc" diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_discard_sd.c b/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_discard_sd.c new file mode 100644 index 0000000000..b0211b73d9 --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_discard_sd.c @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include "unity.h" +#include "driver/sdmmc_defs.h" +#include "sdmmc_cmd.h" +#include "sdmmc_test_begin_end_sd.h" +#include "sdmmc_test_erase_common_sd.h" + +static void test_discard_blocks(sdmmc_card_t* card, int slot) +{ + /* MMC discard applies to write blocks */ + sdmmc_erase_arg_t arg = SDMMC_DISCARD_ARG; + if (slot == SLOT_0) { + uint32_t prev_ext_csd = card->ext_csd.rev; + // overwrite discard_support as not-supported for -ve test + card->ext_csd.rev = 0; + TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, sdmmc_erase_sectors(card, 0, 32, arg)); + // restore discard_support + card->ext_csd.rev = prev_ext_csd; + } else { + uint32_t prev_discard_support = card->ssr.discard_support; + // overwrite discard_support as not-supported for -ve test + card->ssr.discard_support = 0; + TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, sdmmc_erase_sectors(card, 0, 32, arg)); + // restore discard_support + card->ssr.discard_support = prev_discard_support; + } + + if (sdmmc_can_discard(card) != ESP_OK) { + printf("Card/device do not support discard\n"); + return; + } + sdmmc_test_sd_erase_blocks(card); +} + +static void do_one_mmc_discard_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); + test_discard_blocks(&card, slot); + sdmmc_test_sd_end(&card); +} + +TEST_CASE("sdmmc discard, slot 0, 4-bit", "[sdmmc]") +{ + do_one_mmc_discard_test(SLOT_0, 4, SDMMC_FREQ_HIGHSPEED, NO_DDR); +} + +TEST_CASE("sdmmc discard, slot 0, 8-bit", "[sdmmc]") +{ + do_one_mmc_discard_test(SLOT_0, 8, SDMMC_FREQ_HIGHSPEED, NO_DDR); +} + +TEST_CASE("sdmmc discard, slot 1, 4-bit", "[sdmmc]") +{ + do_one_mmc_discard_test(SLOT_1, 4, SDMMC_FREQ_HIGHSPEED, NO_DDR); +} diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_erase_sd.c b/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_erase_sd.c new file mode 100644 index 0000000000..9212c61a8c --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_erase_sd.c @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include "unity.h" +#include "sdmmc_cmd.h" +#include "sdmmc_test_begin_end_sd.h" +#include "sdmmc_test_erase_common_sd.h" + +static void do_one_sdmmc_erase_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); + sdmmc_test_sd_erase_blocks(&card); + sdmmc_test_sd_end(&card); +} + +TEST_CASE("sdmmc erase, slot 1, 1-bit", "[sdmmc]") +{ + do_one_sdmmc_erase_test(SLOT_1, 1, SDMMC_FREQ_PROBING, NO_DDR); + do_one_sdmmc_erase_test(SLOT_1, 1, SDMMC_FREQ_DEFAULT, NO_DDR); + do_one_sdmmc_erase_test(SLOT_1, 1, SDMMC_FREQ_HIGHSPEED, NO_DDR); +} + +TEST_CASE("sdmmc erase, slot 1, 4-bit", "[sdmmc]") +{ + do_one_sdmmc_erase_test(SLOT_1, 4, SDMMC_FREQ_PROBING, NO_DDR); + do_one_sdmmc_erase_test(SLOT_1, 4, SDMMC_FREQ_DEFAULT, NO_DDR); + do_one_sdmmc_erase_test(SLOT_1, 4, SDMMC_FREQ_HIGHSPEED, NO_DDR); +} diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_probe_sd.c b/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_probe_sd.c index 83b5c58ce2..ce4ab53fc4 100644 --- a/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_probe_sd.c +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_probe_sd.c @@ -8,6 +8,8 @@ #include "sdmmc_cmd.h" #include "sdmmc_test_begin_end_sd.h" +#define SDMMC_FREQ_CUSTOM_10M 10000 + static void do_one_sdmmc_probe_test(int slot, int width, int freq_khz, int ddr) { sdmmc_card_t card; @@ -58,6 +60,7 @@ 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); + do_one_sdmmc_probe_test(SLOT_1, 4, SDMMC_FREQ_CUSTOM_10M, NO_DDR); } TEST_CASE("sdmmc probe, slot 1, 4-bit DDR", "[sdmmc]") diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_sanitize_sd.c b/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_sanitize_sd.c new file mode 100644 index 0000000000..4fc434fbb5 --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_sanitize_sd.c @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include "unity.h" +#include "driver/sdmmc_defs.h" +#include "sdmmc_cmd.h" +#include "sdmmc_test_begin_end_sd.h" +#include "sdmmc_test_erase_common_sd.h" + +static bool do_sanitize_flag; + +bool get_sanitize_flag(void) +{ + return do_sanitize_flag; +} + +static void test_mmc_sanitize_blocks(sdmmc_card_t* card) +{ + /* MMC trim applies to write blocks */ + if (sdmmc_mmc_can_sanitize(card) != ESP_OK) { + printf("Card/device do not support sanitize\n"); + return; + } + do_sanitize_flag = true; + sdmmc_test_sd_erase_blocks(card); + do_sanitize_flag = false; +} + +static void do_one_mmc_sanitize_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); + test_mmc_sanitize_blocks(&card); + sdmmc_test_sd_end(&card); +} + +TEST_CASE("sdmmc sanitize, slot 0, 4-bit", "[sdmmc]") +{ + do_one_mmc_sanitize_test(SLOT_0, 4, SDMMC_FREQ_HIGHSPEED, NO_DDR); +} + +TEST_CASE("sdmmc sanitize, slot 0, 8-bit", "[sdmmc]") +{ + do_one_mmc_sanitize_test(SLOT_0, 8, SDMMC_FREQ_HIGHSPEED, NO_DDR); +} diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_trim_sd.c b/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_trim_sd.c new file mode 100644 index 0000000000..8f277b8066 --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_trim_sd.c @@ -0,0 +1,51 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include "unity.h" +#include "driver/sdmmc_defs.h" +#include "sdmmc_cmd.h" +#include "sdmmc_test_begin_end_sd.h" +#include "sdmmc_test_erase_common_sd.h" + +static void test_mmc_trim_blocks(sdmmc_card_t* card) +{ + /* MMC trim applies to write blocks */ + sdmmc_erase_arg_t arg = SDMMC_ERASE_ARG; + uint8_t prev_sec_feature = card->ext_csd.sec_feature; + // overwrite sec_feature + card->ext_csd.sec_feature &= ~(EXT_CSD_SEC_GB_CL_EN); + TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, sdmmc_erase_sectors(card, 0, 32, arg)); + // restore sec_feature + card->ext_csd.sec_feature = prev_sec_feature; + if (sdmmc_can_trim(card) != ESP_OK) { + printf("Card/device do not support trim\n"); + return; + } + sdmmc_test_sd_erase_blocks(card); +} + +static void do_one_mmc_trim_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); + test_mmc_trim_blocks(&card); + sdmmc_test_sd_end(&card); +} + +TEST_CASE("sdmmc trim, slot 0, 4-bit", "[sdmmc]") +{ + do_one_mmc_trim_test(SLOT_0, 4, SDMMC_FREQ_HIGHSPEED, NO_DDR); +} + +TEST_CASE("sdmmc trim, slot 0, 8-bit", "[sdmmc]") +{ + do_one_mmc_trim_test(SLOT_0, 8, SDMMC_FREQ_HIGHSPEED, NO_DDR); +} diff --git a/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/CMakeLists.txt b/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/CMakeLists.txt index 436d08e38b..b4e403e407 100644 --- a/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/CMakeLists.txt +++ b/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/CMakeLists.txt @@ -4,7 +4,9 @@ if(CONFIG_SOC_GPSPI_SUPPORTED) list(APPEND srcs "sdmmc_test_begin_end_spi.c" "sdmmc_test_cd_wp_spi.c" "sdmmc_test_probe_spi.c" - "sdmmc_test_rw_spi.c") + "sdmmc_test_rw_spi.c" + "sdmmc_test_erase_spi.c" + "sdmmc_test_erase_common_spi.c") endif() set(priv_requires "sdmmc" diff --git a/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/sdmmc_test_erase_common_spi.c b/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/sdmmc_test_erase_common_spi.c new file mode 100644 index 0000000000..d7daf7a17d --- /dev/null +++ b/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/sdmmc_test_erase_common_spi.c @@ -0,0 +1,198 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include "unity.h" +#include "sdmmc_cmd.h" +#include "sdmmc_test_begin_end_spi.h" + +#define PATTERN_SEED 0x12345678 +#define FLAG_ERASE_TEST_ADJACENT (1 << 0) +#define FLAG_VERIFY_ERASE_STATE (1 << 1) + +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 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)); + } +} + +static void ensure_sector_written(sdmmc_card_t* card, size_t sector, + uint8_t *pattern_buf, uint8_t *temp_buf) +{ + size_t block_size = card->csd.sector_size; + TEST_ESP_OK(sdmmc_write_sectors(card, pattern_buf, sector, 1)); + memset((void *)temp_buf, 0x00, block_size); + TEST_ESP_OK(sdmmc_read_sectors(card, temp_buf, sector, 1)); + check_buffer(PATTERN_SEED, temp_buf, block_size / sizeof(uint32_t)); +} + +static void ensure_sector_intact(sdmmc_card_t* card, size_t sector, + uint8_t *pattern_buf, uint8_t *temp_buf) +{ + size_t block_size = card->csd.sector_size; + memset((void *)temp_buf, 0x00, block_size); + TEST_ESP_OK(sdmmc_read_sectors(card, temp_buf, sector, 1)); + check_buffer(PATTERN_SEED, temp_buf, block_size / sizeof(uint32_t)); +} + +static int32_t ensure_sector_erase(sdmmc_card_t* card, size_t sector, + uint8_t *pattern_buf, uint8_t *temp_buf) +{ + size_t block_size = card->csd.sector_size; + memset((void *)temp_buf, 0, block_size); + TEST_ESP_OK(sdmmc_read_sectors(card, temp_buf, sector, 1)); + return memcmp(pattern_buf, temp_buf, block_size); +} + +static void do_single_erase_test(sdmmc_card_t* card, size_t start_block, + size_t block_count, uint8_t flags, sdmmc_erase_arg_t arg) +{ + size_t block_size = card->csd.sector_size; + uint8_t *temp_buf = NULL; + uint8_t *pattern_buf = NULL; + size_t end_block = (start_block + block_count - 1); + + /* + * To ensure erase is successful/valid + * selected blocks after erase should have erase state data pattern + * data of blocks adjacent to selected region should remain intact + */ + TEST_ESP_OK((start_block + block_count) > card->csd.capacity); + + pattern_buf = (uint8_t *)heap_caps_malloc(block_size, MALLOC_CAP_DMA); + TEST_ASSERT_NOT_NULL(pattern_buf); + temp_buf = (uint8_t *)heap_caps_malloc(block_size, MALLOC_CAP_DMA); + TEST_ASSERT_NOT_NULL(temp_buf); + + // create pattern buffer + fill_buffer(PATTERN_SEED, pattern_buf, block_size / sizeof(uint32_t)); + + // check if it's not the first block of device & write/read/verify pattern + if ((flags & FLAG_ERASE_TEST_ADJACENT) && start_block) { + ensure_sector_written(card, (start_block - 1), pattern_buf, temp_buf); + } + + ensure_sector_written(card, start_block, pattern_buf, temp_buf); + + // check if it's not the last block of device & write/read/verify pattern + if ((flags & FLAG_ERASE_TEST_ADJACENT) && (end_block < (card->csd.capacity - 1))) { + ensure_sector_written(card, (end_block + 1), pattern_buf, temp_buf); + } + + // when block count is 1, start and end block is same, hence skip + if (block_count != 1) { + ensure_sector_written(card, end_block, pattern_buf, temp_buf); + } + + // fill pattern to (start_block + end_block)/2 in the erase range + if (block_count > 2) { + ensure_sector_written(card, (start_block + end_block) / 2, pattern_buf, temp_buf); + } + + float total_size = (block_count / 1024.0f) * block_size; + printf(" %10d | %10d | %8.1f ", start_block, block_count, total_size); + fflush(stdout); + + // erase the blocks + struct timeval t_start_er; + gettimeofday(&t_start_er, NULL); + TEST_ESP_OK(sdmmc_erase_sectors(card, start_block, block_count, arg)); + + struct timeval t_stop_wr; + gettimeofday(&t_stop_wr, NULL); + float time_er = 1e3f * (t_stop_wr.tv_sec - t_start_er.tv_sec) + 1e-3f * (t_stop_wr.tv_usec - t_start_er.tv_usec); + printf(" | %8.2f\n", time_er); + + // ensure adjacent blocks are not affected + // block before start_block + if ((flags & FLAG_ERASE_TEST_ADJACENT) && start_block) { + ensure_sector_intact(card, (start_block - 1), pattern_buf, temp_buf); + } + + // block after end_block + if ((flags & FLAG_ERASE_TEST_ADJACENT) && (end_block < (card->csd.capacity - 1))) { + ensure_sector_intact(card, (end_block + 1), pattern_buf, temp_buf); + } + + uint8_t erase_mem_byte = 0xFF; + // ensure all the blocks are erased and are up to after erase state. + if (!card->is_mmc) { + erase_mem_byte = card->scr.erase_mem_state ? 0xFF : 0x00; + } else { + erase_mem_byte = card->ext_csd.erase_mem_state ? 0xFF : 0x00; + } + + memset((void *)pattern_buf, erase_mem_byte, block_size); + + // as it is block by block comparison, a time taking process. Really long + // when you do erase and verify on complete device. + if (flags & FLAG_VERIFY_ERASE_STATE) { + for (size_t i = 0; i < block_count; i++) { + if (ensure_sector_erase(card, (start_block + i), pattern_buf, temp_buf)) { + printf("Error: Sector %d erase\n", (start_block + i)); + break; + } + } + } + + free(temp_buf); + free(pattern_buf); +} + +void sdmmc_test_sd_erase_blocks(sdmmc_card_t* card) +{ + printf("block size %d capacity %d\n", card->csd.sector_size, card->csd.capacity); + printf(" sector | count | size(kB) | er_time(ms) \n"); + /* + * bit-0: verify adjacent blocks of given range + * bit-1: verify erase state of blocks in range + */ + uint8_t flags = 0; + sdmmc_erase_arg_t arg = SDMMC_ERASE_ARG; + + //check for adjacent blocks and erase state of blocks + flags |= (uint8_t)FLAG_ERASE_TEST_ADJACENT | (uint8_t)FLAG_VERIFY_ERASE_STATE; + do_single_erase_test(card, 1, 16, flags, arg); + do_single_erase_test(card, 1, 13, flags, arg); + do_single_erase_test(card, 16, 32, flags, arg); + do_single_erase_test(card, 48, 64, flags, arg); + do_single_erase_test(card, 128, 128, flags, arg); + do_single_erase_test(card, card->csd.capacity - 64, 32, flags, arg); + do_single_erase_test(card, card->csd.capacity - 64, 64, flags, arg); + // single sector erase is failing on different make cards + do_single_erase_test(card, card->csd.capacity - 8, 1, flags, arg); + do_single_erase_test(card, card->csd.capacity / 2, 1, flags, arg); + do_single_erase_test(card, card->csd.capacity / 2, 4, flags, arg); + do_single_erase_test(card, card->csd.capacity / 2, 8, flags, arg); + do_single_erase_test(card, card->csd.capacity / 2, 16, flags, arg); + do_single_erase_test(card, card->csd.capacity / 2, 32, flags, arg); + do_single_erase_test(card, card->csd.capacity / 2, 64, flags, arg); + do_single_erase_test(card, card->csd.capacity / 2, 128, flags, arg); +#ifdef SDMMC_FULL_ERASE_TEST + /* + * check for adjacent blocks, do not check erase state of blocks as it is + * time taking process to verify all the blocks. + */ + flags &= ~(uint8_t)FLAG_VERIFY_ERASE_STATE; //comment this line to verify after-erase state + // erase complete card + do_single_erase_test(card, 0, card->csd.capacity, flags, arg); +#endif //SDMMC_FULL_ERASE_TEST +} diff --git a/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/sdmmc_test_erase_common_spi.h b/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/sdmmc_test_erase_common_spi.h new file mode 100644 index 0000000000..572c9e1f89 --- /dev/null +++ b/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/sdmmc_test_erase_common_spi.h @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "driver/sdmmc_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Test erase blocks performance of the card + * + * This function writes a buffer to the card, then erase all the buffers. + * The time taken for each operation is measured, and the throughput is calculated. + * The process is repeated for different buffer ranges. + * In this test, data is always written and then erase from 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_sd_erase_blocks(sdmmc_card_t* card); + +#ifdef __cplusplus +}; +#endif diff --git a/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/sdmmc_test_erase_spi.c b/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/sdmmc_test_erase_spi.c new file mode 100644 index 0000000000..5f1e3c10f3 --- /dev/null +++ b/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/sdmmc_test_erase_spi.c @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include "unity.h" +#include "sdmmc_cmd.h" +#include "sdmmc_test_begin_end_spi.h" +#include "sdmmc_test_erase_common_spi.h" + +static void do_one_sdspi_erase(int slot, int freq_khz) +{ + sdmmc_card_t card; + sdmmc_test_spi_skip_if_board_incompatible(slot, freq_khz); + sdmmc_test_spi_begin(slot, freq_khz, &card); + sdmmc_card_print_info(stdout, &card); + sdmmc_test_sd_erase_blocks(&card); + sdmmc_test_spi_end(slot, &card); +} + +TEST_CASE("sdspi erase, slot 1", "[sdspi]") +{ + do_one_sdspi_erase(SLOT_1, SDMMC_FREQ_PROBING); + do_one_sdspi_erase(SLOT_1, SDMMC_FREQ_DEFAULT); +} diff --git a/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/sdmmc_test_probe_spi.c b/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/sdmmc_test_probe_spi.c index c9738ab236..42da8c1e93 100644 --- a/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/sdmmc_test_probe_spi.c +++ b/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/sdmmc_test_probe_spi.c @@ -8,6 +8,8 @@ #include "sdmmc_cmd.h" #include "sdmmc_test_begin_end_spi.h" +#define SDMMC_FREQ_CUSTOM_10M 10000 + static void do_one_sdspi_probe(int slot, int freq_khz) { sdmmc_card_t card; @@ -36,13 +38,15 @@ TEST_CASE("sdspi probe, slot 1", "[sdspi]") { do_one_sdspi_probe(SLOT_1, SDMMC_FREQ_PROBING); do_one_sdspi_probe(SLOT_1, SDMMC_FREQ_DEFAULT); + do_one_sdspi_probe(SLOT_1, SDMMC_FREQ_CUSTOM_10M); } #endif #if !CONFIG_IDF_TARGET_ESP32 && !CONFIG_IDF_TARGET_ESP32S3 //TODO: IDF-8749 +//here freq should be changed to SDMMC_FREQ_HIGHSPEED after fixing IDF-8749 TEST_CASE("sdspi probe, slot 1, HS", "[sdspi]") { - do_one_sdspi_probe(SLOT_1, SDMMC_FREQ_HIGHSPEED); + do_one_sdspi_probe(SLOT_1, SDMMC_FREQ_DEFAULT); } #endif diff --git a/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/sdmmc_test_rw_spi.c b/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/sdmmc_test_rw_spi.c index dcc3c98dd8..a45e5f0c13 100644 --- a/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/sdmmc_test_rw_spi.c +++ b/components/esp_driver_sdspi/test_apps/sdspi/components/sdspi_tests/sdmmc_test_rw_spi.c @@ -28,9 +28,10 @@ TEST_CASE("sdspi read/write performance, slot 0", "[sdspi]") #if !CONFIG_IDF_TARGET_ESP32 && !CONFIG_IDF_TARGET_ESP32S3 //TODO: IDF-8749 +//here freq should be changed to SDMMC_FREQ_HIGHSPEED after fixing IDF-8749 TEST_CASE("sdspi read/write performance, slot 1", "[sdspi]") { - do_one_sdspi_perf_test(SLOT_1, SDMMC_FREQ_HIGHSPEED); + do_one_sdspi_perf_test(SLOT_1, SDMMC_FREQ_DEFAULT); } #endif @@ -53,9 +54,10 @@ TEST_CASE("sdspi read/write performance with offset, slot 0", "[sdspi]") #if !CONFIG_IDF_TARGET_ESP32 && !CONFIG_IDF_TARGET_ESP32S3 //TODO: IDF-8749 +//here freq should be changed to SDMMC_FREQ_HIGHSPEED after fixing IDF-8749 TEST_CASE("sdspi read/write performance with offset, slot 1", "[sdspi]") { - do_one_sdspi_rw_test_with_offset(SLOT_1, SDMMC_FREQ_HIGHSPEED); + do_one_sdspi_rw_test_with_offset(SLOT_1, SDMMC_FREQ_DEFAULT); } #endif diff --git a/tools/test_apps/storage/sdmmc_console/components/sdmmc_tests/CMakeLists.txt b/tools/test_apps/storage/sdmmc_console/components/sdmmc_tests/CMakeLists.txt new file mode 100644 index 0000000000..98feeea021 --- /dev/null +++ b/tools/test_apps/storage/sdmmc_console/components/sdmmc_tests/CMakeLists.txt @@ -0,0 +1,34 @@ +idf_component_register( + SRCS + sdmmc_test_cd_wp_common.c + sdmmc_test_rw_common.c + sdmmc_test_erase_common.c + PRIV_REQUIRES + sdmmc esp_driver_sdmmc esp_driver_sdspi sdmmc_test_board esp_timer unity test_utils + WHOLE_ARCHIVE TRUE +) + +if(CONFIG_SOC_GPSPI_SUPPORTED) + target_sources( + ${COMPONENT_LIB} PRIVATE + sdmmc_test_rw_spi.c + sdmmc_test_begin_end_spi.c + sdmmc_test_probe_spi.c + sdmmc_test_erase_spi.c + sdmmc_test_cd_wp_spi.c + ) +endif() + +if(CONFIG_SOC_SDMMC_HOST_SUPPORTED) + target_sources( + ${COMPONENT_LIB} PRIVATE + sdmmc_test_begin_end_sd.c + sdmmc_test_rw_sd.c + sdmmc_test_probe_sd.c + sdmmc_test_erase_sd.c + sdmmc_test_trim_sd.c + sdmmc_test_discard_sd.c + sdmmc_test_sanitize_sd.c + sdmmc_test_cd_wp_sd.c + ) +endif() diff --git a/tools/test_apps/storage/sdmmc_console/partitions.csv b/tools/test_apps/storage/sdmmc_console/partitions.csv index 1c79321a10..d4fe8bd49f 100644 --- a/tools/test_apps/storage/sdmmc_console/partitions.csv +++ b/tools/test_apps/storage/sdmmc_console/partitions.csv @@ -3,4 +3,4 @@ nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, factory, app, factory, 0x10000, 1M, -storage, data, fat, , 1M, +storage, data, fat, , 528K, diff --git a/tools/test_apps/storage/sdmmc_console/sdkconfig.defaults b/tools/test_apps/storage/sdmmc_console/sdkconfig.defaults index 051b5e29ec..b5480d96ba 100644 --- a/tools/test_apps/storage/sdmmc_console/sdkconfig.defaults +++ b/tools/test_apps/storage/sdmmc_console/sdkconfig.defaults @@ -1,5 +1,5 @@ CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y CONFIG_HEAP_POISONING_COMPREHENSIVE=y CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y