Skip to content

Commit

Permalink
Merge pull request #451 from RathiSonika/feat/spi_nand_flash_diagnost…
Browse files Browse the repository at this point in the history
…ic_app

feat(spi_nand_flash): add diagnostics application for NAND flash
  • Loading branch information
RathiSonika authored Jan 8, 2025
2 parents eef3909 + 3986dd8 commit 9757be1
Show file tree
Hide file tree
Showing 17 changed files with 480 additions and 41 deletions.
1 change: 1 addition & 0 deletions spi_nand_flash/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
}
8 changes: 8 additions & 0 deletions spi_nand_flash/examples/nand_flash_debug_app/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
51 changes: 51 additions & 0 deletions spi_nand_flash/examples/nand_flash_debug_app/README.md
Original file line number Diff line number Diff line change
@@ -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
...
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
idf_component_register(SRCS "spi_nand_flash_debug_app_main.c"
INCLUDE_DIRS "."
PRIV_REQUIRES spi_nand_flash esp_timer
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
dependencies:
espressif/spi_nand_flash:
version: '*'
override_path: '../../../'
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#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, &sector_num));
ESP_ERROR_CHECK(spi_nand_flash_get_sector_size(flash, &sector_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);
}
Original file line number Diff line number Diff line change
@@ -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")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CONFIG_NAND_FLASH_VERIFY_WRITE=y
CONFIG_ESP_TASK_WDT_EN=n
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.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
Expand Down
Loading

0 comments on commit 9757be1

Please sign in to comment.