From 0402874d3c529d25d70b80d34b8009f20e22d1f5 Mon Sep 17 00:00:00 2001 From: "radek.tandler" Date: Wed, 22 Feb 2023 15:49:47 +0100 Subject: [PATCH] host_test: wl migrated to Cmake and linux emulation of esp_partition - build system changed to CMake - host tests changed to use partition api on linux instead of mocked code - extended wl flash host tests to cover power off recovery code --- .gitlab/ci/host-test.yml | 10 - components/spi_flash/CMakeLists.txt | 2 + .../wear_levelling/.build-test-rules.yml | 4 + components/wear_levelling/crc32.cpp | 4 +- .../wear_levelling/host_test/CMakeLists.txt | 10 + components/wear_levelling/host_test/README.md | 2 + .../host_test/main/CMakeLists.txt | 8 + .../main}/esp_error_check_stub.cpp | 0 .../{test_wl_host => host_test/main}/main.cpp | 0 .../wear_levelling/host_test/main/test_wl.cpp | 428 ++++++++++++++++++ .../partition_table.csv | 0 .../host_test/pytest_wear_levelling_linux.py | 10 + .../host_test/sdkconfig.defaults | 12 + .../test_wl_host/Makefile.files | 1 + .../wear_levelling/test_wl_host/test_wl.cpp | 215 --------- 15 files changed, 479 insertions(+), 227 deletions(-) create mode 100644 components/wear_levelling/host_test/CMakeLists.txt create mode 100644 components/wear_levelling/host_test/README.md create mode 100644 components/wear_levelling/host_test/main/CMakeLists.txt rename components/wear_levelling/{test_wl_host => host_test/main}/esp_error_check_stub.cpp (100%) rename components/wear_levelling/{test_wl_host => host_test/main}/main.cpp (100%) create mode 100644 components/wear_levelling/host_test/main/test_wl.cpp rename components/wear_levelling/{test_wl_host => host_test}/partition_table.csv (100%) create mode 100644 components/wear_levelling/host_test/pytest_wear_levelling_linux.py create mode 100644 components/wear_levelling/host_test/sdkconfig.defaults delete mode 100644 components/wear_levelling/test_wl_host/test_wl.cpp diff --git a/.gitlab/ci/host-test.yml b/.gitlab/ci/host-test.yml index 27f5d07e47..ee451e2f57 100644 --- a/.gitlab/ci/host-test.yml +++ b/.gitlab/ci/host-test.yml @@ -40,16 +40,6 @@ test_partition_table_on_host: - cd components/partition_table/test_gen_esp32part_host - ./gen_esp32part_tests.py -test_wl_on_host: - extends: .host_test_template - artifacts: - paths: - - components/wear_levelling/test_wl_host/coverage_report.zip - expire_in: 1 week - script: - - cd components/wear_levelling/test_wl_host - - make test - test_fatfs_on_host: extends: .host_test_template script: diff --git a/components/spi_flash/CMakeLists.txt b/components/spi_flash/CMakeLists.txt index e6242bd07f..013759a3db 100644 --- a/components/spi_flash/CMakeLists.txt +++ b/components/spi_flash/CMakeLists.txt @@ -1,5 +1,7 @@ idf_build_get_property(target IDF_TARGET) if(${target} STREQUAL "linux") + idf_component_register(INCLUDE_DIRS include + PRIV_INCLUDE_DIRS include/spi_flash) return() endif() diff --git a/components/wear_levelling/.build-test-rules.yml b/components/wear_levelling/.build-test-rules.yml index f596e6c08a..d0aadb1bfa 100644 --- a/components/wear_levelling/.build-test-rules.yml +++ b/components/wear_levelling/.build-test-rules.yml @@ -1,5 +1,9 @@ # Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps +components/wear_levelling/host_test: + enable: + - if: IDF_TARGET == "linux" + reason: only test on linux components/wear_levelling/test_apps: enable: - if: IDF_TARGET in ["esp32", "esp32c3"] diff --git a/components/wear_levelling/crc32.cpp b/components/wear_levelling/crc32.cpp index 5658e14dd9..82a382756f 100644 --- a/components/wear_levelling/crc32.cpp +++ b/components/wear_levelling/crc32.cpp @@ -4,9 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ #include "crc32.h" -#include "esp32/rom/crc.h" +#include "esp_rom_crc.h" unsigned int crc32::crc32_le(unsigned int crc, unsigned char const *buf, unsigned int len) { - return ::crc32_le(crc, buf, len); + return ::esp_rom_crc32_le(crc, buf, len); } diff --git a/components/wear_levelling/host_test/CMakeLists.txt b/components/wear_levelling/host_test/CMakeLists.txt new file mode 100644 index 0000000000..a40e7fc5b6 --- /dev/null +++ b/components/wear_levelling/host_test/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +set(COMPONENTS main) +# Freertos is included via common components. However, CATCH isn't compatible with the FreeRTOS component yet, hence +# using the FreeRTOS mock component. +# target. +list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/freertos/") + +project(wear_levelling_host_test) diff --git a/components/wear_levelling/host_test/README.md b/components/wear_levelling/host_test/README.md new file mode 100644 index 0000000000..37c142df16 --- /dev/null +++ b/components/wear_levelling/host_test/README.md @@ -0,0 +1,2 @@ +| Supported Targets | Linux | +| ----------------- | ----- | diff --git a/components/wear_levelling/host_test/main/CMakeLists.txt b/components/wear_levelling/host_test/main/CMakeLists.txt new file mode 100644 index 0000000000..92fb22c7bd --- /dev/null +++ b/components/wear_levelling/host_test/main/CMakeLists.txt @@ -0,0 +1,8 @@ +idf_component_register(SRCS "main.cpp" + "test_wl.cpp" + INCLUDE_DIRS "$ENV{IDF_PATH}/tools/catch" + PRIV_INCLUDE_DIRS "../../private_include" + "../.." + REQUIRES wear_levelling + WHOLE_ARCHIVE + ) diff --git a/components/wear_levelling/test_wl_host/esp_error_check_stub.cpp b/components/wear_levelling/host_test/main/esp_error_check_stub.cpp similarity index 100% rename from components/wear_levelling/test_wl_host/esp_error_check_stub.cpp rename to components/wear_levelling/host_test/main/esp_error_check_stub.cpp diff --git a/components/wear_levelling/test_wl_host/main.cpp b/components/wear_levelling/host_test/main/main.cpp similarity index 100% rename from components/wear_levelling/test_wl_host/main.cpp rename to components/wear_levelling/host_test/main/main.cpp diff --git a/components/wear_levelling/host_test/main/test_wl.cpp b/components/wear_levelling/host_test/main/test_wl.cpp new file mode 100644 index 0000000000..b197e1be06 --- /dev/null +++ b/components/wear_levelling/host_test/main/test_wl.cpp @@ -0,0 +1,428 @@ +/* + * SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include + +#include "esp_partition.h" +#include "esp_private/partition_linux.h" + +#include "wear_levelling.h" +#include "WL_Flash.h" +#include "crc32.h" + + +#include "catch.hpp" + +#include "sdkconfig.h" + +#include "esp_log.h" +static const char *TAG = "test_wl"; + +// Number of test cycles. Prime number close to 100 +#define TEST_COUNT_MAX 101 + +// Number of erase operations until emulated power off error is raised +// Prime number close to 100 +#define ERASE_CYCLES_TILL_POWER_OFF 97 + +TEST_CASE("write and read back data", "[wear_levelling]") +{ + esp_err_t result; + wl_handle_t wl_handle; + + int flash_handle; + const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage"); + + // Mount wear-levelled partition + result = wl_mount(partition, &wl_handle); + REQUIRE(result == ESP_OK); + + // Get the sector size + uint32_t sector_size = wl_sector_size(wl_handle); + REQUIRE(sector_size == CONFIG_WL_SECTOR_SIZE); + + uint8_t* data = (uint8_t*) malloc(partition->size); + uint8_t* read = (uint8_t*) malloc(partition->size); + + uint32_t sectors = partition->size / sector_size; + + // Generate data + for(uint32_t sector = 0; sector < sectors; sector++) + { + uint32_t sector_address = sector * sector_size; + + for(uint32_t i = 0; i < sector_size / sizeof(i); i++) + { + ((uint32_t*) data)[i] = sector_address + i; + } + } + + // Write data + result = wl_write(wl_handle, 0, data, partition->size); + REQUIRE(result == ESP_OK); + + // Read data + result = wl_read(wl_handle, 0, read, partition->size); + REQUIRE(result == ESP_OK); + + // Verify that written and read data match + REQUIRE(memcmp(data, read, partition->size)); + + // Erase some ranges + result = wl_erase_range(wl_handle, 0, sector_size); + REQUIRE(result == ESP_OK); + result = wl_erase_range(wl_handle, 12288, sector_size * 2); + REQUIRE(result == ESP_OK); + result = wl_erase_range(wl_handle, 28672, sector_size * 3); + REQUIRE(result == ESP_OK); + + // Expected data after erasure + memset(data + 0, 0xFF, sector_size); + memset(data + 12288, 0xFF, sector_size * 2); + memset(data + 28672, 0xFF, sector_size * 3); + + // Read again, with erased ranges + result = wl_read(wl_handle, 0, read, partition->size); + REQUIRE(result == ESP_OK); + + // Verify that written and read data match + REQUIRE(memcmp(data, read, partition->size)); + + // Unmount + result = wl_unmount(wl_handle); + REQUIRE(result == ESP_OK); + + free(data); + free(read); +} + +TEST_CASE("power down test", "[wear_levelling]") +{ + esp_err_t result; + wl_handle_t wl_handle; + + int flash_handle; + const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage"); + + // Disable power down failure counting + esp_partition_fail_after(SIZE_MAX, 0); + + // Mount wear-levelled partition + result = wl_mount(partition, &wl_handle); + REQUIRE(result == ESP_OK); + + // Get wl partition information + size_t sector_size = wl_sector_size(wl_handle); + int32_t sectors_count = wl_size(wl_handle) / sector_size; + + uint32_t add_const = 0; + uint32_t *sector_data = new uint32_t[sector_size / sizeof(uint32_t)]; + + // Fill partition with check data + for (int32_t i = 0; i < sectors_count; i++) { + ESP_LOGV(TAG, "%s(%d): wl_erase_range (*, %lu, %zu)", __FUNCTION__, __LINE__, i * sector_size, sector_size); + REQUIRE(wl_erase_range(wl_handle, i * sector_size, sector_size) == ESP_OK); + for (uint32_t m = 0; m < sector_size / sizeof(uint32_t); m++) { + uint32_t temp_data = i * sector_size + add_const + m; + sector_data[m] = temp_data; + } + ESP_LOGV(TAG, "%s(%d): wl_write (*, %lu, *, %zu)", __FUNCTION__, __LINE__, i * sector_size, sector_size); + REQUIRE(wl_write(wl_handle, i * sector_size, sector_data, sector_size) == ESP_OK); + } + + for (int32_t i = 0; i < sectors_count; i++) { + ESP_LOGV(TAG, "%s(%d): wl_read (*, %lu, *, %zu)", __FUNCTION__, __LINE__, i * sector_size, sector_size); + result |= wl_read(wl_handle, i * sector_size, sector_data, sector_size); + for (uint32_t m = 0; m < sector_size / sizeof(uint32_t); m++) { + uint32_t temp_data = i * sector_size + add_const + m; + REQUIRE(temp_data == sector_data[m]); + if (temp_data != sector_data[m]) { + printf("Error - read: %08x, expected %08x\n", sector_data[m], temp_data); + } + } + } + + // Perform test + int32_t max_count = ERASE_CYCLES_TILL_POWER_OFF; + int32_t max_check_count = TEST_COUNT_MAX; + + ESP_LOGI(TAG, "%s(%d): max_check_count = %d)", __FUNCTION__, __LINE__, max_check_count); + + for (int32_t k = 0; k < max_check_count; k++) { + + // Enable power down failure after max_count cycles + esp_partition_fail_after(max_count, ESP_PARTITION_FAIL_AFTER_MODE_BOTH); + + int32_t err_sector = -1; + for (int32_t i = 0; i < sectors_count; i++) { + result = ESP_OK; + ESP_LOGV(TAG, "%s(%d): wl_erase_range (*, %lu, %zu)", __FUNCTION__, __LINE__, i * sector_size, sector_size); + result = wl_erase_range(wl_handle, i * sector_size, sector_size); + if (result != ESP_OK) { + err_sector = i; + break; + } + + for (uint32_t m = 0; m < sector_size / sizeof(uint32_t); m++) { + uint32_t temp_data = i * sector_size + add_const + m; + sector_data[m] = temp_data; + } + ESP_LOGV(TAG, "%s(%d): wl_write (*, %lu, *, %zu)", __FUNCTION__, __LINE__, i * sector_size, sector_size); + result = wl_write(wl_handle, i * sector_size, sector_data, sector_size); + if (result != ESP_OK) { + err_sector = i; + break; + } + } + + if (err_sector >= 0) { + max_count++; + } else { + max_count = 0; + } + + // Call unmount, but don't care about the result as the power down failure may be persisting or even arise during the unmount. + // In real power down scenario, this function won't be called, here in the test, we need it to free wl handles in driver. + ESP_LOGV(TAG, "%s(%d): wl_unmount", __FUNCTION__, __LINE__); + wl_unmount(wl_handle); + + // Disable power down failure counting + esp_partition_fail_after(SIZE_MAX, 0); + + ESP_LOGV(TAG, "%s(%d): wl_mount", __FUNCTION__, __LINE__); + result = wl_mount(partition, &wl_handle); + REQUIRE(result == ESP_OK); + + for (int32_t i = 0; i < sectors_count; i++) { + if (i != err_sector) { + ESP_LOGV(TAG, "%s(%d): wl_read (*, %lu, *, %zu)", __FUNCTION__, __LINE__, i * sector_size, sector_size); + result |= wl_read(wl_handle, i * sector_size, sector_data, sector_size); + for (uint32_t m = 0; m < sector_size / sizeof(uint32_t); m++) { + uint32_t temp_data = i * sector_size + add_const + m; + REQUIRE(temp_data == sector_data[m]); + if (temp_data != sector_data[m]) { + printf("Error - read: %08x, expected %08x, m=%i, sector=%i\n", sector_data[m], temp_data, m, i); + } + } + } + } + + if (err_sector != -1) { + ESP_LOGV(TAG, "%s(%d): wl_erase_range (*, %lu, %zu)", __FUNCTION__, __LINE__, err_sector * sector_size, sector_size); + result |= wl_erase_range(wl_handle, err_sector * sector_size, sector_size); + for (uint32_t m = 0; m < sector_size / sizeof(uint32_t); m++) { + uint32_t temp_data = err_sector * sector_size + add_const + m; + sector_data[m] = temp_data; + } + ESP_LOGV(TAG, "%s(%d): wl_write (*, %lu, *, %zu)", __FUNCTION__, __LINE__, err_sector * sector_size, sector_size); + result |= wl_write(wl_handle, err_sector * sector_size, sector_data, sector_size); + } + } + + delete[] sector_data; + + // Unmount + ESP_LOGV(TAG, "%s(%d): wl_unmount", __FUNCTION__, __LINE__); + result = wl_unmount(wl_handle); + REQUIRE(result == ESP_OK); +} + +// Calculates wl status blocks offsets and status block size +void calculate_wl_state_address_info(const esp_partition_t *partition, size_t *offset_state_1, size_t *offset_state_2, size_t *state_size) +{ + // This code follows ::init of WL_Flash.cpp + // and define directives from wear_levelling.cpp + + // get sector size + esp_err_t result; + wl_handle_t wl_handle; + + // Try to mount wear-levelled partition + ESP_LOGD(TAG, "wl_mount"); + result = wl_mount(partition, &wl_handle); + REQUIRE(result == ESP_OK); + + size_t sector_size = wl_sector_size(wl_handle); //SPI_FLASH_SEC_SIZE 4096; + REQUIRE(sector_size == CONFIG_WL_SECTOR_SIZE); + + // Unmount + ESP_LOGD(TAG, "wl_unmount"); + result = wl_unmount(wl_handle); + REQUIRE(result == ESP_OK); + + // rest of parameters + size_t full_mem_size = partition->size; + size_t start_addr = 0; // WL_DEFAULT_START_ADDR 0 + size_t wr_size = 16; // WL_DEFAULT_WRITE_SIZE 16 + size_t cfg_size = 0; + + + *state_size = sector_size; + if (*state_size < (sizeof(wl_state_t) + (full_mem_size / sector_size) * wr_size)) { + *state_size = ((sizeof(wl_state_t) + (full_mem_size / sector_size) * wr_size) + sector_size - 1) / sector_size; + *state_size = *state_size * sector_size; + } + cfg_size = (sizeof(wl_config_t) + sector_size - 1) / sector_size; + cfg_size = cfg_size * sector_size; + + *offset_state_1 = start_addr + full_mem_size - *state_size * 2 - cfg_size; + *offset_state_2 = start_addr + full_mem_size - *state_size * 1 - cfg_size; +} + +#ifndef WL_CFG_CRC_CONST +#define WL_CFG_CRC_CONST UINT32_MAX +#endif // WL_CFG_CRC_CONST + +// calculates crc of wear levelling state block +void calculate_wl_state_crc(WL_State_s *state_ptr) +{ + int check_size = WL_STATE_CRC_LEN_V2; + // Chech CRC and recover state + state_ptr->crc = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)state_ptr, check_size); + } + +TEST_CASE("power down during WL status 1 update", "[wear_levelling]") +{ + // Manipulates wl status block 1 as if it wasn't written correctly due to power down event + // Tries to let such a damaged flash wl_mount (and recover) + + ESP_LOGI(TAG, "power down during WL status 1 update"); + + esp_err_t result; + wl_handle_t wl_handle; + + int flash_handle; + const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage"); + + size_t offset_state_1, offset_state_2, size_state = 0; + + // get offsets of respective status blocks in flash + calculate_wl_state_address_info(partition, &offset_state_1, &offset_state_2, &size_state); + + // allocate temporary buffer for status manipulation + uint8_t* tmp_state = (uint8_t*) malloc(size_state); + + // damage 1st status block + memset(tmp_state, 0xff, size_state); + + ESP_LOGD(TAG, "esp_partition_erase_range offset: %zu size: %zu", offset_state_1, size_state); + result = esp_partition_erase_range(partition, offset_state_1, size_state); + REQUIRE(result == ESP_OK); + ESP_LOGD(TAG, "esp_partition_write offset: %zu size: %zu", offset_state_1, size_state); + result = esp_partition_write(partition, offset_state_1, tmp_state, size_state); + REQUIRE(result == ESP_OK); + + // Try to mount wear-levelled partition + ESP_LOGD(TAG, "wl_mount"); + result = wl_mount(partition, &wl_handle); + REQUIRE(result == ESP_OK); + + // Unmount + ESP_LOGD(TAG, "wl_unmount"); + result = wl_unmount(wl_handle); + REQUIRE(result == ESP_OK); + + free(tmp_state); +} + +TEST_CASE("power down during WL status 2 update", "[wear_levelling]") +{ + // Manipulates wl status block 2 as if it wasn't written correctly due to power down event + // Tries to let such a damaged flash wl_mount (and recover) + + ESP_LOGI(TAG, "power down during WL status 2 update"); + + esp_err_t result; + wl_handle_t wl_handle; + + int flash_handle; + const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage"); + + size_t offset_state_1, offset_state_2, size_state = 0; + + // get offsets of respective status blocks in flash + calculate_wl_state_address_info(partition, &offset_state_1, &offset_state_2, &size_state); + + // allocate temporary buffer for status manipulation + uint8_t* tmp_state = (uint8_t*) malloc(size_state); + + // damage 2nd status block + memset(tmp_state, 0xff, size_state); + + ESP_LOGD(TAG, "esp_partition_erase_range offset: %zu size: %zu", offset_state_2, size_state); + result = esp_partition_erase_range(partition, offset_state_2, size_state); + REQUIRE(result == ESP_OK); + ESP_LOGD(TAG, "esp_partition_write offset: %zu size: %zu", offset_state_2, size_state); + result = esp_partition_write(partition, offset_state_2, tmp_state, size_state); + REQUIRE(result == ESP_OK); + + // Try to mount wear-levelled partition + ESP_LOGD(TAG, "wl_mount"); + result = wl_mount(partition, &wl_handle); + REQUIRE(result == ESP_OK); + + // Unmount + ESP_LOGD(TAG, "wl_unmount"); + result = wl_unmount(wl_handle); + REQUIRE(result == ESP_OK); + + free(tmp_state); +} + +TEST_CASE("power down between WL status 1 and WL status 2 update", "[wear_levelling]") +{ + // Manipulates wl status block 2 and reclaculates its crc just to have two different ones as if it wasn't updates due to power down event + // Tries to let such a damaged flash wl_mount (and recover) + + ESP_LOGI(TAG, "power down between WL status 1 and WL status 2 update"); + + esp_err_t result; + wl_handle_t wl_handle; + + int flash_handle; + const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage"); + + size_t offset_state_1, offset_state_2, size_state = 0; + + // get offsets of respective status blocks in flash + calculate_wl_state_address_info(partition, &offset_state_1, &offset_state_2, &size_state); + + // allocate temporary buffer for status manipulation + uint8_t* tmp_state = (uint8_t*) malloc(size_state); + + // unsync 1st and 2nd block state - change move count in 2nd block and recalculate its crc + + // read actual status2 + ESP_LOGD(TAG, "esp_partition_read offset: %zu size: %zu", offset_state_2, size_state); + result = esp_partition_read(partition, offset_state_2, tmp_state, size_state); + REQUIRE(result == ESP_OK); + + // change move count and recalc crc + WL_State_s *state_ptr = (WL_State_s *) tmp_state; + state_ptr->move_count++; + calculate_wl_state_crc(state_ptr); + + // write back modified status2 + ESP_LOGD(TAG, "esp_partition_erase_range offset: %zu size: %zu", offset_state_2, size_state); + result = esp_partition_erase_range(partition, offset_state_2, size_state); + REQUIRE(result == ESP_OK); + ESP_LOGD(TAG, "esp_partition_write offset: %zu size: %zu", offset_state_2, size_state); + result = esp_partition_write(partition, offset_state_2, tmp_state, size_state); + REQUIRE(result == ESP_OK); + + // Try to mount wear-levelled partition + ESP_LOGD(TAG, "wl_mount"); + result = wl_mount(partition, &wl_handle); + REQUIRE(result == ESP_OK); + + // Unmount + ESP_LOGD(TAG, "wl_unmount"); + result = wl_unmount(wl_handle); + REQUIRE(result == ESP_OK); + + free(tmp_state); +} diff --git a/components/wear_levelling/test_wl_host/partition_table.csv b/components/wear_levelling/host_test/partition_table.csv similarity index 100% rename from components/wear_levelling/test_wl_host/partition_table.csv rename to components/wear_levelling/host_test/partition_table.csv diff --git a/components/wear_levelling/host_test/pytest_wear_levelling_linux.py b/components/wear_levelling/host_test/pytest_wear_levelling_linux.py new file mode 100644 index 0000000000..64e79de405 --- /dev/null +++ b/components/wear_levelling/host_test/pytest_wear_levelling_linux.py @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 +import pytest +from pytest_embedded import Dut + + +@pytest.mark.linux +@pytest.mark.host_test +def test_wear_levelling_linux(dut: Dut) -> None: + dut.expect_exact('All tests passed', timeout=120) diff --git a/components/wear_levelling/host_test/sdkconfig.defaults b/components/wear_levelling/host_test/sdkconfig.defaults new file mode 100644 index 0000000000..39dac6d5a9 --- /dev/null +++ b/components/wear_levelling/host_test/sdkconfig.defaults @@ -0,0 +1,12 @@ +CONFIG_IDF_TARGET="linux" +CONFIG_COMPILER_CXX_EXCEPTIONS=y +CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n +CONFIG_WL_SECTOR_SIZE=4096 +CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partition_table.csv" +CONFIG_ESPTOOLPY_FLASHSIZE="8MB" +CONFIG_SPI_FLASH_USE_LEGACY_IMPL=1 +CONFIG_MMU_PAGE_SIZE=0X10000 +CONFIG_ESP_PARTITION_ENABLE_STATS=y diff --git a/components/wear_levelling/test_wl_host/Makefile.files b/components/wear_levelling/test_wl_host/Makefile.files index 6c6f1b2705..a7d4cfdd5f 100644 --- a/components/wear_levelling/test_wl_host/Makefile.files +++ b/components/wear_levelling/test_wl_host/Makefile.files @@ -2,6 +2,7 @@ SOURCE_FILES := \ $(addprefix ../, \ wear_levelling.cpp \ crc32.cpp \ + ../esp_rom/linux/esp_rom_crc.c \ WL_Flash.cpp \ Partition.cpp \ ) diff --git a/components/wear_levelling/test_wl_host/test_wl.cpp b/components/wear_levelling/test_wl_host/test_wl.cpp deleted file mode 100644 index fcf8ad7c44..0000000000 --- a/components/wear_levelling/test_wl_host/test_wl.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ -#include -#include -#include - -#include "spi_flash_mmap.h" -#include "esp_partition.h" -#include "wear_levelling.h" -#include "WL_Flash.h" -#include "SpiFlash.h" - -#include "catch.hpp" - -#include "sdkconfig.h" - -extern "C" void _spi_flash_init(const char* chip_size, size_t block_size, size_t sector_size, size_t page_size, const char* partition_bin); -extern SpiFlash spiflash; - -#define TEST_COUNT_MAX 100 - -TEST_CASE("write and read back data", "[wear_levelling]") -{ - _spi_flash_init(CONFIG_ESPTOOLPY_FLASHSIZE, CONFIG_WL_SECTOR_SIZE * 16, CONFIG_WL_SECTOR_SIZE, CONFIG_WL_SECTOR_SIZE, "partition_table.bin"); - - esp_err_t result; - wl_handle_t wl_handle; - - int flash_handle; - const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage"); - - // Mount wear-levelled partition - result = wl_mount(partition, &wl_handle); - REQUIRE(result == ESP_OK); - - // Get the sector size - uint32_t sector_size = wl_sector_size(wl_handle); - REQUIRE(sector_size == CONFIG_WL_SECTOR_SIZE); - - uint8_t* data = (uint8_t*) malloc(partition->size); - uint8_t* read = (uint8_t*) malloc(partition->size); - - uint32_t sectors = partition->size / sector_size; - - // Generate data - for(uint32_t sector = 0; sector < sectors; sector++) - { - uint32_t sector_address = sector * sector_size; - - for(uint32_t i = 0; i < sector_size / sizeof(i); i++) - { - ((uint32_t*) data)[i] = sector_address + i; - } - } - - // Write data - result = wl_write(wl_handle, 0, data, partition->size); - REQUIRE(result == ESP_OK); - - // Read data - result = wl_read(wl_handle, 0, read, partition->size); - REQUIRE(result == ESP_OK); - - // Verify that written and read data match - REQUIRE(memcmp(data, read, partition->size)); - - // Erase some ranges - result = wl_erase_range(wl_handle, 0, sector_size); - REQUIRE(result == ESP_OK); - result = wl_erase_range(wl_handle, 12288, sector_size * 2); - REQUIRE(result == ESP_OK); - result = wl_erase_range(wl_handle, 28672, sector_size * 3); - REQUIRE(result == ESP_OK); - - // Expected data after erasure - memset(data + 0, 0xFF, sector_size); - memset(data + 12288, 0xFF, sector_size * 2); - memset(data + 28672, 0xFF, sector_size * 3); - - // Read again, with erased ranges - result = wl_read(wl_handle, 0, read, partition->size); - REQUIRE(result == ESP_OK); - - // Verify that written and read data match - REQUIRE(memcmp(data, read, partition->size)); - - // Unmount - result = wl_unmount(wl_handle); - REQUIRE(result == ESP_OK); - - free(data); - free(read); -} - -TEST_CASE("power down test", "[wear_levelling]") -{ - _spi_flash_init(CONFIG_ESPTOOLPY_FLASHSIZE, CONFIG_WL_SECTOR_SIZE * 16, CONFIG_WL_SECTOR_SIZE, CONFIG_WL_SECTOR_SIZE, "partition_table.bin"); - - esp_err_t result; - wl_handle_t wl_handle; - - int flash_handle; - const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage"); - - // Mount wear-levelled partition - result = wl_mount(partition, &wl_handle); - REQUIRE(result == ESP_OK); - - // Get wl partition information - size_t sector_size = wl_sector_size(wl_handle); - int32_t sectors_count = wl_size(wl_handle) / sector_size; - - uint32_t add_const = 0; - uint32_t *sector_data = new uint32_t[sector_size / sizeof(uint32_t)]; - - // Fill partition with check data - for (int32_t i = 0; i < sectors_count; i++) { - REQUIRE(wl_erase_range(wl_handle, i * sector_size, sector_size) == ESP_OK); - for (uint32_t m = 0; m < sector_size / sizeof(uint32_t); m++) { - uint32_t temp_data = i * sector_size + add_const + m; - sector_data[m] = temp_data; - } - REQUIRE(wl_write(wl_handle, i * sector_size, sector_data, sector_size) == ESP_OK); - } - - for (int32_t i = 0; i < sectors_count; i++) { - result |= wl_read(wl_handle, i * sector_size, sector_data, sector_size); - for (uint32_t m = 0; m < sector_size / sizeof(uint32_t); m++) { - uint32_t temp_data = i * sector_size + add_const + m; - REQUIRE(temp_data == sector_data[m]); - if (temp_data != sector_data[m]) { - printf("Error - read: %08x, expected %08x\n", sector_data[m], temp_data); - } - } - } - - // Perform test - int32_t max_count = 100; - int32_t max_check_count = TEST_COUNT_MAX; - - printf("used_sectors_count=%d\n", max_check_count); - - for (int32_t k = 0; k < max_check_count; k++) { - - spiflash.set_total_erase_cycles_limit(max_count); - - int32_t err_sector = -1; - for (int32_t i = 0; i < sectors_count; i++) { - result = ESP_OK; - result = wl_erase_range(wl_handle, i * sector_size, sector_size); - if (result != ESP_OK) { - err_sector = i; - break; - } - for (uint32_t m = 0; m < sector_size / sizeof(uint32_t); m++) { - uint32_t temp_data = i * sector_size + add_const + m; - sector_data[m] = temp_data; - } - result = wl_write(wl_handle, i * sector_size, sector_data, sector_size); - if (result != ESP_OK) { - err_sector = i; - break; - } - } - - if (err_sector >= 0) { - max_count++; - } else { - max_count = 0; - } - - spiflash.set_total_erase_cycles_limit(0); - - result = wl_unmount(wl_handle); - REQUIRE(result == ESP_OK); - - result = wl_mount(partition, &wl_handle); - REQUIRE(result == ESP_OK); - - for (int32_t i = 0; i < sectors_count; i++) { - if (i != err_sector) { - result |= wl_read(wl_handle, i * sector_size, sector_data, sector_size); - for (uint32_t m = 0; m < sector_size / sizeof(uint32_t); m++) { - uint32_t temp_data = i * sector_size + add_const + m; - REQUIRE(temp_data == sector_data[m]); - if (temp_data != sector_data[m]) { - printf("Error - read: %08x, expected %08x, m=%i, sector=%i\n", sector_data[m], temp_data, m, i); - } - } - } - } - - if (err_sector != -1) { - result |= wl_erase_range(wl_handle, err_sector * sector_size, sector_size); - for (uint32_t m = 0; m < sector_size / sizeof(uint32_t); m++) { - uint32_t temp_data = err_sector * sector_size + add_const + m; - sector_data[m] = temp_data; - } - result |= wl_write(wl_handle, err_sector * sector_size, sector_data, sector_size); - } - - spiflash.reset_total_erase_cycles(); - - printf("[%3.f%%] err_sector=%i\n", (float)k / ((float)max_check_count) * 100.0f, err_sector); - } - - delete[] sector_data; - - // Unmount - result = wl_unmount(wl_handle); - REQUIRE(result == ESP_OK); -}