spiflash: added esp_flash API concurrency pressure test

This commit is contained in:
Armando 2023-06-25 15:17:40 +08:00
parent 0e6d3aa9cd
commit 267ab08a29
12 changed files with 357 additions and 0 deletions

View File

@ -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)

View File

@ -0,0 +1,2 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |

View File

@ -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)

View File

@ -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();
}

View File

@ -0,0 +1,211 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#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

View File

@ -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,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,
6 flash_test, data, fat, , 512K,

View File

@ -0,0 +1,57 @@
# 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.run_all_single_board_cases(group='esp_flash')
@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.run_all_single_board_cases()
@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.run_all_single_board_cases()
@pytest.mark.esp32c3
@pytest.mark.flash_suspend
@pytest.mark.parametrize(
'config',
[
'esp32c3_suspend',
],
indirect=True,
)
def test_flash_auto_suspend(dut: Dut) -> None:
dut.run_all_single_board_cases()

View File

@ -0,0 +1,4 @@
# ESP32S3, F8R8, Flash 80M DDR, PSRAM 80M DDR, XIP_PSRAM
CONFIG_IDF_TARGET="esp32c3"
CONFIG_SPI_FLASH_AUTO_SUSPEND=y

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,3 @@
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@ -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"