From e7b88a52d1ce8b7b543df0de6dba2568d46198f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20M=C3=BAdry?= Date: Tue, 20 Jun 2023 12:00:26 +0200 Subject: [PATCH] feat(examples): Add storage IO speed benchmark example New example which can benchmark IO speed of SPI flash partition (raw, FATFS, SPIFFS) and SD card (raw, FATFS) connected via SPI and SDMMC. --- .../storage/perf_benchmark/CMakeLists.txt | 6 + examples/storage/perf_benchmark/README.md | 263 ++++++++++++++ .../perf_benchmark/main/CMakeLists.txt | 2 + .../perf_benchmark/main/Kconfig.projbuild | 194 ++++++++++ examples/storage/perf_benchmark/main/main.c | 337 ++++++++++++++++++ examples/storage/perf_benchmark/main/tests.c | 254 +++++++++++++ examples/storage/perf_benchmark/main/tests.h | 25 ++ .../perf_benchmark/partitions_example.csv | 6 + .../pytest_perf_benchmark_example.py | 73 ++++ .../perf_benchmark/sdkconfig.ci.sdmmc_1line | 17 + .../perf_benchmark/sdkconfig.ci.sdmmc_4line | 17 + .../perf_benchmark/sdkconfig.ci.sdspi_1line | 15 + .../perf_benchmark/sdkconfig.ci.spiflash | 18 + .../storage/perf_benchmark/sdkconfig.defaults | 14 + 14 files changed, 1241 insertions(+) create mode 100644 examples/storage/perf_benchmark/CMakeLists.txt create mode 100644 examples/storage/perf_benchmark/README.md create mode 100644 examples/storage/perf_benchmark/main/CMakeLists.txt create mode 100644 examples/storage/perf_benchmark/main/Kconfig.projbuild create mode 100644 examples/storage/perf_benchmark/main/main.c create mode 100644 examples/storage/perf_benchmark/main/tests.c create mode 100644 examples/storage/perf_benchmark/main/tests.h create mode 100644 examples/storage/perf_benchmark/partitions_example.csv create mode 100644 examples/storage/perf_benchmark/pytest_perf_benchmark_example.py create mode 100644 examples/storage/perf_benchmark/sdkconfig.ci.sdmmc_1line create mode 100644 examples/storage/perf_benchmark/sdkconfig.ci.sdmmc_4line create mode 100644 examples/storage/perf_benchmark/sdkconfig.ci.sdspi_1line create mode 100644 examples/storage/perf_benchmark/sdkconfig.ci.spiflash create mode 100644 examples/storage/perf_benchmark/sdkconfig.defaults diff --git a/examples/storage/perf_benchmark/CMakeLists.txt b/examples/storage/perf_benchmark/CMakeLists.txt new file mode 100644 index 0000000000..58789b1e4b --- /dev/null +++ b/examples/storage/perf_benchmark/CMakeLists.txt @@ -0,0 +1,6 @@ +# 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.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(perf_benchmark) diff --git a/examples/storage/perf_benchmark/README.md b/examples/storage/perf_benchmark/README.md new file mode 100644 index 0000000000..f9dd9440fb --- /dev/null +++ b/examples/storage/perf_benchmark/README.md @@ -0,0 +1,263 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | + +# Storage performance benchmark example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +**WARNING: This example will format both your SPI flash "storage" partition and SD card (if used)!** + +This example demonstrates a benchmark of a storage media such as SPI flash memory and SD card. +Only ESP32 and ESP32-S3 targets can use SDMMC mode when connecting to a SD card. + +### Pin assignments for SD card connection + +The GPIO pin numbers used to connect an SD card can be customized using the following: + +- Run `idf.py menuconfig` in the project directory and open "Performance Benchmark Example Configuration" menu. + +However, pins cannot be customized when using ESP32 target in SDMMC mode (`SDMMC_HOST_SLOT_1`). + +This example doesn't utilize card detect (CD) and write protect (WP) signals from SD card slot. + +The table below shows the default pin assignments. + +SD card pin | SPI pin | ESP32 pin | ESP32-S2 | ESP32-S3 | ESP32-H2 | ESP32-C3 and other chips | Notes +------------|---------|---------------|----------|----------|----------|--------------------------|------------- + D0 | MISO | GPIO2 | GPIO37 | GPIO37 | GPIO0 | GPIO6 | 10k pullup + D1 | - | GPIO4 | - | GPIO38 | - | - | not used in 1-line SD mode; 10k pullup in 4-line mode + D2 | - | GPIO12 (MTDI) | - | GPIO33 | - | - | not used in 1-line SD mode; 10k pullup in 4-line mode + D3 | CS | GPIO13 (MTCK) | GPIO34 | GPIO34 | GPIO1 | GPIO1 | not used in 1-line SD mode, but card's D3 pin must have a 10k pullup + CLK | SCK | GPIO14 (MTMS) | GPIO36 | GPIO36 | GPIO4 | GPIO5 | 10k pullup + CMD | MOSI | GPIO15 (MTDO) | GPIO35 | GPIO35 | GPIO5 | GPIO4 | 10k pullup + + ### 4-line and 1-line SD modes + +By default, this example uses 4 line SD mode, utilizing 6 pins: CLK, CMD, D0 - D3. It is possible to use 1-line mode (CLK, CMD, D0) by changing "SD/MMC bus width" in the example configuration menu (see `CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_1`). + +Note that even if card's D3 line is not connected to the ESP chip, it still has to be pulled up, otherwise the card will go into SPI protocol mode. + +### Note about GPIO2 (ESP32 only) + +GPIO2 pin is used as a bootstrapping pin, and should be low to enter UART download mode. One way to do this is to connect GPIO0 and GPIO2 using a jumper, and then the auto-reset circuit on most development boards will pull GPIO2 low along with GPIO0, when entering download mode. + +- Some boards have pulldown and/or LED on GPIO2. LED is usually ok, but pulldown will interfere with D0 signals and must be removed. Check the schematic of your development board for anything connected to GPIO2. + +### Note about GPIO12 (ESP32 only) + +GPIO12 is used as a bootstrapping pin to select output voltage of an internal regulator which powers the flash chip (VDD_SDIO). This pin has an internal pulldown so if left unconnected it will read low at reset (selecting default 3.3V operation). When adding a pullup to this pin for SD card operation, consider the following: + +- For boards which don't use the internal regulator (VDD_SDIO) to power the flash, GPIO12 can be pulled high. +- For boards which use 1.8V flash chip, GPIO12 needs to be pulled high at reset. This is fully compatible with SD card operation. +- On boards which use the internal regulator and a 3.3V flash chip, GPIO12 must be low at reset. This is incompatible with SD card operation. + * In most cases, external pullup can be omitted and an internal pullup can be enabled using a `gpio_pullup_en(GPIO_NUM_12);` call. Most SD cards work fine when an internal pullup on GPIO12 line is enabled. Note that if ESP32 experiences a power-on reset while the SD card is sending data, high level on GPIO12 can be latched into the bootstrapping register, and ESP32 will enter a boot loop until external reset with correct GPIO12 level is applied. + * Another option is to burn the flash voltage selection efuses. This will permanently select 3.3V output voltage for the internal regulator, and GPIO12 will not be used as a bootstrapping pin. Then it is safe to connect a pullup resistor to GPIO12. This option is suggested for production use. + +The following command can be used to program flash voltage selection efuses **to 3.3V**: + +```sh + components/esptool_py/esptool/espefuse.py set_flash_voltage 3.3V +``` + +This command will burn the `XPD_SDIO_TIEH`, `XPD_SDIO_FORCE`, and `XPD_SDIO_REG` efuses. With all three burned to value 1, the internal VDD_SDIO flash voltage regulator is permanently enabled at 3.3V. See the technical reference manual for more details. + +`espefuse.py` has a `--do-not-confirm` option if running from an automated flashing script. + +See [the document about pullup requirements](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/sd_pullup_requirements.html) for more details about pullup support and compatibility of modules and development boards. + +## How to use example + +### Configure + +1. Set the target chip: `idf.py set-target TARGET` (replace TARGET with ESP32 chip series name) + +1. Configure the example (`idf.py menuconfig` -> "Performance Benchmark Example Configuration") wheter you want to: + +- Test internal SPI flash + - Raw access + - FATFS + - SPIFFS + +- Test SD card + - Select SD card interface + - SDSPI + - SDMMC + - SD card test configuration + - Raw access + - FATFS + - Set SD card frequency + - Set GPIO pins + +Some configuration options may be available for ESP32 and ESP32-S3 only. + +### Build and flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT build flash monitor +``` + +(Replace PORT with serial port name.) + +(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 + +``` +I (345) example: Start of the example +I (355) example: Internal flash test +I (355) example: Mountig WL layer... +I (415) example: WL layer mounted +I (505) example: Test with 128kiB file +Wrote 131072 bytes in 320.295ms (0.390MiB/s) to address 0 +I (895) example: WL layer unmounted +I (895) example: Mounting FATFS partition... +W (895) vfs_fat_spiflash: f_mount failed (13) +I (895) vfs_fat_spiflash: Formatting FATFS partition, allocation unit size=16384 +I (1105) vfs_fat_spiflash: Mounting again +I (1105) example: FATFS mounted to /spiflash +I (1205) example: Test with 128kiB file +Wrote 131072 bytes (buffer size 4096B) in 1295.988ms (0.096MiB/s) +Wrote 131072 bytes (buffer size 8192B) in 1277.569ms (0.098MiB/s) +Wrote 131072 bytes (buffer size 16384B) in 1439.405ms (0.087MiB/s) +Read 131072 bytes (buffer size 4096B) in 17.721ms (7.054MiB/s) +Read 131072 bytes (buffer size 8192B) in 16.945ms (7.377MiB/s) +Read 131072 bytes (buffer size 16384B) in 16.596ms (7.532MiB/s) +I (5705) example: Test with 256kiB file +Wrote 262144 bytes (buffer size 4096B) in 2529.380ms (0.099MiB/s) +Wrote 262144 bytes (buffer size 8192B) in 2867.352ms (0.087MiB/s) +Wrote 262144 bytes (buffer size 16384B) in 2879.113ms (0.087MiB/s) +Read 262144 bytes (buffer size 4096B) in 34.809ms (7.182MiB/s) +Read 262144 bytes (buffer size 8192B) in 33.315ms (7.504MiB/s) +Read 262144 bytes (buffer size 16384B) in 32.601ms (7.668MiB/s) +I (14535) example: Test with 512kiB file +Wrote 524288 bytes (buffer size 4096B) in 5119.009ms (0.098MiB/s) +Wrote 524288 bytes (buffer size 8192B) in 5897.010ms (0.085MiB/s) +Wrote 524288 bytes (buffer size 16384B) in 5888.743ms (0.085MiB/s) +Read 524288 bytes (buffer size 4096B) in 68.975ms (7.249MiB/s) +Read 524288 bytes (buffer size 8192B) in 66.063ms (7.569MiB/s) +Read 524288 bytes (buffer size 16384B) in 64.624ms (7.737MiB/s) +I (32295) example: FATFS partition unmounted +I (32295) example: Mounting SPIFFS partition... +W (32295) SPIFFS: mount failed, -10025. formatting... +I (38195) example: SPIFFS mounted to /spiflash +I (41555) example: Partition size: total: 896321, used: 0 +I (41645) example: Test with 128kiB file +Wrote 131072 bytes (buffer size 4096B) in 819.950ms (0.152MiB/s) +Wrote 131072 bytes (buffer size 8192B) in 777.949ms (0.161MiB/s) +Wrote 131072 bytes (buffer size 16384B) in 751.682ms (0.166MiB/s) +Read 131072 bytes (buffer size 4096B) in 101.810ms (1.228MiB/s) +Read 131072 bytes (buffer size 8192B) in 99.518ms (1.256MiB/s) +Read 131072 bytes (buffer size 16384B) in 98.472ms (1.269MiB/s) +I (45335) example: Test with 256kiB file +Wrote 262144 bytes (buffer size 4096B) in 1650.525ms (0.151MiB/s) +Wrote 262144 bytes (buffer size 8192B) in 3220.004ms (0.078MiB/s) +Wrote 262144 bytes (buffer size 16384B) in 7139.803ms (0.035MiB/s) +Read 262144 bytes (buffer size 4096B) in 204.933ms (1.220MiB/s) +Read 262144 bytes (buffer size 8192B) in 200.330ms (1.248MiB/s) +Read 262144 bytes (buffer size 16384B) in 198.129ms (1.262MiB/s) +I (60365) example: Test with 512kiB file +Wrote 524288 bytes (buffer size 4096B) in 13917.135ms (0.036MiB/s) +Wrote 524288 bytes (buffer size 8192B) in 14302.046ms (0.035MiB/s) +Wrote 524288 bytes (buffer size 16384B) in 14411.794ms (0.035MiB/s) +Read 524288 bytes (buffer size 4096B) in 317.944ms (1.573MiB/s) +Read 524288 bytes (buffer size 8192B) in 308.865ms (1.619MiB/s) +Read 524288 bytes (buffer size 16384B) in 304.305ms (1.643MiB/s) +I (108835) example: SPIFFS partition unmounted +I (108835) example: SD card test +I (108845) example: Initializing SD card +I (108845) example: Using SDMMC peripheral +I (108855) example: Mounting SD card - raw access +I (108855) gpio: GPIO[36]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (108865) gpio: GPIO[35]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (108875) gpio: GPIO[37]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (108885) gpio: GPIO[38]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (108895) gpio: GPIO[33]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (108905) gpio: GPIO[34]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +I (108955) gpio: GPIO[34]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (108965) example: SD card mounted - raw access +Name: USD00 +Type: SDHC/SDXC +Speed: 40.00 MHz (limit: 40.00 MHz) +Size: 15080MB +CSD: ver=2, sector_size=512, capacity=30883840 read_bl_len=9 +SSR: bus_width=4 +Wrote 65536 bytes in 25.321ms (2.468MiB/s) to sector 415075 +Read 65536 bytes in 3.809ms (16.409MiB/s) from sector 415075 +Wrote 131072 bytes in 7.415ms (16.858MiB/s) to sector 823598 +Read 131072 bytes in 7.149ms (17.485MiB/s) from sector 823598 +Wrote 196608 bytes in 10.829ms (17.315MiB/s) to sector 180761 +Read 196608 bytes in 10.539ms (17.791MiB/s) from sector 180761 +I (109045) example: SD card unmounted - raw access +I (109045) example: Mounting SD card - FATFS +I (109045) gpio: GPIO[36]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (109055) gpio: GPIO[35]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (109065) gpio: GPIO[37]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (109075) gpio: GPIO[38]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (109085) gpio: GPIO[33]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (109095) gpio: GPIO[34]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +I (109145) gpio: GPIO[34]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +W (109155) vfs_fat_sdmmc: failed to mount card (13) +W (109155) vfs_fat_sdmmc: partitioning card +W (109155) vfs_fat_sdmmc: formatting card, allocation unit size=16384 +W (110525) vfs_fat_sdmmc: mounting again +Name: USD00 +Type: SDHC/SDXC +Speed: 40.00 MHz (limit: 40.00 MHz) +Size: 15080MB +CSD: ver=2, sector_size=512, capacity=30883840 read_bl_len=9 +SSR: bus_width=4 +I (110535) example: SD card mounted - FATFS +I (110715) example: Test with 1MiB file +Wrote 1048576 bytes (buffer size 4096B) in 200.241ms (4.994MiB/s) +Wrote 1048576 bytes (buffer size 8192B) in 129.756ms (7.707MiB/s) +Wrote 1048576 bytes (buffer size 16384B) in 91.955ms (10.875MiB/s) +Wrote 1048576 bytes (buffer size 32768B) in 91.424ms (10.938MiB/s) +Read 1048576 bytes (buffer size 4096B) in 121.086ms (8.259MiB/s) +Read 1048576 bytes (buffer size 8192B) in 87.896ms (11.377MiB/s) +Read 1048576 bytes (buffer size 16384B) in 72.525ms (13.788MiB/s) +Read 1048576 bytes (buffer size 32768B) in 74.664ms (13.393MiB/s) +I (111655) example: Test with 4MiB file +Wrote 4194304 bytes (buffer size 4096B) in 797.167ms (5.018MiB/s) +Wrote 4194304 bytes (buffer size 8192B) in 533.538ms (7.497MiB/s) +Wrote 4194304 bytes (buffer size 16384B) in 530.830ms (7.535MiB/s) +Wrote 4194304 bytes (buffer size 32768B) in 497.953ms (8.033MiB/s) +Read 4194304 bytes (buffer size 4096B) in 473.495ms (8.448MiB/s) +Read 4194304 bytes (buffer size 8192B) in 351.240ms (11.388MiB/s) +Read 4194304 bytes (buffer size 16384B) in 289.767ms (13.804MiB/s) +Read 4194304 bytes (buffer size 32768B) in 287.260ms (13.925MiB/s) +I (115495) example: Test with 16MiB file +Wrote 16777216 bytes (buffer size 4096B) in 4754.141ms (3.365MiB/s) +Wrote 16777216 bytes (buffer size 8192B) in 2290.545ms (6.985MiB/s) +Wrote 16777216 bytes (buffer size 16384B) in 2088.811ms (7.660MiB/s) +Wrote 16777216 bytes (buffer size 32768B) in 2148.595ms (7.447MiB/s) +Read 16777216 bytes (buffer size 4096B) in 1899.620ms (8.423MiB/s) +Read 16777216 bytes (buffer size 8192B) in 1419.345ms (11.273MiB/s) +Read 16777216 bytes (buffer size 16384B) in 1158.909ms (13.806MiB/s) +Read 16777216 bytes (buffer size 32768B) in 1148.426ms (13.932MiB/s) +I (132515) example: SD card unmounted - FATFS +I (132515) example: End of the example +``` + +## Troubleshooting + +### Failure to download the example + +``` +Connecting........_____....._____....._____....._____....._____....._____....._____ + +A fatal error occurred: Failed to connect to Espressif device: Invalid head of packet (0x34) +``` + +Disconnect the SD card D0/MISO line from GPIO2 and try uploading again. Read "Note about GPIO2" above. + +### Card fails to initialize with `sdmmc_init_sd_scr: send_scr (1) returned 0x107` error + +Check connections between the card and the ESP32. For example, if you have disconnected GPIO2 to work around the flashing issue, connect it back and reset the ESP32 (using a button on the development board, or by pressing Ctrl-T Ctrl-R in IDF Monitor). + +### Card fails to initialize with `sdmmc_check_scr: send_scr returned 0xffffffff` error + +Connections between the card and the ESP32 are too long for the frequency used. Try using shorter connections, or try reducing the clock speed of SD interface. diff --git a/examples/storage/perf_benchmark/main/CMakeLists.txt b/examples/storage/perf_benchmark/main/CMakeLists.txt new file mode 100644 index 0000000000..4dbe18314c --- /dev/null +++ b/examples/storage/perf_benchmark/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" "tests.c" + INCLUDE_DIRS ".") diff --git a/examples/storage/perf_benchmark/main/Kconfig.projbuild b/examples/storage/perf_benchmark/main/Kconfig.projbuild new file mode 100644 index 0000000000..6f25458017 --- /dev/null +++ b/examples/storage/perf_benchmark/main/Kconfig.projbuild @@ -0,0 +1,194 @@ +menu "Performance Benchmark Example Configuration" + + config EXAMPLE_USE_MEGABYTES + bool "Use megabytes instead of megabits" + default y + help + If this config item is set, the IO speed will be displayed in megabytes per second + instead of megabits per second. + + config EXAMPLE_TEST_SPIFLASH + bool "Test internal SPI flash" + default y + help + If this config item is set, the internal SPI flash will be tested. + + menu "Internal flash test config" + depends on EXAMPLE_TEST_SPIFLASH + + config EXAMPLE_TEST_SPIFLASH_RAW + bool "Test raw access" + default y + help + If this config item is set, raw access will be tested. + + config EXAMPLE_TEST_SPIFLASH_FATFS + bool "Test FATFS" + default y + help + If this config item is set, FATFS will be tested. + + config EXAMPLE_TEST_SPIFLASH_SPIFFS + bool "Test SPIFFS" + default y + help + If this config item is set, SPIFFS will be tested. + + endmenu # "Internal flash test config" + + config EXAMPLE_TEST_SD_CARD + bool "Test SD card" + default y + help + If this config item is set, the SD card will be tested after it is mounted. + + choice EXAMPLE_SD_CARD_INTERFACE + prompt "SD card interface" + depends on EXAMPLE_TEST_SD_CARD + default EXAMPLE_USE_SDMMC if SOC_SDMMC_HOST_SUPPORTED + default EXAMPLE_USE_SDSPI if !SOC_SDMMC_HOST_SUPPORTED + help + Select the SD card interface. + + if SOC_SDMMC_HOST_SUPPORTED + config EXAMPLE_USE_SDMMC + bool "SDMMC" + endif # SOC_SDMMC_HOST_SUPPORTED + + config EXAMPLE_USE_SDSPI + bool "SDSPI" + endchoice # EXAMPLE_SD_CARD_INTERFACE + + menu "SD card test config" + depends on EXAMPLE_TEST_SD_CARD + + config EXAMPLE_TEST_SD_CARD_RAW + bool "Test raw access" + default y + help + If this config item is set, raw access will be tested. + + config EXAMPLE_TEST_SD_CARD_FATFS + bool "Test FATFS" + default y + help + If this config item is set, FATFS will be tested. + + choice EXAMPLE_SD_CARD_FREQ_PICKER + prompt "SD card frequency" + default EXAMPLE_SD_FREQ_HIGHSPEED if EXAMPLE_USE_SDMMC + default EXAMPLE_SD_FREQ_DEFAULT if EXAMPLE_USE_SDSPI + help + Select the frequency of SD card interface. + + config EXAMPLE_SD_FREQ_PROBING + bool "Probing frequency (400kHz)" + + config EXAMPLE_SD_FREQ_DEFAULT + bool "Default frequency (20MHz)" + + config EXAMPLE_SD_FREQ_HIGHSPEED + bool "High speed frequency (40MHz)" + depends on EXAMPLE_USE_SDMMC + + config EXAMPLE_SD_FREQ_CUSTOM + bool "Custom frequency" + endchoice # EXAMPLE_SD_CARD_FREQ_PICKER + + config EXAMPLE_SD_FREQ_CUSTOM_VAL + int "Custom frequency (kHz)" + default 20000 + depends on EXAMPLE_SD_FREQ_CUSTOM + help + Enter the custom frequency of SD card interface. + + if EXAMPLE_USE_SDMMC + + choice EXAMPLE_SDMMC_BUS_WIDTH + prompt "SD/MMC bus width" + help + Select the bus width of SD or MMC interface. + Note that even if 1 line mode is used, + D3 pin of the SD card must have a pull-up resistor connected. + Otherwise the card may enter SPI mode, + the only way to recover from which is to cycle power to the card. + + config EXAMPLE_SDMMC_BUS_WIDTH_4 + bool "4 lines (D0 - D3)" + + config EXAMPLE_SDMMC_BUS_WIDTH_1 + bool "1 line (D0)" + endchoice # EXAMPLE_SDMMC_BUS_WIDTH + + if SOC_SDMMC_USE_GPIO_MATRIX + + config EXAMPLE_PIN_CMD + int "CMD GPIO number" + default 35 if IDF_TARGET_ESP32S3 + + config EXAMPLE_PIN_CLK + int "CLK GPIO number" + default 36 if IDF_TARGET_ESP32S3 + + config EXAMPLE_PIN_D0 + int "D0 GPIO number" + default 37 if IDF_TARGET_ESP32S3 + + if EXAMPLE_SDMMC_BUS_WIDTH_4 + + config EXAMPLE_PIN_D1 + int "D1 GPIO number" + default 38 if IDF_TARGET_ESP32S3 + + config EXAMPLE_PIN_D2 + int "D2 GPIO number" + default 33 if IDF_TARGET_ESP32S3 + + config EXAMPLE_PIN_D3 + int "D3 GPIO number" + default 34 if IDF_TARGET_ESP32S3 + + endif # EXAMPLE_SDMMC_BUS_WIDTH_4 + + endif # SOC_SDMMC_USE_GPIO_MATRIX + + endif # EXAMPLE_USE_SDMMC + + if EXAMPLE_USE_SDSPI + + config EXAMPLE_PIN_MOSI + int "MOSI GPIO number" + default 15 if IDF_TARGET_ESP32 + default 35 if IDF_TARGET_ESP32S2 + default 35 if IDF_TARGET_ESP32S3 + default 5 if IDF_TARGET_ESP32H2 + default 4 # C3 and others + + config EXAMPLE_PIN_MISO + int "MISO GPIO number" + default 2 if IDF_TARGET_ESP32 + default 37 if IDF_TARGET_ESP32S2 + default 37 if IDF_TARGET_ESP32S3 + default 0 if IDF_TARGET_ESP32H2 + default 6 # C3 and others + + config EXAMPLE_PIN_CLK + int "CLK GPIO number" + default 14 if IDF_TARGET_ESP32 + default 36 if IDF_TARGET_ESP32S2 + default 36 if IDF_TARGET_ESP32S3 + default 4 if IDF_TARGET_ESP32H2 + default 5 # C3 and others + + config EXAMPLE_PIN_CS + int "CS GPIO number" + default 13 if IDF_TARGET_ESP32 + default 34 if IDF_TARGET_ESP32S2 + default 34 if IDF_TARGET_ESP32S3 + default 1 # C3 and others + + endif # EXAMPLE_USE_SDSPI + + endmenu # "SD card test config" + +endmenu # "Performance Monitor Example Configuration" diff --git a/examples/storage/perf_benchmark/main/main.c b/examples/storage/perf_benchmark/main/main.c new file mode 100644 index 0000000000..1fd0409e81 --- /dev/null +++ b/examples/storage/perf_benchmark/main/main.c @@ -0,0 +1,337 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +// This example uses SDMMC or SDSPI peripheral to communicate with SD card. + +#include +#include +#include +#include "esp_log.h" +#include "esp_check.h" +#include "esp_partition.h" +#include "esp_vfs_fat.h" +#include "esp_spiffs.h" +#include "soc/soc_caps.h" +#if SOC_SDMMC_HOST_SUPPORTED +#include "driver/sdmmc_host.h" +#endif +#include "driver/sdspi_host.h" +#include "driver/sdmmc_defs.h" +#include "sdmmc_cmd.h" +#include "wear_levelling.h" + +#include "sdkconfig.h" +#include "tests.h" + +static const char *TAG = "example"; +static const char *flash_partition_label = "storage"; +wl_handle_t s_wl_handle = WL_INVALID_HANDLE; +sdmmc_host_t host_g; +#ifdef CONFIG_EXAMPLE_USE_SDMMC +sdmmc_slot_config_t slot_config_g; +#else // CONFIG_EXAMPLE_USE_SDMMC +sdspi_device_config_t slot_config_g; +#endif // CONFIG_EXAMPLE_USE_SDSPI + +#ifdef CONFIG_EXAMPLE_SD_FREQ_PROBING +#define EXAMPLE_SD_FREQ SDMMC_FREQ_PROBING +#elif CONFIG_EXAMPLE_SD_FREQ_DEFAULT +#define EXAMPLE_SD_FREQ SDMMC_FREQ_DEFAULT +#elif CONFIG_EXAMPLE_SD_FREQ_HIGHSPEED +#define EXAMPLE_SD_FREQ SDMMC_FREQ_HIGHSPEED +#elif CONFIG_EXAMPLE_SD_FREQ_CUSTOM +#define EXAMPLE_SD_FREQ CONFIG_EXAMPLE_SD_FREQ_CUSTOM_VAL +#else +#define EXAMPLE_SD_FREQ SDMMC_FREQ_DEFAULT +#endif + +#ifdef CONFIG_EXAMPLE_USE_SDMMC +void init_sd_config(sdmmc_host_t *out_host, sdmmc_slot_config_t *out_slot_config, int freq_khz); +#else // CONFIG_EXAMPLE_USE_SDMMC +void init_sd_config(sdmmc_host_t *out_host, sdspi_device_config_t *out_slot_config, int freq_khz); +#endif // CONFIG_EXAMPLE_USE_SDSPI +void test_spiflash_raw(void); +void test_spiflash_fatfs(void); +void test_spiflash_spiffs(void); +void test_sd_raw(void); +void test_sd_fatfs(void); + +void app_main(void) +{ +#ifdef CONFIG_EXAMPLE_TEST_SPIFLASH + ESP_LOGI(TAG, "Internal flash test"); + +/* SPI flash - raw access */ +#ifdef CONFIG_EXAMPLE_TEST_SPIFLASH_RAW + test_spiflash_raw(); +#endif // CONFIG_EXAMPLE_TEST_SPIFLASH_RAW + +/* SPI flash - FATFS */ +#ifdef CONFIG_EXAMPLE_TEST_SPIFLASH_FATFS + test_spiflash_fatfs(); +#endif // CONFIG_EXAMPLE_TEST_SPIFLASH_FATFS + +/* SPI flash - SPIFFS */ +#ifdef CONFIG_EXAMPLE_TEST_SPIFLASH_SPIFFS + test_spiflash_spiffs(); +#endif // CONFIG_EXAMPLE_TEST_SPIFLASH_SPIFFS + +#endif // CONFIG_EXAMPLE_TEST_SPIFLASH +#ifdef CONFIG_EXAMPLE_TEST_SD_CARD + ESP_LOGI(TAG, "SD card test"); + + ESP_LOGI(TAG, "Initializing SD card"); + init_sd_config(&host_g, &slot_config_g, EXAMPLE_SD_FREQ); + +/* SD card - raw access */ +#ifdef CONFIG_EXAMPLE_TEST_SD_CARD_RAW + test_sd_raw(); +#endif // CONFIG_EXAMPLE_TEST_SD_CARD_RAW + +/* SD card - FATFS */ +#ifdef CONFIG_EXAMPLE_TEST_SD_CARD_FATFS + test_sd_fatfs(); +#endif // CONFIG_EXAMPLE_TEST_SD_CARD_FATFS + +#endif // CONFIG_EXAMPLE_TEST_SD_CARD +} + +void test_spiflash_raw(void) +{ + esp_err_t ret = ESP_OK; + ESP_LOGI(TAG, "Mountig WL layer..."); + const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, + ESP_PARTITION_SUBTYPE_DATA_UNDEFINED, flash_partition_label); + if (data_partition == NULL) { + ESP_LOGE(TAG, "Could not find partition \"%s\"", flash_partition_label); + ESP_ERROR_CHECK(ESP_ERR_NOT_FOUND); + } + ret = wl_mount(data_partition, &s_wl_handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to mount wear levelling layer, ret = %i", ret); + ESP_ERROR_CHECK(ret); + } + ESP_LOGI(TAG, "WL layer mounted"); + + spiflash_speed_test_raw_run(); + + ret = wl_unmount(s_wl_handle); + ESP_ERROR_CHECK(ret); + ESP_LOGI(TAG, "WL layer unmounted"); +} + +void test_spiflash_fatfs(void) +{ + esp_err_t ret = ESP_OK; + ESP_LOGI(TAG, "Mounting FATFS partition..."); + esp_vfs_fat_sdmmc_mount_config_t mount_config_spiflash = { + .format_if_mount_failed = true, + .max_files = 5, + .allocation_unit_size = 16 * 1024}; + ret = esp_vfs_fat_spiflash_mount_rw_wl(FLASH_BASE_PATH, flash_partition_label, &mount_config_spiflash, &s_wl_handle); + if (ret == ESP_OK) { + ESP_LOGI(TAG, "FATFS mounted to %s", FLASH_BASE_PATH); + } else { + ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(ret)); + ESP_ERROR_CHECK(ret); + } + + spiflash_speed_test_fs_run(); + + // Unregister FATFS and unmount storage partition + ret = esp_vfs_fat_spiflash_unmount_rw_wl(FLASH_BASE_PATH, s_wl_handle); + ESP_ERROR_CHECK(ret); + ESP_LOGI(TAG, "FATFS partition unmounted"); +} + +void test_spiflash_spiffs(void) +{ + esp_err_t ret = ESP_OK; + ESP_LOGI(TAG, "Mounting SPIFFS partition..."); + esp_vfs_spiffs_conf_t conf = { + .base_path = FLASH_BASE_PATH, + .partition_label = flash_partition_label, + .max_files = 5, + .format_if_mount_failed = true}; + + // Use settings defined above to initialize and mount SPIFFS filesystem. + // Note: esp_vfs_spiffs_register is an all-in-one convenience function. + ret = esp_vfs_spiffs_register(&conf); + + if (ret == ESP_OK) { + ESP_LOGI(TAG, "SPIFFS mounted to %s", FLASH_BASE_PATH); + } else { + if (ret == ESP_FAIL) { + ESP_LOGE(TAG, "Failed to mount or format filesystem"); + } else if (ret == ESP_ERR_NOT_FOUND) { + ESP_LOGE(TAG, "Failed to find SPIFFS partition"); + } else { + ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret)); + } + ESP_ERROR_CHECK(ret); + } + + spiflash_speed_test_fs_run(); + + // Unregister SPIFFS and unmount storage partition + ret = esp_vfs_spiffs_unregister(conf.partition_label); + ESP_ERROR_CHECK(ret); + ESP_LOGI(TAG, "SPIFFS partition unmounted"); +} + +void test_sd_raw(void) +{ + esp_err_t ret = ESP_OK; + sdmmc_card_t *card; + + ESP_LOGI(TAG, "Mounting SD card - raw access"); + card = (sdmmc_card_t *)malloc(sizeof(sdmmc_card_t)); + if (card == NULL) { + ESP_LOGE(TAG, "Failed to allocate sdmmc_card_t structure"); + ESP_ERROR_CHECK(ESP_ERR_NO_MEM); + } +// Initialize the interface +#ifdef CONFIG_EXAMPLE_USE_SDMMC + ret = sdmmc_host_init(); + ESP_ERROR_CHECK(ret); + ret = sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config_g); + ESP_ERROR_CHECK(ret); +#elif CONFIG_EXAMPLE_USE_SDSPI // CONFIG_EXAMPLE_USE_SDMMC + int card_handle = -1; + ret = sdspi_host_init_device((const sdspi_device_config_t *)&slot_config_g, &card_handle); + ESP_ERROR_CHECK(ret); +#endif // CONFIG_EXAMPLE_USE_SDSPI + + ret = sdmmc_card_init(&host_g, card); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize SD card (%s)", esp_err_to_name(ret)); + ESP_LOGE(TAG, "If you were using SDMMC and switched to SPI reinsert the SD card or power cycle the board"); + free(card); + ESP_ERROR_CHECK(ret); + } + ESP_LOGI(TAG, "SD card mounted - raw access"); + sdmmc_card_print_info(stdout, card); + + sdcard_speed_test_raw_run(card); + +// Unmount SD card +#ifdef CONFIG_EXAMPLE_USE_SDMMC + sdmmc_host_deinit(); +#else // CONFIG_EXAMPLE_USE_SDMMC + sdspi_host_deinit(); +#endif // CONFIG_EXAMPLE_USE_SDSPI + free(card); + ESP_LOGI(TAG, "SD card unmounted - raw access"); +} + +void test_sd_fatfs(void) +{ + esp_err_t ret = ESP_OK; + sdmmc_card_t *card; + + ESP_LOGI(TAG, "Mounting SD card - FATFS"); + esp_vfs_fat_sdmmc_mount_config_t mount_config_sdcard = { + .format_if_mount_failed = true, + .max_files = 5, + .allocation_unit_size = 16 * 1024}; +#ifdef CONFIG_EXAMPLE_USE_SDMMC + ret = esp_vfs_fat_sdmmc_mount(SD_BASE_PATH, &host_g, &slot_config_g, &mount_config_sdcard, &card); +#else // CONFIG_EXAMPLE_USE_SDMMC + ret = esp_vfs_fat_sdspi_mount(SD_BASE_PATH, &host_g, &slot_config_g, &mount_config_sdcard, &card); +#endif // CONFIG_EXAMPLE_USE_SDSPI + + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize the card (%s). " + "Make sure SD card lines have pull-up resistors in place.", + esp_err_to_name(ret)); + ESP_ERROR_CHECK(ret); + } + sdmmc_card_print_info(stdout, card); + ESP_LOGI(TAG, "SD card mounted - FATFS"); + + sdcard_speed_test_fatfs_run(); + + // Unmount SD card + esp_vfs_fat_sdcard_unmount(SD_BASE_PATH, card); + ESP_LOGI(TAG, "SD card unmounted - FATFS"); +} + +#ifdef CONFIG_EXAMPLE_TEST_SD_CARD +#ifdef CONFIG_EXAMPLE_USE_SDMMC +void init_sd_config(sdmmc_host_t *out_host, sdmmc_slot_config_t *out_slot_config, int freq_khz) { +#else // CONFIG_EXAMPLE_USE_SDMMC +void init_sd_config(sdmmc_host_t *out_host, sdspi_device_config_t *out_slot_config, int freq_khz) { +#endif // CONFIG_EXAMPLE_USE_SDSPI + + // By default, SD card frequency is initialized to SDMMC_FREQ_DEFAULT (20MHz) + // For setting a specific frequency, use host.max_freq_khz + // (range 400kHz - 40MHz for SDMMC, 400kHz - 20MHz for SDSPI) + // Example: for fixed frequency of 10MHz, use host.max_freq_khz = 10000; +#ifdef CONFIG_EXAMPLE_USE_SDMMC + ESP_LOGI(TAG, "Using SDMMC peripheral"); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.max_freq_khz = freq_khz; + + // This initializes the slot without card detect (CD) and write protect (WP) signals. + // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals. + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + + // Set bus width to use: + #ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4 + slot_config.width = 4; + #else + slot_config.width = 1; + #endif + + // On chips where the GPIOs used for SD card can be configured, set them in + // the slot_config structure: + #ifdef CONFIG_SOC_SDMMC_USE_GPIO_MATRIX + slot_config.clk = CONFIG_EXAMPLE_PIN_CLK; + slot_config.cmd = CONFIG_EXAMPLE_PIN_CMD; + slot_config.d0 = CONFIG_EXAMPLE_PIN_D0; + #ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4 + slot_config.d1 = CONFIG_EXAMPLE_PIN_D1; + slot_config.d2 = CONFIG_EXAMPLE_PIN_D2; + slot_config.d3 = CONFIG_EXAMPLE_PIN_D3; + #endif // CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4 + #endif // CONFIG_SOC_SDMMC_USE_GPIO_MATRIX + + // Enable internal pullups on enabled pins. The internal pullups + // are insufficient however, please make sure 10k external pullups are + // connected on the bus. This is for debug / example purpose only. + slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; +#else // CONFIG_EXAMPLE_USE_SDMMC + ESP_LOGI(TAG, "Using SPI peripheral"); + sdmmc_host_t host = SDSPI_HOST_DEFAULT(); + host.max_freq_khz = freq_khz; + + spi_bus_config_t bus_cfg = { + .mosi_io_num = CONFIG_EXAMPLE_PIN_MOSI, + .miso_io_num = CONFIG_EXAMPLE_PIN_MISO, + .sclk_io_num = CONFIG_EXAMPLE_PIN_CLK, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = 4000, + }; + esp_err_t ret = spi_bus_initialize(host.slot, &bus_cfg, SDSPI_DEFAULT_DMA); + ESP_ERROR_CHECK(ret); + + // This initializes the slot without card detect (CD) and write protect (WP) signals. + // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals. + sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT(); + slot_config.gpio_cs = CONFIG_EXAMPLE_PIN_CS; + slot_config.host_id = host.slot; +#endif // CONFIG_EXAMPLE_USE_SDSPI + *out_host = host; + *out_slot_config = slot_config; +} +#endif // CONFIG_EXAMPLE_TEST_SD_CARD diff --git a/examples/storage/perf_benchmark/main/tests.c b/examples/storage/perf_benchmark/main/tests.c new file mode 100644 index 0000000000..e14d149b7d --- /dev/null +++ b/examples/storage/perf_benchmark/main/tests.c @@ -0,0 +1,254 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include +#include +#include +#include +#include "esp_random.h" +#include "driver/sdmmc_types.h" +#include "sdmmc_cmd.h" +#include "wear_levelling.h" + +#include "sdkconfig.h" +#include "tests.h" + +static const char *TAG = "example_tests"; + +void spiflash_speed_test_raw_run(void); +void spiflash_speed_test_fs_run(void); +void sdcard_speed_test_raw_run(sdmmc_card_t *card); +void sdcard_speed_test_fatfs_run(void); + +static void print_bench_output(struct timeval tv_start, struct timeval tv_end, size_t size, size_t start_offset, bool is_write) +{ + bool use_megabytes; +#ifdef CONFIG_EXAMPLE_USE_MEGABYTES + use_megabytes = true; +#else + use_megabytes = false; +#endif // CONFIG_EXAMPLE_USE_MEGABYTES + + float t_s = tv_end.tv_sec - tv_start.tv_sec + 1e-6f * (tv_end.tv_usec - tv_start.tv_usec); + double io_speed = size / (1024.0f * 1024.0f * t_s); + printf("%s %d bytes in %.3fms (%.3f%s) %s address %u\n", + (is_write)?"Wrote":"Read", size, t_s * 1e3, (use_megabytes)?io_speed:io_speed*8.0, + (use_megabytes)?"MiB/s":"Mib/s", (is_write)?"to":"from", start_offset); +} + +static void test_rw_raw_spiflash_speed(wl_handle_t handle, size_t src_dest_addr, void *src_dest_buff, size_t size, bool is_write) +{ + struct timeval tv_start; + gettimeofday(&tv_start, NULL); + + if (is_write) { + wl_write(handle, src_dest_addr, src_dest_buff, size); + } else { + wl_read(handle, src_dest_addr, src_dest_buff, size); + } + + struct timeval tv_end; + gettimeofday(&tv_end, NULL); + print_bench_output(tv_start, tv_end, size, src_dest_addr, is_write); +} + +static void test_rw_raw_sd_speed(sdmmc_card_t *card, char* buf, uint32_t start_sector, size_t sector_count, size_t sector_size, bool is_write) +{ + int file_size = sector_count * sector_size; + struct timeval tv_start; + gettimeofday(&tv_start, NULL); + + if (is_write) { + ESP_ERROR_CHECK(sdmmc_write_sectors(card, buf, start_sector, sector_count)); + } else { + ESP_ERROR_CHECK(sdmmc_read_sectors(card, buf, start_sector, sector_count)); + } + + struct timeval tv_end; + gettimeofday(&tv_end, NULL); + print_bench_output(tv_start, tv_end, file_size, start_sector, is_write); +} + +static void test_rw_fs_speed(const char* filename, void* buf, size_t buf_size, size_t file_size, bool is_write) +{ + bool use_megabytes; +#ifdef CONFIG_EXAMPLE_USE_MEGABYTES + use_megabytes = true; +#else + use_megabytes = false; +#endif // CONFIG_EXAMPLE_USE_MEGABYTES + + const size_t buf_count = file_size / buf_size; + FILE* f = fopen(filename, (is_write) ? "wb" : "rb"); + + struct timeval tv_start; + gettimeofday(&tv_start, NULL); + for (size_t n = 0; n < buf_count; ++n) { + if (is_write) { + write(fileno(f), buf, buf_size); + } else { + if (read(fileno(f), buf, buf_size) != buf_size) { + printf("reading at n=%d, eof=%d", n, feof(f)); + } + } + } + struct timeval tv_end; + gettimeofday(&tv_end, NULL); + fclose(f); + + float t_s = tv_end.tv_sec - tv_start.tv_sec + 1e-6f * (tv_end.tv_usec - tv_start.tv_usec); + double io_speed = file_size / (1024.0f * 1024.0f * t_s); + printf("%s %d bytes (buffer size %dB) in %.3fms (%.3f%s)\n", + (is_write)?"Wrote":"Read", file_size, buf_size, t_s * 1e3, + (use_megabytes)?io_speed:io_speed*8.0, (use_megabytes)?"MiB/s":"Mib/s"); +} + +void spiflash_speed_test_raw_run(void) +{ + const size_t buf_size = 16 * 1024; + uint32_t* buf = (uint32_t*) calloc(1, buf_size); + esp_fill_random(buf, buf_size); + size_t file_size; + + ESP_LOGI(TAG, "Test with 128kiB file"); + file_size = 128 * 1024; + + test_rw_raw_spiflash_speed(s_wl_handle, 0, buf, file_size, true); + + free(buf); +} + +static void spiflash_speed_test(void *buf, size_t buf_size, size_t file_size, bool write) +{ + test_rw_fs_speed(FLASH_BASE_PATH"/test.bin", buf, buf_size, file_size, write); +} + +void spiflash_speed_test_fs_run(void) +{ + const size_t buf_size = 16 * 1024; + uint32_t* buf = (uint32_t*) calloc(1, buf_size); + esp_fill_random(buf, buf_size); + size_t file_size; + + ESP_LOGI(TAG, "Test with 128kiB file"); + file_size = 128 * 1024; + // Write + spiflash_speed_test(buf, 4 * 1024, file_size, true); + spiflash_speed_test(buf, 8 * 1024, file_size, true); + spiflash_speed_test(buf, 16 * 1024, file_size, true); + // Read + spiflash_speed_test(buf, 4 * 1024, file_size, false); + spiflash_speed_test(buf, 8 * 1024, file_size, false); + spiflash_speed_test(buf, 16 * 1024, file_size, false); + + ESP_LOGI(TAG, "Test with 256kiB file"); + file_size *= 2; + // Write + spiflash_speed_test(buf, 4 * 1024, file_size, true); + spiflash_speed_test(buf, 8 * 1024, file_size, true); + spiflash_speed_test(buf, 16 * 1024, file_size, true); + // Read + spiflash_speed_test(buf, 4 * 1024, file_size, false); + spiflash_speed_test(buf, 8 * 1024, file_size, false); + spiflash_speed_test(buf, 16 * 1024, file_size, false); + + ESP_LOGI(TAG, "Test with 512kiB file"); + file_size *= 2; + // Write + spiflash_speed_test(buf, 4 * 1024, file_size, true); + spiflash_speed_test(buf, 8 * 1024, file_size, true); + spiflash_speed_test(buf, 16 * 1024, file_size, true); + // Read + spiflash_speed_test(buf, 4 * 1024, file_size, false); + spiflash_speed_test(buf, 8 * 1024, file_size, false); + spiflash_speed_test(buf, 16 * 1024, file_size, false); + + free(buf); +} + +static void sd_speed_test(void *buf, size_t buf_size, size_t file_size, bool write) +{ + test_rw_fs_speed(SD_BASE_PATH"/test.bin", buf, buf_size, file_size, write); +} + +void sdcard_speed_test_fatfs_run(void) +{ + const size_t buf_size = 32 * 1024; + uint32_t* buf = (uint32_t*) calloc(1, buf_size); + esp_fill_random(buf, buf_size); + size_t file_size; + + ESP_LOGI(TAG, "Test with 1MiB file"); + file_size = 1 * 1024 * 1024; + // Write + sd_speed_test(buf, 4 * 1024, file_size, true); + sd_speed_test(buf, 8 * 1024, file_size, true); + sd_speed_test(buf, 16 * 1024, file_size, true); + sd_speed_test(buf, 32 * 1024, file_size, true); + // Read + sd_speed_test(buf, 4 * 1024, file_size, false); + sd_speed_test(buf, 8 * 1024, file_size, false); + sd_speed_test(buf, 16 * 1024, file_size, false); + sd_speed_test(buf, 32 * 1024, file_size, false); + + ESP_LOGI(TAG, "Test with 4MiB file"); + file_size = 4 * 1024 * 1024; + // Write + sd_speed_test(buf, 4 * 1024, file_size, true); + sd_speed_test(buf, 8 * 1024, file_size, true); + sd_speed_test(buf, 16 * 1024, file_size, true); + sd_speed_test(buf, 32 * 1024, file_size, true); + // Read + sd_speed_test(buf, 4 * 1024, file_size, false); + sd_speed_test(buf, 8 * 1024, file_size, false); + sd_speed_test(buf, 16 * 1024, file_size, false); + sd_speed_test(buf, 32 * 1024, file_size, false); + + ESP_LOGI(TAG, "Test with 16MiB file"); + file_size = 16 * 1024 * 1024; + // Write + sd_speed_test(buf, 4 * 1024, file_size, true); + sd_speed_test(buf, 8 * 1024, file_size, true); + sd_speed_test(buf, 16 * 1024, file_size, true); + sd_speed_test(buf, 32 * 1024, file_size, true); + // Read + sd_speed_test(buf, 4 * 1024, file_size, false); + sd_speed_test(buf, 8 * 1024, file_size, false); + sd_speed_test(buf, 16 * 1024, file_size, false); + sd_speed_test(buf, 32 * 1024, file_size, false); + + free(buf); +} + +static uint32_t sd_random_start_sector(uint32_t sector_size) +{ + // Should be a little less than maximum number of sectors on a 512 MiB card + // (512 Mib - 64 MiB) / sector_size (most probably 512) + uint32_t upper_bound = (536870912 - 67108864) / sector_size; + return (esp_random() % (upper_bound + 1)); +} + +void sdcard_speed_test_raw_run(sdmmc_card_t *card) +{ + size_t sector_count = 128; + uint32_t sector_size = card->csd.sector_size; + char *buf = (char *) calloc(1, sector_count * sector_size); + assert(buf != NULL); + uint32_t start_sector[3] = {sd_random_start_sector(sector_size), sd_random_start_sector(sector_size), sd_random_start_sector(sector_size)}; + for(uint8_t i = 0; i < 3; i++) { + test_rw_raw_sd_speed(card, buf, start_sector[i], sector_count*(i+1), sector_size, true); + test_rw_raw_sd_speed(card, buf, start_sector[i], sector_count*(i+1), sector_size, false); + } + free(buf); +} diff --git a/examples/storage/perf_benchmark/main/tests.h b/examples/storage/perf_benchmark/main/tests.h new file mode 100644 index 0000000000..7d0b5c7709 --- /dev/null +++ b/examples/storage/perf_benchmark/main/tests.h @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#pragma once + +#include "driver/sdmmc_types.h" +#include "wear_levelling.h" + +#define FLASH_BASE_PATH "/spiflash" +#define SD_BASE_PATH "/sdcard" +extern wl_handle_t s_wl_handle; + +void spiflash_speed_test_raw_run(void); +void spiflash_speed_test_fs_run(void); +void sdcard_speed_test_raw_run(sdmmc_card_t *card); +void sdcard_speed_test_fatfs_run(void); diff --git a/examples/storage/perf_benchmark/partitions_example.csv b/examples/storage/perf_benchmark/partitions_example.csv new file mode 100644 index 0000000000..b272a1bd05 --- /dev/null +++ b/examples/storage/perf_benchmark/partitions_example.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 1M, +storage, data, undefined, , 0xF0000, diff --git a/examples/storage/perf_benchmark/pytest_perf_benchmark_example.py b/examples/storage/perf_benchmark/pytest_perf_benchmark_example.py new file mode 100644 index 0000000000..34758fbe96 --- /dev/null +++ b/examples/storage/perf_benchmark/pytest_perf_benchmark_example.py @@ -0,0 +1,73 @@ +# 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.supported_targets +@pytest.mark.generic +@pytest.mark.parametrize( + 'config', + [ + 'spiflash' + ], + indirect=True +) +def test_examples_perf_benchmark_spiflash(dut: Dut) -> None: + # SPI flash + dut.expect('example: Mountig WL layer...', timeout=10) + dut.expect('example: WL layer mounted', timeout=90) + dut.expect('example: WL layer unmounted', timeout=240) # SPI flash has slow write speed + + dut.expect('example: Mounting FATFS partition...', timeout=10) + dut.expect('example: FATFS mounted to', timeout=90) # Increased timeout due to formatting + dut.expect('example: FATFS partition unmounted', timeout=240) # SPI flash has slow write speed + + dut.expect('example: Mounting SPIFFS partition...', timeout=10) + dut.expect('example: SPIFFS mounted to', timeout=90) # Increased timeout due to formatting + dut.expect('example: SPIFFS partition unmounted', timeout=240) # SPI flash has slow write speed + + +@pytest.mark.esp32 +@pytest.mark.sdcard_sdmode +@pytest.mark.parametrize( + 'config', + [ + 'sdmmc_1line', + 'sdmmc_4line', + ], + indirect=True, +) +def test_examples_perf_benchmark_sdcard_sdmmc(dut: Dut) -> None: + # SD card + dut.expect('example: Mounting SD card - raw access', timeout=10) + dut.expect('example: SD card mounted - raw access', timeout=10) + dut.expect('example: SD card unmounted - raw access', timeout=60) + + dut.expect('example: Mounting SD card - FATFS', timeout=30) + dut.expect('example: SD card mounted - FATFS', timeout=120) # Increased timeout due to formatting + dut.expect('example: SD card unmounted - FATFS', timeout=180) + + +@pytest.mark.esp32 +@pytest.mark.esp32s2 +@pytest.mark.esp32c3 +@pytest.mark.sdcard_spimode +@pytest.mark.parametrize( + 'config', + [ + 'sdspi_1line', + ], + indirect=True, +) +def test_examples_perf_benchmark_sdcard_spi(dut: Dut) -> None: + # SD card + dut.expect('example: Mounting SD card - raw access', timeout=10) + dut.expect('example: SD card mounted - raw access', timeout=10) + dut.expect('example: SD card unmounted - raw access', timeout=180) + + dut.expect('example: Mounting SD card - FATFS', timeout=30) + dut.expect('example: SD card mounted - FATFS', timeout=120) # Increased timeout due to formatting + dut.expect('example: SD card unmounted - FATFS', timeout=180) diff --git a/examples/storage/perf_benchmark/sdkconfig.ci.sdmmc_1line b/examples/storage/perf_benchmark/sdkconfig.ci.sdmmc_1line new file mode 100644 index 0000000000..166dbdd03a --- /dev/null +++ b/examples/storage/perf_benchmark/sdkconfig.ci.sdmmc_1line @@ -0,0 +1,17 @@ +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv" +CONFIG_ESP_TASK_WDT_EN=n + +# FATFS +CONFIG_FATFS_VFS_FSTAT_BLKSIZE=4096 + +# SPI flash +CONFIG_EXAMPLE_TEST_SPIFLASH=n + +# SD card +CONFIG_EXAMPLE_TEST_SD_CARD=y +CONFIG_EXAMPLE_USE_SDSPI=n +CONFIG_EXAMPLE_USE_SDMMC=y +CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_1=y +CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4=n diff --git a/examples/storage/perf_benchmark/sdkconfig.ci.sdmmc_4line b/examples/storage/perf_benchmark/sdkconfig.ci.sdmmc_4line new file mode 100644 index 0000000000..b73bcabc2f --- /dev/null +++ b/examples/storage/perf_benchmark/sdkconfig.ci.sdmmc_4line @@ -0,0 +1,17 @@ +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv" +CONFIG_ESP_TASK_WDT_EN=n + +# FATFS +CONFIG_FATFS_VFS_FSTAT_BLKSIZE=4096 + +# SPI flash +CONFIG_EXAMPLE_TEST_SPIFLASH=n + +# SD card +CONFIG_EXAMPLE_TEST_SD_CARD=y +CONFIG_EXAMPLE_USE_SDSPI=n +CONFIG_EXAMPLE_USE_SDMMC=y +CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_1=n +CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4=y diff --git a/examples/storage/perf_benchmark/sdkconfig.ci.sdspi_1line b/examples/storage/perf_benchmark/sdkconfig.ci.sdspi_1line new file mode 100644 index 0000000000..ac60b59671 --- /dev/null +++ b/examples/storage/perf_benchmark/sdkconfig.ci.sdspi_1line @@ -0,0 +1,15 @@ +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv" +CONFIG_ESP_TASK_WDT_EN=n + +# FATFS +CONFIG_FATFS_VFS_FSTAT_BLKSIZE=4096 + +# SPI flash +CONFIG_EXAMPLE_TEST_SPIFLASH=n + +# SD card +CONFIG_EXAMPLE_TEST_SD_CARD=y +CONFIG_EXAMPLE_USE_SDSPI=y +CONFIG_EXAMPLE_USE_SDMMC=n diff --git a/examples/storage/perf_benchmark/sdkconfig.ci.spiflash b/examples/storage/perf_benchmark/sdkconfig.ci.spiflash new file mode 100644 index 0000000000..f3ae318e22 --- /dev/null +++ b/examples/storage/perf_benchmark/sdkconfig.ci.spiflash @@ -0,0 +1,18 @@ +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv" +CONFIG_ESP_TASK_WDT_EN=n + +# FATFS +CONFIG_FATFS_VFS_FSTAT_BLKSIZE=4096 + +# SPI flash +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHMODE="qio" +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M_DEFAULT=y +CONFIG_ESPTOOLPY_FLASHFREQ="80m" +CONFIG_EXAMPLE_TEST_SPIFLASH=y + +# SD card +CONFIG_EXAMPLE_TEST_SD_CARD=n diff --git a/examples/storage/perf_benchmark/sdkconfig.defaults b/examples/storage/perf_benchmark/sdkconfig.defaults new file mode 100644 index 0000000000..bb0c413037 --- /dev/null +++ b/examples/storage/perf_benchmark/sdkconfig.defaults @@ -0,0 +1,14 @@ +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv" +CONFIG_ESP_TASK_WDT_EN=n + +# FATFS +CONFIG_FATFS_VFS_FSTAT_BLKSIZE=4096 + +# SPI flash +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHMODE="qio" +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M_DEFAULT=y +CONFIG_ESPTOOLPY_FLASHFREQ="80m"