docs: updated CN trans for memory-types and external-ram

pull/8238/head
daiziyan 2021-12-02 10:55:27 +08:00
rodzic 7d43be9675
commit 9e7f31337d
4 zmienionych plików z 240 dodań i 149 usunięć

Wyświetl plik

@ -18,9 +18,13 @@ Hardware
{IDF_TARGET_NAME} supports SPI PSRAM (Psuedostatic RAM) connected in parallel with the SPI flash chip. While {IDF_TARGET_NAME} is capable of supporting several types of RAM chips, ESP-IDF currently only supports Espressif branded PSRAM chips (e.g., ESP-PSRAM32, ESP-PSRAM64, etc).
.. note:: Some PSRAM chips are 1.8 V devices and some are 3.3 V. The working voltage of the PSRAM chip must match the working voltage of the flash component. Consult the datasheet for your PSRAM chip and {IDF_TARGET_NAME} device to find out the working voltages. For a 1.8 V PSRAM chip, make sure to either set the MTDI pin to a high signal level on bootup, or program {IDF_TARGET_NAME} eFuses to always use the VDD_SIO level of 1.8 V. Not doing this can damage the PSRAM and/or flash chip.
.. note::
.. note:: Espressif produces both modules and system-in-package chips that integrate compatible PSRAM and flash and are ready to mount on a product PCB. Consult the Espressif website for more information.
Some PSRAM chips are 1.8 V devices and some are 3.3 V. The working voltage of the PSRAM chip must match the working voltage of the flash component. Consult the datasheet for your PSRAM chip and {IDF_TARGET_NAME} device to find out the working voltages. For a 1.8 V PSRAM chip, make sure to either set the MTDI pin to a high signal level on bootup, or program {IDF_TARGET_NAME} eFuses to always use the VDD_SIO level of 1.8 V. Not doing this can damage the PSRAM and/or flash chip.
.. note::
Espressif produces both modules and system-in-package chips that integrate compatible PSRAM and flash and are ready to mount on a product PCB. Consult the Espressif website for more information.
For specific details about connecting the SoC or module pins to an external PSRAM chip, consult the SoC or module datasheet.

Wyświetl plik

