mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/add_flash_copy_to_psram_example' into 'master'
system: add an example showing moving Flash content to PSRAM feature Closes IDF-5156 See merge request espressif/esp-idf!19340
This commit is contained in:
commit
1005c3b9d5
@ -71,6 +71,14 @@ example_test_pytest_esp32s3_generic:
|
||||
- build_pytest_examples_esp32s3
|
||||
tags: [ esp32s3, generic ]
|
||||
|
||||
example_test_pytest_esp32s3_f4r8:
|
||||
extends:
|
||||
- .pytest_examples_dir_template
|
||||
- .rules:test:example_test-esp32s3
|
||||
needs:
|
||||
- build_pytest_examples_esp32s3
|
||||
tags: [ esp32s3, MSPI_F4R8 ]
|
||||
|
||||
example_test_pytest_esp32c2_generic:
|
||||
extends:
|
||||
- .pytest_examples_dir_template
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include "esp_private/spi_common_internal.h"
|
||||
|
||||
#define SPI_FLASH_CACHE_NO_DISABLE (CONFIG_SPI_FLASH_AUTO_SUSPEND || (CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_RODATA))
|
||||
static const char TAG[] = "spi_flash";
|
||||
|
||||
/*
|
||||
@ -60,14 +61,14 @@ static inline bool on_spi1_check_yield(spi1_app_func_arg_t* ctx);
|
||||
|
||||
IRAM_ATTR static void cache_enable(void* arg)
|
||||
{
|
||||
#ifndef CONFIG_SPI_FLASH_AUTO_SUSPEND
|
||||
#if !SPI_FLASH_CACHE_NO_DISABLE
|
||||
spi_flash_enable_interrupts_caches_and_other_cpu();
|
||||
#endif
|
||||
}
|
||||
|
||||
IRAM_ATTR static void cache_disable(void* arg)
|
||||
{
|
||||
#ifndef CONFIG_SPI_FLASH_AUTO_SUSPEND
|
||||
#if !SPI_FLASH_CACHE_NO_DISABLE
|
||||
spi_flash_disable_interrupts_caches_and_other_cpu();
|
||||
#endif
|
||||
}
|
||||
|
@ -195,3 +195,10 @@ examples/system/ulp_riscv/i2c:
|
||||
examples/system/ulp_riscv/uart_print:
|
||||
enable:
|
||||
- if: SOC_RISCV_COPROC_SUPPORTED == 1
|
||||
examples/system/xip_from_psram:
|
||||
enable:
|
||||
- if: SOC_SPIRAM_SUPPORTED == 1
|
||||
reason: this feature is supported on chips that have PSRAM
|
||||
disable:
|
||||
- if: IDF_TARGET == "esp32"
|
||||
reason: target esp32 doesn't support this feature.
|
||||
|
8
examples/system/xip_from_psram/CMakeLists.txt
Normal file
8
examples/system/xip_from_psram/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
||||
# For more information about build system see
|
||||
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
|
||||
# The following five 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(xip_from_psram)
|
112
examples/system/xip_from_psram/README.md
Normal file
112
examples/system/xip_from_psram/README.md
Normal file
@ -0,0 +1,112 @@
|
||||
| Supported Targets | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | -------- | -------- |
|
||||
|
||||
# XIP (Execute-In-Place) From PSRAM Example
|
||||
|
||||
This example illustrates a typical usage of XIP (Execute-In-Place) From PSRAM. With this feature:
|
||||
- You can optimize internal RAM usage
|
||||
- System can avoid disabling the Cache during an SPI1 Flash operation.
|
||||
|
||||
## Overview
|
||||
|
||||
Here we define two sets of operations related to external memory:
|
||||
SET1: Operations where CPU fetches data and instructions from external memory.
|
||||
SET2: `ESP Flash` driver operations and other operations from drivers based on `ESP Flash` (NVS, Partition drivers, etc.).
|
||||
|
||||
By default, during `SET2` operations, concurrent access requests to the Flash and PSRAM (`SET1` operations) will be disabled otherwise both the `SET1` and `SET2` operations are not guaranteed to be safe (this is an undefined behaviour).
|
||||
|
||||
Only ISRs in internal RAM will get executed during `SET2` operations. Besides, if any functions or data are accessed in these ISRs (usually this happens in ISR callbacks), they need to be placed into internal RAM as well. For interrupt handlers which need to execute when the cache is disabled (e.g., for low latency operations), you need to set the ESP_INTR_FLAG_IRAM flag when the interrupt handler is registered.
|
||||
|
||||
When **CONFIG_SPIRAM_FETCH_INSTRUCTIONS** and **CONFIG_SPIRAM_RODATA** are both enabled, the `flash.text` sections (for instructions) and the `.rodata` section (read only data) will be moved to PSRAM. Corresponding virtual memory range will be re-mapped to PSRAM. Under this condition, ESP-IDF won't disable concurrent accesses to external memory (`SET1` operations) anymore.
|
||||
|
||||
By using this feature, during `SET2` operations, placement of ISRs, ISR callbacks, and related data are no longer limited to internal RAM.
|
||||
|
||||
## Example Process
|
||||
|
||||
To show this feature, in this example we go through the following steps:
|
||||
|
||||
`General Steps`:
|
||||
1. Create a partition for Flash Erase Operation
|
||||
2. Create an esp_timer in one-shot mode
|
||||
|
||||
`PSRAM Steps`:
|
||||
3. Do a Flash erase operation, and start the timer
|
||||
4. ESP-Timer callback is dispatched and it calls a function in PSRAM during the flash erase operation
|
||||
5. The Flash erase operation finishes
|
||||
6. Show the result about the callback(in PSRAM) response and execute time
|
||||
|
||||
`IRAM Steps`:
|
||||
7. Do a Flash erase operation, and start the timer
|
||||
8. ESP-Timer callback is dispatched and it calls a function in IRAM during the flash erase operation
|
||||
9. The flash erase operation finishes
|
||||
10. Show the result about the callback(in IRAM) response and execute time
|
||||
|
||||
### Timeline
|
||||
|
||||
Initialization and config -> Flash erase start -> ESP-Timer callback(in PSRAM) appear -> Flash erase finish -> Flash erase start -> ESP-Timer callback(in IRAM) appear -> Flash erase finish
|
||||
|
||||
ISR CPU
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| * <----flash operation starts
|
||||
| *
|
||||
callback starts * --------> *
|
||||
(in PSRAM) * *
|
||||
callback finishes * <-------- *
|
||||
| *
|
||||
| *
|
||||
| * <----flash operation finishes
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| * <----flash operation starts
|
||||
| *
|
||||
callback starts * --------> *
|
||||
(in IRAM) * *
|
||||
callback finishes * <-------- *
|
||||
| *
|
||||
| *
|
||||
| * <----flash operation finishes
|
||||
| |
|
||||
| |
|
||||
|
||||
## Example Result
|
||||
|
||||
The ISR which call a function in IRAM happening during Flash erase operations. CPU fetches instructions and data from internal RAM.
|
||||
The ISR which call a function in PSRAM happening during Flash erase operations and its response time is longer than calling a function in IRAM. That's because fetching instructions from PSRAM takes more time than fetching from IRAM.
|
||||
|
||||
## Configure the project
|
||||
|
||||
Open the project configuration menu (`idf.py menuconfig`).
|
||||
|
||||
1. Set the `Partition Table -> Custom partition table CSV` to `y`.
|
||||
2. Set the `Component config -> High resolution timer -> Support ISR dispatch method` to `y`.
|
||||
3. Set the `Component config -> ESP PSRAM -> SPI RAM config` to `y` then set `Cache fetch instructions from SPI RAM` and `Cache load read only data from SPI RAM` to `y`.
|
||||
|
||||
|
||||
## 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 (742) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations
|
||||
I (742) example: found partition 'storage1' at offset 0x110000 with size 0x10000
|
||||
|
||||
I (1152) example: callback(in PSRAM) response time: 7 us
|
||||
I (1362) example: callback(in IRAM) response time: 5 us
|
||||
|
||||
```
|
||||
|
||||
## 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/xip_from_psram/main/CMakeLists.txt
Normal file
2
examples/system/xip_from_psram/main/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "xip_from_psram_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_cpu.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_partition.h"
|
||||
#include "esp_flash.h"
|
||||
#include "esp_timer.h"
|
||||
|
||||
static void oneshot_timer_callback(void* arg);
|
||||
static void cb_in_psram(void);
|
||||
static void cb_in_iram(void);
|
||||
static const esp_partition_t *s_get_partition(void);
|
||||
|
||||
static int time_cb_start; //Time when ISR callback start
|
||||
static int time_cb_end; //Time when ISR callback end
|
||||
|
||||
const static char *TAG = "example";
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
bool instructions_in_psram = true; //Flags to indicate where the instructions in
|
||||
|
||||
esp_timer_handle_t oneshot_timer;
|
||||
const esp_timer_create_args_t oneshot_timer_args = {
|
||||
.callback = &oneshot_timer_callback,
|
||||
.arg = &instructions_in_psram,
|
||||
.dispatch_method = ESP_TIMER_ISR,
|
||||
.name = "one-shot"
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_timer_create(&oneshot_timer_args, &oneshot_timer));
|
||||
|
||||
const esp_partition_t *part = s_get_partition();
|
||||
ESP_LOGI(TAG, "found partition '%s' at offset 0x%"PRIx32" with size 0x%"PRIx32, part->label, part->address, part->size);
|
||||
ESP_ERROR_CHECK(esp_flash_erase_region(part->flash_chip, part->address, part->size));
|
||||
|
||||
ESP_ERROR_CHECK(esp_timer_start_once(oneshot_timer, 1 * 10 * 1000));
|
||||
ESP_ERROR_CHECK(esp_flash_erase_region(part->flash_chip, part->address, part->size));
|
||||
|
||||
ESP_LOGI(TAG, "callback(in PSRAM) response time: %d us", time_cb_end - time_cb_start);
|
||||
|
||||
instructions_in_psram = false;
|
||||
|
||||
ESP_ERROR_CHECK(esp_timer_start_once(oneshot_timer, 1 * 10 * 1000));
|
||||
ESP_ERROR_CHECK(esp_flash_erase_region(part->flash_chip, part->address, part->size));
|
||||
|
||||
ESP_LOGI(TAG, "callback(in IRAM) response time: %d us", time_cb_end - time_cb_start);
|
||||
|
||||
ESP_ERROR_CHECK(esp_timer_delete(oneshot_timer));
|
||||
}
|
||||
|
||||
static IRAM_ATTR void NOINLINE_ATTR oneshot_timer_callback(void* arg)
|
||||
{
|
||||
bool in_psram = (bool*) arg;
|
||||
time_cb_start = esp_timer_get_time();
|
||||
if (in_psram == true) {
|
||||
cb_in_psram();
|
||||
} else {
|
||||
cb_in_iram();
|
||||
}
|
||||
}
|
||||
|
||||
static IRAM_ATTR NOINLINE_ATTR void cb_in_iram(void)
|
||||
{
|
||||
for (int i = 0; i < 100; i++) {
|
||||
asm volatile("nop");
|
||||
}
|
||||
time_cb_end = esp_timer_get_time();
|
||||
}
|
||||
|
||||
static NOINLINE_ATTR void cb_in_psram(void)
|
||||
{
|
||||
for (int i = 0; i < 100; i++) {
|
||||
asm volatile("nop");
|
||||
}
|
||||
time_cb_end = esp_timer_get_time();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
6
examples/system/xip_from_psram/partitions.csv
Normal file
6
examples/system/xip_from_psram/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, , 64K,
|
|
49
examples/system/xip_from_psram/pytest_xip_from_psram.py
Normal file
49
examples/system/xip_from_psram/pytest_xip_from_psram.py
Normal file
@ -0,0 +1,49 @@
|
||||
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import pytest
|
||||
from pytest_embedded.dut import Dut
|
||||
|
||||
|
||||
@pytest.mark.esp32s2
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.generic
|
||||
# in order to build the default sdkconfig(the CI won't build the sdkconfig.defaults if there is a sdkconfig.ci.xx)
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'generic',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
def test_xip_from_psram_example_generic(dut: Dut) -> None:
|
||||
dut.expect_exact('found partition')
|
||||
|
||||
res = dut.expect(r'callback\(in PSRAM\) response time: (\d{1,3}) us')
|
||||
response_time = res.group(1).decode('utf8')
|
||||
assert float(response_time) <= 12
|
||||
|
||||
res = dut.expect(r'callback\(in IRAM\) response time: (\d{1,3}) us')
|
||||
response_time = res.group(1).decode('utf8')
|
||||
assert float(response_time) <= 12
|
||||
|
||||
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.MSPI_F4R8
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'esp32s3_f4r8',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
def test_xip_from_psram_example_f4r8(dut: Dut) -> None:
|
||||
dut.expect_exact('found partition')
|
||||
|
||||
res = dut.expect(r'callback\(in PSRAM\) response time: (\d{1,3}) us')
|
||||
response_time = res.group(1).decode('utf8')
|
||||
assert float(response_time) <= 12
|
||||
|
||||
res = dut.expect(r'callback\(in IRAM\) response time: (\d{1,3}) us')
|
||||
response_time = res.group(1).decode('utf8')
|
||||
assert float(response_time) <= 12
|
1
examples/system/xip_from_psram/sdkconfig.ci.esp32s3_f4r8
Normal file
1
examples/system/xip_from_psram/sdkconfig.ci.esp32s3_f4r8
Normal file
@ -0,0 +1 @@
|
||||
CONFIG_SPIRAM_MODE_OCT=y
|
0
examples/system/xip_from_psram/sdkconfig.ci.generic
Normal file
0
examples/system/xip_from_psram/sdkconfig.ci.generic
Normal file
7
examples/system/xip_from_psram/sdkconfig.defaults
Normal file
7
examples/system/xip_from_psram/sdkconfig.defaults
Normal file
@ -0,0 +1,7 @@
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||
CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD=y
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y
|
||||
CONFIG_SPIRAM_RODATA=y
|
5
tools/unit-test-app/configs/spi_flash_config_s3
Normal file
5
tools/unit-test-app/configs/spi_flash_config_s3
Normal file
@ -0,0 +1,5 @@
|
||||
TEST_COMPONENTS=spi_flash
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y
|
||||
CONFIG_SPIRAM_RODATA=y
|
||||
CONFIG_IDF_TARGET="esp32s3"
|
Loading…
Reference in New Issue
Block a user