mirror of
https://github.com/espressif/esp-idf.git
synced 2025-09-30 19:19:21 +00:00
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:
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user