@ -3,16 +3,18 @@
Memory Types
------------
:link_to_translation:`zh_CN:[中文]`
{IDF_TARGET_NAME} chip has multiple memory types and flexible memory mapping features. This section describes how ESP-IDF uses these features by default.
ESP-IDF distinguishes between instruction memory bus (IRAM, IROM, RTC FAST memory) and data memory bus (DRAM, DROM). Instruction memory is executable, and can only be read or written via 4-byte aligned words. Data memory is not executable and can be accessed via individual byte operations. For more information about the different memory buses consult the {IDF_TARGET_NAME} Technical Reference Manual* > *System and Memory* [`PDF <{IDF_TARGET_TRM_EN_URL}#sysmem>`__].
ESP-IDF distinguishes between instruction memory bus (IRAM, IROM, RTC FAST memory) and data memory bus (DRAM, DROM). Instruction memory is executable, and can only be read or written via 4-byte aligned words. Data memory is not executable and can be accessed via individual byte operations. For more information about the different memory buses consult the *{IDF_TARGET_NAME} Technical Reference Manual* > *System and Memory* [`PDF <{IDF_TARGET_TRM_EN_URL}#sysmem>`__].
.. _dram:
DRAM (Data RAM)
^^^^^^^^^^^^^^^
Non-constant static data (.data) and zero-initialized data (.bss) is placed by the linker into Internal SRAM as data memory. Remaining space in this region is used for the runtime heap.
Non-constant static data (.data) and zero-initialized data (.bss) is placed by the linker into Internal SRAM as data memory. The remaining space in this region is used for the runtime heap.
.. only:: esp32 or esp32s2
@ -20,16 +22,17 @@ Non-constant static data (.data) and zero-initialized data (.bss) is placed by t
.. only:: esp32
The available size of the internal DRAM region is reduced by 64kB (by shifting start address to ``0x3FFC0000``) if Bluetooth stack is used. Length of this region is also reduced by 16 kB or 32kB if trace memory is used. Due to some memory fragmentation issues caused by ROM, it is also not possible to use all available DRAM for static allocations - however the remaining DRAM is still available as heap at runtime.
The available size of the internal DRAM region is reduced by 64 KB (by shifting start address to ``0x3FFC0000``) if Bluetooth stack is used. Length of this region is also reduced by 16 KB or 32 KB if trace memory is used. Due to some memory fragmentation issues caused by ROM, it is also not possible to use all available DRAM for static allocations - however the remaining DRAM is still available as heap at runtime.
.. only:: not esp32
.. note::
The maximum statically allocated DRAM size is reduced by the :ref:`iram` size of the compiled application. The available heap memory at runtime is reduced by the total static IRAM and DRAM usage of the application.
The maximum statically allocated DRAM size is reduced by the :ref:`iram` size of the compiled application. The available heap memory at runtime is reduced by the total static IRAM and DRAM usage of the application.
Constant data may also be placed into DRAM, for example if it is used in an non-flash-safe ISR (see explanation under :ref:`how-to-place-code-in-iram`).
"noinit" DRAM
=============
@ -37,12 +40,13 @@ The macro ``__NOINIT_ATTR`` can be used as attribute to place data into ``.noini
.. only:: esp32
By applying the ``EXT_RAM_NOINIT_ATTR`` macro, Non-initialized value could also be placed in external RAM. To do this, the :ref:`CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY` needs to be enabled. See :ref:`external_ram_config_noinit`. If the :ref:`CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY` is not enabled, ``EXT_RAM_NOINIT_ATTR`` will behave just as ``__NOINIT_ATTR``, it will make data to be placed into ``.noinit`` segment in internal RAM.
By applying the ``EXT_RAM_NOINIT_ATTR`` macro, non-initialized value could also be placed in external RAM. To do this, the :ref:`CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY` needs to be enabled. See :ref:`external_ram_config_noinit`. If the :ref:`CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY` is not enabled, ``EXT_RAM_NOINIT_ATTR`` will behave just as ``__NOINIT_ATTR``, it will make data to be placed into ``.noinit`` segment in internal RAM.
Example::
__NOINIT_ATTR uint32_t noinit_data;
.. _iram:
IRAM (Instruction RAM)
@ -50,25 +54,29 @@ IRAM (Instruction RAM)
.. only:: esp32
ESP-IDF allocates part of Internal SRAM0 region for instruction RAM. The region is defined in *{IDF_TARGET_NAME} Technical Reference Manual* > *System and Memory* > *Embedded Memory* [`PDF <{IDF_TARGET_TRM_EN_URL}#sysmem>`__]. Except for the first 64 kB block which is used for PRO and APP MMU caches, the rest of this memory range (i.e. from ``0x40080000`` to ``0x400A0000``) is used to store parts of the application which need to run from RAM.
ESP-IDF allocates part of the Internal SRAM0 region for instruction RAM. The region is defined in *{IDF_TARGET_NAME} Technical Reference Manual* > *System and Memory* > *Embedded Memory* [`PDF <{IDF_TARGET_TRM_EN_URL}#sysmem>`__]. Except for the first 64 KB block which is used for PRO and APP MMU caches, the rest of this memory range (i.e. from ``0x40080000`` to ``0x400A0000``) is used to store parts of the application which need to run from RAM.
.. only:: esp32s2
ESP-IDF allocates part of Internal SRAM region for instruction RAM. The region is defined in *{IDF_TARGET_NAME} Technical Reference Manual* > *System and Memory* > *Internal Memory* [`PDF <{IDF_TARGET_TRM_EN_URL}#sysmem>`__]. Except for the first block (up to 32 kB) which is used for MMU cache, the rest of this memory range is used to store parts of application which need to run from RAM.
ESP-IDF allocates part of the Internal SRAM region for instruction RAM. The region is defined in *{IDF_TARGET_NAME} Technical Reference Manual* > *System and Memory* > *Internal Memory* [`PDF <{IDF_TARGET_TRM_EN_URL}#sysmem>`__]. Except for the first block (up to 32 KB) which is used for MMU cache, the rest of this memory range is used to store parts of application which need to run from RAM.
.. only:: not esp32
.. note:: Any internal SRAM which is not used for Instruction RAM will be made available as :ref:`dram` for static data and dynamic allocation (heap).
.. note::
Why place code in IRAM
======================
Any internal SRAM which is not used for Instruction RAM will be made available as :ref:`dram` for static data and dynamic allocation (heap).
Cases when parts of application should be placed into IRAM:
When to place code in IRAM
================================
Cases when parts of the application should be placed into IRAM:
- Interrupt handlers must be placed into IRAM if ``ESP_INTR_FLAG_IRAM`` is used when registering the interrupt handler. For more information, see :ref:`iram-safe-interrupt-handlers`.
- Some timing critical code may be placed into IRAM to reduce the penalty associated with loading the code from flash. {IDF_TARGET_NAME} reads code and data from flash via the MMU cache. In some cases, placing a function into IRAM may reduce delays caused by a cache miss and significantly improve that function's performance.
.. _how-to-place-code-in-iram:
How to place code in IRAM
@ -76,7 +84,7 @@ How to place code in IRAM
Some code is automatically placed into the IRAM region using the linker script.
If some specific application code needs to be placed into IRAM, it can be done by using the :doc:`linker-script-generation` feature and adding a linker script fragment file to your component that targets entire source files or functions with the ``noflash`` placement. See the :doc:`linker-script-generation` docs for more information.
If some specific application code needs to be placed into IRAM, it can be done by using the :doc:`linker-script-generation` feature and adding a linker script fragment file to your component that targets at the entire source files or functions with the ``noflash`` placement. See the :doc:`linker-script-generation` docs for more information.
Alternatively, it's possible to specify IRAM placement in the source code using the ``IRAM_ATTR`` macro::
@ -99,57 +107,56 @@ There are some possible issues with placement in IRAM, that may cause problems w
const static char *MSG = DRAM_STR("I am a string stored in RAM");
}
Note that knowing which data should be marked with ``DRAM_ATTR`` can be hard, the compiler will sometimes recognize that a variable or expression is constant (even if it is not marked ``const``) and optimize it into flash, unless it is marked with ``DRAM_ATTR``.
Note that knowing which data should be marked with ``DRAM_ATTR`` can be hard, the compiler will sometimes recognize that a variable or expression is constant (even if it is not marked ``const``) and optimize it into flash, unless it is marked with ``DRAM_ATTR``.
* GCC optimizations that automatically generate jump tables or switch/case lookup tables place these tables in flash. IDF by default builds all files with ``-fno-jump-tables -fno-tree-switch-conversion`` flags to avoid this.
* GCC optimizations that automatically generate jump tables or switch/case lookup tables place these tables in flash. IDF by default builds all files with `-fno-jump-tables -fno-tree-switch-conversion` flags to avoid this.
Jump table optimizations can be re-enabled for individual source files that don't need to be placed in IRAM. For instructions on how to add the -fjump-tables -ftree-switch-conversion options when compiling individual source files, see :ref:`component_build_control`
Jump table optimizations can be re-enabled for individual source files that don't need to be placed in IRAM. For instructions on how to add the ``-fno-jump-tables -fno-tree-switch-conversion`` options when compiling individual source files, see :ref:`component_build_control`.
.. _irom:
IROM (code executed from Flash)
IROM (code executed from flash)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If a function is not explicitly placed into :ref:`iram` or RTC memory, it is placed into flash. The mechanism by which Flash MMU is used to allow code execution from flash is described in *{IDF_TARGET_NAME} Technical Reference Manual* > *Memory Management and Protection Units (MMU, MPU)* [`PDF <{IDF_TARGET_TRM_EN_URL}#mpummu>`__]. As IRAM is limited, most of an application's binary code must be placed into IROM instead.
During :doc:`startup`, the bootloader (which runs from IRAM) configures the MMU flash cache to map the app's instruction code region to the instruction space. Flash accessed via the MMU is cached using some internal SRAM and accessing cached flash data is as fast as accessing other types of internal memory.
RTC fast memory
RTC FAST memory
^^^^^^^^^^^^^^^
The same region of RTC fast memory can be accessed as both instruction and data memory. Code which has to run after wake-up from deep sleep mode has to be placed into RTC memory. Please check detailed description in :doc:`deep sleep <deep-sleep-stub>` documentation.
The same region of RTC FAST memory can be accessed as both instruction and data memory. Code which has to run after wake-up from deep sleep mode has to be placed into RTC memory. Please check detailed description in :doc:`deep sleep <deep-sleep-stub>` documentation.
.. only:: esp32
RTC fast memory can only be accessed by the PRO CPU.
RTC FAST memory can only be accessed by the PRO CPU.
In single core mode, remaining RTC fast memory is added to the heap unless the option :ref:`CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP` is disabled. This memory can be used interchangeably with :ref:`DRAM`, but is slightly slower to access and not DMA capable.
In single core mode, remaining RTC FAST memory is added to the heap unless the option :ref:`CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP` is disabled. This memory can be used interchangeably with :ref:`DRAM`, but is slightly slower to access and not DMA capable.
.. only:: not esp32 and not esp8684
Remaining RTC fast memory is added to the heap unless the option :ref:`CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP` is disabled. This memory can be used interchangeably with :ref:`DRAM`, but is slightly slower to access.
Remaining RTC FAST memory is added to the heap unless the option :ref:`CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP` is disabled. This memory can be used interchangeably with :ref:`DRAM`, but is slightly slower to access.
.. _drom:
DROM (data stored in Flash)
DROM (data stored in flash)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. highlight:: c
By default, constant data is placed by the linker into a region mapped to the MMU flash cache. This is the same as the :ref:`irom` section, but is for read-only data not executable code.
The only constant data not placed into into this memory type by default are literal constants which are embedded by the compiler into application code. These are placed as the surrounding function's executable instructions.
The only constant data not placed into this memory type by default are literal constants which are embedded by the compiler into application code. These are placed as the surrounding function's executable instructions.
The ``DRAM_ATTR`` attribute can be used to force constants from DROM into the :ref:`dram` section (see above).
.. only:: SOC_RTC_SLOW_MEM_SUPPORTED
RTC slow memory
RTC Slow memory
^^^^^^^^^^^^^^^
Global and static variables used by code which runs from RTC memory must be placed into RTC slow memory. For example :doc:`deep sleep <deep-sleep-stub>` variables can be placed here instead of RTC fast memory, or code and variables accessed by the :doc:`/api-guides/ulp`.
Global and static variables used by code which runs from RTC memory must be placed into RTC Slow memory. For example :doc:`deep sleep <deep-sleep-stub>` variables can be placed here instead of RTC FAST memory, or code and variables accessed by the :doc:`/api-guides/ulp`.
The attribute macro named ``RTC_NOINIT_ATTR`` can be used to place data into this type of memory. The values placed into this section keep their value after waking from deep sleep.
@ -157,14 +164,13 @@ The ``DRAM_ATTR`` attribute can be used to force constants from DROM into the :r
RTC_NOINIT_ATTR uint32_t rtc_noinit_data;
DMA Capable Requirement
^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. highlight:: c
Most peripheral DMA controllers (e.g. SPI, sdmmc, etc.) have requirements that sending/receiving buffers should be placed in DRAM
and word-aligned. We suggest to place DMA buffers in static variables rather than in the stack. Use macro ``DMA_ATTR``
to declare global/local static variables like::
Most peripheral DMA controllers (e.g. SPI, sdmmc, etc.) have requirements that sending/receiving buffers should be placed in DRAM and word-aligned. We suggest to place DMA buffers in static variables rather than in the stack. Use macro ``DMA_ATTR`` to declare global/local static variables like::
DMA_ATTR uint8_t buffer[]="I want to send something";

