diff --git a/spi_nand_flash/CMakeLists.txt b/spi_nand_flash/CMakeLists.txt index b7aed7cc4c..db55dcf50b 100644 --- a/spi_nand_flash/CMakeLists.txt +++ b/spi_nand_flash/CMakeLists.txt @@ -5,6 +5,7 @@ set(srcs "src/nand.c" "src/nand_micron.c" "src/nand_impl.c" "src/nand_impl_wrap.c" + "src/nand_diag_api.c" "src/spi_nand_oper.c" "src/dhara_glue.c" "vfs/vfs_fat_spinandflash.c" diff --git a/spi_nand_flash/examples/nand_flash/main/spi_nand_flash_example_main.c b/spi_nand_flash/examples/nand_flash/main/spi_nand_flash_example_main.c index 2d226e8ddd..22b684bc72 100644 --- a/spi_nand_flash/examples/nand_flash/main/spi_nand_flash_example_main.c +++ b/spi_nand_flash/examples/nand_flash/main/spi_nand_flash_example_main.c @@ -41,13 +41,56 @@ static const char *TAG = "example"; // Mount path for the partition const char *base_path = "/nandflash"; -static spi_nand_flash_device_t *example_init_nand_flash(void); +static void example_init_nand_flash(spi_nand_flash_device_t **out_handle, spi_device_handle_t *spi_handle) +{ + const spi_bus_config_t bus_config = { + .mosi_io_num = PIN_MOSI, + .miso_io_num = PIN_MISO, + .sclk_io_num = PIN_CLK, + .quadhd_io_num = PIN_HD, + .quadwp_io_num = PIN_WP, + .max_transfer_sz = 4096 * 2, + }; + + // Initialize the SPI bus + ESP_LOGI(TAG, "DMA CHANNEL: %d", SPI_DMA_CHAN); + ESP_ERROR_CHECK(spi_bus_initialize(HOST_ID, &bus_config, SPI_DMA_CHAN)); + + spi_device_interface_config_t devcfg = { + .clock_speed_hz = EXAMPLE_FLASH_FREQ_KHZ * 1000, + .mode = 0, + .spics_io_num = PIN_CS, + .queue_size = 10, + .flags = SPI_DEVICE_HALFDUPLEX, + }; + + spi_device_handle_t spi; + ESP_ERROR_CHECK(spi_bus_add_device(HOST_ID, &devcfg, &spi)); + + spi_nand_flash_config_t nand_flash_config = { + .device_handle = spi, + }; + spi_nand_flash_device_t *nand_flash_device_handle; + ESP_ERROR_CHECK(spi_nand_flash_init_device(&nand_flash_config, &nand_flash_device_handle)); + + *out_handle = nand_flash_device_handle; + *spi_handle = spi; +} + +static void example_deinit_nand_flash(spi_nand_flash_device_t *flash, spi_device_handle_t spi) +{ + ESP_ERROR_CHECK(spi_nand_flash_deinit_device(flash)); + ESP_ERROR_CHECK(spi_bus_remove_device(spi)); + ESP_ERROR_CHECK(spi_bus_free(HOST_ID)); +} void app_main(void) { esp_err_t ret; // Set up SPI bus and initialize the external SPI Flash chip - spi_nand_flash_device_t *flash = example_init_nand_flash(); + spi_device_handle_t spi; + spi_nand_flash_device_t *flash; + example_init_nand_flash(&flash, &spi); if (flash == NULL) { return; } @@ -109,40 +152,5 @@ void app_main(void) esp_vfs_fat_nand_unmount(base_path, flash); - spi_nand_flash_deinit_device(flash); -} - -static spi_nand_flash_device_t *example_init_nand_flash(void) -{ - const spi_bus_config_t bus_config = { - .mosi_io_num = PIN_MOSI, - .miso_io_num = PIN_MISO, - .sclk_io_num = PIN_CLK, - .quadhd_io_num = PIN_HD, - .quadwp_io_num = PIN_WP, - .max_transfer_sz = 4096 * 2, - }; - - // Initialize the SPI bus - ESP_LOGI(TAG, "DMA CHANNEL: %d", SPI_DMA_CHAN); - ESP_ERROR_CHECK(spi_bus_initialize(HOST_ID, &bus_config, SPI_DMA_CHAN)); - - spi_device_interface_config_t devcfg = { - .clock_speed_hz = EXAMPLE_FLASH_FREQ_KHZ * 1000, - .mode = 0, - .spics_io_num = PIN_CS, - .queue_size = 10, - .flags = SPI_DEVICE_HALFDUPLEX, - }; - - spi_device_handle_t spi; - spi_bus_add_device(HOST_ID, &devcfg, &spi); - - spi_nand_flash_config_t nand_flash_config = { - .device_handle = spi, - }; - spi_nand_flash_device_t *nand_flash_device_handle; - ESP_ERROR_CHECK(spi_nand_flash_init_device(&nand_flash_config, &nand_flash_device_handle)); - - return nand_flash_device_handle; + example_deinit_nand_flash(flash, spi); } diff --git a/spi_nand_flash/examples/nand_flash_debug_app/CMakeLists.txt b/spi_nand_flash/examples/nand_flash_debug_app/CMakeLists.txt new file mode 100644 index 0000000000..dffd9c93d6 --- /dev/null +++ b/spi_nand_flash/examples/nand_flash_debug_app/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(COMPONENTS main) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(nand_flash_debug_app) diff --git a/spi_nand_flash/examples/nand_flash_debug_app/README.md b/spi_nand_flash/examples/nand_flash_debug_app/README.md new file mode 100644 index 0000000000..f3b5ad7072 --- /dev/null +++ b/spi_nand_flash/examples/nand_flash_debug_app/README.md @@ -0,0 +1,51 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | + +# SPI NAND Flash debug example + +This example is designed to gather diagnostic statistics for NAND flash, as outlined below: + +1. Bad block statistics. +2. ECC error statistics. +3. Read-write NAND sector throughput (both at the lower level and through the Dhara library). +4. Verification of NAND write operations using the Kconfig option `CONFIG_NAND_FLASH_VERIFY_WRITE`. + +## How to use example + +To run the example, type the following command: + +```CMake +# CMake +idf.py -p PORT flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example output + +Here is the example's console output: +``` +... +I (353) debug_app: Get bad block statistics: +I (533) nand_diag: +Total number of Blocks: 1024 +Bad Blocks: 1 +Valid Blocks: 1023 + +I (533) debug_app: Read-Write Throughput via Dhara: +I (3423) debug_app: Wrote 2048000 bytes in 2269005 us, avg 902.60 kB/s +I (3423) debug_app: Read 2048000 bytes in 617570 us, avg 3316.22 kB/s + +I (3423) debug_app: Read-Write Throughput at lower level (bypassing Dhara): +I (5913) debug_app: Wrote 2048000 bytes in 1883853 us, avg 1087.13 kB/s +I (5913) debug_app: Read 2048000 bytes in 585556 us, avg 3497.53 kB/s + +I (5913) debug_app: ECC errors statistics: +I (17173) nand_diag: +Total number of ECC errors: 42 +ECC not corrected count: 0 +ECC errors exceeding threshold (4): 0 +... +``` diff --git a/spi_nand_flash/examples/nand_flash_debug_app/main/CMakeLists.txt b/spi_nand_flash/examples/nand_flash_debug_app/main/CMakeLists.txt new file mode 100644 index 0000000000..c86e77b1ab --- /dev/null +++ b/spi_nand_flash/examples/nand_flash_debug_app/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "spi_nand_flash_debug_app_main.c" + INCLUDE_DIRS "." + PRIV_REQUIRES spi_nand_flash esp_timer + ) diff --git a/spi_nand_flash/examples/nand_flash_debug_app/main/idf_component.yml b/spi_nand_flash/examples/nand_flash_debug_app/main/idf_component.yml new file mode 100644 index 0000000000..f44853b4fd --- /dev/null +++ b/spi_nand_flash/examples/nand_flash_debug_app/main/idf_component.yml @@ -0,0 +1,4 @@ +dependencies: + espressif/spi_nand_flash: + version: '*' + override_path: '../../../' diff --git a/spi_nand_flash/examples/nand_flash_debug_app/main/spi_nand_flash_debug_app_main.c b/spi_nand_flash/examples/nand_flash_debug_app/main/spi_nand_flash_debug_app_main.c new file mode 100644 index 0000000000..232647c550 --- /dev/null +++ b/spi_nand_flash/examples/nand_flash_debug_app/main/spi_nand_flash_debug_app_main.c @@ -0,0 +1,187 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include + +#include "esp_system.h" +#include "soc/spi_pins.h" +#include "spi_nand_flash.h" +#include "nand_diag_api.h" +#include "nand_private/nand_impl_wrap.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_timer.h" + +#define EXAMPLE_FLASH_FREQ_KHZ 40000 +#define PATTERN_SEED 0x12345678 + +static const char *TAG = "debug_app"; + +// Pin mapping +// ESP32 (VSPI) +#ifdef CONFIG_IDF_TARGET_ESP32 +#define HOST_ID SPI3_HOST +#define PIN_MOSI SPI3_IOMUX_PIN_NUM_MOSI +#define PIN_MISO SPI3_IOMUX_PIN_NUM_MISO +#define PIN_CLK SPI3_IOMUX_PIN_NUM_CLK +#define PIN_CS SPI3_IOMUX_PIN_NUM_CS +#define PIN_WP SPI3_IOMUX_PIN_NUM_WP +#define PIN_HD SPI3_IOMUX_PIN_NUM_HD +#define SPI_DMA_CHAN SPI_DMA_CH_AUTO +#else // Other chips (SPI2/HSPI) +#define HOST_ID SPI2_HOST +#define PIN_MOSI SPI2_IOMUX_PIN_NUM_MOSI +#define PIN_MISO SPI2_IOMUX_PIN_NUM_MISO +#define PIN_CLK SPI2_IOMUX_PIN_NUM_CLK +#define PIN_CS SPI2_IOMUX_PIN_NUM_CS +#define PIN_WP SPI2_IOMUX_PIN_NUM_WP +#define PIN_HD SPI2_IOMUX_PIN_NUM_HD +#define SPI_DMA_CHAN SPI_DMA_CH_AUTO +#endif + +static void example_init_nand_flash(spi_nand_flash_device_t **out_handle, spi_device_handle_t *spi_handle) +{ + const spi_bus_config_t bus_config = { + .mosi_io_num = PIN_MOSI, + .miso_io_num = PIN_MISO, + .sclk_io_num = PIN_CLK, + .quadhd_io_num = PIN_HD, + .quadwp_io_num = PIN_WP, + .max_transfer_sz = 4096 * 2, + }; + + // Initialize the SPI bus + ESP_LOGI(TAG, "DMA CHANNEL: %d", SPI_DMA_CHAN); + ESP_ERROR_CHECK(spi_bus_initialize(HOST_ID, &bus_config, SPI_DMA_CHAN)); + + spi_device_interface_config_t devcfg = { + .clock_speed_hz = EXAMPLE_FLASH_FREQ_KHZ * 1000, + .mode = 0, + .spics_io_num = PIN_CS, + .queue_size = 10, + .flags = SPI_DEVICE_HALFDUPLEX, + }; + + spi_device_handle_t spi; + ESP_ERROR_CHECK(spi_bus_add_device(HOST_ID, &devcfg, &spi)); + + spi_nand_flash_config_t nand_flash_config = { + .device_handle = spi, + }; + spi_nand_flash_device_t *nand_flash_device_handle; + ESP_ERROR_CHECK(spi_nand_flash_init_device(&nand_flash_config, &nand_flash_device_handle)); + + *out_handle = nand_flash_device_handle; + *spi_handle = spi; +} + +static void example_deinit_nand_flash(spi_nand_flash_device_t *flash, spi_device_handle_t spi) +{ + ESP_ERROR_CHECK(spi_nand_flash_deinit_device(flash)); + ESP_ERROR_CHECK(spi_bus_remove_device(spi)); + ESP_ERROR_CHECK(spi_bus_free(HOST_ID)); +} + +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 esp_err_t read_write_sectors_tp(spi_nand_flash_device_t *flash, uint32_t start_sec, uint16_t sec_count, bool get_raw_tp) +{ + esp_err_t ret = ESP_OK; + uint8_t *temp_buf = NULL; + uint8_t *pattern_buf = NULL; + uint32_t sector_size, sector_num; + + ESP_ERROR_CHECK(spi_nand_flash_get_capacity(flash, §or_num)); + ESP_ERROR_CHECK(spi_nand_flash_get_sector_size(flash, §or_size)); + + ESP_RETURN_ON_FALSE((start_sec + sec_count) < sector_num, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + pattern_buf = (uint8_t *)heap_caps_malloc(sector_size, MALLOC_CAP_DEFAULT); + ESP_RETURN_ON_FALSE(pattern_buf != NULL, ESP_ERR_NO_MEM, TAG, "nomem"); + temp_buf = (uint8_t *)heap_caps_malloc(sector_size, MALLOC_CAP_DEFAULT); + ESP_RETURN_ON_FALSE(temp_buf != NULL, ESP_ERR_NO_MEM, TAG, "nomem"); + + fill_buffer(PATTERN_SEED, pattern_buf, sector_size / sizeof(uint32_t)); + + int64_t read_time = 0; + int64_t write_time = 0; + + for (int i = start_sec; i < (start_sec + sec_count); i++) { + int64_t start = esp_timer_get_time(); + if (get_raw_tp) { + ESP_ERROR_CHECK(nand_wrap_prog(flash, i, pattern_buf)); + } else { + ESP_ERROR_CHECK(spi_nand_flash_write_sector(flash, pattern_buf, i)); + } + write_time += esp_timer_get_time() - start; + + memset((void *)temp_buf, 0x00, sector_size); + + start = esp_timer_get_time(); + if (get_raw_tp) { + ESP_ERROR_CHECK(nand_wrap_read(flash, i, 0, sector_size, temp_buf)); + } else { + ESP_ERROR_CHECK(spi_nand_flash_read_sector(flash, temp_buf, i)); + } + read_time += esp_timer_get_time() - start; + } + free(pattern_buf); + free(temp_buf); + + ESP_LOGI(TAG, "Wrote %" PRIu32 " bytes in %" PRId64 " us, avg %.2f kB/s", sector_size * sec_count, write_time, (float)sector_size * sec_count / write_time * 1000); + ESP_LOGI(TAG, "Read %" PRIu32 " bytes in %" PRId64 " us, avg %.2f kB/s\n", sector_size * sec_count, read_time, (float)sector_size * sec_count / read_time * 1000); + return ret; +} + +void app_main(void) +{ + // Set up SPI bus and initialize the external SPI Flash chip + spi_device_handle_t spi; + spi_nand_flash_device_t *flash; + example_init_nand_flash(&flash, &spi); + if (flash == NULL) { + return; + } + + uint32_t num_blocks; + ESP_ERROR_CHECK(spi_nand_flash_get_block_num(flash, &num_blocks)); + + // Get bad block statistics + uint32_t bad_block_count; + ESP_LOGI(TAG, "Get bad block statistics:"); + ESP_ERROR_CHECK(nand_get_bad_block_stats(flash, &bad_block_count)); + ESP_LOGI(TAG, "\nTotal number of Blocks: %"PRIu32"\nBad Blocks: %"PRIu32"\nValid Blocks: %"PRIu32"\n", + num_blocks, bad_block_count, num_blocks - bad_block_count); + + // Calculate read and write throughput via Dhara + uint32_t start_sec = 1; + uint16_t sector_count = 1000; + bool get_raw_tp = false; + ESP_LOGI(TAG, "Read-Write Throughput via Dhara:"); + ESP_ERROR_CHECK(read_write_sectors_tp(flash, start_sec, sector_count, get_raw_tp)); + + // Calculate read and write throughput via Dhara + start_sec = 1001; + sector_count = 1000; + get_raw_tp = true; + ESP_LOGI(TAG, "Read-Write Throughput at lower level (bypassing Dhara):"); + ESP_ERROR_CHECK(read_write_sectors_tp(flash, start_sec, sector_count, get_raw_tp)); + + // Get ECC error statistics + ESP_LOGI(TAG, "ECC errors statistics:"); + ESP_ERROR_CHECK(nand_get_ecc_stats(flash)); + + example_deinit_nand_flash(flash, spi); +} diff --git a/spi_nand_flash/examples/nand_flash_debug_app/pytest_nand_flash_debug_example.py b/spi_nand_flash/examples/nand_flash_debug_app/pytest_nand_flash_debug_example.py new file mode 100644 index 0000000000..fbd3ef4b81 --- /dev/null +++ b/spi_nand_flash/examples/nand_flash_debug_app/pytest_nand_flash_debug_example.py @@ -0,0 +1,10 @@ +import pytest + + +@pytest.mark.spi_nand_flash +def test_nand_flash_debug_example(dut) -> None: + dut.expect_exact("Get bad block statistics:") + dut.expect_exact("Read-Write Throughput via Dhara:") + dut.expect_exact("Read-Write Throughput at lower level (bypassing Dhara):") + dut.expect_exact("ECC errors statistics:") + dut.expect_exact("Returned from app_main") diff --git a/spi_nand_flash/examples/nand_flash_debug_app/sdkconfig.defaults b/spi_nand_flash/examples/nand_flash_debug_app/sdkconfig.defaults new file mode 100644 index 0000000000..740d22bb4a --- /dev/null +++ b/spi_nand_flash/examples/nand_flash_debug_app/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_NAND_FLASH_VERIFY_WRITE=y +CONFIG_ESP_TASK_WDT_EN=n diff --git a/spi_nand_flash/idf_component.yml b/spi_nand_flash/idf_component.yml index 5601020289..15e4943846 100644 --- a/spi_nand_flash/idf_component.yml +++ b/spi_nand_flash/idf_component.yml @@ -1,4 +1,4 @@ -version: "0.7.0" +version: "0.8.0" description: Driver for accessing SPI NAND Flash url: https://github.com/espressif/idf-extra-components/tree/master/spi_nand_flash issues: https://github.com/espressif/idf-extra-components/issues diff --git a/spi_nand_flash/include/nand_diag_api.h b/spi_nand_flash/include/nand_diag_api.h new file mode 100644 index 0000000000..a82d55bdf4 --- /dev/null +++ b/spi_nand_flash/include/nand_diag_api.h @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_err.h" +#include "spi_nand_flash.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// These API used for diagnostic purpose of SPI NAND Flash + +/** @brief Get bad block statistics for the NAND Flash. + * + * This function scans all the blocks in the NAND Flash and returns the total count of bad blocks. + * + * @param flash The handle to the SPI nand flash chip. + * @param[out] bad_block_count A pointer of where to put the return value + * @return ESP_OK on success, or a flash error code if it fails to get bad block statistics. + */ +esp_err_t nand_get_bad_block_stats(spi_nand_flash_device_t *flash, uint32_t *bad_block_count); + +/** @brief Get ECC error statistics for the NAND Flash. + * + * This function displays the total ECC errors reported, ECC not corrected error count and ECC error count exceeding threshold. + * + * @param flash The handle to the SPI nand flash chip. + * @return ESP_OK on success, or a flash error code if it failed to read the page. + */ +esp_err_t nand_get_ecc_stats(spi_nand_flash_device_t *flash); + +#ifdef __cplusplus +} +#endif diff --git a/spi_nand_flash/include/nand_private/nand_impl_wrap.h b/spi_nand_flash/include/nand_private/nand_impl_wrap.h index 18de8cf621..c34f94e5af 100644 --- a/spi_nand_flash/include/nand_private/nand_impl_wrap.h +++ b/spi_nand_flash/include/nand_private/nand_impl_wrap.h @@ -25,6 +25,7 @@ esp_err_t nand_wrap_prog(spi_nand_flash_device_t *handle, uint32_t p, const uint esp_err_t nand_wrap_is_free(spi_nand_flash_device_t *handle, uint32_t p, bool *is_free_status); esp_err_t nand_wrap_read(spi_nand_flash_device_t *handle, uint32_t p, size_t offset, size_t length, uint8_t *data); esp_err_t nand_wrap_copy(spi_nand_flash_device_t *handle, uint32_t src, uint32_t dst); +esp_err_t nand_wrap_get_ecc_status(spi_nand_flash_device_t *handle, uint32_t page); #ifdef __cplusplus } diff --git a/spi_nand_flash/priv_include/nand_impl.h b/spi_nand_flash/priv_include/nand_impl.h index a6546965e5..2b72fac990 100644 --- a/spi_nand_flash/priv_include/nand_impl.h +++ b/spi_nand_flash/priv_include/nand_impl.h @@ -24,6 +24,7 @@ esp_err_t nand_prog(spi_nand_flash_device_t *handle, uint32_t p, const uint8_t * esp_err_t nand_is_free(spi_nand_flash_device_t *handle, uint32_t p, bool *is_free_status); esp_err_t nand_read(spi_nand_flash_device_t *handle, uint32_t p, size_t offset, size_t length, uint8_t *data); esp_err_t nand_copy(spi_nand_flash_device_t *handle, uint32_t src, uint32_t dst); +esp_err_t nand_get_ecc_status(spi_nand_flash_device_t *handle, uint32_t page); #ifdef __cplusplus } diff --git a/spi_nand_flash/src/nand_diag_api.c b/spi_nand_flash/src/nand_diag_api.c new file mode 100644 index 0000000000..419e1a9f29 --- /dev/null +++ b/spi_nand_flash/src/nand_diag_api.c @@ -0,0 +1,97 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include + +#include "esp_system.h" +#include "spi_nand_flash.h" +#include "nand.h" +#include "nand_private/nand_impl_wrap.h" +#include "esp_log.h" +#include "esp_check.h" + +static const char *TAG = "nand_diag"; + +esp_err_t nand_get_bad_block_stats(spi_nand_flash_device_t *flash, uint32_t *bad_block_count) +{ + esp_err_t ret = ESP_OK; + uint32_t bad_blocks = 0; + uint32_t num_blocks; + spi_nand_flash_get_block_num(flash, &num_blocks); + for (uint32_t blk = 0; blk < num_blocks; blk++) { + bool is_bad = false; + ret = nand_wrap_is_bad(flash, blk, &is_bad); + if (ret == ESP_OK && is_bad) { + bad_blocks++; + ESP_LOGD(TAG, "bad block num=%"PRIu32"", blk); + } else if (ret) { + ESP_LOGE(TAG, "Failed to get bad block status for blk=%"PRIu32"", blk); + return ret; + } + } + *bad_block_count = bad_blocks; + return ret; +} + +static bool is_ecc_exceed_threshold(spi_nand_flash_device_t *handle) +{ + uint8_t min_bits_corrected = 0; + bool ret = false; + if (handle->chip.ecc_data.ecc_corrected_bits_status == STAT_ECC_1_TO_3_BITS_CORRECTED) { + min_bits_corrected = 1; + } else if (handle->chip.ecc_data.ecc_corrected_bits_status == STAT_ECC_4_TO_6_BITS_CORRECTED) { + min_bits_corrected = 4; + } else if (handle->chip.ecc_data.ecc_corrected_bits_status == STAT_ECC_7_8_BITS_CORRECTED) { + min_bits_corrected = 7; + } + + if (min_bits_corrected >= handle->chip.ecc_data.ecc_data_refresh_threshold) { + ret = true; + } + return ret; +} + +esp_err_t nand_get_ecc_stats(spi_nand_flash_device_t *flash) +{ + esp_err_t ret = ESP_OK; + uint32_t sector_size, block_size, num_blocks; + uint32_t ecc_err_total_count = 0; + uint32_t ecc_err_exceeding_threshold_count = 0; + uint32_t ecc_err_not_corrected_count = 0; + + spi_nand_flash_get_sector_size(flash, §or_size); + spi_nand_flash_get_block_size(flash, &block_size); + spi_nand_flash_get_block_num(flash, &num_blocks); + uint32_t pages_per_block = block_size / sector_size; + uint32_t num_pages = num_blocks * pages_per_block; + + bool is_free = true; + for (uint32_t page = 0; page < num_pages; page++) { + ret = nand_wrap_is_free(flash, page, &is_free); + if (!is_free) { + ret = nand_wrap_get_ecc_status(flash, page); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to read ecc error for page=%" PRIu32 "", page); + return ret; + } + if (flash->chip.ecc_data.ecc_corrected_bits_status) { + ecc_err_total_count++; + if (flash->chip.ecc_data.ecc_corrected_bits_status == STAT_ECC_NOT_CORRECTED) { + ecc_err_not_corrected_count++; + ESP_LOGD(TAG, "ecc error not corrected for page=%" PRIu32 "", page); + } else if (is_ecc_exceed_threshold(flash)) { + ecc_err_exceeding_threshold_count++; + } + } + } + } + + ESP_LOGI(TAG, "\nTotal number of ECC errors: %"PRIu32"\nECC not corrected count: %"PRIu32"\nECC errors exceeding threshold (%d): %"PRIu32"\n", + ecc_err_total_count, ecc_err_not_corrected_count, flash->chip.ecc_data.ecc_data_refresh_threshold, ecc_err_exceeding_threshold_count); + return ret; +} diff --git a/spi_nand_flash/src/nand_impl.c b/spi_nand_flash/src/nand_impl.c index bcd3f89a0c..23f5ed243a 100644 --- a/spi_nand_flash/src/nand_impl.c +++ b/spi_nand_flash/src/nand_impl.c @@ -351,3 +351,19 @@ esp_err_t nand_copy(spi_nand_flash_device_t *handle, uint32_t src, uint32_t dst) ESP_LOGE(TAG, "Error in nand_copy %d", ret); return ret; } + +esp_err_t nand_get_ecc_status(spi_nand_flash_device_t *handle, uint32_t page) +{ + esp_err_t ret = ESP_OK; + uint8_t status; + ESP_GOTO_ON_ERROR(read_page_and_wait(handle, page, &status), fail, TAG, ""); + + if (is_ecc_error(handle, status)) { + ESP_LOGD(TAG, "read ecc error, page=%"PRIu32"", page); + } + return ret; + +fail: + ESP_LOGE(TAG, "Error in nand_is_ecc_error %d", ret); + return ret; +} diff --git a/spi_nand_flash/src/nand_impl_wrap.c b/spi_nand_flash/src/nand_impl_wrap.c index 202e3d28e5..6aebe04c27 100644 --- a/spi_nand_flash/src/nand_impl_wrap.c +++ b/spi_nand_flash/src/nand_impl_wrap.c @@ -82,3 +82,12 @@ esp_err_t nand_wrap_copy(spi_nand_flash_device_t *handle, uint32_t src, uint32_t xSemaphoreGive(handle->mutex); return ret; } + +esp_err_t nand_wrap_get_ecc_status(spi_nand_flash_device_t *handle, uint32_t page) +{ + esp_err_t ret = ESP_OK; + xSemaphoreTake(handle->mutex, portMAX_DELAY); + ret = nand_get_ecc_status(handle, page); + xSemaphoreGive(handle->mutex); + return ret; +} diff --git a/spi_nand_flash/test_app/main/test_spi_nand_flash.c b/spi_nand_flash/test_app/main/test_spi_nand_flash.c index b0be05ad64..7856e2be2e 100644 --- a/spi_nand_flash/test_app/main/test_spi_nand_flash.c +++ b/spi_nand_flash/test_app/main/test_spi_nand_flash.c @@ -75,7 +75,7 @@ static void setup_chip(spi_device_handle_t *spi) .flags = SPI_DEVICE_HALFDUPLEX, }; - spi_bus_add_device(HOST_ID, &devcfg, spi); + TEST_ESP_OK(spi_bus_add_device(HOST_ID, &devcfg, spi)); } static void setup_nand_flash(spi_nand_flash_device_t **out_handle, spi_device_handle_t *spi_handle) @@ -87,7 +87,7 @@ static void setup_nand_flash(spi_nand_flash_device_t **out_handle, spi_device_ha .device_handle = spi, }; spi_nand_flash_device_t *device_handle; - ESP_ERROR_CHECK(spi_nand_flash_init_device(&nand_flash_config, &device_handle)); + TEST_ESP_OK(spi_nand_flash_init_device(&nand_flash_config, &device_handle)); *out_handle = device_handle; *spi_handle = spi;