diff --git a/components/spi_flash/test_apps/esp_flash_stress/CMakeLists.txt b/components/spi_flash/test_apps/esp_flash_stress/CMakeLists.txt new file mode 100644 index 0000000000..5565d0d2f6 --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_stress/CMakeLists.txt @@ -0,0 +1,8 @@ +# This is the project CMakeLists.txt file for the test subproject +cmake_minimum_required(VERSION 3.16) + +set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components") + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(test_esp_flash_stress) diff --git a/components/spi_flash/test_apps/esp_flash_stress/README.md b/components/spi_flash/test_apps/esp_flash_stress/README.md new file mode 100644 index 0000000000..b5be4985c5 --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_stress/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | diff --git a/components/spi_flash/test_apps/esp_flash_stress/main/CMakeLists.txt b/components/spi_flash/test_apps/esp_flash_stress/main/CMakeLists.txt new file mode 100644 index 0000000000..17e9f27daf --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_stress/main/CMakeLists.txt @@ -0,0 +1,7 @@ +set(srcs "test_app_main.c" + "test_esp_flash_stress.c") + +# In order for the cases defined by `TEST_CASE` to be linked into the final elf, +# the component can be registered as WHOLE_ARCHIVE +idf_component_register(SRCS ${srcs} + WHOLE_ARCHIVE) diff --git a/components/spi_flash/test_apps/esp_flash_stress/main/test_app_main.c b/components/spi_flash/test_apps/esp_flash_stress/main/test_app_main.c new file mode 100644 index 0000000000..16da07c2ab --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_stress/main/test_app_main.c @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "unity_test_utils.h" +#include "esp_heap_caps.h" + +// load partition table in tests will use memory +#define TEST_MEMORY_LEAK_THRESHOLD (600) + +void setUp(void) +{ + unity_utils_record_free_mem(); +} + +void tearDown(void) +{ + esp_reent_cleanup(); //clean up some of the newlib's lazy allocations + 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/spi_flash/test_apps/esp_flash_stress/main/test_esp_flash_stress.c b/components/spi_flash/test_apps/esp_flash_stress/main/test_esp_flash_stress.c new file mode 100644 index 0000000000..4ef8dec10c --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_stress/main/test_esp_flash_stress.c @@ -0,0 +1,211 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "unity.h" +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_system.h" +#include "esp_check.h" +#include "esp_attr.h" +#include "esp_flash.h" +#include "esp_partition.h" + +portMUX_TYPE s_test_spinlock = portMUX_INITIALIZER_UNLOCKED; + +/*--------------------------------------------------------------- + ESP Flash API Concurrency Pressure Test +---------------------------------------------------------------*/ +#define TEST_FLASH_CONCURRENCY_TASK_NUM 12 + +static int s_test_concurrency_id; +//only used for finite loop for CI test +SemaphoreHandle_t s_test_concurrency_smphr; + +typedef struct { + bool is_pressure_test; + const esp_partition_t *part; +} test_concurrency_ctx_t; + + +static const esp_partition_t *get_test_flash_partition(void) +{ + /* This finds "flash_test" partition defined in partition_table_unit_test_app.csv */ + const esp_partition_t *result = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, + ESP_PARTITION_SUBTYPE_ANY, "flash_test"); + assert(result != NULL); /* means partition table set wrong */ + return result; +} + + +static void s_test_flash_ops_task(void *arg) +{ + test_concurrency_ctx_t *test_ctx = (test_concurrency_ctx_t *)arg; + bool is_pressure_test = test_ctx->is_pressure_test;; + esp_flash_t *chip = test_ctx->part->flash_chip; + uint32_t offs = test_ctx->part->address; + + portENTER_CRITICAL(&s_test_spinlock); + int id = s_test_concurrency_id; + s_test_concurrency_id++; + portEXIT_CRITICAL(&s_test_spinlock); + + int flash_block_sz = 4096; + char *buffer = calloc(1, flash_block_sz); + TEST_ASSERT(buffer); + srand(id + 299); + int delay_us = rand() % 1000; + printf("delay_us: %d\n", delay_us); + + if (is_pressure_test) { + printf("INFINITE_LOOP\n"); + while (1) { + printf("%s %d is running\n", __func__, id); + TEST_ESP_OK(esp_flash_erase_region(chip, id * flash_block_sz + offs, flash_block_sz)); + TEST_ESP_OK(esp_flash_write(chip, buffer, id * flash_block_sz + offs, flash_block_sz)); + TEST_ESP_OK(esp_flash_read(chip, buffer, id * flash_block_sz + offs, flash_block_sz)); + vTaskDelay(pdMS_TO_TICKS(delay_us)); + } + } else { + printf("FINITE_LOOP\n"); + for (int i = 0; i < 10; i++) { + TEST_ESP_OK(esp_flash_erase_region(chip, id * flash_block_sz + offs, 4096)); + TEST_ESP_OK(esp_flash_write(chip, buffer, id * flash_block_sz + offs, flash_block_sz)); + TEST_ESP_OK(esp_flash_read(chip, buffer, id * flash_block_sz + offs, flash_block_sz)); + vTaskDelay(pdMS_TO_TICKS(delay_us)); + } + printf("%s %d finishes\n", __func__, id); + free(buffer); + xSemaphoreGive(s_test_concurrency_smphr); + vTaskDelete(NULL); + } +} + +/*------------------------------------------- + Uni Core +-------------------------------------------*/ +TEST_CASE("Flash UniCore: Test ESP Flash API Concurrency", "[esp_flash]") +{ + const esp_partition_t* part = get_test_flash_partition(); + + s_test_concurrency_id = 0; + s_test_concurrency_smphr = xSemaphoreCreateCounting(TEST_FLASH_CONCURRENCY_TASK_NUM, 0); + TEST_ASSERT(s_test_concurrency_smphr); + + char flash_task_name[32]; + test_concurrency_ctx_t ctx = { + .is_pressure_test = false, + .part = part, + }; + + for (int i = 0; i < TEST_FLASH_CONCURRENCY_TASK_NUM; i++) { + int l = snprintf(flash_task_name, 32, "flash_ops_task%d", i); + flash_task_name[l] = '\0'; + xTaskCreatePinnedToCore(&s_test_flash_ops_task, flash_task_name, 4096, (void *)(&ctx), 5, NULL, 0); + } + + for (int i = 0; i < TEST_FLASH_CONCURRENCY_TASK_NUM; i++) { + TEST_ASSERT(xSemaphoreTake(s_test_concurrency_smphr, portMAX_DELAY) == pdTRUE); + } + vTaskDelay(1); + vSemaphoreDelete(s_test_concurrency_smphr); +} + +/** + * esp_flash APIs concurrency pressure test + * This test is for manually test + */ +TEST_CASE("Flash UniCore: Test ESP Flash API Concurrency [Stress]", "[esp_flash][stress][manual][ignore]") +{ + const esp_partition_t* part = get_test_flash_partition(); + + s_test_concurrency_id = 0; + char flash_task_name[32]; + test_concurrency_ctx_t ctx = { + .is_pressure_test = true, + .part = part, + }; + + for (int i = 0; i < TEST_FLASH_CONCURRENCY_TASK_NUM; i++) { + int l = snprintf(flash_task_name, 32, "flash_ops_task%d", i); + flash_task_name[l] = '\0'; + xTaskCreatePinnedToCore(&s_test_flash_ops_task, flash_task_name, 4096, (void *)(&ctx), 5, NULL, 0); + } + + while(1); +} + +#if !CONFIG_FREERTOS_UNICORE +/*------------------------------------------- + Dual Core +-------------------------------------------*/ +TEST_CASE("Flash DualCore: Test ESP Flash API Concurrency", "[esp_flash]") +{ + const esp_partition_t* part = get_test_flash_partition(); + + s_test_concurrency_id = 0; + s_test_concurrency_smphr = xSemaphoreCreateCounting(TEST_FLASH_CONCURRENCY_TASK_NUM, 0); + TEST_ASSERT(s_test_concurrency_smphr); + + char flash_task_name[32]; + test_concurrency_ctx_t ctx = { + .is_pressure_test = false, + .part = part, + }; + + for (int i = 0; i < TEST_FLASH_CONCURRENCY_TASK_NUM / 2; i++) { + int l = snprintf(flash_task_name, 32, "core0_flash_ops_task%d", i); + flash_task_name[l] = '\0'; + xTaskCreatePinnedToCore(&s_test_flash_ops_task, flash_task_name, 4096, (void *)(&ctx), 5, NULL, 0); + } + + for (int i = 0; i < TEST_FLASH_CONCURRENCY_TASK_NUM / 2; i++) { + int l = snprintf(flash_task_name, 32, "core1_flash_ops_task%d", i); + flash_task_name[l] = '\0'; + xTaskCreatePinnedToCore(&s_test_flash_ops_task, flash_task_name, 4096, (void *)(&ctx), 5, NULL, 1); + } + + for (int i = 0; i < TEST_FLASH_CONCURRENCY_TASK_NUM; i++) { + TEST_ASSERT(xSemaphoreTake(s_test_concurrency_smphr, portMAX_DELAY) == pdTRUE); + } + vTaskDelay(1); + vSemaphoreDelete(s_test_concurrency_smphr); +} + + +/** + * esp_flash APIs concurrency pressure test + * This test is for manually test + */ +TEST_CASE("Flash DualCore: Test ESP Flash API Concurrency [Stress]", "[esp_flash][stress][manual][ignore]") +{ + const esp_partition_t* part = get_test_flash_partition(); + + s_test_concurrency_id = 0; + char flash_task_name[32]; + test_concurrency_ctx_t ctx = { + .is_pressure_test = true, + .part = part, + }; + + for (int i = 0; i < TEST_FLASH_CONCURRENCY_TASK_NUM / 2; i++) { + int l = snprintf(flash_task_name, 32, "core0_flash_ops_task%d", i); + flash_task_name[l] = '\0'; + xTaskCreatePinnedToCore(&s_test_flash_ops_task, flash_task_name, 4096, (void *)(&ctx), 5, NULL, 0); + } + + for (int i = 0; i < TEST_FLASH_CONCURRENCY_TASK_NUM / 2; i++) { + int l = snprintf(flash_task_name, 32, "core1_flash_ops_task%d", i); + flash_task_name[l] = '\0'; + xTaskCreatePinnedToCore(&s_test_flash_ops_task, flash_task_name, 4096, (void *)(&ctx), 5, NULL, 1); + } + + while(1); +} +#endif //#if !CONFIG_FREERTOS_UNICORE diff --git a/components/spi_flash/test_apps/esp_flash_stress/partitions.csv b/components/spi_flash/test_apps/esp_flash_stress/partitions.csv new file mode 100644 index 0000000000..f236288aad --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_stress/partitions.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 1M, +flash_test, data, fat, , 512K, diff --git a/components/spi_flash/test_apps/esp_flash_stress/pytest_esp_flash_stress.py b/components/spi_flash/test_apps/esp_flash_stress/pytest_esp_flash_stress.py new file mode 100644 index 0000000000..f5554b70a5 --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_stress/pytest_esp_flash_stress.py @@ -0,0 +1,65 @@ +# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +import pytest +from pytest_embedded import Dut + + +@pytest.mark.supported_targets +@pytest.mark.generic +@pytest.mark.parametrize( + 'config', + [ + 'release', + ], + indirect=True, +) +def test_esp_flash_stress(dut: Dut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('![ignore]') + dut.expect_unity_test_output(timeout=600) + + +@pytest.mark.esp32s3 +@pytest.mark.MSPI_F8R8 +@pytest.mark.parametrize( + 'config', + [ + 'esp32s3_f8r8', + ], + indirect=True, +) +def test_esp_flash_stress_f8r8(dut: Dut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('![ignore]') + dut.expect_unity_test_output(timeout=600) + + +@pytest.mark.esp32s3 +@pytest.mark.generic +@pytest.mark.parametrize( + 'config', + [ + 'esp32s3_rom_xip_psram', + ], + indirect=True, +) +def test_esp_flash_stress_rom_xip_psram(dut: Dut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('![ignore]') + dut.expect_unity_test_output(timeout=600) + + +@pytest.mark.esp32c3 +@pytest.mark.flash_suspend +@pytest.mark.parametrize( + 'config', + [ + 'esp32c3_suspend', + ], + indirect=True, +) +def test_flash_auto_suspend(dut: Dut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('![ignore]') + dut.expect_unity_test_output(timeout=600) diff --git a/components/spi_flash/test_apps/esp_flash_stress/sdkconfig.ci.esp32c3_suspend b/components/spi_flash/test_apps/esp_flash_stress/sdkconfig.ci.esp32c3_suspend new file mode 100644 index 0000000000..0addf025a2 --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_stress/sdkconfig.ci.esp32c3_suspend @@ -0,0 +1,4 @@ +# ESP32S3, F8R8, Flash 80M DDR, PSRAM 80M DDR, XIP_PSRAM + +CONFIG_IDF_TARGET="esp32c3" +CONFIG_SPI_FLASH_AUTO_SUSPEND=y diff --git a/components/spi_flash/test_apps/esp_flash_stress/sdkconfig.ci.esp32s3_f8r8 b/components/spi_flash/test_apps/esp_flash_stress/sdkconfig.ci.esp32s3_f8r8 new file mode 100644 index 0000000000..405443be20 --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_stress/sdkconfig.ci.esp32s3_f8r8 @@ -0,0 +1,12 @@ +# ESP32S3, F8R8, Flash 80M DDR, PSRAM 80M DDR, XIP_PSRAM + +CONFIG_IDF_TARGET="esp32s3" +CONFIG_ESPTOOLPY_OCT_FLASH=y +CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y +CONFIG_SPIRAM_RODATA=y diff --git a/components/spi_flash/test_apps/esp_flash_stress/sdkconfig.ci.esp32s3_rom_xip_psram b/components/spi_flash/test_apps/esp_flash_stress/sdkconfig.ci.esp32s3_rom_xip_psram new file mode 100644 index 0000000000..10230eddda --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_stress/sdkconfig.ci.esp32s3_rom_xip_psram @@ -0,0 +1,8 @@ +# ESP32S3, F8R8, ESP Flash ROM Version, XIP_PSRAM + +CONFIG_IDF_TARGET="esp32s3" + +CONFIG_SPIRAM=y +CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y +CONFIG_SPIRAM_RODATA=y +CONFIG_SPI_FLASH_ROM_IMPL=y diff --git a/components/spi_flash/test_apps/esp_flash_stress/sdkconfig.ci.release b/components/spi_flash/test_apps/esp_flash_stress/sdkconfig.ci.release new file mode 100644 index 0000000000..3cff15d49e --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_stress/sdkconfig.ci.release @@ -0,0 +1,3 @@ +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y diff --git a/components/spi_flash/test_apps/esp_flash_stress/sdkconfig.defaults b/components/spi_flash/test_apps/esp_flash_stress/sdkconfig.defaults new file mode 100644 index 0000000000..9dd2f662f8 --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_stress/sdkconfig.defaults @@ -0,0 +1,4 @@ +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"