Wyświetl plik

@ -8,8 +8,9 @@
简介
============
{IDF_TARGET_PSRAM_SIZE:default="Value not updated", esp32="4 MB", esp32s2="10.5 MB", esp32s3="16 MB"}
{IDF_TARGET_NAME} 提供了好几百 KB 的片上 RAM可以满足大部分需求。但有些场景可能需要更多 RAM因此 {IDF_TARGET_NAME} 另外提供了高达 4 MB 的片外 SPI RAM 存储器供用户使用。片外 RAM 已经集成到内存映射中,在某些范围内与片上 RAM 使用方式相同。
{IDF_TARGET_NAME} 提供了好几百 KB 的片上 RAM可以满足大部分需求。但有些场景可能需要更多 RAM因此 {IDF_TARGET_NAME} 另外提供了高达 {IDF_TARGET_PSRAM_SIZE} 的片外 SPI RAM 存储器供用户使用。片外 RAM 已经集成到内存映射中,在某些范围内与片上 RAM 使用方式相同。
硬件
@ -17,9 +18,13 @@
{IDF_TARGET_NAME} 支持与 SPI Flash 芯片并联的 SPI PSRAM伪静态随机存储器。虽然 {IDF_TARGET_NAME} 支持多种类型的 RAM 芯片,但 ESP-IDF 当前仅支持乐鑫品牌的 PSRAM 芯片,如 ESP-PSRAM32、ESP-PSRAM64 等。
.. note:: PSRAM 芯片的工作电压分为 1.8 V 和 3.3 V。其工作电压必须与 flash 的工作电压匹配。请查询您 PSRAM 芯片以及 {IDF_TARGET_NAME} 的技术规格书获取准确的工作电压。对于 1.8 V 的 PSRAM 芯片,请确保在启动时将 MTDI 管脚设置为高电平,或者将 {IDF_TARGET_NAME} 中的 eFuses 设置为始终使用 1.8 V 的 VDD_SIO 电平,否则有可能会损坏 PSRAM 和/或 flash 芯片。
.. note::
.. note:: 乐鑫同时提供模组和系统级封装芯片,集成了兼容的 PSRAM 和 flash可直接用于终端产品 PCB 中。如需了解更多信息,请前往乐鑫官网。
PSRAM 芯片的工作电压分为 1.8 V 和 3.3 V。其工作电压必须与 flash 的工作电压匹配。请查询您 PSRAM 芯片以及 {IDF_TARGET_NAME} 的技术规格书获取准确的工作电压。对于 1.8 V 的 PSRAM 芯片,请确保在启动时将 MTDI 管脚设置为高电平,或者将 {IDF_TARGET_NAME} 中的 eFuses 设置为始终使用 1.8 V 的 VDD_SIO 电平,否则有可能会损坏 PSRAM 和/或 flash 芯片。
.. note::
乐鑫同时提供模组和系统级封装芯片,集成了兼容的 PSRAM 和 flash可直接用于终端产品 PCB 中。如需了解更多信息,请前往乐鑫官网。
有关将 SoC 或模组管脚连接到片外 PSRAM 芯片的具体细节,请查阅 SoC 或模组技术规格书。
@ -36,19 +41,20 @@ ESP-IDF 完全支持将片外 RAM 集成到您的应用程序中。在启动并
* :ref:`external_ram_config_memory_map`
* :ref:`external_ram_config_capability_allocator`
* :ref:`external_ram_config_malloc` (default)
:esp32: * :ref:`external_ram_config_bss`
:esp32 or esp32s2: * :ref:`external_ram_config_bss`
:esp32: * :ref:`external_ram_config_noinit`
.. _external_ram_config_memory_map:
集成片外 RAM 到 {IDF_TARGET_NAME} 内存映射
-------------------------------------------
{IDF_TARGET_PSRAM_ADDR_START:default="Value not updated", esp32="0x3F800000", esp32s2="0x3F500000", esp32s3="0x3D000000"}
:ref:`CONFIG_SPIRAM_USE` 中选择 "Integrate RAM into memory map集成片外 RAM 到 {IDF_TARGET_NAME} 内存映射)" 选项。
这是集成片外 RAM 最基础的设置选项,大多数用户需要用到其他更高级的选项。
ESP-IDF 启动过程中,片外 RAM 被映射到以 0x3F800000 起始的数据地址空间(字节可寻址),空间大小正好为 SPI RAM 的大小 (4 MB)。
ESP-IDF 启动过程中,片外 RAM 被映射到以 {IDF_TARGET_PSRAM_ADDR_START} 起始的数据地址空间(字节可寻址),空间大小正好为 SPI RAM 的大小 ({IDF_TARGET_PSRAM_SIZE})。
应用程序可以通过创建指向该区域的指针手动将数据放入片外存储器,同时应用程序全权负责管理片外 SPI RAM包括协调 Buffer 的使用、防止发生损坏等。
@ -60,7 +66,7 @@ ESP-IDF 启动过程中,片外 RAM 被映射到以 0x3F800000 起始的数据
:ref:`CONFIG_SPIRAM_USE` 中选择 "Make RAM allocatable using heap_caps_malloc(..., MALLOC_CAP_SPIRAM)" 选项。
启用上述选项后,片外 RAM 被映射到地址 0x3F800000,并将这个区域添加到携带 ``MALLOC_CAP_SPIRAM`` 标志的 :doc:`堆内存分配器 </api-reference/system/mem_alloc>`
启用上述选项后,片外 RAM 被映射到地址 {IDF_TARGET_PSRAM_ADDR_START},并将这个区域添加到携带 ``MALLOC_CAP_SPIRAM`` 标志的 :doc:`堆内存分配器 </api-reference/system/mem_alloc>`
程序如果想从片外存储器分配存储空间,则需要调用 ``heap_caps_malloc(size, MALLOC_CAP_SPIRAM)``,之后可以调用 ``free()`` 函数释放这部分存储空间。
@ -85,7 +91,7 @@ ESP-IDF 启动过程中,片外 RAM 被映射到以 0x3F800000 起始的数据
由于有些内存缓冲器仅可在内部存储器中分配,因此需要使用第二个配置项 :ref:`CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL` 定义一个内部内存池,仅限显式的内部存储器分配使用(例如用于 DMA 的存储器)。常规 ``malloc()`` 将不会从该池中分配,但可以使用 :ref:`MALLOC_CAP_DMA <dma-capable-memory>```MALLOC_CAP_INTERNAL`` 标志从该池中分配存储器。
.. only:: esp32
.. only:: esp32 or esp32s2
.. _external_ram_config_bss:
@ -94,7 +100,7 @@ ESP-IDF 启动过程中,片外 RAM 被映射到以 0x3F800000 起始的数据
通过勾选 :ref:`CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY` 启用该选项,此选项配置与其它三个选项互不影响。
启用该选项后,从 0x3F800000 起始的地址空间将用于存储来自 lwip、net80211、libpp 和 bluedroid ESP-IDF 库中零初始化的数据BSS 段)。
启用该选项后,从 {IDF_TARGET_PSRAM_ADDR_START} 起始的地址空间将用于存储来自 lwip、net80211、libpp 和 bluedroid ESP-IDF 库中零初始化的数据BSS 段)。
``EXT_RAM_ATTR`` 宏应用于任何静态声明(未初始化为非零值)之后,可以将附加数据从内部 BSS 段移到片外 RAM。
@ -104,6 +110,9 @@ ESP-IDF 启动过程中,片外 RAM 被映射到以 0x3F800000 起始的数据
剩余的片外 RAM 也可以通过上述方法添加到堆分配器中。
.. only:: esp32
.. _external_ram_config_noinit:
允许 .noinit 段放入片外存储器
@ -135,7 +144,7 @@ ESP-IDF 启动过程中,片外 RAM 被映射到以 0x3F800000 起始的数据
默认情况下,片外 RAM 初始化失败将终止 ESP-IDF 启动。如果想禁用此功能,可启用 :ref:`CONFIG_SPIRAM_IGNORE_NOTFOUND` 配置选项。
.. only:: esp32
.. only:: esp32 or esp32s2
如果启用 :ref:`CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY`,忽略失败的选项将无法使用,这是因为在链接时,链接器已经向片外存储器分配标志符。

