Browse Source

Merge branch 'bugfix/erase_with_vtaskdelay' into 'master'

spi_flash: Add vTaskDelay while a long erasing

Closes IDF-1561 and IDFGH-261

See merge request espressif/esp-idf!8211
Angus Gratton 5 years ago
parent
commit
b2417819fd

+ 30 - 0
components/spi_flash/Kconfig

@@ -100,6 +100,36 @@ menu "SPI Flash driver"
             this option, and the lock will be bypassed on SPI1 bus. Otherwise if extra devices
             are needed to attach to SPI1 bus, enable this option.
 
+    config SPI_FLASH_BYPASS_BLOCK_ERASE
+        bool "Bypass a block erase and always do sector erase"
+        default n
+        help
+            Some flash chips can have very high "max" erase times, especially for block erase (32KB or 64KB).
+            This option allows to bypass "block erase" and always do sector erase commands.
+            This will be much slower overall in most cases, but improves latency for other code to run.
+
+    config SPI_FLASH_YIELD_DURING_ERASE
+        bool "Enables yield operation during flash erase"
+        default y
+        help
+            This allows to yield the CPUs between erase commands.
+            Prevents starvation of other tasks.
+
+    config SPI_FLASH_ERASE_YIELD_DURATION_MS
+        int "Duration of erasing to yield CPUs (ms)"
+        depends on SPI_FLASH_YIELD_DURING_ERASE
+        default 20
+        help
+            If a duration of one erase command is large
+            then it will yield CPUs after finishing a current command.
+
+    config SPI_FLASH_ERASE_YIELD_TICKS
+        int "CPU release time (tick)"
+        depends on SPI_FLASH_YIELD_DURING_ERASE
+        default 1
+        help
+            Defines how many ticks will be before returning to continue a erasing.
+
     menu "Auto-detect flash chips"
 
         config SPI_FLASH_SUPPORT_ISSI_CHIP

+ 20 - 3
components/spi_flash/esp_flash_api.c

@@ -23,6 +23,8 @@
 #include "sdkconfig.h"
 #include "esp_heap_caps.h"
 #include "esp_flash_internal.h"
+#include <freertos/task.h>
+#include "esp_timer.h"
 
 static const char TAG[] = "spi_flash";
 
@@ -337,20 +339,27 @@ esp_err_t IRAM_ATTR esp_flash_erase_region(esp_flash_t *chip, uint32_t start, ui
         err = spiflash_end(chip, err);
     }
 
-
+#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE
+    int64_t no_yield_time_us = 0;
+#endif
     while (err == ESP_OK && len >= sector_size) {
+#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE
+        int64_t start_time_us = esp_timer_get_time();
+#endif
         err = spiflash_start(chip);
         if (err != ESP_OK) {
             return err;
         }
 
+#ifndef CONFIG_SPI_FLASH_BYPASS_BLOCK_ERASE
         // If possible erase an entire multi-sector block
         if (block_erase_size > 0 && len >= block_erase_size && (start % block_erase_size) == 0) {
             err = chip->chip_drv->erase_block(chip, start);
             start += block_erase_size;
             len -= block_erase_size;
-        }
-        else {
+        } else
+#endif
+        {
             // Otherwise erase individual sector only
             err = chip->chip_drv->erase_sector(chip, start);
             start += sector_size;
@@ -358,6 +367,14 @@ esp_err_t IRAM_ATTR esp_flash_erase_region(esp_flash_t *chip, uint32_t start, ui
         }
 
         err = spiflash_end(chip, err);
+
+#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE
+        no_yield_time_us += (esp_timer_get_time() - start_time_us);
+        if (no_yield_time_us / 1000 >= CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS) {
+            no_yield_time_us = 0;
+            vTaskDelay(CONFIG_SPI_FLASH_ERASE_YIELD_TICKS);
+        }
+#endif
     }
     return err;
 }

+ 18 - 1
components/spi_flash/flash_ops.c

