esp_flash: refactor to support various type of yield

There is a periodically yield in the esp_flash driver, to ensure the
cache will not be disabled for too long on ESP32.

On ESP32-S2 and later, we need to support more different kind of yield:

1. polling conditions, including timeout, SW read request, etc.
2. wait for events, including HW done/error/auto-suspend, timeout
semaphore, etc.

The check_yield() and yield() is separated into two parts, because we
may need to insert suspend, etc. between them.
This commit is contained in:
Michael (XIAO Xufeng)
2020-09-11 18:20:08 +08:00
parent 33e7784806
commit 8ae09194ac
5 changed files with 258 additions and 77 deletions

View File

@@ -15,6 +15,7 @@
#include <stdarg.h>
#include <sys/param.h> //For max/min
#include "esp_attr.h"
#include "esp_private/system_internal.h"
#include "esp_spi_flash.h" //for ``g_flash_guard_default_ops``
#include "esp_flash.h"
#include "esp_flash_partitions.h"
@@ -41,11 +42,29 @@ typedef struct {
spi_bus_lock_dev_handle_t dev_lock;
} app_func_arg_t;
/*
* Time yield algorithm:
* Every time spi_flash_os_check_yield() is called:
*
* 1. If the time since last end() function is longer than CONFIG_SPI_FLASH_ERASE_YIELD_TICKS (time
* to yield), all counters will be reset, as if the yield has just ends;
* 2. If the time since last yield() is longer than CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS, will
* return a yield request. When the yield() is called, all counters will be reset.
* Note: Short intervals between start() and end() after the last yield() will not reset the
* counter mentioned in #2, but still be counted into the time mentioned in #2.
*/
typedef struct {
app_func_arg_t common_arg; //shared args, must be the first item
bool no_protect; //to decide whether to check protected region (for the main chip) or not.
uint32_t acquired_since_us; // Time since last explicit yield()
uint32_t released_since_us; // Time since last end() (implicit yield)
} spi1_app_func_arg_t;
static inline IRAM_ATTR void on_spi1_released(spi1_app_func_arg_t* ctx);
static inline IRAM_ATTR void on_spi1_acquired(spi1_app_func_arg_t* ctx);
static inline IRAM_ATTR void on_spi1_yielded(spi1_app_func_arg_t* ctx);
static inline IRAM_ATTR bool on_spi1_check_yield(spi1_app_func_arg_t* ctx);
IRAM_ATTR static void cache_enable(void* arg)
{
g_flash_guard_default_ops.end();
@@ -82,18 +101,48 @@ static IRAM_ATTR esp_err_t spi1_start(void *arg)
#else
//directly disable the cache and interrupts when lock is not used
cache_disable(NULL);
#endif
on_spi1_acquired((spi1_app_func_arg_t*)arg);
return ESP_OK;
#endif
}
static IRAM_ATTR esp_err_t spi1_end(void *arg)
{
esp_err_t ret = ESP_OK;
#if CONFIG_SPI_FLASH_SHARE_SPI1_BUS
return spi_end(arg);
ret = spi_end(arg);
#else
cache_enable(NULL);
return ESP_OK;
#endif
on_spi1_released((spi1_app_func_arg_t*)arg);
return ret;
}
static IRAM_ATTR esp_err_t spi1_flash_os_check_yield(void *arg, uint32_t chip_status, uint32_t* out_request)
{
assert (chip_status == 0); //TODO: support suspend
esp_err_t ret = ESP_ERR_TIMEOUT; //Nothing happened
uint32_t request = 0;
if (on_spi1_check_yield((spi1_app_func_arg_t *)arg)) {
request = SPI_FLASH_YIELD_REQ_YIELD;
ret = ESP_OK;
}
if (out_request) {
*out_request = request;
}
return ret;
}
static IRAM_ATTR esp_err_t spi1_flash_os_yield(void *arg, uint32_t* out_status)
{
#ifdef CONFIG_SPI_FLASH_ERASE_YIELD_TICKS
vTaskDelay(CONFIG_SPI_FLASH_ERASE_YIELD_TICKS);
#else
vTaskDelay(1);
#endif
on_spi1_yielded((spi1_app_func_arg_t*)arg);
return ESP_OK;
}
static IRAM_ATTR esp_err_t delay_us(void *arg, uint32_t us)
@@ -101,13 +150,6 @@ static IRAM_ATTR esp_err_t delay_us(void *arg, uint32_t us)
esp_rom_delay_us(us);
return ESP_OK;
}
static IRAM_ATTR esp_err_t spi_flash_os_yield(void *arg)
{
#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE
vTaskDelay(CONFIG_SPI_FLASH_ERASE_YIELD_TICKS);
#endif
return ESP_OK;
}
static IRAM_ATTR void* get_buffer_malloc(void* arg, size_t reqest_size, size_t* out_size)
{
@@ -155,7 +197,8 @@ static const DRAM_ATTR esp_flash_os_functions_t esp_flash_spi1_default_os_functi
.delay_us = delay_us,
.get_temp_buffer = get_buffer_malloc,
.release_temp_buffer = release_buffer_malloc,
.yield = spi_flash_os_yield,
.check_yield = spi1_flash_os_check_yield,
.yield = spi1_flash_os_yield,
};
static const esp_flash_os_functions_t esp_flash_spi23_default_os_functions = {
@@ -164,7 +207,9 @@ static const esp_flash_os_functions_t esp_flash_spi23_default_os_functions = {
.delay_us = delay_us,
.get_temp_buffer = get_buffer_malloc,
.release_temp_buffer = release_buffer_malloc,
.yield = spi_flash_os_yield
.region_protected = NULL,
.check_yield = NULL,
.yield = NULL,
};
static spi_bus_lock_dev_handle_t register_dev(int host_id)
@@ -269,3 +314,45 @@ esp_err_t esp_flash_app_enable_os_functions(esp_flash_t* chip)
chip->os_func_data = &main_flash_arg;
return ESP_OK;
}
// The goal of this part is to manually insert one valid task execution interval, if the time since
// last valid interval exceed the limitation (CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS).
//
// Valid task execution interval: continuous time with the cache enabled, which is longer than
// CONFIG_SPI_FLASH_ERASE_YIELD_TICKS. Yield time shorter than CONFIG_SPI_FLASH_ERASE_YIELD_TICKS is
// not treated as valid interval.
static inline IRAM_ATTR bool on_spi1_check_yield(spi1_app_func_arg_t* ctx)
{
#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE
uint32_t time = esp_system_get_time();
// We handle the reset here instead of in `on_spi1_acquired()`, when acquire() and release() is
// larger than CONFIG_SPI_FLASH_ERASE_YIELD_TICKS, to save one `esp_system_get_time()` call
if ((time - ctx->released_since_us) >= CONFIG_SPI_FLASH_ERASE_YIELD_TICKS * portTICK_PERIOD_MS * 1000) {
// Reset the acquired time as if the yield has just happened.
ctx->acquired_since_us = time;
} else if ((time - ctx->acquired_since_us) >= CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS * 1000) {
return true;
}
#endif
return false;
}
static inline IRAM_ATTR void on_spi1_released(spi1_app_func_arg_t* ctx)
{
#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE
ctx->released_since_us = esp_system_get_time();
#endif
}
static inline IRAM_ATTR void on_spi1_acquired(spi1_app_func_arg_t* ctx)
{
// Ideally, when the time after `on_spi1_released()` before this function is called is larger
// than CONFIG_SPI_FLASH_ERASE_YIELD_TICKS, the acquired time should be reset. We assume the
// time after `on_spi1_check_yield()` before this function is so short that we can do the reset
// in that function instead.
}
static inline IRAM_ATTR void on_spi1_yielded(spi1_app_func_arg_t* ctx)
{
uint32_t time = esp_system_get_time();
ctx->acquired_since_us = time;
}