mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 10:40:47 +03:00
fix(mmap): fixed some API read wrong data via mmap when flash being erased/written while XIP on PSRAM
Before: The cache won't be disabled when XIP on psram. But during flash erasing/programming, read data will be courrupt. When XIP in psram is enabled, the image is not mapped to the cache so usually there will be no flash access. The only way to read from flash is via the driver or use mmap. The driver has protection during erasing, while th mmap region not. Now: Mmap APIs provide a flag to make mmap->unmap region mutually exclusive to flash erase/programming when XIP from psram. SPI Flash write APIs will benefit from this. When the flag is used, no concurrent access to mapped region will happen while writing; otherwise the cache will be disable to avoid data corruption. Most ESP-IDF APIs calls mmap with this flag. As for users calling mmap-like APIs directly, they can choose whether to enable this by a flag. Closes https://github.com/espressif/esp-idf/issues/14897
This commit is contained in:
committed by
Michael (XIAO Xufeng)
parent
fd0b33dfda
commit
3d76ced5bb
@@ -172,6 +172,31 @@ Note that since memory mapping happens in pages, it may be possible to read data
|
||||
|
||||
mmap is supported by cache, so it can only be used on main flash.
|
||||
|
||||
.. only:: not esp32
|
||||
|
||||
.. _blocks_write_flag:
|
||||
|
||||
About the :cpp:enumerator:`SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE` flag
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When flash erasing/writing happen while cache mapping exists, it often causes some cache region to be invalidated and reloaded again. To improve the performance, it is suggested to specify this flag when you are sure:
|
||||
|
||||
1. This mapping will end in a short time, and
|
||||
2. Before the mapping ends, you don't need to erase or write the flash.
|
||||
|
||||
This flag will prevent all writes until the corresponding munmap is called. Most ESP-IDF APIs that rely on the mapping to flash internally use this flag.
|
||||
|
||||
.. only:: SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND or SOC_SPIRAM_XIP_SUPPORTED
|
||||
|
||||
This flag also helps to prevent the cache being disabled when you are using following modes. See their documentation for more details.
|
||||
|
||||
.. list::
|
||||
|
||||
:SOC_SPIRAM_XIP_SUPPORTED: - :ref:`xip_from_psram`
|
||||
:SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND: - :ref:`auto-suspend`
|
||||
|
||||
This flag is implemented by a lock in the SPI Flash driver. The lock is taken when :cpp:func:`spi_flash_mmap` (or mmap-like APIs) is called with the flag and released until corresponding unmap is called. There is a reference counter internally allowing concurrent mapping to the flash. Only after the last mapping to Flash with the flag is revoked (counter equals 0) can the flash erasing/writing APIs start execution.
|
||||
|
||||
SPI Flash Implementation
|
||||
------------------------
|
||||
|
||||
|
||||
@@ -1,61 +1,90 @@
|
||||
.. _concurrency-constraints-flash:
|
||||
|
||||
Concurrency Constraints for Flash on SPI1
|
||||
=========================================
|
||||
Concurrency Constraints for Flash on SPI0/1
|
||||
===========================================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
The SPI0/1 bus is shared between the instruction & data cache (for firmware execution) and the SPI1 peripheral (controlled by the drivers including this SPI Flash driver). Hence, operations to SPI1 will cause significant influence to the whole system. This kind of operations include calling SPI Flash API or other drivers on SPI1 bus, any operations like read/write/erase or other user defined SPI operations, regardless to the main flash or other SPI slave devices.
|
||||
The SPI0/1 bus is shared between the cache and the SPI1 peripheral (controlled by the drivers including this SPI Flash driver). Operations to SPI1 may cause significant influence to the cache and hence the whole system. There are no such constraints and impacts for flash chips connected to other SPI buses, which are not covered in this document.
|
||||
|
||||
.. only:: not (esp32c3 or SOC_SPIRAM_XIP_SUPPORTED)
|
||||
There are three kinds of activities that can happen on SPI0/1 bus:
|
||||
|
||||
On {IDF_TARGET_NAME}, these caches must be disabled while reading/writing/erasing.
|
||||
- Flash writing operations (via SPI1). For example, erasing, page programming, or status register writing commands (e.g., ``SE``, ``PP``, and ``WRSR``). During these commands, the flash is in a unreadable state. The CPU and the cache have to wait until the writing command is completed. APIs below can trigger writing commands:
|
||||
|
||||
.. only:: SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND
|
||||
- Calling non_encrypted SPI flash write API (:cpp:func:`esp_flash_write`, :cpp:func:`esp_flash_erase_region`, etc.)
|
||||
- Calling :cpp:func:`esp_flash_write_encrypted`
|
||||
|
||||
On {IDF_TARGET_NAME}, the config option :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND` allows the cache to read flash concurrently with SPI1 operations. This is an optional feature that depends on special SPI Flash models, hence disabled by default. See :ref:`auto-suspend` for more details.
|
||||
- Short operations (via SPI1, includes non-writing flash commands). APIs below can trigger short operations:
|
||||
|
||||
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`. These constraints will cause more IRAM/DRAM usages.
|
||||
.. list::
|
||||
|
||||
.. only:: SOC_SPIRAM_XIP_SUPPORTED
|
||||
- Calling non_encrypted SPI flash read API (:cpp:func:`esp_flash_read`, etc.)
|
||||
:esp32: - Or other drivers on SPI1 bus for user defined SPI operations (enable experimental feature :ref:`CONFIG_SPI_FLASH_SHARE_SPI1_BUS`)
|
||||
|
||||
On {IDF_TARGET_NAME}, the config options :ref:`CONFIG_SPIRAM_XIP_FROM_PSRAM` (disabled by default) allows the cache to read/write PSRAM concurrently with SPI1 operations. See :ref:`xip_from_psram` for more details.
|
||||
- Cache read (via SPI0). Following API and operations can trigger cache read:
|
||||
|
||||
If these options are 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`. These constraints will cause more IRAM/DRAM usages.
|
||||
- Code execution from SPI Flash or PSRAM
|
||||
- Fetch static data of .data/.rodata/.bss segment from SPI Flash or PSRAM
|
||||
- All other read/write operation to the PSRAM via the heap or `esp_himem`
|
||||
- Read from area mapped to SPI Flash, includes:
|
||||
|
||||
.. _impact_disabled_cache:
|
||||
- mmap-like functions: :cpp:func:`spi_flash_mmap`, :cpp:func:`spi_flash_mmap_pages`, :cpp:func:`esp_mmu_map`, :cpp:func:`bootloader_mmap`, and :cpp:func:`esp_partition_mmap`.
|
||||
- Functions relying on :cpp:func:`spi_flash_mmap`: :cpp:func:`esp_partition_find`, :cpp:func:`esp_partition_register_external`.
|
||||
- Encrypted flash read/write APIs :cpp:func:`esp_flash_read_encrypted` and :cpp:func:`esp_flash_write_encrypted` (on esp32, or for data validation).
|
||||
|
||||
When the Caches Are Disabled
|
||||
----------------------------
|
||||
.. only:: esp32
|
||||
|
||||
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.
|
||||
Caches are disabled during all SPI1 operations. Most tasks will be disabled, and access to Flash/PSRAM is forbidden. See :ref:`cache_disabled` for more details.
|
||||
|
||||
.. only:: esp32c3
|
||||
.. only:: not esp32
|
||||
|
||||
.. note::
|
||||
All SPI flash APIs are exclusive to each other by some internal mutex provided by the driver.
|
||||
|
||||
When :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND` is enabled, these APIs will not disable the caches. The hardware will handle the arbitration between them.
|
||||
For all SPI1 operations (read/write), caches are disabled during these operations by default. Most tasks will be disabled, and access to Flash/PSRAM is forbidden. See :ref:`cache_disabled` for more details.
|
||||
|
||||
.. only:: SOC_SPIRAM_XIP_SUPPORTED
|
||||
|
||||
.. note::
|
||||
.. only:: SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND or SOC_SPIRAM_XIP_SUPPORTED
|
||||
|
||||
When :ref:`CONFIG_SPIRAM_XIP_FROM_PSRAM` is enabled, these APIs will not disable the caches.
|
||||
Some options help reduce the impact of cache disabling. The impact of write operations differs between modes.
|
||||
|
||||
.. only:: SOC_SPIRAM_XIP_SUPPORTED
|
||||
|
||||
- **XIP from PSRAM**: In this mode, all segments that were previously executed from Flash are loaded and executed from PSRAM instead. As a result, the cache can remain enabled while the flash is being erased or written, and code execution is not affected by write operations in most cases. See :ref:`xip_from_psram` for more details.
|
||||
|
||||
.. only:: SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND
|
||||
|
||||
- **Auto Suspend**: In this mode, when cache access to flash misses during flash erase/write operations, it is allowed to suspend the flash writing to read from it transparently with some latency. As a result, caches are kept enabled and code execution won't be affected so much during writing operations.
|
||||
|
||||
This is an optional feature that depends on special SPI Flash models, hence disabled by default. See :doc:`spi_flash_optional_feature` and :ref:`auto-suspend` for more details.
|
||||
|
||||
|
||||
See :ref:`esp_flash_os_func` and :ref:`spi_bus_lock` for the detailed information of software implementation.
|
||||
|
||||
|
||||
.. _cache_disabled:
|
||||
|
||||
Cache Disabled (Default)
|
||||
------------------------
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
Caches are disabled during SPI1 operations. All SPI1 operations will automatically and transparently disable the caches.
|
||||
|
||||
.. only:: not esp32
|
||||
|
||||
By default, caches are disabled during SPI1 operations (read/write). All SPI1 operations will automatically and transparently disable the caches.
|
||||
|
||||
.. only:: SOC_HP_CPU_HAS_MULTIPLE_CORES
|
||||
|
||||
The way that these APIs disable the caches suspends 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.
|
||||
When the caches are disabled, all non-IRAM-safe interrupts will be disabled, and all other tasks are suspended. The other core will be polling in a busy loop. Only IRAM-safe interrupt handlers will be executed. These will be restored when the Flash operation completes.
|
||||
|
||||
.. only:: not SOC_HP_CPU_HAS_MULTIPLE_CORES
|
||||
|
||||
The way that these APIs disable the caches also disables non-IRAM-safe interrupts. These will be restored until the Flash operation completes.
|
||||
When the caches are disabled, all non-IRAM-safe interrupts will be disabled, and all other tasks are suspended. Only IRAM-safe interrupt handlers will be executed. These will be restored when the Flash operation completes.
|
||||
|
||||
See also :ref:`esp_flash_os_func` and :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.
|
||||
See :ref:`iram-safe-interrupt-handlers` for information on how to prevent an interrupt handler from being disabled when the cache is disabled.
|
||||
|
||||
When the cache is disabled, all CPUs should execute code and access data only from internal RAM. 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:
|
||||
|
||||
@@ -66,7 +95,7 @@ For interrupt handlers which need to execute when the cache is disabled (e.g., f
|
||||
|
||||
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`.
|
||||
|
||||
If a function or symbol is not correctly put into IRAM/DRAM, and the interrupt handler reads from the flash cache during a flash operation, it will cause a crash due to Illegal Instruction exception (for code which should be in IRAM) or garbage data to be read (for constant data which should be in DRAM).
|
||||
If a function or symbol is not correctly put into IRAM/DRAM, and the interrupt handler reads from the flash cache during a flash operation, it will cause a crash. This may be due to an Illegal Instruction exception (for code which should be in IRAM) or garbage data being read (for constant data which should be in DRAM).
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -75,15 +104,16 @@ If a function or symbol is not correctly put into IRAM/DRAM, and the interrupt h
|
||||
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 will not see the corresponding hardware event happening.
|
||||
If the ``ESP_INTR_FLAG_IRAM`` flag is not set when registering, the interrupt handler will not be 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, the corresponding hardware events will not occur.
|
||||
|
||||
.. only:: SOC_DMA_CAN_ACCESS_FLASH
|
||||
|
||||
When DMA Read Data from Flash
|
||||
-----------------------------
|
||||
|
||||
When DMA is reading data from Flash, erase/write operations from SPI1 take higher priority in hardware, resulting in unpredictable data read by DMA if auto-suspend is not enabled. It is recommended to stop DMA access to Flash before erasing or writing to it. If DMA cannot be stopped (for example, the LCD needs to continuously refresh image data stored in Flash), it is advisable to copy such data to PSRAM or internal SRAM.
|
||||
The Flash device doesn't allow reading while it is being erased/programmed, even when the data is not in the region being erased/programmed.
|
||||
|
||||
When the flash is being erased/programmed, the Flash data read by DMA is unpredictable. It is recommended to stop DMA access to Flash before erasing or writing to it. If DMA cannot be stopped (for example, the LCD needs to continuously refresh image data stored in Flash), it is advisable to copy such data to PSRAM or internal SRAM.
|
||||
|
||||
.. only:: SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND
|
||||
|
||||
@@ -92,3 +122,4 @@ If the ``ESP_INTR_FLAG_IRAM`` flag is not set when registering, the interrupt ha
|
||||
.. only:: SOC_SPIRAM_XIP_SUPPORTED
|
||||
|
||||
.. include:: xip_from_psram.inc
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ The support for ESP32-P4 may be added in the future.
|
||||
|
||||
List of flash chips that support this feature:
|
||||
|
||||
1. XM25QxxC series
|
||||
1. XM25xxD series
|
||||
2. GD25QxxE series
|
||||
3. FM25Q32
|
||||
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
.. _xip_from_psram:
|
||||
|
||||
XIP from PSRAM Feature
|
||||
----------------------
|
||||
Executing Code from PSRAM
|
||||
-------------------------
|
||||
|
||||
If :ref:`CONFIG_SPIRAM_XIP_FROM_PSRAM` is enabled, the flash ``.text`` sections (used for instructions) and the flash ``.rodata`` sections (used for read only data) will be placed in PSRAM.
|
||||
Select :ref:`CONFIG_SPIRAM_XIP_FROM_PSRAM` config to enable this mode. In this mode, code is executed from PSRAM, and the cache will not be disabled during write APIs in most cases.
|
||||
|
||||
The corresponding virtual memory range will be mapped to PSRAM.
|
||||
In this mode, the flash ``.text`` sections (used for instructions) and the flash ``.rodata`` sections (used for read-only data) will be loaded into PSRAM at startup. The corresponding virtual addresses will be mapped to PSRAM. You do not need to ensure that code and data executed while the flash is being erased or programmed reside in IRAM.
|
||||
|
||||
If both of the above options are enabled, the Cache won't be disabled during an SPI1 Flash operation. You don't need to make sure ISRs, ISR callbacks and involved data are placed in internal RAM.
|
||||
Exception: Cache-Mapped Regions in Flash
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Due to the restriction from SPI Nor Flash parts, access to cache mapped regions in flash (mapped via APIs like spi_flash_mmap) is still not allowed while the flash is being erased/written, regardless of whether the erase/write region and the mapped region overlap. In this case, cache should still be disabled to prevent reading corrupted data from the cache.
|
||||
|
||||
To prevent cache disabling, a lock is implemented inside the SPI Flash driver to ensure mutual exclusion between cache mapping and flash writing, and most ESP-IDF APIs that perform flash mapping use this flag. If mmap-like APIs are called by yourself, you can specify this flag :cpp:enumerator:`SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE` to prevent cache disabling. You cannot use this flag in a task that uses ``esp_flash_erase_*`` or ``esp_flash_write`` between ``spi_flash_mmap`` and ``spi_flash_munmap`` (regardless of whether the write region and mapped region overlap), otherwise it will cause a deadlock. See :ref:`blocks_write_flag` for more details about the flag.
|
||||
|
||||
If mmap-like APIs are called without this flag, the cache will still be disabled when flash erasing or writing happens.
|
||||
|
||||
Reference in New Issue
Block a user