Wyświetl plik

@ -1,153 +1,225 @@
.. _memory-layout:
应用程序的内存布局
存储器类型
------------------
{IDF_TARGET_NAME} 芯片具有灵活的内存映射功能,本小节将介绍 ESP-IDF 默认使用这些功能的方式。
:link_to_translation:`en:[English]`
ESP-IDF 应用程序的代码可以放在以下内存区域之一
{IDF_TARGET_NAME} 芯片具有不同类型的存储器和灵活的存储器映射特性,本小节将介绍 ESP-IDF 默认如何使用这些功能
.. _iram:
IRAM指令 RAM
~~~~~~~~~~~~~~~~
ESP-IDF 将内部 SRAM0 区域(在技术参考手册中有定义)的一部分分配为指令 RAM。除了开始的 64kB 用作 PRO CPU 和 APP CPU 的高速缓存外,剩余内存区域(从 ``0x40080000````0x400A0000`` )被用来存储应用程序中部分需要在 RAM 中运行的代码。
一些 ESP-IDF 的组件和 WiFi 协议栈的部分代码通过链接脚本文件被存放到了这块内存区域。
如果一些应用程序的代码需要放在 IRAM 中运行,可以使用 ``IRAM_ATTR`` 宏定义进行声明。
.. code:: c
#include "esp_attr.h"
void IRAM_ATTR gpio_isr_handler(void* arg)
{
// ...
}
下面列举了应用程序中可能或者应该放入 IRAM 中运行例子。
- 当注册中断处理程序的时候设置了 ``ESP_INTR_FLAG_IRAM`` ,那么中断处理程序就必须要放在 IRAM 中运行。这种情况下ISR 只能调用存放在 IRAM 或者 ROM 中的函数。 *注意* :目前所有 FreeRTOS 的 API 都已经存放到了 IRAM 中,所以在中断中调用 FreeRTOS 的中断专属 API 是安全的。如果将 ISR 放在 IRAM 中运行,那么必须使用宏定义 ``DRAM_ATTR`` 将该 ISR 用到所有常量数据和调用的函数(包括但不限于 ``const char`` 数组)放入 DRAM 中。
- 可以将一些时间关键的代码放在 IRAM 中,这样可以缩减从 Flash 加载代码所消耗的时间。{IDF_TARGET_NAME} 是通过 32kB 的高速缓存来从外部 Flash 中读取代码和数据的,将函数放在 IRAM 中运行可以减少由高速缓存未命中引起的时间延迟。
.. _irom:
IROM代码从 Flash 中运行)
~~~~~~~~~~~~~~~~~~~~~~~~~~~
如果一个函数没有被显式地声明放在 IRAM 或者 RTC 内存中,则将其置于 Flash 中。Flash 技术参考手册中介绍了 Flash MMU 允许代码从 Flash 执行的机制。ESP-IDF 将从 Flash 中执行的代码放在 ``0x400D0000 — 0x40400000`` 区域的开始,在启动阶段,二级引导程序会初始化 Flash MMU将代码在 Flash 中的位置映射到这个区域的开头。对这个区域的访问会被透明地缓存到 ``0x40070000 — 0x40080000`` 范围内的两个 32kB 的块中。
请注意,使用 Window ABI ``CALLx`` 指令可能无法访问 ``0x40000000 — 0x40400000`` 区域以外的代码,所以要特别留意应用程序是否使用了 ``0x40400000 — 0x40800000`` 或者 ``0x40800000 — 0x40C00000`` 区域ESP-IDF 默认不会使用这两个区域。
RTC 快速内存
~~~~~~~~~~~~
从深度睡眠模式唤醒后必须要运行的代码要放在 RTC 内存中,更多信息请查阅文档 :doc:`深度睡眠 <deep-sleep-stub>`
ESP-IDF 区分了指令总线IRAM、IROM、RTC FAST memory和数据总线 (DRAM、DROM)。指令存储器是可执行的,只能通过 4 字节对齐字读取或写入。数据存储器不可执行,可以通过单独的字节操作访问。有关总线的更多信息,请参阅 *{IDF_TARGET_NAME} 技术参考手册* > *系统和存储器* [`PDF <{IDF_TARGET_TRM_CN_URL}#sysmem>`__]。
.. _dram:
DRAM数据 RAM
~~~~~~~~~~~~~~~~
^^^^^^^^^^^^^^^^^^^^^^^^
链接器将非常量静态数据和零初始化数据放入 ``0x3FFB0000 — 0x3FFF0000`` 这 256kB 的区域。注意,如果使用蓝牙堆栈,此区域会减少 64kB通过将起始地址移至 ``0x3FFC0000`` )。如果使用了内存跟踪的功能,该区域的长度还要减少 16kB 或者 32kB。放置静态数据后留在此区域中的剩余空间都用作运行时堆。
非常量静态数据(.data 段)和零初始化数据(.bss 段)由链接器放入内部 SRAM 作为数据存储。此区域中的剩余空间可在程序运行时用作堆。
常量数据也可以放在 DRAM 中,例如,用在 ISR 中的常量数据(参见上面 IRAM 部分的介绍),为此需要使用 ``DRAM_ATTR`` 宏来声明。
.. only:: esp32 or esp32s2
.. code:: c
通过应用 ``EXT_RAM_ATTR`` 宏,零初始化数据也可以放入外部 RAM。使用这个宏需要启用 :ref:`CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY`。详情请见 :ref:`external_ram_config_bss`
DRAM_ATTR const char[] format_string = "%p %x";
char buffer[64];
sprintf(buffer, format_string, ptr, val);
.. only:: esp32
毋庸置疑,不建议在 ISR 中使用 ``printf`` 和其余输出函数。出于调试的目的,可以在 ISR 中使用 ``ESP_EARLY_LOGx`` 来输出日志,不过要确保将 ``TAG`` 和格式字符串都放在了 ``DRAM`` 中。
如果使用蓝牙堆栈,内部 DRAM 区域的可用大小将减少 64 KB由于起始地址移动到 ``0x3FFC0000``)。如果使用内存跟踪功能,该区域的长度还会减少 16 KB 或 32 KB。由于 ROM 引起的一些内存碎片问题,不可能将所有可用的 DRAM 用于静态分配,但是剩余的 DRAM 在运行时仍可用作堆。
``__NOINIT_ATTR`` 可以用来声明将数据放在 ``.noinit`` 段中,放在此段中的数据不会在启动时被初始化,并且在软件重启后会保留原来的值。
.. only:: not esp32
例子:
.. note::
.. code:: c
静态分配的 DRAM 的最大值也会因编译应用程序的 :ref:`iram` 大小而减小。运行时可用的堆内存会因应用程序的总静态 IRAM 和 DRAM 使用而减少。
__NOINIT_ATTR uint32_t noinit_data;
常量数据也可能被放入 DRAM例如当它被用于 non-flash-safe ISR 时(具体请参考 :ref:`how-to-place-code-in-iram`)。
"noinit" DRAM
=============
可以将 ``__NOINIT_ATTR`` 宏用作属性,从而将数据放入 ``.noinit`` 部分。放入该部分的值在启动时不会被初始化,在软件重启后也会保持值不变。
.. only:: esp32
通过使用 ``EXT_RAM_NOINIT_ATTR``noinit 数据也可以放入外部 RAM 中。为此,需要启用 :ref:`CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY`,可参考 :ref:`external_ram_config_noinit`。如果没有启用 :ref:`CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY` ``EXT_RAM_NOINIT_ATTR`` 会和 ``__NOINIT_ATTR`` 一样,将数据放入内部 RAM 的 ``.noinit`` 部分。
示例::
__NOINIT_ATTR uint32_t noinit_data;
.. _iram:
IRAM指令 RAM
^^^^^^^^^^^^^^^^^^^^^^
.. only:: esp32
ESP-IDF 将内部 SRAM0 的部分区域分配为指令 RAM。可在 *{IDF_TARGET_NAME} 技术参考手册* > *系统和存储器* > *内部存储器* [`PDF <{IDF_TARGET_TRM_CN_URL}#sysmem>`__] 中查看 IRAM 区域的定义。该内存中第一个 64 KB 块用于 PRO 和 APP MMU 缓存,其余部分(即从 ``0x40080000````0x400A0000``)用于存储需要从 RAM 运行的应用程序部分。
.. only:: esp32s2
ESP-IDF 将内部 SRAM 的部分区域分配为指令 RAM。可在 *{IDF_TARGET_NAME} 技术参考手册* > *系统和存储器* > *内部存储器* [`PDF <{IDF_TARGET_TRM_CN_URL}#sysmem>`__] 中查看 IRAM 区域的定义。该内存中第一个块(最多 32 KB用于 MMU 缓存,其余部分用于存储需要从 RAM 运行的应用程序部分。
.. only:: not esp32
.. note::
内部 SRAM 中不用于指令 RAM 的部分都会作为 :ref:`dram` 供静态数据和动态分配(堆)使用。
何时需要将代码放入 IRAM
======================================
以下情况时应将部分应用程序放入 IRAM
- 如果在注册中断处理程序时使用了 ``ESP_INTR_FLAG_IRAM``,则中断处理程序必须要放入 IRAM。更多信息可参考 :ref:`iram-safe-interrupt-handlers`
- 可将一些时序关键代码放入 IRAM以减少从 flash 中加载代码造成的相关损失。{IDF_TARGET_NAME} 通过 MMU 缓存从 flash 中读取代码和数据。在某些情况下,将函数放入 IRAM 可以减少由缓存未命中造成的延迟,从而显著提高函数的性能。
.. _how-to-place-code-in-iram:
如何将代码放入 IRAM
=====================================
借助链接器脚本,一些代码会被自动放入 IRAM 区域中。
如果需要将某些特定的应用程序代码放入 IRAM可以使用 :doc:`linker-script-generation` 功能并在组件中添加链接器脚本片段文件,在该片段文件中,可以给整个目标源文件或其中的个别函数打上 ``noflash`` 标签。更多信息可参考 :doc:`linker-script-generation`
或者,也可以通过使用 ``IRAM_ATTR`` 宏在源代码中指定需要放入 IRAM 的代码::
#include "esp_attr.h"
void IRAM_ATTR gpio_isr_handler(void* arg)
{
// ...
}
放入 IRAM 后可能会导致 IRAM 安全中断处理程序出现问题:
* ``IRAM_ATTR`` 函数中的字符串或常量可能没有自动放入 RAM 中,这时可以使用 ``DRAM_ATTR`` 属性进行标记,或者也可以使用链接器脚本方法将它们自动放入 RAM 中。
.. code-block:: c
void IRAM_ATTR gpio_isr_handler(void* arg)
{
const static DRAM_ATTR uint8_t INDEX_DATA[] = { 45, 33, 12, 0 };
const static char *MSG = DRAM_STR("I am a string stored in RAM");
}
注意,具体哪些数据需要被标记为 ``DRAM_ATTR`` 可能很难确定。如果没有被标记为 ``DRAM_ATTR``,某些变量或表达式有时会被编译器别为常量(即使它们没有被标记为 ``const``)并将其放入 flash 中。
* GCC 的优化会自动生成跳转表或 switch/case 查找表,并将这些表放在 flash 中。IDF 默认在编译所有文件时使用 ``-fno-jump-tables -fno-tree-switch-conversion`` 标志来避免这种情况。
可以为不需要放置在 IRAM 中的单个源文件重新启用跳转表优化。关于如何在编译单个源文件时添加 ``-fno-jump-tables -fno-tree-switch-conversion`` 选项,请参考 :ref:`component_build_control`
.. _irom:
IROM代码从 flash 中运行)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
如果一个函数没有被显式地声明放在 IRAM 或者 RTC 存储器中,则它会放在 flash 中。允许从 flash 中执行代码的 Flash MMU 机制可参考 {IDF_TARGET_NAME} 技术参考手册* > *存储器管理和保护单元 (MMU, MPU)* [`PDF <{IDF_TARGET_TRM_CN_URL}#mpummu>`__]。由于 IRAM 空间有限,应用程序的大部分二进制代码都需要放入 IROM 中。
:doc:`启动 <startup>` 过程中,从 IRAM 中运行的引导加载程序配置 MMU flash 缓存,将应用程序的指令代码区域映射到指令空间。通过 MMU 访问的 flash 使用一些内部 SRAM 进行缓存,访问缓存的 flash 数据与访问其他类型的内部存储器一样快。
RTC FAST memoryRTC 快速存储器)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RTC FAST memory 的同一区域既可以作为指令存储器也可以作为数据存储器进行访问。从深度睡眠模式唤醒后必须要运行的代码要放在 RTC 存储器中,更多信息请查阅文档 :doc:`深度睡眠 <deep-sleep-stub>`
.. only:: esp32
RTC FAST memory 只可以被 PRO CPU 访问。
在单核模式下,除非禁用 :ref:`CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP` 选项,否则剩余的 RTC FAST memory 会被添加到堆中。该部分内存可以和 :ref:`DRAM` 互换使用,但是访问速度稍慢,且不具备 DMA 功能。
.. only:: not esp32 and not esp8684
除非禁用 :ref:`CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP` 选项,否则剩余的 RTC FAST memory 会被添加到堆中。该部分内存可以和 :ref:`DRAM` 互换使用,但是访问速度稍慢一点。
.. _drom:
DROM数据存储在 Flash 中)
~~~~~~~~~~~~~~~~~~~~~~~~~~~
DROM数据存储在 flash 中)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
默认情况下,链接器将常量数据放入一个 4MB 区域 (``0x3F400000 — 0x3F800000``) ,该区域用于通过 Flash MMU 和高速缓存来访问外部 Flash。一种特例情况是字面量会被编译器嵌入到应用程序代码中。
.. highlight:: c
RTC 慢速内存
~~~~~~~~~~~~
默认情况下,链接器将常量数据放入一个映射到 MMU flash 缓存的区域中。这与 :ref:`irom` 部分相同,但此处用于只读数据而不是可执行代码。
从 RTC 内存运行的代码(例如深度睡眠模块的代码)使用的全局和静态变量必须要放在 RTC 慢速内存中。更多详细说明请查看文档 :doc:`深度睡眠 <deep-sleep-stub>`
唯一没有默认放入 DROM 的常量数据是被编译器嵌入到应用程序代码中的字面常量。这些被放置在周围函数的可执行指令中
``RTC_NOINIT_ATTR`` 用来声明将数据放入 RTC 慢速内存中,该数据在深度睡眠唤醒后将保持不变。
``DRAM_ATTR`` 属性可以用来强制将常量从 DRAM 放入 :ref:`dram` 部分(见上文)
例子:
.. only:: SOC_RTC_SLOW_MEM_SUPPORTED
.. code:: c
RTC Slow memoryRTC 慢速存储器)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RTC_NOINIT_ATTR uint32_t rtc_noinit_data;
从 RTC 存储器运行的代码中使用的全局和静态变量必须放入 RTC Slow memory 中。例如 :doc:`深度睡眠 <deep-sleep-stub>` 变量可以放在 RTC Slow memory 中,而不是 RTC FAST memory或者也可以放入由 :doc:`/api-guides/ulp` 访问的代码和变量。
DMA 能力要求
------------
``RTC_NOINIT_ATTR`` 属性宏可以用来将数据放入 RTC Slow memory。放入此类型存储器的值从深度睡眠模式中醒来后会保持值不变。
大多数的 DMA 控制器(比如 SPISDMMC 等)都要求发送/接收缓冲区放在 DRAM 中,并且按字对齐。我们建议将 DMA 缓冲区放在静态变量中而不是堆栈中。使用 ``DMA_ATTR`` 宏可以声明该全局/本地的静态变量具备 DMA 能力,例如:
示例::
.. code:: c
RTC_NOINIT_ATTR uint32_t rtc_noinit_data;
DMA_ATTR uint8_t buffer[]="I want to send something";
void app_main()
{
// 初始化代码...
spi_transaction_t temp = {
.tx_buffer = buffer,
.length = 8*sizeof(buffer),
};
spi_device_transmit( spi, &temp );
// 其他程序
}
具备 DMA 功能
^^^^^^^^^^^^^^^^^^^
或者:
.. highlight:: c
.. code:: c
大多数的 DMA 控制器(比如 SPI、sdmmc 等)都要求发送/接收缓冲区放在 DRAM 中,并且按字对齐。我们建议将 DMA 缓冲区放在静态变量而不是堆栈中。使用 ``DMA_ATTR`` 宏可以声明该全局/本地的静态变量具备 DMA 功能,例如::
void app_main()
{
DMA_ATTR static uint8_t buffer[]="I want to send something";
// 初始化代码...
spi_transaction_t temp = {
.tx_buffer = buffer,
.length = 8*sizeof(buffer),
};
spi_device_transmit( spi, &temp );
// 其他程序
}
DMA_ATTR uint8_t buffer[]="I want to send something";
在堆栈中放置 DMA 缓冲区仍然是允许的,但是你必须记住:
void app_main()
{
// 初始化代码
spi_transaction_t temp = {
.tx_buffer = buffer,
.length = 8 * sizeof(buffer),
};
spi_device_transmit(spi, &temp);
// 其它程序
}
或者::
void app_main()
{
DMA_ATTR static uint8_t buffer[] = "I want to send something";
// 初始化代码
spi_transaction_t temp = {
.tx_buffer = buffer,
.length = 8 * sizeof(buffer),
};
spi_device_transmit(spi, &temp);
// 其它程序
}
也可以通过使用 :ref:`MALLOC_CAP_DMA <dma-capable-memory>` 标志来动态分配具备 DMA 能力的内存缓冲区。
在堆栈中放置 DMA 缓冲区
^^^^^^^^^^^^^^^^^^^^^^^^^^^
可以在堆栈中放置 DMA 缓冲区,但建议尽量避免。如果实在有需要的话,请注意以下几点:
.. list::
:SOC_SPIRAM_SUPPORTED:- 如果堆栈在 pSRAM 中,切勿尝试这么做,因为堆栈在 pSRAM 中的话就要按照 :doc:`片外SRAM <external-ram>` 文档介绍的步骤来操作(至少要在 ``menuconfig`` 中使能 ``SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY`` ),所以请确保你的任务不在 PSRAM 中。
:SOC_SPIRAM_SUPPORTED: - 如果堆栈在 PSRAM 中,则不建议将 DRAM 缓冲区放在堆栈上。如果任务堆栈在 PSRAM 中,则必须执行 :doc:`external-ram` 中描述的几个步骤。
- 在函数中使用 ``WORD_ALIGNED_ATTR`` 宏来修饰变量,将其放在适当的位置上,比如::
- 在函数中使用 ``WORD_ALIGNED_ATTR`` 宏来修饰变量,将其放在适当的位置上,比如:
.. code:: c
void app_main()
{
void app_main()
{
uint8_t stuff;
WORD_ALIGNED_ATTR uint8_t buffer[]="I want to send something"; //否则buffer数组会被存储在stuff变量的后面
// 初始化代码...
WORD_ALIGNED_ATTR uint8_t buffer[] = "I want to send something"; //否则 buffer 会被存储在 stuff 变量后面
// 初始化代码
spi_transaction_t temp = {
.tx_buffer = buffer,
.length = 8*sizeof(buffer),
.tx_buffer = buffer,
.length = 8 * sizeof(buffer),
};
spi_device_transmit( spi, &temp );
// 其他程序
}
spi_device_transmit(spi, &temp);
// 其程序
}