Merge branch 'doc/general_notes' into 'master'

doc: Update docs about startup sequence and memory types

Closes IDF-2321, IDF-2409, and IDFGH-1540

See merge request espressif/esp-idf!12636
This commit is contained in:
Angus Gratton 2021-04-05 23:51:25 +00:00
commit 94ac251091
19 changed files with 772 additions and 574 deletions

View File

@ -31,10 +31,11 @@ extern bool g_spiram_ok; // [refactor-todo] better way to communicate this from
// array, one per core.
typedef void (*sys_startup_fn_t)(void);
/* This array of per-CPU system layer startup functions is initialized in the non-port part of esp_system */
#if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
extern sys_startup_fn_t g_startup_fn[SOC_CPU_CORES_NUM];
extern sys_startup_fn_t const g_startup_fn[SOC_CPU_CORES_NUM];
#else
extern sys_startup_fn_t g_startup_fn[1];
extern sys_startup_fn_t const g_startup_fn[1];
#endif
// Utility to execute `sys_startup_fn_t` for the current core.

View File

@ -112,7 +112,7 @@ void esp_startup_start_app_other_cores(void) __attribute__((weak, alias("esp_sta
static volatile bool s_system_inited[SOC_CPU_CORES_NUM] = { false };
sys_startup_fn_t g_startup_fn[SOC_CPU_CORES_NUM] = { [0] = start_cpu0,
const sys_startup_fn_t g_startup_fn[SOC_CPU_CORES_NUM] = { [0] = start_cpu0,
#if SOC_CPU_CORES_NUM > 1
[1 ... SOC_CPU_CORES_NUM - 1] = start_cpu_other_cores
#endif
@ -120,7 +120,7 @@ sys_startup_fn_t g_startup_fn[SOC_CPU_CORES_NUM] = { [0] = start_cpu0,
static volatile bool s_system_full_inited = false;
#else
sys_startup_fn_t g_startup_fn[1] = { start_cpu0 };
const sys_startup_fn_t g_startup_fn[1] = { start_cpu0 };
#endif
#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS

View File

@ -1203,6 +1203,10 @@ BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION;
/**
* Starts the real time kernel tick processing.
*
* NOTE: In ESP-IDF the scheduler is started automatically during
* application startup, vTaskStartScheduler() should not be called from
* ESP-IDF applications.
*
* After calling the kernel has control over which tasks are executed and when.
*
* See the demo application file main.c for an example of creating

View File

@ -53,26 +53,7 @@ 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>`.
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.
Use the ``IRAM_ATTR`` attribute for functions::
#include "esp_attr.h"
void IRAM_ATTR gpio_isr_handler(void* arg)
{
// ...
}
Use the ``DRAM_ATTR`` and ``DRAM_STR`` attributes for constant data::
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");
}
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``.
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).

View File

@ -2,13 +2,18 @@ Bootloader
=====================
:link_to_translation:`zh_CN:[中文]`
Bootloader performs the following functions:
{IDF_TARGET_BOOTLOADER_OFFSET:default="0x0", esp32="0x1000", esp32s2="0x1000"}
The ESP-IDF Software Bootloader performs the following functions:
1. Minimal initial configuration of internal modules;
2. Select the application partition to boot, based on the partition table and ota_data (if any);
3. Load this image to RAM (IRAM & DRAM) and transfer management to it.
2. Initialize :doc:`/security/flash-encryption` and/or :doc:`Secure </security/secure-boot-v2>` features, if configured;
3. Select the application partition to boot, based on the partition table and ota_data (if any);
4. Load this image to RAM (IRAM & DRAM) and transfer management to it.
Bootloader is located at the address `0x1000` in the flash.
Bootloader is located at the address {IDF_TARGET_BOOTLOADER_OFFSET} in the flash.
For a full description of the startup process including the the ESP-IDF bootloader, see :doc:`startup`.
Bootloader compatibility
------------------------
@ -67,8 +72,9 @@ Fast boot from Deep Sleep
-------------------------
The bootloader has the :ref:`CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP` option which allows to reduce the wake-up time (useful to reduce consumption). This option is available when the :ref:`CONFIG_SECURE_BOOT` option is disabled. Reduction of time is achieved due to the lack of image verification. During the first boot, the bootloader stores the address of the application being launched in the RTC FAST memory. And during the awakening, this address is used for booting without any checks, thus fast loading is achieved.
Customer bootloader
---------------------
Custom bootloader
-----------------
The current bootloader implementation allows a project to override it. To do this, you must copy the directory ``/esp-idf/components/bootloader`` to your project components directory and then edit ``/your_project/components/bootloader/subproject/main/bootloader_start.c``.
In the bootloader space, you cannot use the drivers and functions from other components. If necessary, then the required functionality should be placed in the project's ``bootloader`` directory (note that this will increase its size).

View File

@ -113,7 +113,14 @@ External RAM use has the following restrictions:
* When flash cache is disabled (for example, if the flash is being written to), the external RAM also becomes inaccessible; any reads from or writes to it will lead to an illegal cache access exception. This is also the reason why ESP-IDF does not by default allocate any task stacks in external RAM (see below).
* External RAM cannot be used as a place to store DMA transaction descriptors or as a buffer for a DMA transfer to read from or write into. Any buffers that will be used in combination with DMA must be allocated using ``heap_caps_malloc(size, MALLOC_CAP_DMA)`` and can be freed using a standard ``free()`` call.
* External RAM uses the same cache region as the external flash. This means that frequently accessed variables in external RAM can be read and modified almost as quickly as in internal ram. However, when accessing large chunks of data (>32 KB), the cache can be insufficient, and speeds will fall back to the access speed of the external RAM. Moreover, accessing large chunks of data can "push out" cached flash, possibly making the execution of code slower afterwards.
* External RAM cannot be used as task stack memory. Due to this, :cpp:func:`xTaskCreate` and similar functions will always allocate internal memory for stack and task TCBs, and functions such as :cpp:func:`xTaskCreateStatic` will check if the buffers passed are internal.
* In general, external RAM cannot be used as task stack memory. Due to this, :cpp:func:`xTaskCreate` and similar functions will always allocate internal memory for stack and task TCBs, and functions such as :cpp:func:`xTaskCreateStatic` will check if the buffers passed are internal.
.. only:: esp32
The option :ref:`CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY` can be used to place task stacks into external memory. In these cases :cpp:func:`xTaskCreateStatic` must be used to specify a task stack buffer allocated from external memory, otherwise task stacks will still be allocated from internal memory.
Failure to initialize
=====================
By default, failure to initialize external RAM will cause the ESP-IDF startup to abort. This can be disabled by enabling the config item :ref:`CONFIG_SPIRAM_IGNORE_NOTFOUND`.

View File

@ -21,7 +21,7 @@ found via https://www.freertos.org/a00106.html
For information regarding features that are exclusive to ESP-IDF FreeRTOS,
see :doc:`ESP-IDF FreeRTOS Additions<../api-reference/system/freertos_additions>`.
.. only:: esp32
.. only:: not CONFIG_FREERTOS_UNICORE
:ref:`tasks-and-task-creation`: Use :cpp:func:`xTaskCreatePinnedToCore` or
:cpp:func:`xTaskCreateStaticPinnedToCore` to create tasks in ESP-IDF FreeRTOS. The
@ -53,6 +53,8 @@ see :doc:`ESP-IDF FreeRTOS Additions<../api-reference/system/freertos_additions>
unaffected. If the other core attemps to take same mutex, it will spin until
the calling core has released the mutex by exiting the critical section.
.. only:: esp32
:ref:`floating-points`: The ESP32 supports hardware acceleration of single
precision floating point arithmetic (``float``). However the use of hardware
acceleration leads to some behavioral restrictions in ESP-IDF FreeRTOS.
@ -70,6 +72,9 @@ set in the project configuration (``idf.py menuconfig``) such as running ESP-IDF
Unicore (single core) Mode, or configuring the number of Thread Local Storage Pointers
each task will have.
It is not necessary to manually start the FreeRTOS scheduler by calling :cpp:func:`vTaskStartScheduler`. In ESP-IDF the
scheduler is started by the :doc:`startup` and is already running when the ``app_main`` function is called (see :ref:`app-main-task` for details).
.. _tasks-and-task-creation:
Tasks and Task Creation

View File

@ -1,201 +1,10 @@
:orphan:
General Notes About ESP-IDF Programming
=======================================
:link_to_translation:`zh_CN:[中文]`
Application startup flow
------------------------
This page has been split into two new pages:
This note explains various steps which happen before ``app_main`` function of an ESP-IDF application is called.
The high level view of startup process is as follows:
1. First-stage bootloader in ROM loads second-stage bootloader image to RAM (IRAM & DRAM) from flash offset 0x1000.
2. Second-stage bootloader loads partition table and main app image from flash. Main app incorporates both RAM segments and read-only segments mapped via flash cache.
3. Main app image executes. At this point the second CPU and RTOS scheduler can be started.
This process is explained in detail in the following sections.
First stage bootloader
^^^^^^^^^^^^^^^^^^^^^^
After SoC reset, PRO CPU will start running immediately, executing reset vector code, while APP CPU will be held in reset. During startup process, PRO CPU does all the initialization. APP CPU reset is de-asserted in the ``call_start_cpu0`` function of application startup code. Reset vector code is located at address 0x40000400 in the mask ROM of the {IDF_TARGET_NAME} chip and can not be modified.
Startup code called from the reset vector determines the boot mode by checking ``GPIO_STRAP_REG`` register for bootstrap pin states. Depending on the reset reason, the following takes place:
1. Reset from deep sleep: if the value in ``RTC_CNTL_STORE6_REG`` is non-zero, and CRC value of RTC memory in ``RTC_CNTL_STORE7_REG`` is valid, use ``RTC_CNTL_STORE6_REG`` as an entry point address and jump immediately to it. If ``RTC_CNTL_STORE6_REG`` is zero, or ``RTC_CNTL_STORE7_REG`` contains invalid CRC, or once the code called via ``RTC_CNTL_STORE6_REG`` returns, proceed with boot as if it was a power-on reset. **Note**: to run customized code at this point, a deep sleep stub mechanism is provided. Please see :doc:`deep sleep <deep-sleep-stub>` documentation for this.
2. For power-on reset, software SOC reset, and watchdog SOC reset: check the ``GPIO_STRAP_REG`` register if UART or SDIO download mode is requested. If this is the case, configure UART or SDIO, and wait for code to be downloaded. Otherwise, proceed with boot as if it was due to software CPU reset.
3. For software CPU reset and watchdog CPU reset: configure SPI flash based on EFUSE values, and attempt to load the code from flash. This step is described in more detail in the next paragraphs. If loading code from flash fails, unpack BASIC interpreter into the RAM and start it. Note that RTC watchdog is still enabled when this happens, so unless any input is received by the interpreter, watchdog will reset the SOC in a few hundred milliseconds, repeating the whole process. If the interpreter receives any input from the UART, it disables the watchdog.
Application binary image is loaded from flash starting at address 0x1000. First 4kB sector of flash is used to store secure boot IV and signature of the application image. Please check secure boot documentation for details about this.
.. TODO: describe application binary image format, describe optional flash configuration commands.
Second stage bootloader
^^^^^^^^^^^^^^^^^^^^^^^
In ESP-IDF, the binary image which resides at offset 0x1000 in flash is the second stage bootloader. Second stage bootloader source code is available in components/bootloader directory of ESP-IDF. Note that this arrangement is not the only one possible with the {IDF_TARGET_NAME} chip. It is possible to write a fully featured application which would work when flashed to offset 0x1000, but this is out of scope of this document. Second stage bootloader is used in ESP-IDF to add flexibility to flash layout (using partition tables), and allow for various flows associated with flash encryption, secure boot, and over-the-air updates (OTA) to take place.
When the first stage bootloader is finished checking and loading the second stage bootloader, it jumps to the second stage bootloader entry point found in the binary image header.
Second stage bootloader reads the partition table found at offset 0x8000. See :doc:`partition tables <partition-tables>` documentation for more information. The bootloader finds factory and OTA partitions, and decides which one to boot based on data found in *OTA info* partition.
For the selected partition, second stage bootloader copies data and code sections which are mapped into IRAM and DRAM to their load addresses. For sections which have load addresses in DROM and IROM regions, flash MMU is configured to provide the correct mapping. Note that the second stage bootloader configures flash MMU for both PRO and APP CPUs, but it only enables flash MMU for PRO CPU. Reason for this is that second stage bootloader code is loaded into the memory region used by APP CPU cache. The duty of enabling cache for APP CPU is passed on to the application. Once code is loaded and flash MMU is set up, second stage bootloader jumps to the application entry point found in the binary image header.
Currently it is not possible to add application-defined hooks to the bootloader to customize application partition selection logic. This may be required to load different application image depending on a state of a GPIO, for example. Such customization features will be added to ESP-IDF in the future. For now, bootloader can be customized by copying bootloader component into application directory and making necessary changes there. ESP-IDF build system will compile the component in application directory instead of ESP-IDF components directory in this case.
Application startup
^^^^^^^^^^^^^^^^^^^
ESP-IDF application entry point is ``call_start_cpu0`` function found in ``components/{IDF_TARGET_PATH_NAME}/cpu_start.c``. Two main things this function does are to enable heap allocator and to make APP CPU jump to its entry point, ``call_start_cpu1``. The code on PRO CPU sets the entry point for APP CPU, de-asserts APP CPU reset, and waits for a global flag to be set by the code running on APP CPU, indicating that it has started. Once this is done, PRO CPU jumps to ``start_cpu0`` function, and APP CPU jumps to ``start_cpu1`` function.
Both ``start_cpu0`` and ``start_cpu1`` are weak functions, meaning that they can be overridden in the application, if some application-specific change to initialization sequence is needed. Default implementation of ``start_cpu0`` enables or initializes components depending on choices made in ``menuconfig``. Please see source code of this function in ``components/{IDF_TARGET_PATH_NAME}/cpu_start.c`` for an up to date list of steps performed. Note that any C++ global constructors present in the application will be called at this stage. Once all essential components are initialized, *main task* is created and FreeRTOS scheduler is started.
While PRO CPU does initialization in ``start_cpu0`` function, APP CPU spins in ``start_cpu1`` function, waiting for the scheduler to be started on the PRO CPU. Once the scheduler is started on the PRO CPU, code on the APP CPU starts the scheduler as well.
Main task is the task which runs ``app_main`` function. Main task stack size and priority can be configured in ``menuconfig``. Application can use this task for initial application-specific setup, for example to launch other tasks. Application can also use main task for event loops and other general purpose activities. If ``app_main`` function returns, main task is deleted.
.. _memory-layout:
Application memory layout
-------------------------
{IDF_TARGET_NAME} chip has flexible memory mapping features. This section describes how ESP-IDF uses these features by default.
Application code in ESP-IDF can be placed into one of the following memory regions.
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 CPU caches, the rest of this memory range (i.e. from ``0x40080000`` to ``0x400A0000``) is used to store parts of application which need to run from RAM.
.. only:: esp32s2
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* > *Internal Memory* [`PDF <{IDF_TARGET_TRM_EN_URL}#sysmem>`__]. Except for the first 64 kB block which is used for PRO and APP CPU caches, the rest of this memory range (i.e. from ``0x40080000`` to ``0x400A0000``) is used to store parts of application which need to run from RAM.
A few components of ESP-IDF and parts of WiFi stack are placed into this region using the linker script.
If some application code needs to be placed into IRAM, it can be done using ``IRAM_ATTR`` define::
#include "esp_attr.h"
void IRAM_ATTR gpio_isr_handler(void* arg)
{
// ...
}
Here are the cases when parts of application may or should be placed into IRAM.
- Interrupt handlers must be placed into IRAM if ``ESP_INTR_FLAG_IRAM`` is used when registering the interrupt handler. In this case, ISR may only call functions placed into IRAM or functions present in ROM. *Note 1:* all FreeRTOS APIs are currently placed into IRAM, so are safe to call from interrupt handlers. If the ISR is placed into IRAM, all constant data used by the ISR and functions called from ISR (including, but not limited to, ``const char`` arrays), must be placed into DRAM using ``DRAM_ATTR``.
- 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 a 32 kB cache. In some cases, placing a function into IRAM may reduce delays caused by a cache miss.
IROM (code executed from Flash)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. only:: SOC_SDIO_SLAVE_SUPPORTED
If a function is not explicitly placed into 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>`__]. ESP-IDF places the code which should be executed from flash starting from the beginning of ``0x400D0000 — 0x40400000`` region. Upon startup, second stage bootloader initializes Flash MMU to map the location in flash where code is located into the beginning of this region. Access to this region is transparently cached using two 32kB blocks in ``0x40070000````0x40080000`` range.
.. only:: not SOC_SDIO_SLAVE_SUPPORTED
If a function is not explicitly placed into 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 <{IDF_TARGET_TRM_EN_URL}>`__. ESP-IDF places the code which should be executed from flash starting from the beginning of ``0x400D0000 — 0x40400000`` region. Upon startup, second stage bootloader initializes Flash MMU to map the location in flash where code is located into the beginning of this region. Access to this region is transparently cached using two 32kB blocks in ``0x40070000````0x40080000`` range.
Note that the code outside ``0x40000000 — 0x40400000`` region may not be reachable with Window ABI ``CALLx`` instructions, so special care is required if ``0x40400000 — 0x40800000`` or ``0x40800000 — 0x40C00000`` regions are used by the application. ESP-IDF doesn't use these regions by default.
RTC fast memory
^^^^^^^^^^^^^^^
The 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.
DRAM (data RAM)
^^^^^^^^^^^^^^^
Non-constant static data and zero-initialized data is placed by the linker into the 256 kB ``0x3FFB0000 — 0x3FFF0000`` region. Note that this 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. All space which is left in this region after placing static data there is used for the runtime heap.
Constant data may also be placed into DRAM, for example if it is used in an ISR (see notes in IRAM section above). To do that, ``DRAM_ATTR`` define can be used::
DRAM_ATTR const char[] format_string = "%p %x";
char buffer[64];
sprintf(buffer, format_string, ptr, val);
Needless to say, it is not advised to use ``printf`` and other output functions in ISRs. For debugging purposes, use ``ESP_EARLY_LOGx`` macros when logging from ISRs. Make sure that both ``TAG`` and format string are placed into ``DRAM`` in that case.
The macro ``__NOINIT_ATTR`` can be used as attribute to place data into ``.noinit`` section. The values placed into this section will not be initialized at startup and keep its value after software restart.
Example::
__NOINIT_ATTR uint32_t noinit_data;
DROM (data stored in Flash)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
By default, constant data is placed by the linker into a 4 MB region (``0x3F400000 — 0x3F800000``) which is used to access external flash memory via Flash MMU and cache. Exceptions to this are literal constants which are embedded by the compiler into application code.
RTC slow memory
^^^^^^^^^^^^^^^
Global and static variables used by code which runs from RTC memory (i.e. deep sleep stub code) must be placed into RTC slow memory. Please check detailed description in :doc:`deep sleep <deep-sleep-stub>` documentation.
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.
Example::
RTC_NOINIT_ATTR uint32_t rtc_noinit_data;
DMA Capable Requirement
-----------------------
Most 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";
void app_main()
{
// initialization code...
spi_transaction_t temp = {
.tx_buffer = buffer,
.length = 8*sizeof(buffer),
};
spi_device_transmit( spi, &temp );
// other stuff
}
Or::
void app_main()
{
DMA_ATTR static uint8_t buffer[]="I want to send something";
// initialization code...
spi_transaction_t temp = {
.tx_buffer = buffer,
.length = 8*sizeof(buffer),
};
spi_device_transmit( spi, &temp );
// other stuff
}
Placing DMA buffers in the stack is still allowed, though you have to keep in mind:
.. list::
:SOC_SPIRAM_SUPPORTED: - Never try to do this if the stack is in the pSRAM. If the stack of a task is placed in the pSRAM, several steps have to be taken as described in :doc:`external-ram` (at least ``SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY`` option enabled in the menuconfig). Make sure your task is not in the pSRAM.
- Use macro ``WORD_ALIGNED_ATTR`` in functions before variables to place them in proper positions like::
void app_main()
{
uint8_t stuff;
WORD_ALIGNED_ATTR uint8_t buffer[]="I want to send something"; //or the buffer will be placed right after stuff.
// initialization code...
spi_transaction_t temp = {
.tx_buffer = buffer,
.length = 8*sizeof(buffer),
};
spi_device_transmit( spi, &temp );
// other stuff
}
- :doc:`startup`
- :doc:`memory-types`

View File

@ -6,6 +6,7 @@ API Guides
:maxdepth: 1
Application Level Tracing <app_trace>
Application Startup Flow <startup>
:SOC_BT_SUPPORTED: BluFi <blufi>
Bootloader <bootloader>
Build System <build-system>
@ -21,11 +22,11 @@ API Guides
Fatal Errors <fatal-errors>
Flash Encryption <../security/flash-encryption>
FreeRTOS SMP Changes <freertos-smp>
General Notes <general-notes>
Hardware Abstraction <hardware-abstraction>
:CONFIG_IDF_TARGET_ARCH_XTENSA: High Level Interrupts <hlinterrupts>
JTAG Debugging <jtag-debugging/index>
Linker Script Generation <linker-script-generation>
Memory Types <memory-types>
lwIP TCP/IP Stack <lwip>
Partition Tables <partition-tables>
:esp32: RF Calibration <RF_calibration>

View File

@ -0,0 +1,217 @@
.. _memory-layout:
Memory Types
------------
{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>`__].
.. _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.
.. 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.
.. 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.
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
=============
The macro ``__NOINIT_ATTR`` can be used as attribute to place data into ``.noinit`` section. The values placed into this section will not be initialized at startup and should keep its value after software restart.
Example::
__NOINIT_ATTR uint32_t noinit_data;
.. _iram:
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.
.. 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.
.. 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).
Why place code in IRAM
======================
Cases when parts of 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
=========================
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.
Alternatively, it's possible to specify IRAM placement in the source code using the ``IRAM_ATTR`` macro::
#include "esp_attr.h"
void IRAM_ATTR gpio_isr_handler(void* arg)
{
// ...
}
There are some possible issues with placement in IRAM, that may cause problems with IRAM-safe interrupt handlers:
* Strings or constants inside an ``IRAM_ATTR`` function may not be placed in RAM automatically. It's possible to use ``DRAM_ATTR`` attributes to mark these, or using the linker script method will cause these to be automatically placed correctly.
.. 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");
}
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. There are two possible ways to resolve this issue:
- Use a :doc:`linker script fragment <linker-script-generation>` to mark the entire source file as ``noflash``
- Pass specific flags to the compiler to disable these optimizations in the relevant source files. For CMake, place the following in the component CMakeLists.txt file:
.. code-block:: cmake
set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/relative/path/to/file" PROPERTIES
COMPILE_FLAGS "-fno-jump-tables -fno-tree-switch-conversion")
.. _irom:
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
^^^^^^^^^^^^^^^
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.
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
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)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. 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 ``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
^^^^^^^^^^^^^^^
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.
Example::
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::
DMA_ATTR uint8_t buffer[]="I want to send something";
void app_main()
{
// initialization code...
spi_transaction_t temp = {
.tx_buffer = buffer,
.length = 8 * sizeof(buffer),
};
spi_device_transmit(spi, &temp);
// other stuff
}
Or::
void app_main()
{
DMA_ATTR static uint8_t buffer[] = "I want to send something";
// initialization code...
spi_transaction_t temp = {
.tx_buffer = buffer,
.length = 8 * sizeof(buffer),
};
spi_device_transmit(spi, &temp);
// other stuff
}
It is also possible to allocate DMA-capable memory buffers dynamically by using the :ref:`MALLOC_CAP_DMA <dma-capable-memory>` capabilities flag.
DMA Buffer in the stack
^^^^^^^^^^^^^^^^^^^^^^^
Placing DMA buffers in the stack is possible but discouraged. If doing so, pay attention to the following:
.. list::
:SOC_SPIRAM_SUPPORTED: - Placing DRAM buffers on the stack is not recommended if if the stack may be in PSRAM. If the stack of a task is placed in the PSRAM, several steps have to be taken as described in :doc:`external-ram`.
- Use macro ``WORD_ALIGNED_ATTR`` in functions before variables to place them in proper positions like::
void app_main()
{
uint8_t stuff;
WORD_ALIGNED_ATTR uint8_t buffer[] = "I want to send something"; //or the buffer will be placed right after stuff.
// initialization code...
spi_transaction_t temp = {
.tx_buffer = buffer,
.length = 8 * sizeof(buffer),
};
spi_device_transmit(spi, &temp);
// other stuff
}

View File

@ -0,0 +1,172 @@
Application Startup Flow
========================
:link_to_translation:`zh_CN:[中文]`
{IDF_TARGET_BOOTLOADER_OFFSET:default="0x0", esp32="0x1000", esp32s2="0x1000"}
This note explains various steps which happen before ``app_main`` function of an ESP-IDF application is called.
The high level view of startup process is as follows:
1. :ref:`first-stage-bootloader` in ROM loads second-stage bootloader image to RAM (IRAM & DRAM) from flash offset {IDF_TARGET_BOOTLOADER_OFFSET}.
2. :ref:`second-stage-bootloader` loads partition table and main app image from flash. Main app incorporates both RAM segments and read-only segments mapped via flash cache.
3. :ref:`application-startup` executes. At this point the second CPU and RTOS scheduler are started.
This process is explained in detail in the following sections.
.. _first-stage-bootloader:
First stage bootloader
^^^^^^^^^^^^^^^^^^^^^^
.. only:: not CONFIG_FREERTOS_UNICORE
After SoC reset, PRO CPU will start running immediately, executing reset vector code, while APP CPU will be held in reset. During startup process, PRO CPU does all the initialization. APP CPU reset is de-asserted in the ``call_start_cpu0`` function of application startup code. Reset vector code is located in the mask ROM of the {IDF_TARGET_NAME} chip and cannot be modified.
.. only:: CONFIG_FREERTOS_UNICORE
After SoC reset, the CPU will start running immediately to perform initialization. The reset vector code is located in the mask ROM of the {IDF_TARGET_NAME} chip and cannot be modified.
Startup code called from the reset vector determines the boot mode by checking ``GPIO_STRAP_REG`` register for bootstrap pin states. Depending on the reset reason, the following takes place:
1. Reset from deep sleep: if the value in ``RTC_CNTL_STORE6_REG`` is non-zero, and CRC value of RTC memory in ``RTC_CNTL_STORE7_REG`` is valid, use ``RTC_CNTL_STORE6_REG`` as an entry point address and jump immediately to it. If ``RTC_CNTL_STORE6_REG`` is zero, or ``RTC_CNTL_STORE7_REG`` contains invalid CRC, or once the code called via ``RTC_CNTL_STORE6_REG`` returns, proceed with boot as if it was a power-on reset. **Note**: to run customized code at this point, a deep sleep stub mechanism is provided. Please see :doc:`deep sleep <deep-sleep-stub>` documentation for this.
2. For power-on reset, software SOC reset, and watchdog SOC reset: check the ``GPIO_STRAP_REG`` register if a custom boot mode (such as UART Download Mode) is requested. If this is the case, this custom loader mode is executed from ROM. Otherwise, proceed with boot as if it was due to software CPU reset. Consult {IDF_TARGET_NAME} datasheet for a description of SoC boot modes and how to execute them.
3. For software CPU reset and watchdog CPU reset: configure SPI flash based on EFUSE values, and attempt to load the code from flash. This step is described in more detail in the next paragraphs.
.. note::
During normal boot modes the RTC watchdog is enabled when this happens, so if the process is interrupted or stalled then the watchdog will reset the SOC automatically and repeat the boot process. This may cause the SoC to strap into a new boot mode, if the stapping GPIOs have changed.
.. only:: esp32
Second stage bootloader binary image is loaded from flash starting at address 0x1000. If :doc:`/security/secure-boot-v1` is in use then the first 4kB sector of flash is used to store secure boot IV and digest of the bootloader image. Otherwise, this sector is unused.
.. only:: esp32s2
Second stage bootloader binary image is loaded from flash starting at address 0x1000. The 4kB sector of flash before this address is unused.
.. only:: not (esp32 or esp32s2)
Second stage bootloader binary image is loaded from the start of flash at offset 0x0.
.. TODO: describe application binary image format, describe optional flash configuration commands.
.. _second-stage-bootloader:
Second stage bootloader
^^^^^^^^^^^^^^^^^^^^^^^
In ESP-IDF, the binary image which resides at offset {IDF_TARGET_BOOTLOADER_OFFSET} in flash is the second stage bootloader. Second stage bootloader source code is available in :idf:`components/bootloader` directory of ESP-IDF. Second stage bootloader is used in ESP-IDF to add flexibility to flash layout (using partition tables), and allow for various flows associated with flash encryption, secure boot, and over-the-air updates (OTA) to take place.
When the first stage bootloader is finished checking and loading the second stage bootloader, it jumps to the second stage bootloader entry point found in the binary image header.
Second stage bootloader reads the partition table found by default at offset 0x8000 (:ref:`configurable value <CONFIG_PARTITION_TABLE_OFFSET>`). See :doc:`partition tables <partition-tables>` documentation for more information. The bootloader finds factory and OTA app partitions. If OTA app partitions are found in the partition table, the bootloader consults the ``otadata`` partition to determine which one should be booted. See :doc:`/api-reference/system/ota` for more information.
For a full description of the configuration options available for the ESP-IDF bootloader, see :doc:`bootloader`.
For the selected partition, second stage bootloader reads the binary image from flash one segment at a time:
- For segments with load addresses in internal :ref:`iram` or :ref:`dram`, the contents are copied from flash to the load address.
- For segments which have load addresses in :ref:`drom` or :ref:`irom` regions, the flash MMU is configured to provide the correct mapping from the flash to the load address.
.. only:: not CONFIG_FREERTOS_UNICORE
Note that the second stage bootloader configures flash MMU for both PRO and APP CPUs, but it only enables flash MMU for PRO CPU. Reason for this is that second stage bootloader code is loaded into the memory region used by APP CPU cache. The duty of enabling cache for APP CPU is passed on to the application.
Once all segments are processed - meaning code is loaded and flash MMU is set up, second stage bootloader verifies the integrity of the application and then jumps to the application entry point found in the binary image header.
.. _application-startup:
Application startup
^^^^^^^^^^^^^^^^^^^
Application startup covers everything that happens after the app starts executing and before the ``app_main`` function starts running inside the main task. This is split into three stages:
- Port initialization of hardware and basic C runtime environment.
- System initialization of software services and FreeRTOS.
- Running the main task and calling ``app_main``.
.. note::
Understanding all stages of ESP-IDF app initialization is often not necessary. To understand initialization from the application developer's perspective only, skip forward to :ref:`app-main-task`.
Port Initialization
-------------------
ESP-IDF application entry point is ``call_start_cpu0`` function found in :idf_file:`components/esp_system/port/cpu_start.c`. This function is executed by the second stage bootloader, and never returns.
This port-layer initialization function initializes the basic C Runtime Environment ("CRT") and performs initial configuration of the SoC's internal hardware:
.. list::
- Reconfigure CPU exceptions for the app (allowing app interrupt handlers to run, and causing :doc:`fatal-errors` to be handled using the options configured for the app rather than the simpler error handler provided by ROM).
- If the option :ref:`CONFIG_BOOTLOADER_WDT_ENABLE` is not set then the RTC watchdog timer is disabled.
- Initialize internal memory (data & bss).
- Finish configuring the MMU cache.
:SOC_SPIRAM_SUPPORTED: - Enable PSRAM if configured.
- Set the CPU clocks to the frequencies configured for the project.
:CONFIG_ESP_SYSTEM_MEMPROT_FEATURE: - Initialize memory protection if configured.
:not CONFIG_FREERTOS_UNICORE: - If the app is configured to run on multiple cores, start the other core and wait for it to initialize as well (inside the similar "port layer" initialization function ``call_start_cpu1``).
.. only:: not CONFIG_FREERTOS_UNICORE
Once ``call_start_cpu0`` completes running, it calls the "system layer" initialization function ``start_cpu0`` found in :idf_file:`components/esp_system/startup.c`. Other cores will also complete port-layer initialization and call ``start_other_cores`` found in the same file.
.. only:: CONFIG_FREERTOS_UNICORE
Once ``call_start_cpu0`` completes running, it calls the "system layer" initialization function ``start_cpu0`` found in :idf_file:`components/esp_system/startup.c`.
System Initialization
---------------------
The main system initialization function is ``start_cpu0``. By default, this function is weak-linked to the function ``start_cpu0_default``. This means that it's possible to override this function to add some additional initialization steps.
The primary system initialization stage includes:
.. list::
- Log information about this application (project name, :ref:`app-version`, etc.) if default log level enables this.
- Initialize the heap allocator (before this point all allocations must be static or on the stack).
- Initialize newlib component syscalls and time functions.
- Configure the brownout detector.
- Setup libc stdin, stdout, and stderr according to the :ref:`serial console configuration <CONFIG_ESP_CONSOLE_UART>`.
:esp32: - Perform any security-related checks, including burning efuses that should be burned for this configuration (including :ref:`disabling ROM download mode on ESP32 V3 <CONFIG_SECURE_UART_ROM_DL_MODE>`, :ref:`CONFIG_ESP32_DISABLE_BASIC_ROM_CONSOLE`).
:not esp32: - Perform any security-related checks, including burning efuses that should be burned for this configuration (including :ref:`permanently limiting ROM download modes <CONFIG_SECURE_UART_ROM_DL_MODE>`).
- Initialize SPI flash API support.
- Call global C++ constructors and any C functions marked with ``__attribute__((constructor))``.
Secondary system initialization allows individual components to be initialized. If a component has an initialization function annotated with the ``ESP_SYSTEM_INIT_FN`` macro, it will be called as part of secondary initialization.
.. _app-main-task:
Running the main task
---------------------
After all other components are initialized, the main task is created and the FreeRTOS scheduler starts running.
After doing some more initialization tasks (that require the scheduler to have started), the main task runs the application-provided function ``app_main`` in the firmware.
The main task that runs ``app_main`` has a fixed RTOS priority (one higher than the minimum) and a :ref:`configurable stack size <CONFIG_ESP_MAIN_TASK_STACK_SIZE>`.
.. only:: not CONFIG_FREERTOS_UNICORE
The main task core affinity is also configurable: :ref:`CONFIG_ESP_MAIN_TASK_AFFINITY`.
Unlike normal FreeRTOS tasks (or embedded C ``main`` functions), the ``app_main`` task is allowed to return. If this happens, The task is cleaned up and the system will continue running with other RTOS tasks scheduled normally. Therefore, it is possible to implement ``app_main`` as either a function that creates other application tasks and then returns, or as a main application task itself.
.. only:: not CONFIG_FREERTOS_UNICORE
Second core startup
-------------------
A similar but simpler startup process happens on the APP CPU:
When running system initialization, the code on PRO CPU sets the entry point for APP CPU, de-asserts APP CPU reset, and waits for a global flag to be set by the code running on APP CPU, indicating that it has started. Once this is done, APP CPU jumps to ``call_start_cpu1`` function in :idf_file:`components/esp_system/port/cpu_start.c`.
While PRO CPU does initialization in ``start_cpu0`` function, APP CPU runs ``start_cpu_other_cores`` function. Similar to ``start_cpu0``, this function is weak-linked and defaults to the ``start_cpu_other_cores_default`` function but can be replaced with a different function by the application.
The ``start_cpu_other_cores_default`` function does some core-specific system initialization and then waits for the PRO CPU to start the FreeRTOS scheduler, at which point it executes ``esp_startup_start_app_other_cores`` which is another weak-linked function defaulting to ``esp_startup_start_app_other_cores_default``.
By default ``esp_startup_start_app_other_cores_default`` does nothing but spin in a busy-waiting loop until the scheduler of the PRO CPU triggers an interrupt to start the RTOS scheduler on the APP CPU.

View File

@ -37,37 +37,14 @@ 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>`.
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. There are two approaches to do this:
Using Attribute Macros
""""""""""""""""""""""
Use the ``IRAM_ATTR`` attribute for functions::
#include "esp_attr.h"
void IRAM_ATTR gpio_isr_handler(void* arg)
{
// ...
}
Use the ``DRAM_ATTR`` and ``DRAM_STR`` attributes for constant data::
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");
}
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``.
Using Linker Scripts
""""""""""""""""""""
See :doc:`/api-guides/linker-script-generation` for details.
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).
.. note::
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.
.. only:: esp32c3
.. include:: auto_suspend.inc

View File

@ -92,6 +92,10 @@ DMA-Capable Memory
Use the ``MALLOC_CAP_DMA`` flag to allocate memory which is suitable for use with hardware DMA engines (for example SPI and I2S). This capability flag excludes any external PSRAM.
.. only SOC_SPIRAM_SUPPORTED and not esp32::
The EDMA hardware feature allows DMA buffers to be placed in external PSRAM, but there may be additional alignment constraints. Consult the {IDF_TARGET_NAME} Technical Reference Manual for details. To allocate a DMA-capable external memory buffer, use the ``MALLOC_CAP_SPIRAM`` capabilities flag together with :cpp:func:`heap_caps_aligned_alloc` with the necessary alignment specified.
.. _32-bit accessible memory:
32-Bit Accessible Memory

View File

@ -153,6 +153,8 @@ To get the version at build time, additional version macros are provided. They c
#endif
.. _app-version:
App version
-----------
Application version is stored in :cpp:class:`esp_app_desc_t` structure. It is located in DROM sector and has a fixed offset from the beginning of the binary file.

View File

@ -21,7 +21,7 @@ Background
- Efuses are used to store the secure bootloader key (in efuse BLOCK2), and also a single Efuse bit (ABS_DONE_0) is burned (written to 1) to permanently enable secure boot on the chip. For more details on eFuses, see *{IDF_TARGET_NAME} Technical Reference Manual* > *eFuse Controller (eFuse)* [`PDF <{IDF_TARGET_TRM_EN_URL}#efuse>`__].
- To understand the secure boot process, first familiarise yourself with the standard :doc:`ESP-IDF boot process <../api-guides/general-notes>`.
- To understand the secure boot process, first familiarise yourself with the standard :doc:`ESP-IDF boot process <../api-guides/startup>`.
- Both stages of the boot process (initial software bootloader load, and subsequent partition & app loading) are verified by the secure boot process, in a "chain of trust" relationship.

View File

@ -1,319 +1,10 @@
:orphan:
ESP-IDF 编程注意事项
======================
:link_to_translation:`en:[English]`
====================
应用程序的启动流程
------------------
This page has been split into two new pages:
本文将会介绍 {IDF_TARGET_NAME} 从上电到运行 ``app_main``
函数中间所经历的步骤(即启动流程)。
- :doc:`startup`
- :doc:`memory-types`
宏观上,该启动流程可以分为如下 3 个步骤:
1. 一级引导程序被固化在了 {IDF_TARGET_NAME} 内部的 ROM 中,它会从 Flash 的
``0x1000`` 偏移地址处加载二级引导程序至 RAM(IRAM & DRAM) 中。
2. 二级引导程序从 Flash 中加载分区表和主程序镜像至内存中,主程序中包含了
RAM 段和通过 Flash 高速缓存映射的只读段。
3. 主程序运行,这时第二个 CPU 和 RTOS 的调度器可以开始运行。
下面会对上述过程进行更为详细的阐述。
一级引导程序
~~~~~~~~~~~~
SoC 复位后PRO CPU 会立即开始运行,执行复位向量代码,而 APP CPU
仍然保持复位状态。在启动过程中PRO CPU 会执行所有的初始化操作。APP CPU
的复位状态会在应用程序启动代码的 ``call_start_cpu0``
函数中失效。复位向量代码位于 {IDF_TARGET_NAME} 芯片掩膜 ROM 的 ``0x40000400``
地址处,该地址不能被修改。
复位向量调用的启动代码会根据 ``GPIO_STRAP_REG`` 寄存器的值来确定 {IDF_TARGET_NAME}
的工作模式,该寄存器保存着复位后 bootstrap
引脚的电平状态。根据不同的复位原因,程序会执行不同的操作:
1. 从深度睡眠模式复位:如果 ``RTC_CNTL_STORE6_REG`` 寄存器的值非零,并且
``RTC_CNTL_STORE7_REG`` 寄存器中的 RTC 内存的 CRC
校验值有效,那么程序会使用 ``RTC_CNTL_STORE6_REG``
寄存器的值作为入口地址,并立即跳转到该地址运行。如果
``RTC_CNTL_STORE6_REG`` 的值为零,或者 ``RTC_CNTL_STORE7_REG`` 中的
CRC 校验值无效,又或者跳转到 ``RTC_CNTL_STORE6_REG``
地址处运行的程序返回,那么将会执行上电复位的相关操作。 **注意** :如果想在这里运行自定义的代码,可以参考
:doc:`深度睡眠 <deep-sleep-stub>` 文档里面介绍的方法。
2. 上电复位、软件 SoC 复位、看门狗 SoC 复位:检查 ``GPIO_STRAP_REG``
寄存器,判断是否 UART 或 SDIO 请求进入下载模式。如果是,则配置好 UART
或者 SDIO然后等待下载代码。否则程序将会执行软件 CPU
复位的相关操作。
3. 软件 CPU 复位、看门狗 CPU 复位:根据 EFUSE 中的值配置 SPI
Flash然后尝试从 Flash
中加载代码,这部分的内存将会在后面一小节详细介绍。如果从 Flash
中加载代码失败,就会将 BASIC 解析器加压缩到 RAM
中启动。需要注意的是,此时 RTC
看门狗还在使能状态,如果在几百毫秒内没有任何输入事件,那么看门狗会再次复位
SoC重复整个过程。如果解析器收到了来自 UART
的输入,程序会关闭看门狗。
应用程序的二进制镜像会从 Flash 的 ``0x1000`` 地址处加载。Flash 的第一个
4kB
扇区用于存储安全引导程序和应用程序镜像的签名。有关详细信息,请查看安全启动文档。
.. TODO: describe application binary image format, describe optional flash configuration commands.
二级引导程序
~~~~~~~~~~~~
在 ESP-IDF 中,存放在 Flash 的 ``0x1000``
偏移地址处的二进制镜像就是二级引导程序。二级引导程序的源码可以在 ESP-IDF
的 components/bootloader 目录下找到。请注意,对于 {IDF_TARGET_NAME}
芯片来说,这并不是唯一的安排程序镜像的方式。事实上用户完全可以把一个功能齐全的应用程序烧写到
Flash 的 ``0x1000`` 偏移地址处运行但这超出本文档的范围。ESP-IDF
使用二级引导程序可以增加 Flash 分区的灵活性(使用分区表),并且方便实现
Flash 加密安全引导和空中升级OTA等功能。
当一级引导程序校验并加载完二级引导程序后,它会从二进制镜像的头部找到二级引导程序的入口点,并跳转过去运行。
二级引导程序从 Flash 的 ``0x8000``
偏移地址处读取分区表。详细信息请参阅分区表文档
:doc:`分区表 <partition-tables>` 。二级引导程序会寻找出厂分区和 OTA
分区,然后根据 OTA *信息* 分区的数据决引导哪个分区。
对于选定的分区,二级引导程序将映射到 IRAM 和 DRAM
的数据和代码段复制到它们的加载地址处。对于一些加载地址位于 DROM 和 IROM
区域的段,会通过配置 Flash MMU
为其提供正确的映射。请注意,二级引导程序会为 PRO CPU 和 APP CPU 都配置
Flash MMU但它只使能了 PRO CPU 的 Flash
MMU。这么做的原因在于二级引导程序的代码被加载到了 APP CPU
的高速缓存使用的内存区域,因此使能 APP CPU
高速缓存的任务就交给了应用程序。一旦代码加载完毕并且设置好 Flash
MMU二级引导程序会从应用程序二进制镜像文件的头部寻找入口地址然后跳转到该地址处运行。
目前还不支持添加钩子函数到二级引导程序中以自定义应用程序分区选择的逻辑,但是可以通过别的途径实现这个需求,比如根据某个
GPIO 的不同状态来引导不同的应用程序镜像。此类自定义的功能将在未来添加到
ESP-IDF 中。目前,可以通过将 bootloader
组件复制到应用程序目录并在那里进行必要的更改来自定义引导程序。在这种情况下ESP-IDF
的编译系统将编译应用程序目录中的组件而不是 ESP-IDF 组件目录。
应用程序启动阶段
~~~~~~~~~~~~~~~~
ESP-IDF 应用程序的入口是 ``components/{IDF_TARGET_PATH_NAME}/cpu_start.c`` 文件中的
``call_start_cpu0``
函数,该函数主要完成了两件事,一是启用堆分配器,二是使 APP CPU
跳转到其入口点—— ``call_start_cpu1`` 函数。PRO CPU 上的代码会给 APP
CPU 设置好入口地址,解除其复位状态,然后等待 APP CPU
上运行的代码设置一个全局标志,以表明 APP CPU 已经正常启动。 完成后PRO
CPU 跳转到 ``start_cpu0`` 函数APP CPU 跳转到 ``start_cpu1`` 函数。
``start_cpu0````start_cpu1``
这两个函数都是弱类型的,这意味着如果某些特定的应用程序需要修改初始化顺序,就可以通过重写这两个函数来实现。 ``start_cpu0``
默认的实现方式是初始化用户在 ``menuconfig``
中选择的组件,具体实现步骤可以阅读 ``components/{IDF_TARGET_PATH_NAME}/cpu_start.c``
文件中的源码。请注意,此阶段会调用应用程序中存在的 C++
全局构造函数。一旦所有必要的组件都初始化好,就会创建 *main
task* ,并启动 FreeRTOS 的调度器。
当 PRO CPU 在 ``start_cpu0`` 函数中进行初始化的时候APP CPU 在
``start_cpu1`` 函数中自旋,等待 PRO CPU 上的调度器启动。一旦 PRO CPU
上的调度器启动后APP CPU 上的代码也会启动调度器。
主任务是指运行 ``app_main`` 函数的任务,主任务的堆栈大小和优先级可以在
``menuconfig``
中进行配置。应用程序可以用此任务来完成用户程序相关的初始化设置,比如启动其他的任务。应用程序还可以将主任务用于事件循环和其他通用活动。如果
``app_main`` 函数返回,那么主任务将会被删除。
.. _memory-layout:
应用程序的内存布局
------------------
{IDF_TARGET_NAME} 芯片具有灵活的内存映射功能,本小节将介绍 ESP-IDF
默认使用这些功能的方式。
ESP-IDF 应用程序的代码可以放在以下内存区域之一。
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代码从 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>`
DRAM数据 RAM
~~~~~~~~~~~~~~~~
链接器将非常量静态数据和零初始化数据放入 ``0x3FFB0000 — 0x3FFF0000``
256kB 的区域。注意,如果使用蓝牙堆栈,此区域会减少
64kB通过将起始地址移至 ``0x3FFC0000`` )。如果使用了内存跟踪的功能,该区域的长度还要减少
16kB 或者 32kB。放置静态数据后留在此区域中的剩余空间都用作运行时堆。
常量数据也可以放在 DRAM 中,例如,用在 ISR 中的常量数据(参见上面 IRAM
部分的介绍),为此需要使用 ``DRAM_ATTR`` 宏来声明。
.. code:: c
DRAM_ATTR const char[] format_string = "%p %x";
char buffer[64];
sprintf(buffer, format_string, ptr, val);
毋庸置疑,不建议在 ISR 中使用 ``printf``
和其余输出函数。出于调试的目的,可以在 ISR 中使用 ``ESP_EARLY_LOGx``
来输出日志,不过要确保将 ``TAG`` 和格式字符串都放在了 ``DRAM`` 中。
``__NOINIT_ATTR`` 可以用来声明将数据放在 ``.noinit``
段中,放在此段中的数据不会在启动时被初始化,并且在软件重启后会保留原来的值。
例子:
.. code:: c
__NOINIT_ATTR uint32_t noinit_data;
DROM数据存储在 Flash 中)
~~~~~~~~~~~~~~~~~~~~~~~~~~~
默认情况下,链接器将常量数据放入一个 4MB 区域
(``0x3F400000 — 0x3F800000``) ,该区域用于通过 Flash MMU
和高速缓存来访问外部
Flash。一种特例情况是字面量会被编译器嵌入到应用程序代码中。
RTC 慢速内存
~~~~~~~~~~~~
从 RTC
内存运行的代码(例如深度睡眠模块的代码)使用的全局和静态变量必须要放在
RTC 慢速内存中。更多详细说明请查看文档
:doc:`深度睡眠 <deep-sleep-stub>`
``RTC_NOINIT_ATTR`` 用来声明将数据放入 RTC
慢速内存中,该数据在深度睡眠唤醒后将保持不变。
例子:
.. code:: c
RTC_NOINIT_ATTR uint32_t rtc_noinit_data;
DMA 能力要求
------------
大多数的 DMA 控制器(比如 SPISDMMC 等)都要求发送/接收缓冲区放在 DRAM
中,并且按字对齐。我们建议将 DMA 缓冲区放在静态变量中而不是堆栈中。使用
``DMA_ATTR`` 宏可以声明该全局/本地的静态变量具备 DMA 能力,例如:
.. code:: c
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 );
// 其他程序
}
或者:
.. code:: c
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 缓冲区仍然是允许的,但是你必须记住:
.. list::
:SOC_SPIRAM_SUPPORTED:- 如果堆栈在 pSRAM 中,切勿尝试这么做,因为堆栈在 pSRAM 中的话就要按照
:doc:`片外SRAM <external-ram>` 文档介绍的步骤来操作(至少要在
``menuconfig`` 中使能
``SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY`` ),所以请确保你的任务不在
pSRAM 中。
- 在函数中使用 ``WORD_ALIGNED_ATTR``
宏来修饰变量,将其放在适当的位置上,比如:
.. code:: c
void app_main()
{
uint8_t 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),
};
spi_device_transmit( spi, &temp );
// 其他程序
}

View File

@ -6,6 +6,7 @@ API 指南
:maxdepth: 1
应用层跟踪 <app_trace>
Application Startup Flow <startup>
:SOC_BT_SUPPORTED: BluFi <blufi>
引导加载程序 <bootloader>
构建系统 <build-system>
@ -21,12 +22,12 @@ API 指南
严重错误 <fatal-errors>
Flash 加密 <../security/flash-encryption>
FreeRTOS SMP 变化 <freertos-smp>
一般注意事项 <general-notes>
硬件抽象层 <hardware-abstraction>
:CONFIG_IDF_TARGET_ARCH_XTENSA: 高层中断 <hlinterrupts>
JTAG 调试 <jtag-debugging/index>
链接脚本生成机制 <linker-script-generation>
lwIP TCP/IP 协议栈 <lwip>
Memory Types <memory-types>
分区表 <partition-tables>
:esp32: 射频校准 <RF_calibration>
:esp32: ROM 调试控制台 <romconsole>

View File

@ -0,0 +1,193 @@
.. _memory-layout:
应用程序的内存布局
------------------
{IDF_TARGET_NAME} 芯片具有灵活的内存映射功能,本小节将介绍 ESP-IDF
默认使用这些功能的方式。
ESP-IDF 应用程序的代码可以放在以下内存区域之一。
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代码从 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>`
DRAM数据 RAM
~~~~~~~~~~~~~~~~
链接器将非常量静态数据和零初始化数据放入 ``0x3FFB0000 — 0x3FFF0000``
256kB 的区域。注意,如果使用蓝牙堆栈,此区域会减少
64kB通过将起始地址移至 ``0x3FFC0000`` )。如果使用了内存跟踪的功能,该区域的长度还要减少
16kB 或者 32kB。放置静态数据后留在此区域中的剩余空间都用作运行时堆。
常量数据也可以放在 DRAM 中,例如,用在 ISR 中的常量数据(参见上面 IRAM
部分的介绍),为此需要使用 ``DRAM_ATTR`` 宏来声明。
.. code:: c
DRAM_ATTR const char[] format_string = "%p %x";
char buffer[64];
sprintf(buffer, format_string, ptr, val);
毋庸置疑,不建议在 ISR 中使用 ``printf``
和其余输出函数。出于调试的目的,可以在 ISR 中使用 ``ESP_EARLY_LOGx``
来输出日志,不过要确保将 ``TAG`` 和格式字符串都放在了 ``DRAM`` 中。
``__NOINIT_ATTR`` 可以用来声明将数据放在 ``.noinit``
段中,放在此段中的数据不会在启动时被初始化,并且在软件重启后会保留原来的值。
例子:
.. code:: c
__NOINIT_ATTR uint32_t noinit_data;
DROM数据存储在 Flash 中)
~~~~~~~~~~~~~~~~~~~~~~~~~~~
默认情况下,链接器将常量数据放入一个 4MB 区域
(``0x3F400000 — 0x3F800000``) ,该区域用于通过 Flash MMU
和高速缓存来访问外部
Flash。一种特例情况是字面量会被编译器嵌入到应用程序代码中。
RTC 慢速内存
~~~~~~~~~~~~
从 RTC
内存运行的代码(例如深度睡眠模块的代码)使用的全局和静态变量必须要放在
RTC 慢速内存中。更多详细说明请查看文档
:doc:`深度睡眠 <deep-sleep-stub>`
``RTC_NOINIT_ATTR`` 用来声明将数据放入 RTC
慢速内存中,该数据在深度睡眠唤醒后将保持不变。
例子:
.. code:: c
RTC_NOINIT_ATTR uint32_t rtc_noinit_data;
DMA 能力要求
------------
大多数的 DMA 控制器(比如 SPISDMMC 等)都要求发送/接收缓冲区放在 DRAM
中,并且按字对齐。我们建议将 DMA 缓冲区放在静态变量中而不是堆栈中。使用
``DMA_ATTR`` 宏可以声明该全局/本地的静态变量具备 DMA 能力,例如:
.. code:: c
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 );
// 其他程序
}
或者:
.. code:: c
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 缓冲区仍然是允许的,但是你必须记住:
.. list::
:SOC_SPIRAM_SUPPORTED:- 如果堆栈在 pSRAM 中,切勿尝试这么做,因为堆栈在 pSRAM 中的话就要按照
:doc:`片外SRAM <external-ram>` 文档介绍的步骤来操作(至少要在
``menuconfig`` 中使能
``SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY`` ),所以请确保你的任务不在
pSRAM 中。
- 在函数中使用 ``WORD_ALIGNED_ATTR``
宏来修饰变量,将其放在适当的位置上,比如:
.. code:: c
void app_main()
{
uint8_t 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),
};
spi_device_transmit( spi, &temp );
// 其他程序
}

View File

@ -0,0 +1,127 @@
应用程序的启动流程
===================
:link_to_translation:`en:[English]`
本文将会介绍 {IDF_TARGET_NAME} 从上电到运行 ``app_main``
函数中间所经历的步骤(即启动流程)。
宏观上,该启动流程可以分为如下 3 个步骤:
1. 一级引导程序被固化在了 {IDF_TARGET_NAME} 内部的 ROM 中,它会从 Flash 的
``0x1000`` 偏移地址处加载二级引导程序至 RAM(IRAM & DRAM) 中。
2. 二级引导程序从 Flash 中加载分区表和主程序镜像至内存中,主程序中包含了
RAM 段和通过 Flash 高速缓存映射的只读段。
3. 主程序运行,这时第二个 CPU 和 RTOS 的调度器可以开始运行。
下面会对上述过程进行更为详细的阐述。
一级引导程序
~~~~~~~~~~~~
SoC 复位后PRO CPU 会立即开始运行,执行复位向量代码,而 APP CPU
仍然保持复位状态。在启动过程中PRO CPU 会执行所有的初始化操作。APP CPU
的复位状态会在应用程序启动代码的 ``call_start_cpu0``
函数中失效。复位向量代码位于 {IDF_TARGET_NAME} 芯片掩膜 ROM 的 ``0x40000400``
地址处,该地址不能被修改。
复位向量调用的启动代码会根据 ``GPIO_STRAP_REG`` 寄存器的值来确定 {IDF_TARGET_NAME}
的工作模式,该寄存器保存着复位后 bootstrap
引脚的电平状态。根据不同的复位原因,程序会执行不同的操作:
1. 从深度睡眠模式复位:如果 ``RTC_CNTL_STORE6_REG`` 寄存器的值非零,并且
``RTC_CNTL_STORE7_REG`` 寄存器中的 RTC 内存的 CRC
校验值有效,那么程序会使用 ``RTC_CNTL_STORE6_REG``
寄存器的值作为入口地址,并立即跳转到该地址运行。如果
``RTC_CNTL_STORE6_REG`` 的值为零,或者 ``RTC_CNTL_STORE7_REG`` 中的
CRC 校验值无效,又或者跳转到 ``RTC_CNTL_STORE6_REG``
地址处运行的程序返回,那么将会执行上电复位的相关操作。 **注意** :如果想在这里运行自定义的代码,可以参考
:doc:`深度睡眠 <deep-sleep-stub>` 文档里面介绍的方法。
2. 上电复位、软件 SoC 复位、看门狗 SoC 复位:检查 ``GPIO_STRAP_REG``
寄存器,判断是否 UART 或 SDIO 请求进入下载模式。如果是,则配置好 UART
或者 SDIO然后等待下载代码。否则程序将会执行软件 CPU
复位的相关操作。
3. 软件 CPU 复位、看门狗 CPU 复位:根据 EFUSE 中的值配置 SPI
Flash然后尝试从 Flash
中加载代码,这部分的内存将会在后面一小节详细介绍。如果从 Flash
中加载代码失败,就会将 BASIC 解析器加压缩到 RAM
中启动。需要注意的是,此时 RTC
看门狗还在使能状态,如果在几百毫秒内没有任何输入事件,那么看门狗会再次复位
SoC重复整个过程。如果解析器收到了来自 UART
的输入,程序会关闭看门狗。
应用程序的二进制镜像会从 Flash 的 ``0x1000`` 地址处加载。Flash 的第一个
4kB
扇区用于存储安全引导程序和应用程序镜像的签名。有关详细信息,请查看安全启动文档。
.. TODO: describe application binary image format, describe optional flash configuration commands.
二级引导程序
~~~~~~~~~~~~
在 ESP-IDF 中,存放在 Flash 的 ``0x1000``
偏移地址处的二进制镜像就是二级引导程序。二级引导程序的源码可以在 ESP-IDF
的 components/bootloader 目录下找到。请注意,对于 {IDF_TARGET_NAME}
芯片来说,这并不是唯一的安排程序镜像的方式。事实上用户完全可以把一个功能齐全的应用程序烧写到
Flash 的 ``0x1000`` 偏移地址处运行但这超出本文档的范围。ESP-IDF
使用二级引导程序可以增加 Flash 分区的灵活性(使用分区表),并且方便实现
Flash 加密安全引导和空中升级OTA等功能。
当一级引导程序校验并加载完二级引导程序后,它会从二进制镜像的头部找到二级引导程序的入口点,并跳转过去运行。
二级引导程序从 Flash 的 ``0x8000``
偏移地址处读取分区表。详细信息请参阅分区表文档
:doc:`分区表 <partition-tables>` 。二级引导程序会寻找出厂分区和 OTA
分区,然后根据 OTA *信息* 分区的数据决引导哪个分区。
对于选定的分区,二级引导程序将映射到 IRAM 和 DRAM
的数据和代码段复制到它们的加载地址处。对于一些加载地址位于 DROM 和 IROM
区域的段,会通过配置 Flash MMU
为其提供正确的映射。请注意,二级引导程序会为 PRO CPU 和 APP CPU 都配置
Flash MMU但它只使能了 PRO CPU 的 Flash
MMU。这么做的原因在于二级引导程序的代码被加载到了 APP CPU
的高速缓存使用的内存区域,因此使能 APP CPU
高速缓存的任务就交给了应用程序。一旦代码加载完毕并且设置好 Flash
MMU二级引导程序会从应用程序二进制镜像文件的头部寻找入口地址然后跳转到该地址处运行。
目前还不支持添加钩子函数到二级引导程序中以自定义应用程序分区选择的逻辑,但是可以通过别的途径实现这个需求,比如根据某个
GPIO 的不同状态来引导不同的应用程序镜像。此类自定义的功能将在未来添加到
ESP-IDF 中。目前,可以通过将 bootloader
组件复制到应用程序目录并在那里进行必要的更改来自定义引导程序。在这种情况下ESP-IDF
的编译系统将编译应用程序目录中的组件而不是 ESP-IDF 组件目录。
应用程序启动阶段
~~~~~~~~~~~~~~~~
ESP-IDF 应用程序的入口是 ``components/{IDF_TARGET_PATH_NAME}/cpu_start.c`` 文件中的
``call_start_cpu0``
函数,该函数主要完成了两件事,一是启用堆分配器,二是使 APP CPU
跳转到其入口点—— ``call_start_cpu1`` 函数。PRO CPU 上的代码会给 APP
CPU 设置好入口地址,解除其复位状态,然后等待 APP CPU
上运行的代码设置一个全局标志,以表明 APP CPU 已经正常启动。 完成后PRO
CPU 跳转到 ``start_cpu0`` 函数APP CPU 跳转到 ``start_cpu1`` 函数。
``start_cpu0````start_cpu1``
这两个函数都是弱类型的,这意味着如果某些特定的应用程序需要修改初始化顺序,就可以通过重写这两个函数来实现。 ``start_cpu0``
默认的实现方式是初始化用户在 ``menuconfig``
中选择的组件,具体实现步骤可以阅读 ``components/{IDF_TARGET_PATH_NAME}/cpu_start.c``
文件中的源码。请注意,此阶段会调用应用程序中存在的 C++
全局构造函数。一旦所有必要的组件都初始化好,就会创建 *main
task* ,并启动 FreeRTOS 的调度器。
当 PRO CPU 在 ``start_cpu0`` 函数中进行初始化的时候APP CPU 在
``start_cpu1`` 函数中自旋,等待 PRO CPU 上的调度器启动。一旦 PRO CPU
上的调度器启动后APP CPU 上的代码也会启动调度器。
.. _app-main-task:
Running the main task
---------------------
主任务是指运行 ``app_main`` 函数的任务,主任务的堆栈大小和优先级可以在
``menuconfig``
中进行配置。应用程序可以用此任务来完成用户程序相关的初始化设置,比如启动其他的任务。应用程序还可以将主任务用于事件循环和其他通用活动。如果
``app_main`` 函数返回,那么主任务将会被删除。