@@ -44,6 +44,7 @@
 #include "cache_utils.h"
 #include "esp_flash.h"
 #include "esp_attr.h"
+#include "esp_timer.h"
 
 esp_rom_spiflash_result_t IRAM_ATTR spi_flash_write_encrypted_chip(size_t dest_addr, const void *src, size_t size);
 
@@ -238,18 +239,34 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(size_t start_addr, size_t size)
     esp_rom_spiflash_result_t rc;
     rc = spi_flash_unlock();
     if (rc == ESP_ROM_SPIFLASH_RESULT_OK) {
+#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE
+        int64_t no_yield_time_us = 0;
+#endif
         for (size_t sector = start; sector != end && rc == ESP_ROM_SPIFLASH_RESULT_OK; ) {
+#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE
+            int64_t start_time_us = esp_timer_get_time();
+#endif
             spi_flash_guard_start();
+#ifndef CONFIG_SPI_FLASH_BYPASS_BLOCK_ERASE
             if (sector % sectors_per_block == 0 && end - sector >= sectors_per_block) {
                 rc = esp_rom_spiflash_erase_block(sector / sectors_per_block);
                 sector += sectors_per_block;
                 COUNTER_ADD_BYTES(erase, sectors_per_block * SPI_FLASH_SEC_SIZE);
-            } else {
+            } else
+#endif
+            {
                 rc = esp_rom_spiflash_erase_sector(sector);
                 ++sector;
                 COUNTER_ADD_BYTES(erase, SPI_FLASH_SEC_SIZE);
             }
             spi_flash_guard_end();
+#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE
+            no_yield_time_us += (esp_timer_get_time() - start_time_us);
+            if (no_yield_time_us / 1000 >= CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS) {
+                no_yield_time_us = 0;
+                vTaskDelay(CONFIG_SPI_FLASH_ERASE_YIELD_TICKS);
+            }
+#endif
         }
     }
     COUNTER_STOP(erase);

+ 2 - 0
components/spi_flash/sim/Makefile.files

@@ -17,6 +17,7 @@ INCLUDE_DIRS := \
 	app_update/include \
 	driver/include \
 	esp32/include \
+	esp_timer/include \
 	freertos/include \
 	log/include \
 	newlib/include \
@@ -32,6 +33,7 @@ INCLUDE_DIRS := \
 	soc/include \
 	soc/soc/include \
 	esp32/include \
+	esp_timer/include \
 	bootloader_support/include \
 	app_update/include \
 	spi_flash/include \

+ 3 - 0
components/spi_flash/sim/stubs/Makefile.files

@@ -4,6 +4,7 @@ SOURCE_FILES := \
 	newlib/lock.c \
 	esp32/crc.cpp \
 	esp32/esp_random.c \
+	esp_timer/src/esp_timer.c \
 	bootloader_support/src/bootloader_common.c
 
 INCLUDE_DIRS := \
@@ -12,6 +13,7 @@ INCLUDE_DIRS := \
 	app_update/include \
 	driver/include \
 	esp32/include \
+	esp_timer/include \
 	freertos/include \
 	log/include \
 	newlib/include \
@@ -25,6 +27,7 @@ INCLUDE_DIRS := \
 	xtensa/include \
 	xtensa/esp32/include \
 	esp32/include \
+	esp_timer/include \
 	bootloader_support/include \
 	app_update/include \
 	spi_flash/include \

+ 15 - 0
components/spi_flash/sim/stubs/esp_timer/include/esp_timer.h

@@ -0,0 +1,15 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int64_t esp_timer_get_time(void);
+
+#ifdef __cplusplus
+}
+#endif
+

+ 20 - 0
components/spi_flash/sim/stubs/esp_timer/src/esp_timer.c

@@ -0,0 +1,20 @@
+// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "esp_timer.h"
+
+int64_t esp_timer_get_time(void)
+{
+    return 0;
+}