Skip to content

Commit

Permalink
feat(spi_nand_flash): Add Kconfig option to verify write operations
Browse files Browse the repository at this point in the history
  • Loading branch information
RathiSonika committed Oct 30, 2024
1 parent 6c23b9c commit 58d59c3
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 7 deletions.
10 changes: 10 additions & 0 deletions spi_nand_flash/Kconfig
Original file line number Diff line number Diff line change
@@ -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
16 changes: 16 additions & 0 deletions spi_nand_flash/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
2 changes: 1 addition & 1 deletion spi_nand_flash/idf_component.yml
Original file line number Diff line number Diff line change
@@ -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
Expand Down
8 changes: 8 additions & 0 deletions spi_nand_flash/include/spi_nand_flash.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ 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.
Expand Down
67 changes: 63 additions & 4 deletions spi_nand_flash/src/dhara_glue.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* SPDX-FileContributor: 2015-2023 Espressif Systems (Shanghai) CO LTD
*/

#include <string.h>
#include "dhara/nand.h"
#include "esp_check.h"
#include "esp_err.h"
Expand All @@ -19,6 +20,22 @@

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)
{
esp_err_t ret = ESP_OK;
if (spi_nand_read(handle->config.device_handle, handle->read_buffer, offset, length)) {
ESP_LOGE(TAG, "failed to read nand flash to verify previous write, err: %d", ret);
return ret;
}

if (memcmp(handle->read_buffer, expected_buffer, length)) {
ret = ESP_FAIL;
}
return ret;
}
#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) {
Expand Down Expand Up @@ -83,7 +100,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;
Expand All @@ -98,6 +115,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", 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);
Expand Down Expand Up @@ -135,7 +159,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;

Expand All @@ -154,6 +178,13 @@ 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);
}
#endif //CONFIG_NAND_FLASH_VERIFY_WRITE

return 0;
fail:
ESP_LOGE(TAG, "Error in dhara_nand_prog %d", ret);
Expand Down Expand Up @@ -212,8 +243,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, "");

Expand All @@ -232,8 +266,33 @@ 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);
}
// 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_LOGD(TAG, "dst page %"PRIu32" read, ecc error", 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;
}
18 changes: 16 additions & 2 deletions spi_nand_flash/src/nand.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,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);

Expand All @@ -283,7 +283,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;
Expand All @@ -310,6 +309,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;
Expand Down
22 changes: 22 additions & 0 deletions spi_nand_flash/test_app/main/test_spi_nand_flash.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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, &sector_num));
TEST_ESP_OK(spi_nand_flash_get_sector_size(nand_flash_device_handle, &sector_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, 10, 1);
TEST_ESP_OK(spi_nand_flash_copy_sector(nand_flash_device_handle, 10, 11));
}
deinit_nand_flash(nand_flash_device_handle, spi);
}

0 comments on commit 58d59c3

Please sign in to comment.