diff --git a/spi_nand_flash/Kconfig b/spi_nand_flash/Kconfig new file mode 100644 index 0000000000..4bfdee2913 --- /dev/null +++ b/spi_nand_flash/Kconfig @@ -0,0 +1,10 @@ +menu "SPI NAND Flash configuration" + + config NAND_FLASH_VERIFY_WRITE + bool "Verify SPI NAND flash writes" + default n + help + If this option is enabled, any time SPI NAND flash is written then the data will be read + back and verified. This can catch hardware problems with SPI NAND flash, or flash which + was not erased before verification. +endmenu diff --git a/spi_nand_flash/README.md b/spi_nand_flash/README.md index 88f68ccd0c..92846e4015 100644 --- a/spi_nand_flash/README.md +++ b/spi_nand_flash/README.md @@ -25,3 +25,19 @@ At present, `spi_nand_flash` component is compatible with the chips produced by * Winbond - W25N01GVxxxG/T/R, W25N512GVxIG/IT, W25N512GWxxR/T, W25N01JWxxxG/T, W25N01JWxxxG/T * Gigadevice - GD5F1GQ5UExxG, GD5F1GQ5RExxG, GD5F2GQ5UExxG, GD5F2GQ5RExxG, GD5F4GQ6UExxG, GD5F4GQ6RExxG, GD5F4GQ6UExxG, GD5F4GQ6RExxG * Alliance - AS5F31G04SND-08LIN, AS5F32G04SND-08LIN, AS5F12G04SND-10LIN, AS5F34G04SND-08LIN, AS5F14G04SND-10LIN, AS5F38G04SND-08LIN, AS5F18G04SND-10LIN +* Micron - MT29F4G01ABAFDWB + +## Troubleshooting + +To verify SPI NAND Flash writes, enable the `NAND_FLASH_VERIFY_WRITE` option in menuconfig. When this option is enabled, every time data is written to the SPI NAND Flash, it will be read back and verified. This helps in identifying hardware issues with the SPI NAND Flash. + +To configure the project for this setting, follow these steps: + +``` +idf.py menuconfig +-> Component config +-> SPI NAND Flash configuration +-> NAND_FLASH_VERIFY_WRITE +``` + +Run `idf.py -p PORT flash monitor` and if the write verification fails, an error log will be printed to the console. diff --git a/spi_nand_flash/idf_component.yml b/spi_nand_flash/idf_component.yml index 068f188442..8c910609f5 100644 --- a/spi_nand_flash/idf_component.yml +++ b/spi_nand_flash/idf_component.yml @@ -1,4 +1,4 @@ -version: "0.4.1" +version: "0.5.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/spi_nand_flash.h b/spi_nand_flash/include/spi_nand_flash.h index cb62434049..becc41c89f 100644 --- a/spi_nand_flash/include/spi_nand_flash.h +++ b/spi_nand_flash/include/spi_nand_flash.h @@ -49,6 +49,15 @@ esp_err_t spi_nand_flash_init_device(spi_nand_flash_config_t *config, spi_nand_f */ esp_err_t spi_nand_flash_read_sector(spi_nand_flash_device_t *handle, uint8_t *buffer, dhara_sector_t sector_id); +/** @brief Copy a sector to another sector from the nand flash. + * + * @param handle The handle to the SPI nand flash chip. + * @param src_sec The source sector id from which data to be copied. + * @param dst_sec The destination sector id to which data should be copied. + * @return ESP_OK on success, or a flash error code if the copy failed. + */ +esp_err_t spi_nand_flash_copy_sector(spi_nand_flash_device_t *handle, dhara_sector_t src_sec, dhara_sector_t dst_sec); + /** @brief Write a sector to the nand flash. * * @param handle The handle to the SPI nand flash chip. diff --git a/spi_nand_flash/src/dhara_glue.c b/spi_nand_flash/src/dhara_glue.c index e75d37705b..f94cfef06c 100644 --- a/spi_nand_flash/src/dhara_glue.c +++ b/spi_nand_flash/src/dhara_glue.c @@ -6,6 +6,7 @@ * SPDX-FileContributor: 2015-2023 Espressif Systems (Shanghai) CO LTD */ +#include #include "dhara/nand.h" #include "esp_check.h" #include "esp_err.h" @@ -19,6 +20,28 @@ static const char *TAG = "dhara_glue"; +#if CONFIG_NAND_FLASH_VERIFY_WRITE +static esp_err_t s_verify_write(spi_nand_flash_device_t *handle, const uint8_t *expected_buffer, uint16_t offset, uint16_t length) +{ + uint8_t *temp_buf = NULL; + temp_buf = heap_caps_malloc(length, MALLOC_CAP_DMA | MALLOC_CAP_8BIT); + ESP_RETURN_ON_FALSE(temp_buf != NULL, ESP_ERR_NO_MEM, TAG, "nomem"); + if (spi_nand_read(handle->config.device_handle, temp_buf, offset, length)) { + ESP_LOGE(TAG, "%s: Failed to read nand flash to verify previous write", __func__); + free(temp_buf); + return ESP_FAIL; + } + + if (memcmp(temp_buf, expected_buffer, length)) { + ESP_LOGE(TAG, "%s: Data mismatch detected. The previously written buffer does not match the read buffer.", __func__); + free(temp_buf); + return ESP_FAIL; + } + free(temp_buf); + return ESP_OK; +} +#endif //CONFIG_NAND_FLASH_VERIFY_WRITE + esp_err_t wait_for_ready(spi_device_handle_t device, uint32_t expected_operation_time_us, uint8_t *status_out) { if (expected_operation_time_us < ROM_WAIT_THRESHOLD_US) { @@ -83,7 +106,7 @@ int dhara_nand_is_bad(const struct dhara_nand *n, dhara_block_t b) void dhara_nand_mark_bad(const struct dhara_nand *n, dhara_block_t b) { spi_nand_flash_device_t *dev = __containerof(n, spi_nand_flash_device_t, dhara_nand); - esp_err_t ret; + esp_err_t ret = ESP_OK; dhara_page_t first_block_page = b * (1 << n->log2_ppb); uint16_t bad_block_indicator = 0; @@ -98,6 +121,13 @@ void dhara_nand_mark_bad(const struct dhara_nand *n, dhara_block_t b) dev->page_size, 2), fail, TAG, ""); ESP_GOTO_ON_ERROR(program_execute_and_wait(dev, first_block_page, NULL), fail, TAG, ""); + +#if CONFIG_NAND_FLASH_VERIFY_WRITE + ret = s_verify_write(dev, (uint8_t *)&bad_block_indicator, dev->page_size, 2); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "%s: mark_bad write verification failed for block=%"PRIu32" and page=%"PRIu32"", __func__, b, first_block_page); + } +#endif //CONFIG_NAND_FLASH_VERIFY_WRITE return; fail: ESP_LOGE(TAG, "Error in dhara_nand_mark_bad %d", ret); @@ -135,7 +165,7 @@ int dhara_nand_prog(const struct dhara_nand *n, dhara_page_t p, const uint8_t *d { ESP_LOGV(TAG, "prog, page=%"PRIu32",", p); spi_nand_flash_device_t *dev = __containerof(n, spi_nand_flash_device_t, dhara_nand); - esp_err_t ret; + esp_err_t ret = ESP_OK; uint8_t status; uint16_t used_marker = 0; @@ -154,6 +184,17 @@ int dhara_nand_prog(const struct dhara_nand *n, dhara_page_t p, const uint8_t *d return -1; } +#if CONFIG_NAND_FLASH_VERIFY_WRITE + ret = s_verify_write(dev, data, 0, dev->page_size); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "%s: prog page=%"PRIu32" write verification failed", __func__, p); + } + ret = s_verify_write(dev, (uint8_t *)&used_marker, dev->page_size + 2, 2); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "%s: prog page=%"PRIu32" used marker write verification failed", __func__, p); + } +#endif //CONFIG_NAND_FLASH_VERIFY_WRITE + return 0; fail: ESP_LOGE(TAG, "Error in dhara_nand_prog %d", ret); @@ -230,8 +271,11 @@ int dhara_nand_copy(const struct dhara_nand *n, dhara_page_t src, dhara_page_t d { ESP_LOGD(TAG, "copy, src=%"PRIu32", dst=%"PRIu32"", src, dst); spi_nand_flash_device_t *dev = __containerof(n, spi_nand_flash_device_t, dhara_nand); - esp_err_t ret; + esp_err_t ret = ESP_OK; uint8_t status; +#if CONFIG_NAND_FLASH_VERIFY_WRITE + uint8_t *temp_buf = NULL; +#endif //CONFIG_NAND_FLASH_VERIFY_WRITE ESP_GOTO_ON_ERROR(read_page_and_wait(dev, src, &status), fail, TAG, ""); @@ -257,8 +301,35 @@ int dhara_nand_copy(const struct dhara_nand *n, dhara_page_t src, dhara_page_t d return -1; } - return 0; +#if CONFIG_NAND_FLASH_VERIFY_WRITE + // First read src page data from cache to temp_buf + temp_buf = heap_caps_malloc(dev->page_size, MALLOC_CAP_DMA | MALLOC_CAP_8BIT); + ESP_RETURN_ON_FALSE(temp_buf != NULL, ESP_ERR_NO_MEM, TAG, "nomem"); + if (spi_nand_read(dev->config.device_handle, temp_buf, 0, dev->page_size)) { + ESP_LOGE(TAG, "%s: Failed to read src_page=%"PRIu32"", __func__, src); + goto fail; + } + // Then read dst page data from nand memory array and load it in cache + ESP_GOTO_ON_ERROR(read_page_and_wait(dev, dst, &status), fail, TAG, ""); + if (is_ecc_error(status)) { + ESP_LOGE(TAG, "%s: dst_page=%"PRIu32" read, ecc error", __func__, dst); + dhara_set_error(err, DHARA_E_ECC); + goto fail; + } + // Check if the data in the src page matches the dst page + ret = s_verify_write(dev, temp_buf, 0, dev->page_size); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "%s: dst_page=%"PRIu32" write verification failed", __func__, dst); + } + + free(temp_buf); +#endif //CONFIG_NAND_FLASH_VERIFY_WRITE + return ret; + fail: +#if CONFIG_NAND_FLASH_VERIFY_WRITE + free(temp_buf); +#endif //CONFIG_NAND_FLASH_VERIFY_WRITE ESP_LOGE(TAG, "Error in dhara_nand_copy %d", ret); return -1; } diff --git a/spi_nand_flash/src/nand.c b/spi_nand_flash/src/nand.c index bf151fe4cb..0fd5cccfa7 100644 --- a/spi_nand_flash/src/nand.c +++ b/spi_nand_flash/src/nand.c @@ -278,7 +278,7 @@ esp_err_t spi_nand_flash_init_device(spi_nand_flash_config_t *config, spi_nand_f esp_err_t spi_nand_erase_chip(spi_nand_flash_device_t *handle) { ESP_LOGW(TAG, "Entire chip is being erased"); - esp_err_t ret; + esp_err_t ret = ESP_OK; xSemaphoreTake(handle->mutex, portMAX_DELAY); @@ -294,7 +294,6 @@ esp_err_t spi_nand_erase_chip(spi_nand_flash_device_t *handle) dhara_map_init(&handle->dhara_map, &handle->dhara_nand, handle->work_buffer, handle->config.gc_factor); dhara_map_clear(&handle->dhara_map); - return ESP_OK; end: xSemaphoreGive(handle->mutex); return ret; @@ -342,6 +341,21 @@ esp_err_t spi_nand_flash_read_sector(spi_nand_flash_device_t *handle, uint8_t *b return ret; } +esp_err_t spi_nand_flash_copy_sector(spi_nand_flash_device_t *handle, dhara_sector_t src_sec, dhara_sector_t dst_sec) +{ + dhara_error_t err; + esp_err_t ret = ESP_OK; + + xSemaphoreTake(handle->mutex, portMAX_DELAY); + + if (dhara_map_copy_sector(&handle->dhara_map, src_sec, dst_sec, &err)) { + ret = ESP_ERR_FLASH_BASE + err; + } + + xSemaphoreGive(handle->mutex); + return ret; +} + esp_err_t spi_nand_flash_write_sector(spi_nand_flash_device_t *handle, const uint8_t *buffer, dhara_sector_t sector_id) { dhara_error_t err; 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 5682378051..06a2232b25 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 @@ -47,6 +47,7 @@ #define PATTERN_SEED 0x12345678 +static void do_single_write_test(spi_nand_flash_device_t *flash, uint32_t start_sec, uint16_t sec_count); static void setup_bus(spi_host_device_t host_id) { spi_bus_config_t spi_bus_cfg = { @@ -104,6 +105,7 @@ TEST_CASE("erase nand flash", "[spi_nand_flash]") spi_device_handle_t spi; setup_nand_flash(&nand_flash_device_handle, &spi); TEST_ESP_OK(spi_nand_erase_chip(nand_flash_device_handle)); + do_single_write_test(nand_flash_device_handle, 1, 1); deinit_nand_flash(nand_flash_device_handle, spi); } @@ -188,3 +190,23 @@ TEST_CASE("write nand flash sectors", "[spi_nand_flash]") deinit_nand_flash(nand_flash_device_handle, spi); } + +TEST_CASE("copy nand flash sectors", "[spi_nand_flash]") +{ + spi_nand_flash_device_t *nand_flash_device_handle; + spi_device_handle_t spi; + setup_nand_flash(&nand_flash_device_handle, &spi); + uint32_t sector_num, sector_size; + uint32_t src_sec = 10; + uint32_t dst_sec = 11; + + TEST_ESP_OK(spi_nand_flash_get_capacity(nand_flash_device_handle, §or_num)); + TEST_ESP_OK(spi_nand_flash_get_sector_size(nand_flash_device_handle, §or_size)); + printf("Number of sectors: %" PRIu32 ", Sector size: %" PRIu32 "\n", sector_num, sector_size); + + if (src_sec < sector_num && dst_sec < sector_num) { + do_single_write_test(nand_flash_device_handle, src_sec, 1); + TEST_ESP_OK(spi_nand_flash_copy_sector(nand_flash_device_handle, src_sec, dst_sec)); + } + deinit_nand_flash(nand_flash_device_handle, spi); +}