mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
Merge branch 'fix/mmap_cache_flash_wr_v5.5' into 'release/v5.5'
fix(mmap): fixed mmap read data wrong when flash being erased/written and cache not disabled (v5.5) See merge request espressif/esp-idf!50144
This commit is contained in:
@@ -172,6 +172,31 @@ flash 在 {IDF_TARGET_CACHE_SIZE} 页进行映射。内存映射硬件既可将
|
||||
|
||||
由于 mmap 是由 cache 支持的,因此,mmap 也仅能用在主 flash 上。
|
||||
|
||||
.. only:: not esp32
|
||||
|
||||
.. _blocks_write_flag:
|
||||
|
||||
关于 :cpp:enumerator:`SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE` 标志
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
当缓存映射存在时发生 flash 擦除/写入,通常会导致某些缓存区域被无效化并重新加载。为提高性能,在确定以下条件时,建议指定此标志:
|
||||
|
||||
1. 此映射将在短时间内结束,且
|
||||
2. 在映射结束之前,不需要擦除或写入 flash。
|
||||
|
||||
此标志将阻止所有写入,直到对应的 munmap 被调用。大多数依赖于 flash 映射的 ESP-IDF API 内部使用此标志。
|
||||
|
||||
.. only:: SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND or SOC_SPIRAM_XIP_SUPPORTED
|
||||
|
||||
此标志还有助于在使用以下模式时防止缓存被禁用。有关更多详细信息,请参阅相应文档。
|
||||
|
||||
.. list::
|
||||
|
||||
:SOC_SPIRAM_XIP_SUPPORTED: - :ref:`xip_from_psram`
|
||||
:SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND: - :ref:`auto-suspend`
|
||||
|
||||
此标志通过 SPI Flash 驱动程序中的锁来实现。当调用带有此标志的 :cpp:func:`spi_flash_mmap` (或类似 mmap 的 API)时获取锁,直到对应的 unmap 被调用时释放。内部有一个引用计数器,允许对 flash 进行并发映射。只有在最后一个带有此标志的 flash 映射被撤销(计数器等于 0)后,flash 擦除/写入 API 才能开始执行。
|
||||
|
||||
SPI flash 实现
|
||||
--------------
|
||||
|
||||
|
||||
@@ -1,60 +1,90 @@
|
||||
.. _concurrency-constraints-flash:
|
||||
|
||||
SPI1 flash 并发约束
|
||||
=========================================
|
||||
SPI0/1 上 Flash 的并发约束
|
||||
===========================================
|
||||
|
||||
:link_to_translation:`en:[English]`
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
指令/数据 cache(用以执行固件)与 SPI1 外设(由像 SPI flash 驱动一样的驱动程序控制)共享 SPI0/1 总线。因此,SPI1 外设上的操作会对整个系统造成显著的影响。这类操作包括调用 SPI flash API 或者 SPI1 总线上的其他驱动、任何 flash 操作(如读取、写入、擦除)或是由其他用户定义的 SPI 操作(对主 flash 或是其他 SPI 从机)。
|
||||
SPI0/1 总线在缓存和 SPI1 外设(由包括此 SPI Flash 驱动在内的驱动程序控制)之间共享。对 SPI1 的操作可能会对缓存以及整个系统造成重大影响。连接到其他 SPI 总线的 flash 芯片没有此类约束和影响,不在本文档的讨论范围中。
|
||||
|
||||
.. only:: not (esp32c3 or SOC_SPIRAM_XIP_SUPPORTED)
|
||||
SPI0/1 总线上可能发生三种活动:
|
||||
|
||||
在 {IDF_TARGET_NAME} 上,flash 读取/写入/擦除时,必须禁用 cache。
|
||||
- Flash 写入操作(通过 SPI1)。例如,擦除、页面编程或状态寄存器写入命令(例如,``SE``、``PP`` 和 ``WRSR``)。在这些命令期间,flash 处于不可读状态。CPU 和缓存必须等待直到写入命令完成。以下 API 可以触发写入命令:
|
||||
|
||||
.. only:: SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND
|
||||
- 调用非加密 SPI flash 写入 API(:cpp:func:`esp_flash_write`、:cpp:func:`esp_flash_erase_region` 等)
|
||||
- 调用 :cpp:func:`esp_flash_write_encrypted`
|
||||
|
||||
在 {IDF_TARGET_NAME} 上,配置选项 :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND` 允许 flash/PSRAM 的 cache 访问和 SPI1 的操作并发执行。该选项是可选的,依赖于特定的 SPI Flash 型号,因此默认是关闭的。请参阅 :ref:`auto-suspend`,查看详细信息。
|
||||
- 短操作(通过 SPI1,包括非写入 flash 命令)。以下 API 可以触发短操作:
|
||||
|
||||
禁用该选项时,在读取/写入/擦除 flash 期间,必须禁用 cache。使用驱动访问 SPI1 的相关约束参见 :ref:`impact_disabled_cache`。这些约束会带来更多的 IRAM/DRAM 消耗。
|
||||
.. list::
|
||||
|
||||
.. only:: SOC_SPIRAM_XIP_SUPPORTED
|
||||
- 调用非加密 SPI flash 读取 API(:cpp:func:`esp_flash_read` 等)
|
||||
:esp32: - 或 SPI1 总线上的其他驱动程序用于用户定义的 SPI 操作(启用实验性功能 :ref:`CONFIG_SPI_FLASH_SHARE_SPI1_BUS`)
|
||||
|
||||
在 {IDF_TARGET_NAME} 上,启用配置选项 :ref:`CONFIG_SPIRAM_XIP_FROM_PSRAM` (默认禁用)后将允许 flash/PSRAM 的 cache 访问和 SPI1 的操作并发执行。请参阅 :ref:`xip_from_psram`,查看详细信息。
|
||||
- 缓存读取(通过 SPI0)。以下 API 和操作可以触发缓存读取:
|
||||
|
||||
禁用该选项时,在读取/写入/擦除 flash 期间,必须禁用 cache。使用驱动访问 SPI1 的相关约束参见 :ref:`impact_disabled_cache`。这些约束会带来更多的 IRAM/DRAM 消耗。
|
||||
- 从 SPI Flash 或 PSRAM 执行代码
|
||||
- 从 SPI Flash 或 PSRAM 获取 .data/.rodata/.bss 段的静态数据
|
||||
- 通过堆或 `esp_himem` 对 PSRAM 的所有其他读/写操作
|
||||
- 从映射到 SPI Flash 的区域读取,包括:
|
||||
|
||||
.. _impact_disabled_cache:
|
||||
- 类似 mmap 的函数::cpp:func:`spi_flash_mmap`、:cpp:func:`spi_flash_mmap_pages`、:cpp:func:`esp_mmu_map`、:cpp:func:`bootloader_mmap` 和 :cpp:func:`esp_partition_mmap`。
|
||||
- 依赖 :cpp:func:`spi_flash_mmap` 的函数::cpp:func:`esp_partition_find`、:cpp:func:`esp_partition_register_external`。
|
||||
- 加密 flash 读/写 API::cpp:func:`esp_flash_read_encrypted` 和 :cpp:func:`esp_flash_write_encrypted` (在 esp32 上,或用于数据验证)。
|
||||
|
||||
禁用 cache 时
|
||||
----------------------------
|
||||
.. only:: esp32
|
||||
|
||||
此时,在 flash 擦写操作中,所有的 CPU 都只能执行 IRAM 中的代码,而且必须从 DRAM 中读取数据。如果使用本文档中的 API 函数,上述限制将自动生效且透明(无需额外关注),但这些限制可能会影响系统中的其他任务的性能。
|
||||
在所有 SPI1 操作期间缓存会被禁用,因此无法访问 Flash/PSRAM,大多数任务将被禁用。有关更多详细信息,请参阅 :ref:`cache_disabled`。
|
||||
|
||||
.. only:: esp32c3
|
||||
.. only:: not esp32
|
||||
|
||||
.. note::
|
||||
所有 SPI flash API 通过驱动程序提供的某些内部互斥锁实现互斥访问。
|
||||
|
||||
启用 :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND` 时,不会禁用 cache,其中的操作将通过硬件仲裁器来协调。
|
||||
对于所有 SPI1 操作(读/写),默认情况下在这些操作期间缓存会被禁用,因此无法访问 Flash/PSRAM,大多数任务将被禁用。有关更多详细信息,请参阅 :ref:`cache_disabled`。
|
||||
|
||||
.. only:: SOC_SPIRAM_XIP_SUPPORTED
|
||||
|
||||
.. note::
|
||||
.. only:: SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND or SOC_SPIRAM_XIP_SUPPORTED
|
||||
|
||||
启用 :ref:`CONFIG_SPIRAM_XIP_FROM_PSRAM` 选项后,不会禁用 cache。
|
||||
有一些选项可以帮助减轻缓存禁用的影响。写入操作的影响在不同模式下是不同的。
|
||||
|
||||
.. only:: SOC_SPIRAM_XIP_SUPPORTED
|
||||
|
||||
- **XIP from PSRAM**:在此模式下,所有过去从 Flash 执行的段都改为从 PSRAM 加载和执行。因此,缓存能在 flash 擦除/写入期间保持启用状态,代码执行在大多数情况下不会受到写入操作的影响。有关更多详细信息,请参阅 :ref:`xip_from_psram`。
|
||||
|
||||
.. only:: SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND
|
||||
|
||||
- **Auto Suspend**:在此模式下,当 flash 区域发生缓存未命中时,允许暂停 flash 写入以透明地从中读取,但会有一些延迟。因此,缓存保持启用状态,代码在写入操作期间影响不会很大。
|
||||
|
||||
这是一个可选功能,依赖于特殊的 SPI Flash 型号,因此默认禁用。有关更多详细信息,请参阅 :doc:`spi_flash_optional_feature` 和 :ref:`auto-suspend`。
|
||||
|
||||
|
||||
有关软件实现的详细信息,请参阅 :ref:`esp_flash_os_func` 和 :ref:`spi_bus_lock`。
|
||||
|
||||
|
||||
.. _cache_disabled:
|
||||
|
||||
缓存禁用(默认)
|
||||
------------------------
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
在 SPI1 操作期间缓存会被禁用。所有 SPI1 操作将自动透明地禁用缓存。
|
||||
|
||||
.. only:: not esp32
|
||||
|
||||
默认情况下,在 SPI1 操作期间缓存会被禁用。所有 SPI1 操作将自动透明地禁用缓存。
|
||||
|
||||
.. only:: SOC_HP_CPU_HAS_MULTIPLE_CORES
|
||||
|
||||
为避免意外读取 flash cache,一个 CPU 在启动 flash 写入或擦除操作时,另一个 CPU 将阻塞。在 flash 操作完成前,会禁用所有在 CPU 上非 IRAM 安全的中断。
|
||||
当禁用缓存时,所有非 IRAM 安全的中断将被禁用,所有其他任务将被暂停。另一个核心将在一个忙循环中空转。只有 IRAM 安全的中断处理程序将被执行。这些将在 Flash 操作完成时恢复。
|
||||
|
||||
.. only:: not SOC_HP_CPU_HAS_MULTIPLE_CORES
|
||||
|
||||
为避免意外读取 flash cache,在 flash 操作完成前,所有 CPU 上,会禁用所有在 CPU 上非 IRAM 安全的中断。
|
||||
当禁用缓存时,所有非 IRAM 安全的中断将被禁用,所有其他任务将被暂停。只有 IRAM 安全的中断处理程序将被执行。这些将在 Flash 操作完成时恢复。
|
||||
|
||||
另请参阅 :ref:`esp_flash_os_func` 和 :ref:`spi_bus_lock`。
|
||||
有关如何在禁用缓存时防止中断处理程序被禁用的信息,请参阅 :ref:`iram-safe-interrupt-handlers`。
|
||||
|
||||
除 SPI0/1 以外,SPI 总线上的其他 flash 芯片则不受这种限制。
|
||||
|
||||
请参阅 :ref:`应用程序内存分布 <memory-layout>`,查看内部 RAM(如 IRAM、DRAM)和 flash cache 的区别。
|
||||
当禁用缓存时,所有 CPU 应该只从内部 RAM 执行代码和访问数据。有关内部 RAM(例如,IRAM、DRAM)和 flash 缓存之间的差异,请参阅 :ref:`应用程序内存布局 <memory-layout>` 文档。
|
||||
|
||||
|
||||
.. _iram-safe-interrupt-handlers:
|
||||
@@ -79,10 +109,12 @@ IRAM 安全中断处理程序
|
||||
|
||||
.. only:: SOC_DMA_CAN_ACCESS_FLASH
|
||||
|
||||
当 DMA 也可以访问 Flash 中的数据时
|
||||
----------------------------------
|
||||
当 DMA 从 Flash 读取数据时
|
||||
-----------------------------
|
||||
|
||||
当 DMA 正在从 Flash 中读取数据时,来自 SPI1 的擦/写操作优先级会更高,如果 Flash 的 auto-suspend 功能没有开启,将会导致 DMA 读到错误的数据。建议在擦写 Flash 之前先停止 DMA 对 Flash 的访问。如果 DMA 不可以停止,比如 LCD 需要持续刷新保存在 Flash 中的图像数据,建议将此类数据拷贝到 PSRAM 或者内部的 SRAM 中。
|
||||
Flash 器件不允许在擦除/编程时读取,即使数据不在正在被擦除/编程的区域中。
|
||||
|
||||
当 flash 正在被擦除/编程时,DMA 读取的 Flash 数据是不可预测的。建议在擦除或写入之前停止 DMA 对 Flash 的访问。如果无法停止 DMA(例如,LCD 需要持续刷新存储在 Flash 中的图像数据),建议将此类数据复制到 PSRAM 或内部 SRAM。
|
||||
|
||||
|
||||
.. only:: SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND
|
||||
|
||||
@@ -40,7 +40,7 @@ flash 的可选功能
|
||||
|
||||
支持此功能的 flash 芯片列表:
|
||||
|
||||
1. XM25QxxC 系列
|
||||
1. XM25xxD 系列
|
||||
2. GD25QxxE 系列
|
||||
3. FM25Q32
|
||||
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
.. _xip_from_psram:
|
||||
|
||||
在 PSRAM 中执行代码
|
||||
从 PSRAM 执行代码功能
|
||||
----------------------
|
||||
|
||||
启用 :ref:`CONFIG_SPIRAM_XIP_FROM_PSRAM` 选项后,flash 中 ``.text`` 部分的数据(用于指令)和 flash 中 ``.rodata`` 部分的数据(用于只读数据)将被放入 PSRAM。
|
||||
选择 :ref:`CONFIG_SPIRAM_XIP_FROM_PSRAM` 配置以启用此模式。在此模式下,代码从 PSRAM 执行,在大多数情况下,缓存不会在写入 API 期间被禁用。
|
||||
|
||||
相应的虚拟内存地址将被映射到 PSRAM。
|
||||
在此模式下,flash ``.text`` 段(用于指令)和 flash ``.rodata`` 段(用于只读数据)将在启动时加载到 PSRAM。相应的虚拟地址将映射到 PSRAM。您无需确保在 flash 被擦除/编程时执行的代码/数据位于 IRAM 中。
|
||||
|
||||
如果同时启用以上两个选项,则在 SPI1 flash 操作期间 cache 不会被禁用,无需确保 ISR、ISR 回调及相关数据放置在内部 RAM 中。
|
||||
例外:当缓存映射 flash 中的区域时
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
由于 SPI Nor Flash 器件的限制,在 flash 被擦除/写入时,仍然不允许访问 Flash 中的缓存映射区域(通过 spi_flash_mmap 等 API 映射),无论擦写区域和映射区域是否重合。在这种情况下,在擦写 Flash 时仍应禁用缓存以防止从缓存读取错误的数据。
|
||||
|
||||
为了防止缓存禁用,在 SPI Flash 驱动程序中实现了一个锁,以确保缓存映射的存在和 Flash 写入是互斥的,大多数进行了 Flash 映射的 ESP-IDF API 都使用了该标志。如果您自己调用类似 mmap 的 API,可以指定此标志 :cpp:enumerator:`SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE` 以防止缓存禁用。您不能在 ``spi_flash_mmap`` 和 ``spi_flash_munmap`` 之间使用 ``esp_flash_erase_*`` 或者 ``esp_flash_write`` 的任务中使用此标志(无论写入区域和映射区域是否重叠),否则会造成死锁。有关该标志的详细信息,请参阅 :ref:`blocks_write_flag`。
|
||||
|
||||
如果在没有此标志的情况下调用类似 mmap 的 API,当 Flash 发生擦除或者写入时,缓存仍将被禁用。
|
||||
|
||||
Reference in New Issue
Block a user