mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/add_flash_suspend_example' into 'master'
system: add an example showing how to run non-iram ISR when operating Flash Closes IDF-4501 See merge request espressif/esp-idf!16658
This commit is contained in:
commit
7967a317d6
@ -54,6 +54,19 @@ example_test_pytest_esp32c3_generic:
|
||||
- ESP32C3
|
||||
- Example_GENERIC
|
||||
|
||||
example_test_pytest_esp32c3_flash_suspend:
|
||||
extends:
|
||||
- .pytest_examples_dir_template
|
||||
- .rules:test:example_test-esp32c3
|
||||
needs:
|
||||
- build_examples_pytest_esp32c3
|
||||
variables:
|
||||
TARGET: esp32c3
|
||||
ENV_MARKER: flash_suspend
|
||||
tags:
|
||||
- ESP32C3_IDF
|
||||
- UT_T1_Flash_Suspend
|
||||
|
||||
# for parallel jobs, CI_JOB_NAME will be "job_name index/total" (for example, "IT_001 1/2")
|
||||
# we need to convert to pattern "job_name_index.yml"
|
||||
.define_config_file_name: &define_config_file_name |
|
||||
|
@ -137,8 +137,30 @@ The following options will reduce IRAM usage of some ESP-IDF features:
|
||||
- Disabling :ref:`CONFIG_SPI_MASTER_ISR_IN_IRAM` prevents spi_master interrupts from being serviced while writing to flash, and may otherwise reduce spi_master performance, but will save some IRAM.
|
||||
- Setting :ref:`CONFIG_HAL_DEFAULT_ASSERTION_LEVEL` to disable assertion for HAL component will save some IRAM especially for HAL code who calls `HAL_ASSERT` a lot and resides in IRAM.
|
||||
|
||||
|
||||
.. only:: esp32c3
|
||||
|
||||
Flash Suspend Feature
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When using ESP Flash APIs and other APIs based on the former (NVS, Partition APIs, etc.), the Cache will be disabled. During this period of time, any code executed must reside in internal RAM (see :ref:`concurrency-constraints-flash`). Hence, interrupt handlers that are not in internal RAM will not be executed.
|
||||
|
||||
To achieve this, ESP-IDF Drivers usually have the following two options:
|
||||
- an option to place the driver's internal ISR handler in internal RAM
|
||||
- an option to place some control functions in internal RAM.
|
||||
|
||||
User ISR callbacks (and involved variables) have to be in internal RAM if they are also used in interrupt contexts.
|
||||
|
||||
Placing additional code into IRAM will exacerbate the IRAM usage. For this reason, there is :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND`, which can alleviate the aforementioned kinds of IRAM usage. By enabling this feature, cache won't be disabled when ESP Flash and ESP-Flash-based APIs are used. Therefore, code and data in Flash can be executed or accessed normally, but with some minor delay. See :ref:`Flash Auto Suspend <auto-suspend>` for more details about this feature.
|
||||
|
||||
Regarding the flash suspend feature usage, and corresponding response time delay, please also see this example :example:`system/flash_suspend` .
|
||||
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
Putting C Library in Flash
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When compiling for ESP32 revisions older than ECO3 (:ref:`CONFIG_ESP32_REV_MIN`), PSRAM cache bug workaround (:ref:`CONFIG_SPIRAM_CACHE_WORKAROUND`) option is enabled, and the C library functions normally located in ROM are recompiled with the workaround and placed into IRAM instead. For most applications, it is safe to move many of the C library functions into Flash, reclaiming some IRAM. Corresponding options include:
|
||||
|
||||
.. list::
|
||||
|
@ -1,11 +1,14 @@
|
||||
.. _auto_suspend:
|
||||
.. _auto-suspend:
|
||||
|
||||
When auto suspend is enabled
|
||||
----------------------------
|
||||
Flash Auto Suspend Feature
|
||||
--------------------------
|
||||
|
||||
.. important::
|
||||
|
||||
The flash chip you are using must have suspend/resume function, even then, and not all flash chips with suspend/resume support auto-suspend on {IDF_TARGET_NAME}. If you use suspend feature on a unsupported chip, it may cause a severe crash. Therefore, we strongly suggest you reading the flash chip datasheets first. Ensure the flash chip satisfies the following conditions at minimum. Even then, thorough testing is recommended.
|
||||
1. The flash chip you are using should have a suspend/resume feature.
|
||||
2. The MSPI hardware should support the auto-suspend feature (hardware can send suspend command automatically).
|
||||
|
||||
If you use suspend feature on an unsupported chip, it may cause a severe crash. Therefore, we strongly suggest you reading the flash chip datasheets first. Ensure the flash chip satisfies the following conditions at minimum.
|
||||
|
||||
1. SUS bit in status registers should in SR2 bit7 (or SR bit15)(This is caused by the restriction of out software implementation).
|
||||
|
||||
@ -15,9 +18,8 @@ When auto suspend is enabled
|
||||
|
||||
4. When the flash is successfully resumed, another suspend can be sent immediately at this state.
|
||||
|
||||
When auto suspend is enabled, the cache will be kept enabled while accessing the SPI1 bus (e.g. erasing/writing/reading main flash). The hardware handles the arbitration between them.
|
||||
|
||||
If SPI1 operation is short (like reading operation), the CPU and the cache will wait until the SPI1 operation is done. However if it's an erasing, auto suspend will happen, interrupting the erasing, making the CPU able to read from cache in limited time.
|
||||
When :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND` is enabled, the caches will be kept enabled (they would be disabled if :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND` is disabled). The hardware handles the arbitration between SPI0 and SPI1. If SPI1 operation is short (like reading operation), the CPU and the cache will wait until the SPI1 operation is done. However, if it is erasing, page programming or status register writing (e.g. `SE`, `PP` and `WRSR`), an auto suspend will happen, interrupting the ongoing flash operation, making the CPU able to read from cache and flash in limited time.
|
||||
|
||||
This way some code/variables can be put into the flash/psram instead of IRAM/DRAM, while still able to be executed during flash erasing. This reduces the some usage of IRAM/DRAM.
|
||||
|
||||
@ -31,3 +33,5 @@ In other words, there are three kinds of code:
|
||||
2. Cached code: inside flash/psram. This kind of code has lower performance requirements or called less often. They will execute during erasing, with some overhead.
|
||||
|
||||
3. Low priority code: inside flash/psram and disabled during erasing. This kind of code should be forbidden from executed to avoid affecting the flash erasing, by setting a lower task priority than the erasing task.
|
||||
|
||||
Regarding the flash suspend feature usage, and corresponding response time delay, please also see this example :example:`system/flash_suspend` .
|
||||
|
@ -1,3 +1,5 @@
|
||||
.. _concurrency-constraints-flash:
|
||||
|
||||
Concurrency Constraints for flash on SPI1
|
||||
=========================================
|
||||
|
||||
@ -9,33 +11,43 @@ The SPI0/1 bus is shared between the instruction & data cache (for firmware exec
|
||||
|
||||
.. only:: esp32c3
|
||||
|
||||
On {IDF_TARGET_NAME}, the config option :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND` (enabled by default) allows the cache to read flash & PSRAM concurrently with SPI1 operations. See :ref:`auto_suspend` for more details.
|
||||
On {IDF_TARGET_NAME}, the config option :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND` (enabled by default) allows the cache to read flash concurrently with SPI1 operations. See :ref:`auto-suspend` for more details.
|
||||
|
||||
If this option is disabled, the caches must be disabled while reading/writing/erasing operations. There are some constraints using driver on the SPI1 bus, see :ref:`impact_disabled_cache`. This constraints will cause more IRAM/DRAM usages.
|
||||
|
||||
|
||||
.. _impact_disabled_cache:
|
||||
|
||||
When the caches are disabled
|
||||
----------------------------
|
||||
|
||||
This means that all CPUs must be running code from IRAM and must only be reading data from DRAM while flash write operations occur. If you use the API functions documented here, then the caches will be disabled automatically and transparently. However, note that it will have some performance impact on other tasks in the system.
|
||||
Under this condition, all CPUs should always execute code and access data from internal RAM. The APIs documented in this file will disable the caches automatically and transparently.
|
||||
|
||||
There are no such constraints and impacts for flash chips on other SPI buses than SPI0/1.
|
||||
.. only:: esp32c3
|
||||
|
||||
For differences between IRAM, DRAM, and flash cache, please refer to the :ref:`application memory layout <memory-layout>` documentation.
|
||||
However, when :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND` is enabled, these APIs won't disable the caches. The hardware will handle the arbitration between them.
|
||||
|
||||
.. only:: not CONFIG_FREERTOS_UNICORE
|
||||
|
||||
To avoid reading flash cache accidentally, when one CPU initiates a flash write or erase operation, the other CPU is put into a blocked state, and all non-IRAM-safe interrupts are disabled on all CPUs until the flash operation completes.
|
||||
The way that these APIs disable the caches will suspend all the other tasks. Besides, all non-IRAM-safe interrupts will be disabled. The other core will be polling in a busy loop. These will be restored until the Flash operation completes.
|
||||
|
||||
.. only:: CONFIG_FREERTOS_UNICORE
|
||||
|
||||
The way that these APIs disable the caches will also disable non-IRAM-safe interrupts. These will be restored until the Flash operation completes.
|
||||
|
||||
See also :ref:`esp_flash_os_func`, :ref:`spi_bus_lock`.
|
||||
|
||||
There are no such constraints and impacts for flash chips on other SPI buses than SPI0/1.
|
||||
|
||||
For differences between internal RAM (e.g. IRAM, DRAM) and flash cache, please refer to the :ref:`application memory layout <memory-layout>` documentation.
|
||||
|
||||
|
||||
.. _iram-safe-interrupt-handlers:
|
||||
|
||||
IRAM-Safe Interrupt Handlers
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you have an interrupt handler that you want to execute while a flash operation is in progress (for example, for low latency operations), set the ``ESP_INTR_FLAG_IRAM`` flag when the :doc:`interrupt handler is registered </api-reference/system/intr_alloc>`.
|
||||
For interrupt handlers which need to execute when the cache is disabled (e.g., for low latency operations), set the ``ESP_INTR_FLAG_IRAM`` flag when the :doc:`interrupt handler is registered </api-reference/system/intr_alloc>`.
|
||||
|
||||
You must ensure that all data and functions accessed by these interrupt handlers, including the ones that handlers call, are located in IRAM or DRAM. See :ref:`how-to-place-code-in-iram`.
|
||||
|
||||
@ -45,6 +57,12 @@ If a function or symbol is not correctly put into IRAM/DRAM, and the interrupt h
|
||||
|
||||
When working with string in ISRs, it is not advised to use ``printf`` and other output functions. For debugging purposes, use :cpp:func:`ESP_DRAM_LOGE` and similar macros when logging from ISRs. Make sure that both ``TAG`` and format string are placed into ``DRAM`` in that case.
|
||||
|
||||
Non-IRAM-Safe Interrupt Handlers
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If the ``ESP_INTR_FLAG_IRAM`` flag is not set when registering, the interrupt handler will not get executed when the caches are disabled. Once the caches are restored, the non-IRAM-safe interrupts will be re-enabled. After this moment, the interrupt handler will run normally again. This means that as long as caches are disabled, users won't see the corresponding hardware event happening.
|
||||
|
||||
|
||||
.. only:: esp32c3
|
||||
|
||||
.. include:: auto_suspend.inc
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
.. _auto_suspend:
|
||||
.. _auto-suspend:
|
||||
|
||||
当使能 flash 擦除的自动暂停
|
||||
--------------------------------------
|
||||
|
@ -1,3 +1,5 @@
|
||||
.. _concurrency-constraints-flash:
|
||||
|
||||
SPI1 Flash 并发约束
|
||||
=========================================
|
||||
|
||||
@ -9,7 +11,7 @@ SPI1 Flash 并发约束
|
||||
|
||||
.. only:: esp32c3
|
||||
|
||||
在 {IDF_TARGET_NAME} 上,默认启用的配置选项 :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND` 允许 flash / PSRAM 的 cache 访问和 SPI1 的操作存并发地执行。更多详情,参见 :ref:`auto_suspend` 。
|
||||
在 {IDF_TARGET_NAME} 上,默认启用的配置选项 :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND` 允许 flash / PSRAM 的 cache 访问和 SPI1 的操作存并发地执行。更多详情,参见 :ref:`auto-suspend` 。
|
||||
|
||||
然而当该选项被禁用时,读取/写入/擦除 flash 时, cache 必须被禁用。使用驱动访问 SPI1 的相关约束参见 :ref:`impact_disabled_cache` 。这些约束会带来更多的 IRAM / DRAM 消耗。
|
||||
|
||||
|
6
examples/system/flash_suspend/CMakeLists.txt
Normal file
6
examples/system/flash_suspend/CMakeLists.txt
Normal file
@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(flash_suspend)
|
115
examples/system/flash_suspend/README.md
Normal file
115
examples/system/flash_suspend/README.md
Normal file
@ -0,0 +1,115 @@
|
||||
| Supported Targets | ESP32-C3 |
|
||||
| ----------------- | -------- |
|
||||
|
||||
# Flash Suspend Example
|
||||
|
||||
## Overview
|
||||
|
||||
One problem in IDF is that during Flash operations (via either ESP Flash, NVS or Partition APIs), concurrent execution of code residing in Flash will lead to a crash. Consequently, all ISRs and their callbacks need to be placed in internal RAM.
|
||||
|
||||
Background: In IDF, executable code (instructions) are usually placed in Flash.
|
||||
|
||||
However, by using the auto-suspend feature, it is not necessary to put ISRs and their callbacks into internal RAM anymore.
|
||||
|
||||
Here we define two sets of Flash operations:
|
||||
SET1: Operations where CPU fetches data and instructions from Flash.
|
||||
SET2: `ESP Flash` driver operations and other operations from drivers based on `ESP Flash` (NVS, Partition drivers, etc.).
|
||||
|
||||
- During Erase Operation (SET 2), if there is a SET 1 request, the ongoing SET 2 Flash operation will be suspended automatically, the Erase Operation is interrupted, CPU will get a chance to read from Flash. After CPU's access, the Flash chip will be resumed automatically and the Erase Operation will continue.
|
||||
|
||||
- During other Flash Operations (SET 2), if there is a SET 1 request, CPU will usually wait until the SET 2 operation finishes, then get the chance to read from Flash.
|
||||
|
||||
|
||||
## Example Process
|
||||
|
||||
As mentioned above, in this example we go through the following steps:
|
||||
|
||||
`General Steps`:
|
||||
1. Create a partition for Flash Erase Operation
|
||||
2. Configure a GP timer to trigger an alarm once
|
||||
3. Register a timer alarm event callback
|
||||
|
||||
`Misc Steps`:
|
||||
4. Make the callback call a function (`s_function_in_flash`) in Flash
|
||||
5. Start the timer
|
||||
6. Start a Flash Erase Operation
|
||||
7. Pause the timer
|
||||
|
||||
`IRAM Steps`:
|
||||
8. Make the callback call a function (`s_function_in_iram`) in Internal RAM
|
||||
9. Start the timer
|
||||
10. Start a Flash Erase Operation
|
||||
|
||||
## Example Result
|
||||
|
||||
You will see an ISR happening during each of the Flash Erase Operations (SET 2).
|
||||
|
||||
### `Misc Steps`
|
||||
|
||||
The ISR callback calls a function, and its main body is in `.flash.text`. You may use
|
||||
```
|
||||
riscv32-esp-elf-objdump
|
||||
```
|
||||
to see the concrete instructions of the function. It contains around 10 instructions. The Cache will read from the Flash, if these instructions are not cached yet. CPU then fetches these instructions from the Cache. For ESP32C3, the time between the call to `s_function_in_flash`, and its execution, is around 32 us, when the CPU frequency is 160MHz. Apart from the context switch time, instruction execution time (please refer to the example comments to know the function body), and other hardware overheads, there is still around 28.5us, which should be the time for the Cache synchronisation (see note 1.).
|
||||
|
||||
----
|
||||
|
||||
*note 1*
|
||||
**Cache synchronisation:**
|
||||
In ESP-IDF, the cache is a small memory system which can provide low-latency access to a block of data, residing in Flash or PSRAM. The CPU accesses Flash and PSRAM via the cache. When the CPU requested data that is not in the cache, we have a cache miss and the data will be synchronized to cache from either Flash or PSRAM.
|
||||
In this example, for `Misc Steps`, the involved cache synchronization time is a bit longer, since a SET 2 Flash Erase Operation is ongoing. The ESP chip will send a suspend command to the Flash chip, to suspend the ongoing Erase Operation, and let the Cache access the Flash chip.
|
||||
|
||||
*The Cache synchronisation time would be even longer, if more instructions need to be fetched.*
|
||||
|
||||
### `IRAM Steps`
|
||||
|
||||
The ISR callback calls a function, and its main body is in `.iram0.text`. For ESP32C3, the time between `s_function_in_iram` is called, and its execution, is around 3.8 us, when the CPU frequency is 160MHz. Under this circumstance, around 400 times instruction execution will cost about 2.5us. Apart from the context switch and other overheads, these instructions in Internal RAM can be fetched within 1us.
|
||||
|
||||
## Conclusion for Code Placement
|
||||
|
||||
For ESP-IDF drivers, we usually expect the ISR callbacks to be short, simple and useful. It's more like a way to inform users of a hardware event. And the event handling routines should be delegated to the main program.
|
||||
|
||||
What should not be used in an ISR callback:
|
||||
- printf (there is mutex underneath)
|
||||
- long log print (even using `esp_rom_printf`)
|
||||
- blocking logic
|
||||
|
||||
Based on these principles,
|
||||
- If the `CONFIG_SPI_FLASH_AUTO_SUSPEND` is not enabled/supported, usually an ISR and its callbacks should be put in Internal RAM, when you also need to operate the Flash via `ESP Flash` driver and the drivers based on `ESP Flash`.
|
||||
- If the `CONFIG_SPI_FLASH_AUTO_SUSPEND` is enabled, the ISR function and its callbacks **can** be put in Flash. But their maximum response time will be **prolonged**.
|
||||
- If the `CONFIG_SPI_FLASH_AUTO_SUSPEND` is enabled, a Flash Erase Operation would be interrupted by the CPU / Cache access request to the Flash. Therefore, when the ISR happens frequently, the Flash Erase Operation may be **prolonged** a lot.
|
||||
|
||||
|
||||
### Configure the project
|
||||
|
||||
To change the `Auto suspend long erase/write operations` (`CONFIG_SPI_FLASH_AUTO_SUSPEND`), open the project configuration menu (`idf.py menuconfig`).
|
||||
|
||||
Navigate to `Component config -> SPI Flash driver` menu.
|
||||
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT flash monitor` to build and flash the project..
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
I (285) Example: found partition 'storage1' at offset 0x110000 with size 0x80000
|
||||
I (5615) Example: Flash Driver Erase Operation finishes, duration:
|
||||
2635600.01 us
|
||||
I (5615) Example: During Erase, ISR callback function(in flash) response time:
|
||||
32.26 us
|
||||
I (8295) Example: Flash Driver Erase Operation finishes, duration:
|
||||
2626830.69 us
|
||||
I (8295) Example: During Erase, ISR callback function(in iram) response time:
|
||||
3.83 us
|
||||
I (8295) Example: Finish
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
2
examples/system/flash_suspend/main/CMakeLists.txt
Normal file
2
examples/system/flash_suspend/main/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "app_main.c"
|
||||
INCLUDE_DIRS "")
|
153
examples/system/flash_suspend/main/app_main.c
Normal file
153
examples/system/flash_suspend/main/app_main.c
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* In this example, we show you a way to make an ISR-based callback work during Flash operations, when the ISR-based
|
||||
* callback is put in Flash.
|
||||
*
|
||||
* Please read the README.md to know more details about this feature!
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_cpu.h"
|
||||
#include "esp_partition.h"
|
||||
#include "driver/gptimer.h"
|
||||
|
||||
|
||||
#define TIMER_RESOLUTION_HZ (1 * 1000 * 1000) // 1MHz resolution
|
||||
#define TIMER_ALARM_PERIOD_S 1 // Alarm period 1s
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
#define CPU_FREQ_MHZ CONFIG_ESP32C3_DEFAULT_CPU_FREQ_MHZ
|
||||
#endif
|
||||
|
||||
#define RECORD_TIME_PREPARE() uint32_t __t1, __t2
|
||||
#define RECORD_TIME_START() do {__t1 = esp_cpu_get_ccount();} while(0)
|
||||
#define RECORD_TIME_END(p_time) do{__t2 = esp_cpu_get_ccount(); p_time = (__t2 - __t1);} while(0)
|
||||
#define GET_US_BY_CCOUNT(t) ((double)(t)/CPU_FREQ_MHZ)
|
||||
|
||||
const static char *TAG = "Example";
|
||||
DRAM_ATTR static uint32_t s_t1;
|
||||
DRAM_ATTR static uint32_t s_flash_func_t2;
|
||||
DRAM_ATTR static uint32_t s_iram_func_t2;
|
||||
|
||||
|
||||
static NOINLINE_ATTR void s_function_in_flash(void)
|
||||
{
|
||||
/**
|
||||
* - Here we will have few instructions in .flash.text
|
||||
* - Cache will read from Flash to get the instructions synchronized.
|
||||
* - CPU will execute around 400 times.
|
||||
*/
|
||||
for (int i = 0; i < 100; i++) {
|
||||
asm volatile("nop");
|
||||
}
|
||||
|
||||
s_flash_func_t2 = esp_cpu_get_ccount();
|
||||
}
|
||||
|
||||
static IRAM_ATTR NOINLINE_ATTR void s_funtion_in_iram(void)
|
||||
{
|
||||
/**
|
||||
* - Here we will have few instructions in .iram0.text
|
||||
* - CPU will execute around 400 times.
|
||||
*/
|
||||
for (int i = 0; i < 100; i++) {
|
||||
asm volatile("nop");
|
||||
}
|
||||
|
||||
s_iram_func_t2 = esp_cpu_get_ccount();
|
||||
}
|
||||
|
||||
static bool IRAM_ATTR on_gptimer_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
bool is_flash = *(bool *)user_ctx;
|
||||
s_t1 = esp_cpu_get_ccount();
|
||||
|
||||
if (is_flash) {
|
||||
s_function_in_flash();
|
||||
} else {
|
||||
s_funtion_in_iram();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const esp_partition_t *s_get_partition(void)
|
||||
{
|
||||
//Find the "storage1" partition defined in `partitions.csv`
|
||||
const esp_partition_t *result = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage1");
|
||||
if (!result) {
|
||||
ESP_LOGE(TAG, "Can't find the partition, please define it correctly in `partitions.csv`");
|
||||
abort();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
//Get the partition used for SPI1 erase operation
|
||||
const esp_partition_t *part = s_get_partition();
|
||||
ESP_LOGI(TAG, "found partition '%s' at offset 0x%x with size 0x%x", part->label, part->address, part->size);
|
||||
//Erase whole region
|
||||
ESP_ERROR_CHECK(esp_flash_erase_region(part->flash_chip, part->address, part->size));
|
||||
|
||||
gptimer_handle_t gptimer = NULL;
|
||||
gptimer_config_t timer_config = {
|
||||
.clk_src = GPTIMER_CLK_SRC_APB,
|
||||
.direction = GPTIMER_COUNT_UP,
|
||||
.resolution_hz = TIMER_RESOLUTION_HZ,
|
||||
};
|
||||
ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));
|
||||
|
||||
gptimer_alarm_config_t alarm_config = {
|
||||
.reload_count = 0,
|
||||
.alarm_count = 1 * 1000 * 1000,
|
||||
.flags.auto_reload_on_alarm = false,
|
||||
};
|
||||
ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config));
|
||||
|
||||
gptimer_event_callbacks_t cbs = {
|
||||
.on_alarm = on_gptimer_alarm_cb,
|
||||
};
|
||||
bool is_flash = true;
|
||||
ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, &is_flash));
|
||||
|
||||
ESP_ERROR_CHECK(gptimer_start(gptimer));
|
||||
|
||||
uint32_t erase_time = 0;
|
||||
RECORD_TIME_PREPARE();
|
||||
|
||||
RECORD_TIME_START();
|
||||
ESP_ERROR_CHECK(esp_flash_erase_region(part->flash_chip, part->address, part->size));
|
||||
RECORD_TIME_END(erase_time);
|
||||
|
||||
ESP_ERROR_CHECK(gptimer_stop(gptimer));
|
||||
ESP_LOGI(TAG, "Flash Driver Erase Operation finishes, duration:\n\t\t%0.2f us", GET_US_BY_CCOUNT(erase_time));
|
||||
ESP_LOGI(TAG, "During Erase, ISR callback function(in flash) response time:\n\t\t%0.2f us", GET_US_BY_CCOUNT(s_flash_func_t2 - s_t1));
|
||||
|
||||
//Let the timer alarm callback to run code reside in .iram0.text
|
||||
is_flash = false;
|
||||
ESP_ERROR_CHECK(gptimer_set_raw_count(gptimer, 0));
|
||||
ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config));
|
||||
ESP_ERROR_CHECK(gptimer_start(gptimer));
|
||||
|
||||
RECORD_TIME_START();
|
||||
//Erase whole region
|
||||
ESP_ERROR_CHECK(esp_flash_erase_region(part->flash_chip, part->address, part->size));
|
||||
RECORD_TIME_END(erase_time);
|
||||
|
||||
ESP_ERROR_CHECK(gptimer_stop(gptimer));
|
||||
ESP_LOGI(TAG, "Flash Driver Erase Operation finishes, duration:\n\t\t%0.2f us", GET_US_BY_CCOUNT(erase_time));
|
||||
ESP_LOGI(TAG, "During Erase, ISR callback function(in iram) response time:\n\t\t%0.2f us", GET_US_BY_CCOUNT(s_iram_func_t2 - s_t1));
|
||||
|
||||
ESP_LOGI(TAG, "Finish");
|
||||
ESP_ERROR_CHECK(gptimer_del_timer(gptimer));
|
||||
}
|
6
examples/system/flash_suspend/partitions.csv
Normal file
6
examples/system/flash_suspend/partitions.csv
Normal file
@ -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,
|
||||
storage1, data, fat, , 512K,
|
|
21
examples/system/flash_suspend/pytest_flash_suspend.py
Normal file
21
examples/system/flash_suspend/pytest_flash_suspend.py
Normal file
@ -0,0 +1,21 @@
|
||||
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import pytest
|
||||
from pytest_embedded.dut import Dut
|
||||
|
||||
|
||||
@pytest.mark.esp32c3
|
||||
@pytest.mark.flash_suspend
|
||||
@pytest.mark.parametrize('config', [
|
||||
'flash_suspend',
|
||||
], indirect=True)
|
||||
def test_flash_suspend_example(dut: Dut) -> None:
|
||||
dut.expect_exact('found partition')
|
||||
res = dut.expect(r'During Erase, ISR callback function\(in flash\) response time:\s+(\d+(\.\d{1,2})) us')
|
||||
response_time = res.group(1).decode('utf8')
|
||||
assert 0 <= float(response_time) < 40
|
||||
|
||||
res = dut.expect(r'During Erase, ISR callback function\(in iram\) response time:\s+(\d+(\.\d{1,2})) us')
|
||||
response_time = res.group(1).decode('utf8')
|
||||
assert 0 <= float(response_time) < 5
|
4
examples/system/flash_suspend/sdkconfig.ci.flash_suspend
Normal file
4
examples/system/flash_suspend/sdkconfig.ci.flash_suspend
Normal file
@ -0,0 +1,4 @@
|
||||
CONFIG_SPI_FLASH_AUTO_SUSPEND=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
2
examples/system/flash_suspend/sdkconfig.defaults.esp32c3
Normal file
2
examples/system/flash_suspend/sdkconfig.defaults.esp32c3
Normal file
@ -0,0 +1,2 @@
|
||||
CONFIG_ESP32C3_DEFAULT_CPU_FREQ_160=y
|
||||
CONFIG_ESP32C3_DEFAULT_CPU_FREQ_MHZ=160
|
@ -13,6 +13,7 @@ markers =
|
||||
esp32s3: support esp32s3 target
|
||||
esp32c3: support esp32c3 target
|
||||
generic: tests should be run on generic runners
|
||||
flash_suspend: support flash suspend feature
|
||||
|
||||
# log related
|
||||
log_auto_indent = True
|
||||
|
Loading…
x
Reference in New Issue
Block a user