docs: Improve/clarify partition, OTA & SPI flash docs

Related to #313 https://github.com/espressif/esp-idf/issues/313
This commit is contained in:
Angus Gratton 2017-02-17 12:43:55 +11:00
parent 4494e15ecf
commit 1f3a2e900c
11 changed files with 340 additions and 187 deletions

View file

@ -82,7 +82,7 @@ esp_err_t esp_ota_begin(const esp_partition_t *partition, size_t image_size, esp
return ESP_ERR_NO_MEM;
}
// if input image size is 0 or OTA_SIZE_UNKNOWN, will erase all areas in this partition
// If input image size is 0 or OTA_SIZE_UNKNOWN, erase entire partition
if ((image_size == 0) || (image_size == OTA_SIZE_UNKNOWN)) {
ret = esp_partition_erase_range(partition, 0, partition->size);
} else {
@ -301,7 +301,7 @@ static esp_err_t esp_rewrite_ota_data(esp_partition_subtype_t subtype)
//so current ota app sub type id is x , dest bin subtype is y,total ota app count is n
//seq will add (x + n*1 + 1 - seq)%n
if (SUB_TYPE_ID(subtype) >= ota_app_count) {
return ESP_ERR_NOT_FOUND;
return ESP_ERR_INVALID_ARG;
}
ret = esp_partition_mmap(find_partition, 0, find_partition->size, SPI_FLASH_MMAP_DATA, &result, &ota_data_map);

View file

@ -27,57 +27,75 @@ extern "C"
{
#endif
#define OTA_SIZE_UNKNOWN 0xffffffff
#define OTA_SIZE_UNKNOWN 0xffffffff /*!< Used for esp_ota_begin() if new image size is unknown */
#define ESP_ERR_OTA_BASE 0x1500 /*!< base error code for ota_ops api */
#define ESP_ERR_OTA_PARTITION_CONFLICT (ESP_ERR_OTA_BASE + 0x01) /*!< want to write or erase current running partition */
#define ESP_ERR_OTA_SELECT_INFO_INVALID (ESP_ERR_OTA_BASE + 0x02) /*!< ota data partition info is error */
#define ESP_ERR_OTA_VALIDATE_FAILED (ESP_ERR_OTA_BASE + 0x03) /*!< validate ota image failed */
#define ESP_ERR_OTA_BASE 0x1500 /*!< Base error code for ota_ops api */
#define ESP_ERR_OTA_PARTITION_CONFLICT (ESP_ERR_OTA_BASE + 0x01) /*!< Error if request was to write or erase the current running partition */
#define ESP_ERR_OTA_SELECT_INFO_INVALID (ESP_ERR_OTA_BASE + 0x02) /*!< Error if OTA data partition contains invalid content */
#define ESP_ERR_OTA_VALIDATE_FAILED (ESP_ERR_OTA_BASE + 0x03) /*!< Error if OTA app image is invalid */
/**
* @brief Opaque handle for application update obtained from app_ops.
* @brief Opaque handle for an application OTA update
*
* esp_ota_begin() returns a handle which is then used for subsequent
* calls to esp_ota_write() and esp_ota_end().
*/
typedef uint32_t esp_ota_handle_t;
/**
* @brief format input partition in flash to 0xFF as input image size,
* if unkown image size ,pass 0x0 or 0xFFFFFFFF, it will erase all the
* partition ,Otherwise, erase the required range
*
* @param partition Pointer to partition structure which need to be updated
* Must be non-NULL.
* @param image_size size of image need to be updated
* @param out_handle handle which should be used for esp_ota_write or esp_ota_end call
* @brief Commence an OTA update writing to the specified partition.
* @return:
* - ESP_OK: if format ota image OK
* - ESP_ERR_OTA_PARTITION_CONFLICT: operate current running bin
* - ESP_ERR_OTA_SELECT_INFO_INVALID: ota bin select info invalid
* The specified partition is erased to the specified image size.
*
* If image size is not yet known, pass OTA_SIZE_UNKNOWN which will
* cause the entire partition to be erased.
*
* On success, this function allocates memory that remains in use
* until esp_ota_end() is called with the returned handle.
*
* @param partition Pointer to info for partition which will receive the OTA update. Required.
* @param image_size Size of new OTA app image. Partition will be erased in order to receive this size of image. If 0 or OTA_SIZE_UNKNOWN, the entire partition is erased.
* @param out_handle On success, returns a handle which should be used for subsequent esp_ota_write() and esp_ota_end() calls.
* @return
* - ESP_OK: OTA operation commenced successfully.
* - ESP_ERR_INVALID_ARG: partition or out_handle arguments were NULL.
* - ESP_ERR_NO_MEM: Cannot allocate memory for OTA operation.
* - ESP_ERR_OTA_PARTITION_CONFLICT: Partition is currently in use, cannot update.
* - ESP_ERR_OTA_SELECT_INFO_INVALID: The OTA data partition contains invalid data.
* - ESP_ERR_INVALID_SIZE: Partition doesn't fit in configured flash size.
* - ESP_ERR_FLASH_OP_TIMEOUT or ESP_ERR_FLASH_OP_FAIL: Flash write failed.
*/
esp_err_t esp_ota_begin(const esp_partition_t* partition, size_t image_size, esp_ota_handle_t* out_handle);
/**
* @brief Write data to input input partition
* @brief Write OTA update data to partition
*
* @param handle Handle obtained from esp_ota_begin
* @param data Pointer to data write to flash
* @param size data size of recieved data
* This function can be called multiple times as
* data is received during the OTA operation. Data is written
* sequentially to the partition.
*
* @return:
* - ESP_OK: if write flash data OK
* - ESP_ERR_OTA_PARTITION_CONFLICT: operate current running bin
* - ESP_ERR_OTA_SELECT_INFO_INVALID: ota bin select info invalid
* @param handle Handle obtained from esp_ota_begin
* @param data Data buffer to write
* @param size Size of data buffer in bytes.
*
* @return
* - ESP_OK: Data was written to flash successfully.
* - ESP_ERR_INVALID_ARG: handle is invalid.
* - ESP_ERR_OTA_VALIDATE_FAILED: First byte of image contains invalid app image magic byte.
* - ESP_ERR_FLASH_OP_TIMEOUT or ESP_ERR_FLASH_OP_FAIL: Flash write failed.
* - ESP_ERR_OTA_SELECT_INFO_INVALID: OTA data partition has invalid contents
*/
esp_err_t esp_ota_write(esp_ota_handle_t handle, const void* data, size_t size);
/**
* @brief Finish the update and validate written data
* @brief Finish OTA update and validate newly written app image.
*
* @param handle Handle obtained from esp_ota_begin.
* @param handle Handle obtained from esp_ota_begin().
*
* @note After calling esp_ota_end(), the handle is no longer valid and any memory associated with it is freed (regardless of result).
*
* @return:
* @return
* - ESP_OK: Newly written OTA app image is valid.
* - ESP_ERR_NOT_FOUND: OTA handle was not found.
* - ESP_ERR_INVALID_ARG: Handle was never written to.
@ -87,24 +105,25 @@ esp_err_t esp_ota_write(esp_ota_handle_t handle, const void* data, size_t size);
esp_err_t esp_ota_end(esp_ota_handle_t handle);
/**
* @brief Set next boot partition, call system_restart() will switch to run it
* @brief Configure OTA data for a new boot partition
*
* @note if you want switch to run a bin file
* has never been checked before,please validate it's signature firstly
* @note If this function returns ESP_OK, calling esp_restart() will boot the newly configured app partition.
*
* @param partition Pointer to partition structure which need to boot
* @param partition Pointer to info for partition containing app image to boot.
*
* @return:
* - ESP_OK: if set next boot partition OK
* - ESP_ERR_OTA_SELECT_INFO_INVALID: ota bin select info invalid
* @return
* - ESP_OK: OTA data updated, next reboot will use specified partition.
* - ESP_ERR_INVALID_ARG: partition argument was NULL or didn't point to a valid OTA partition of type "app".
* - ESP_ERR_OTA_VALIDATE_FAILED: Partition contained invalid app image. Also returned if secure boot is enabled and signature validation failed.
* - ESP_ERR_NOT_FOUND: OTA data partition not found.
* - ESP_ERR_FLASH_OP_TIMEOUT or ESP_ERR_FLASH_OP_FAIL: Flash erase or write failed.
*/
esp_err_t esp_ota_set_boot_partition(const esp_partition_t* partition);
/**
* @brief Get partition info of current running image
*
* @return pointer to esp_partition_t structure, or NULL if no partition is found or
* operate flash failed,This pointer is valid for the lifetime of the application.
* @brief Get partition info of currently running app
*
* @return Pointer to info for partition structure, or NULL if no partition is found or flash read operation failed. Returned pointer is valid for the lifetime of the application.
*/
const esp_partition_t* esp_ota_get_boot_partition(void);

View file

@ -106,14 +106,13 @@ void IRAM_ATTR call_start_cpu0()
}
/**
* @function : load_partition_table
* @description: Parse partition table, get useful data such as location of
* OTA info sector, factory app sector, and test app sector.
/** @brief Load partition table
*
* @inputs: bs bootloader state structure used to save the data
* @return: return true, if the partition table is loaded (and MD5 checksum is valid)
* Parse partition table, get useful data such as location of
* OTA data partition, factory app partition, and test app partition.
*
* @param bs bootloader state structure used to save read data
* @return return true if the partition table was succesfully loaded and MD5 checksum is valid.
*/
bool load_partition_table(bootloader_state_t* bs)
{

View file

@ -33,7 +33,7 @@
*
* @return true if flash encryption is enabled.
*/
static inline IRAM_ATTR bool esp_flash_encryption_enabled(void) {
static inline /** @cond */ IRAM_ATTR /** @endcond */ bool esp_flash_encryption_enabled(void) {
uint32_t flash_crypt_cnt = REG_GET_FIELD(EFUSE_BLK0_RDATA0_REG, EFUSE_RD_FLASH_CRYPT_CNT);
/* __builtin_parity is in flash, so we calculate parity inline */
bool enabled = false;

View file

@ -1,15 +1,15 @@
SPI flash related APIs
======================
SPI Flash APIs
==============
Overview
--------
Spi_flash component contains APIs related to reading, writing, erasing,
The spi_flash component contains APIs related to reading, writing, erasing,
memory mapping data in the external SPI flash. It also has higher-level
APIs which work with partition table and partitions.
APIs which work with partitions defined in the :doc:`partition table </partition-tables>`.
Note that all the functionality is limited to the "main" flash chip,
i.e. the flash chip from which program runs. For ``spi_flash_*`` functions,
this is software limitation. Underlying ROM functions which work with SPI flash
Note that all the functionality is limited to the "main" SPI flash chip,
the same SPI flash chip from which program runs. For ``spi_flash_*`` functions,
this is a software limitation. The underlying ROM functions which work with SPI flash
do not have provisions for working with flash chips attached to SPI peripherals
other than SPI0.
@ -24,74 +24,113 @@ This is the set of APIs for working with data in flash:
- ``spi_flash_erase_range`` used to erase range of addresses in flash
- ``spi_flash_get_chip_size`` returns flash chip size, in bytes, as configured in menuconfig
There are some data alignment limitations which need to be considered when using
spi_flash_read/spi_flash_write functions:
Generally, try to avoid using the raw SPI flash functions in favour of
partition-specific functions.
- buffer in RAM must be 4-byte aligned
- size must be 4-byte aligned
- address in flash must be 4-byte aligned
SPI Flash Size
--------------
These alignment limitations are purely software, and should be removed in future
versions.
The SPI flash size is configured by writing a field in the software bootloader
image header, flashed at offset 0x1000.
It is assumed that correct SPI flash chip size is set at compile time using
menuconfig. While run-time detection of SPI flash chip size is possible, it is
not implemented yet. Applications which need this (e.g. to provide one firmware
binary for different flash sizes) can do flash chip size detection and set
the correct flash chip size in ``chip_size`` member of ``g_rom_flashchip``
structure. This size is used by ``spi_flash_*`` functions for bounds checking.
By default, the SPI flash size is detected by esptool.py when this bootloader is
written to flash, and the header is updated with the correct
size. Alternatively, it is possible to generate a fixed flash size by disabling
detection in ``make menuconfig`` (under Serial Flasher Config).
SPI flash APIs disable instruction and data caches while reading/writing/erasing.
See implementation notes below on details how this happens. For application
this means that at some periods of time, code can not be run from flash,
and constant data can not be fetched from flash by the CPU. This is not an
issue for normal code which runs in a task, because SPI flash APIs prevent
other tasks from running while caches are disabled. This is an issue for
interrupt handlers, which can still be called while flash operation is in
progress. If the interrupt handler is not placed into IRAM, there is a
possibility that interrupt will happen at the time when caches are disabled,
which will cause an illegal instruction exception.
If it is necessary to override the configured flash size at runtime, is is
possible to set the ``chip_size`` member of ``g_rom_flashchip`` structure. This
size is used by ``spi_flash_*`` functions (in both software & ROM) for bounds
checking.
To prevent this, make sure that all ISR code, and all functions called from ISR
code are placed into IRAM, or are located in ROM. Most useful C library
functions are located in ROM, so they can be called from ISR.
Concurrency Constraints
-----------------------
To place a function into IRAM, use ``IRAM_ATTR`` attribute, e.g.::
Because the SPI flash is also used for firmware execution (via the instruction &
data caches), these caches much be disabled while reading/writing/erasing. This
means that both CPUs must be running code from IRAM and only reading data from
DRAM while flash write operations occur.
#include "esp_attr.h"
void IRAM_ATTR gpio_isr_handler(void* arg)
{
// ...
}
When flash encryption is enabled, ``spi_flash_read`` will read data as it is
stored in flash (without decryption), and ``spi_flash_write`` will write data
in plain text. In other words, ``spi_flash_read/write`` APIs don't have
provisions to deal with encrypted data.
Refer to the :ref:`application memory layout <memory-layout>` documentation for
an explanation of the differences between IRAM, DRAM and flash cache.
To avoid reading flash cache accidentally, when one CPU commences a flash write
or erase operation the other CPU is put into a blocked state and all
non-IRAM-safe interrupts are disabled on both CPUs, until the flash operation
completes.
.. _iram-safe-interrupt-handlers:
IRAM-Safe Interrupt Handlers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you have an interrupt handler that you want to execute even when 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/system/intr_alloc>`.
You must ensure all data and functions accessed by these interrupt handlers are
located in IRAM or DRAM. This includes any functions that the handler calls.
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 recognise that a variable or expression is constant
(even if it is not marked ``const``) and optimise it into flash, unless it is
marked with ``DRAM_ATTR``.
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).
Partition table APIs
--------------------
ESP-IDF uses partition table to maintain information about various regions of
ESP-IDF projects use a partition table to maintain information about various regions of
SPI flash memory (bootloader, various application binaries, data, filesystems).
More information about partition tables can be found in docs/partition_tables.rst.
More information about partition tables can be found :doc:`here </partition-tables>`.
This component provides APIs to enumerate partitions found in the partition table
and perform operations on them. These functions are declared in ``esp_partition.h``:
- ``esp_partition_find`` used to search partition table for entries with specific type, returns an opaque iterator
- ``esp_partition_find`` used to search partition table for entries with
specific type, returns an opaque iterator
- ``esp_partition_get`` returns a structure describing the partition, for the given iterator
- ``esp_partition_next`` advances iterator to the next partition found
- ``esp_partition_iterator_release`` releases iterator returned by ``esp_partition_find``
- ``esp_partition_find_first`` is a convenience function which returns structure describing the first partition found by esp_partition_find
- ``esp_partition_read``, ``esp_partition_write``, ``esp_partition_erase_range`` are equivalent to ``spi_flash_read``, ``spi_flash_write``, ``spi_flash_erase_range``, but operate within partition boundaries
- ``esp_partition_find_first`` is a convenience function which returns structure
describing the first partition found by esp_partition_find
- ``esp_partition_read``, ``esp_partition_write``, ``esp_partition_erase_range``
are equivalent to ``spi_flash_read``, ``spi_flash_write``,
``spi_flash_erase_range``, but operate within partition boundaries
Most application code should use ``esp_partition_*`` APIs instead of lower level
``spi_flash_*`` APIs. Partition APIs do bounds checking and calculate correct
offsets in flash based on data stored in partition table.
SPI Flash Encryption
--------------------
It is possible to encrypt SPI flash contents, and have it transparenlty decrypted by hardware.
Refer to the :doc:`Flash Encryption documentation </security/flash-encryption>` for more details.
Memory mapping APIs
-------------------
@ -105,10 +144,10 @@ about memory mapping hardware.
Note that some number of 64KB pages is used to map the application
itself into memory, so the actual number of available 64KB pages may be less.
Reading data from flash using a memory mapped region is the only way to decrypt
contents of flash when flash encryption is enabled. Decryption is performed at
hardware level.
contents of flash when :doc:`flash encryption </security/flash-encryption>` is enabled.
Decryption is performed at hardware level.
Memory mapping APIs are declared in ``esp_spi_flash.h`` and ``esp_partition.h``:
@ -119,40 +158,8 @@ Memory mapping APIs are declared in ``esp_spi_flash.h`` and ``esp_partition.h``:
Differences between ``spi_flash_mmap`` and ``esp_partition_mmap`` are as follows:
- ``spi_flash_mmap`` must be given a 64KB aligned physical address
- ``esp_partition_mmap`` may be given an arbitrary offset within the partition, it will adjust returned pointer to mapped memory as necessary
- ``esp_partition_mmap`` may be given an arbitrary offset within the partition,
it will adjust returned pointer to mapped memory as necessary
Note that because memory mapping happens in 64KB blocks, it may be possible to
read data outside of the partition provided to ``esp_partition_mmap``.
Implementation notes
--------------------
In order to perform some flash operations, we need to make sure both CPUs
are not running any code from flash for the duration of the flash operation.
In a single-core setup this is easy: we disable interrupts/scheduler and do
the flash operation. In the dual-core setup this is slightly more complicated.
We need to make sure that the other CPU doesn't run any code from flash.
When SPI flash API is called on CPU A (can be PRO or APP), we start
spi_flash_op_block_func function on CPU B using esp_ipc_call API. This API
wakes up high priority task on CPU B and tells it to execute given function,
in this case spi_flash_op_block_func. This function disables cache on CPU B and
signals that cache is disabled by setting s_flash_op_can_start flag.
Then the task on CPU A disables cache as well, and proceeds to execute flash
operation.
While flash operation is running, interrupts can still run on CPUs A and B.
We assume that all interrupt code is placed into RAM. Once interrupt allocation
API is added, we should add a flag to request interrupt to be disabled for
the duration of flash operations.
Once flash operation is complete, function on CPU A sets another flag,
s_flash_op_complete, to let the task on CPU B know that it can re-enable
cache and release the CPU. Then the function on CPU A re-enables the cache on
CPU A as well and returns control to the calling code.
Additionally, all API functions are protected with a mutex (s_flash_op_mutex).
In a single core environment (CONFIG_FREERTOS_UNICORE enabled), we simply
disable both caches, no inter-CPU communication takes place.
read data outside of the partition provided to ``esp_partition_mmap``.

View file

@ -1,5 +1,12 @@
.. include:: ../../../components/spi_flash/README.rst
See also
--------
- :doc:`Partition Table documentation </partition-tables>`
- :doc:`Over The Air Update (OTA) API </api/system/ota>` provides high-level API for updating app firmware stored in flash.
- :doc:`Non-Volatile Storage (NVS) API <nvs_flash>` provides a structured API for storing small items of data in SPI flash.
API Reference
-------------
@ -63,3 +70,37 @@ Functions
.. doxygenfunction:: esp_partition_mmap
.. doxygenfunction:: esp_flash_encryption_enabled
.. _spi-flash-implementation-details:
Implementation details
----------------------
In order to perform some flash operations, we need to make sure both CPUs
are not running any code from flash for the duration of the flash operation.
In a single-core setup this is easy: we disable interrupts/scheduler and do
the flash operation. In the dual-core setup this is slightly more complicated.
We need to make sure that the other CPU doesn't run any code from flash.
When SPI flash API is called on CPU A (can be PRO or APP), we start
spi_flash_op_block_func function on CPU B using esp_ipc_call API. This API
wakes up high priority task on CPU B and tells it to execute given function,
in this case spi_flash_op_block_func. This function disables cache on CPU B and
signals that cache is disabled by setting s_flash_op_can_start flag.
Then the task on CPU A disables cache as well, and proceeds to execute flash
operation.
While flash operation is running, interrupts can still run on CPUs A and B.
We assume that all interrupt code is placed into RAM. Once interrupt allocation
API is added, we should add a flag to request interrupt to be disabled for
the duration of flash operations.
Once flash operation is complete, function on CPU A sets another flag,
s_flash_op_complete, to let the task on CPU B know that it can re-enable
cache and release the CPU. Then the function on CPU A re-enables the cache on
CPU A as well and returns control to the calling code.
Additionally, all API functions are protected with a mutex (s_flash_op_mutex).
In a single core environment (CONFIG_FREERTOS_UNICORE enabled), we simply
disable both caches, no inter-CPU communication takes place.

View file

@ -7,7 +7,7 @@ System API
Memory Allocation <mem_alloc>
Interrupt Allocation <intr_alloc>
Watchdogs <wdts>
OTA <ota>
Over The Air Updates (OTA) <ota>
Deep Sleep <deep_sleep>
Logging <log>

View file

@ -32,30 +32,56 @@ even when the int for DevB was cleared) the interrupt is never serviced.)
Multicore issues
----------------
Peripherals that can generate interrupts can be divided in two types: external peripherals, outside the Xtensa
cores in the ESP32, and internal peripherals, inside the ESP32. Interrupt handling differs slightly between
these two types of peripherals.
Peripherals that can generate interrupts can be divided in two types:
Each Xtensa core has its own set of internal peripherals: three timer comparators, a performance monitor and two
software interrupts. These peripherals can only be configured from the core they are associated with. When
generating an interrupt, the interrupt they generate is hard-wired to their associated core; it's not possible
to have e.g. an internal timer comparator of one core generate an interrupt on another core. That is why these
sources can only be managed using a task running on that specific core. Internal interrupt sources are still
allocatable using esp_intr_alloc as normal, but they cannot be shared and will always have a fixed interrupt
level (namely, the one associated in hardware with the peripheral). Internal interrupt sources are defined
in esp_intr_alloc.h as ETS_INTERNAL_*_INTR_SOURCE.
- External peripherals, within the ESP32 but outside the Xtensa cores themselves. Most ESP32 peripherals are of this type.
- Internal peripherals, part of the Xtensa CPU cores themselves.
The remaining interrupt slots in both cores are wired to an interrupt multiplexer, which can be used to
route any external interrupt source to any of these interrupt slots. Allocating an external interrupt will always
allocate it on the core that does the allocation, and freeing the interrupt should always happen on the same
core. Disabling and enabling the interrupt from another core is allowed, however. External interrupts can
share an interrupt slot bu passing ESP_INTR_FLAG_SHARED as a flag to esp_intr_alloc. External interrupt sources
are defined in soc/soc.h as ETS_*_INTR_SOURCE.
Interrupt handling differs slightly between these two types of peripherals.
Care should be taken when allocating an interrupt using a task not pinned to a certain core; while running
code not in a critical secion, these tasks can migrate between cores at any moment, possibly making an
interrupt operation fail because of the reasons mentioned above. It is advised to always use
xTaskCreatePinnedToCore with a specific CoreID argument to create tasks that will handle interrupts.
Internal peripheral interrupts
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Each Xtensa CPU core has its own set of six internal peripherals:
- Three timer comparators
- A performance monitor
- Two software interrupts.
Internal interrupt sources are defined in esp_intr_alloc.h as ``ETS_INTERNAL_*_INTR_SOURCE``.
These peripherals can only be configured from the core they are associated with. When generating an interrupt,
the interrupt they generate is hard-wired to their associated core; it's not possible to have e.g. an internal
timer comparator of one core generate an interrupt on another core. That is why these sources can only be managed
using a task running on that specific core. Internal interrupt sources are still allocatable using esp_intr_alloc
as normal, but they cannot be shared and will always have a fixed interrupt level (namely, the one associated in
hardware with the peripheral).
External Peripheral Interrupts
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The remaining interrupt sources are from external peripherals. These are defined in soc/soc.h as ``ETS_*_INTR_SOURCE``.
Non-internal interrupt slots in both CPU cores are wired to an interrupt multiplexer, which can be used to
route any external interrupt source to any of these interrupt slots.
- Allocating an external interrupt will always allocate it on the core that does the allocation.
- Freeing an external interrupt must always happen on the same core it was allocated on.
- Disabling and enabling external interrupts from another core is allowed.
- Multiple external interrupt sources can share an interrupt slot by passing ``ESP_INTR_FLAG_SHARED`` as a flag to esp_intr_alloc().
Care should be taken when calling esp_intr_alloc() from a task which is not pinned to a core. During task switching, these tasks can migrate between cores. Therefore it is impossible to tell which CPU the interrupt is allocated on, which makes it difficult to free the interrupt handle and may also cause debugging difficulties. It is advised to use xTaskCreatePinnedToCore() with a specific CoreID argument to create tasks that will allocate interrupts. In the case of internal interrupt sources, this is required.
IRAM-Safe Interrupt Handlers
----------------------------
The ``ESP_INTR_FLAG_IRAM`` flag registers an interrupt handler that always runs from IRAM (and reads all its data from DRAM), and therefore does not need to be disabled during flash erase and write operations.
This is useful for interrupts which need a guaranteed minimum execution latency, as flash write and erase operations can be slow (erases can take tens or hundreds of milliseconds to complete).
It can also be useful to keep an interrupt handler in IRAM if it is called very frequently, to avoid flash cache misses.
Refer to the :ref:`SPI flash API documentation <iram-safe-interrupt-handlers>` for more details.
Application Example
-------------------
@ -86,15 +112,6 @@ Macros
.. doxygendefine:: ESP_INTR_FLAG_IRAM
.. doxygendefine:: ESP_INTR_FLAG_INTRDISABLED
Type Definitions
^^^^^^^^^^^^^^^^
Enumerations
^^^^^^^^^^^^
Structures
^^^^^^^^^^
Functions
^^^^^^^^^

View file

@ -1,10 +1,47 @@
OTA
===
Over The Air Updates (OTA)
==========================
OTA Process Overview
^^^^^^^^^^^^^^^^^^^^
The OTA update mechanism allows a device to update itself based on data received while the normal firmware is running
(for example, over WiFi or Bluetooth.)
OTA requires configuring the :doc:`Partition Table </partition-tables>` of the device with at least two "OTA app slot"
partitions (ie `ota_0` and `ota_1`) and an "OTA Data Partition".
The OTA operation functions write a new app firmware image to whichever OTA app slot is not currently being used for
booting. Once the image is verified, the OTA Data partition is updated to specify that this image should be used for the
next boot.
.. _ota_data_partition:
OTA Data Partition
^^^^^^^^^^^^^^^^^^
An OTA data partition (type ``data``, subtype ``ota``) must be included in the :doc:`Partition Table </partition-tables>`
of any project which uses the OTA functions.
For factory boot settings, the OTA data partition should contain no data (all bytes erased to 0xFF). In this case the
esp-idf software bootloader will boot the factory app if it is present in the the partition table. If no factory app is
included in the partition table, the first available OTA slot (usually ``ota_0``) is booted.
After the first OTA update, the OTA data partition is updated to specify which OTA app slot partition should be booted next.
The OTA data partition is two flash sectors (0x2000 bytes) in size, to prevent problems if there is a power failure
while it is being written. Sectors are independently erased and written with matching data, and if they disagree a
counter field is used to determine which sector was written more recently.
See also
--------
* :doc:`Partition Table documentation </partition-tables>`
* :doc:`Lower-Level SPI Flash/Partition API </api/storage/spi_flash>`
Application Example
-------------------
Demonstration of OTA (over the air) firmware update workflow: :example:`system/ota`.
End-to-end example of OTA firmware update workflow: :example:`system/ota`.
API Reference
-------------
@ -21,6 +58,7 @@ Macros
.. doxygendefine:: ESP_ERR_OTA_PARTITION_CONFLICT
.. doxygendefine:: ESP_ERR_OTA_SELECT_INFO_INVALID
.. doxygendefine:: ESP_ERR_OTA_VALIDATE_FAILED
.. doxygendefine:: OTA_SIZE_UNKNOWN
Type Definitions
^^^^^^^^^^^^^^^^

View file

@ -55,6 +55,7 @@ While PRO CPU does initialization in ``start_cpu0`` function, APP CPU spins in `
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
-------------------------

View file

@ -46,7 +46,6 @@ Here is the summary printed for the "Factory app, two OTA definitions" configura
* The type of all three are set as "app", but the subtype varies between the factory app at 0x10000 and the next two "OTA" apps.
* There is also a new "ota data" slot, which holds the data for OTA updates. The bootloader consults this data in order to know which app to execute. If "ota data" is empty, it will execute the factory app.
Creating Custom Tables
----------------------
@ -61,7 +60,7 @@ The CSV format is the same format as printed in the summaries shown above. Howev
factory, app, factory, 0x10000, 1M
ota_0, app, ota_0, , 1M
ota_1, app, ota_1, , 1M
* Whitespace between fields is ignored, and so is any line starting with # (comments).
* Each non-comment line in the CSV file is a partition definition.
* Only the offset for the first partition is supplied. The gen_esp32part.py tool fills in each remaining offset to start after the preceding partition.
@ -74,16 +73,50 @@ Name field can be any meaningful name. It is not significant to the ESP32. Names
Type field
~~~~~~~~~~
Type field can be specified as app (0) or data (1). Or it can be a number 0-254 (or as hex 0x00-0xFE). Types 0x00-0x3F are reserved for Espressif. If your application needs to store data, please add a custom partition type in the range 0x40-0xFE.
Partition type field can be specified as app (0) or data (1). Or it can be a number 0-254 (or as hex 0x00-0xFE). Types 0x00-0x3F are reserved for esp-idf core functions.
The bootloader ignores any types other than 0 & 1.
If your application needs to store data, please add a custom partition type in the range 0x40-0xFE.
The bootloader ignores any partition types other than app (0) & data (1).
Subtype
~~~~~~~
When type is "app", the subtype field can be specified as factory (0), ota_0 (0x10) ... ota_15 (0x1F) and test (0x20). Or it can be any number 0-255 (0x00-0xFF). The bootloader will execute the factory app unless there it sees a partition of type data/ota, in which case it reads this partition to determine which OTA image to boot
The 8-bit subtype field is specific to a given partition type.
When type is "data", the subtype field can be specified as ota (0), phy (1), nvs (2). Or it can be a number 0x00-0xFF. The bootloader ignores all data subtypes except for ota. Subtypes 0-0x7f are reserved for Espressif use. To create custom data partition subtypes use "data" type, and choose any unused subtype in 0x80-0xFF range. If you are porting a filesystem to the ESP-IDF, consider opening a PR to add the new subtype to esp_partition.h file.
esp-idf currently only specifies the meaning of the subtype field for "app" and "data" partition types.
App Subtypes
~~~~~~~~~~~~
When type is "app", the subtype field can be specified as factory (0), ota_0 (0x10) ... ota_15 (0x1F) or test (0x20).
- factory (0) is the default app partition. The bootloader will execute the factory app unless there it sees a partition of type data/ota, in which case it reads this partition to determine which OTA image to boot.
- OTA never updates the factory partition.
- If you want to conserve flash usage in an OTA project, you can remove the factory partition and use ota_0 instead.
- ota_0 (0x10) ... ota_15 (0x1F) are the OTA app slots. Refer to the :doc:`OTA documentation <api/system/ota>` for more details, which then use the OTA data partition to configure which app slot the bootloader should boot. If using OTA, an application should have at least two OTA application slots (ota_0 & ota_1). Refer to the :doc:`OTA documentation <api/system/ota>` for more details.
- test (0x2) is a reserved subtype for factory test procedures. It is not currently supported by the esp-idf bootloader.
Data Subtypes
~~~~~~~~~~~~~
When type is "data", the subtype field can be specified as ota (0), phy (1), nvs (2).
- ota (0) is the :ref:`OTA data partition <ota_data_partition>` which stores information about the currently selected OTA application. This partition should be 0x2000 bytes in size. Refer to the :ref:`OTA documentation <ota_data_partition>` for more details.
- phy (1) is for storing PHY initialisation data. This allows PHY to be configured per-device, instead of in firmware.
- In the default configuration, the phy partition is not used and PHY initialisation data is compiled into the app itself. As such, this partition can be removed from the partition table to save space.
- To load PHY data from this partition, run ``make menuconfig`` and enable "Component Config" -> "PHY" -> "Use a partition to store PHY init data". You will also need to flash your devices with phy init data as the esp-idf build system does not do this automatically.
- nvs (2) is for the :doc:`Non-Volatile Storage (NVS) API <api/storage/nvs_flash>`.
- NVS is used to store per-device PHY calibration data (different to initialisation data).
- NVS is used to store WiFi data if the :doc:`esp_wifi_set_storage(WIFI_STORAGE_FLASH) <api/wifi/esp_wifi>` initialisation function is used.
- The NVS API can also be used for other application data.
- It is strongly recommended that you include an NVS partition of at least 0x3000 bytes in your project.
- If using NVS API to store a lot of data, increase the NVS partition size from the default 0x6000 bytes.
Other data subtypes are reserved for future esp-idf uses.
Offset & Size
~~~~~~~~~~~~~
@ -92,28 +125,26 @@ Only the first offset field is required (we recommend using 0x10000). Partitions
App partitions have to be at offsets aligned to 0x10000 (64K). If you leave the offset field blank, the tool will automatically align the partition. If you specify an unaligned offset for an app partition, the tool will return an error.
Sizes and offsets can be specified as decimal numbers, hex numbers with the prefix 0x, or size multipliers M or K (1024 and 1024*1024 bytes).
NVS data partition has to be at least 0x3000 bytes long, and OTA data parition has to be 0x2000 bytes long. If you are using NVS in your application to store a lot of data, consider using a custom partition table with larger NVS partition.
Sizes and offsets can be specified as decimal numbers, hex numbers with the prefix 0x, or size multipliers K or M (1024 and 1024*1024 bytes).
Generating Binary Partition Table
---------------------------------
The partition table which is flashed to the ESP32 is in a binary format, not CSV. The tool bin/gen_esp32part.py is used to convert between CSV and binary formats.
The partition table which is flashed to the ESP32 is in a binary format, not CSV. The tool :component_file:`partition_table/gen_esp32part.py` is used to convert between CSV and binary formats.
If you configure the partition table CSV name in ``make menuconfig`` and then ``make partition_table``, this conversion is done for you.
If you configure the partition table CSV name in ``make menuconfig`` and then ``make partition_table``, this conversion is done as part of the build process.
To convert CSV to Binary manually::
python bin/gen_esp32part.py --verify input_partitions.csv binary_partitions.bin
python gen_esp32part.py --verify input_partitions.csv binary_partitions.bin
To convert binary format back to CSV::
python bin/gen_esp32part.py --verify binary_partitions.bin input_partitions.csv
python gen_esp32part.py --verify binary_partitions.bin input_partitions.csv
To display the contents of a binary partition table on stdout (this is how the summaries displayed when running `make partition_table` are generated::
python bin/gen_esp32part.py binary_partitions.bin
python gen_esp32part.py binary_partitions.bin
``gen_esp32part.py`` takes one optional argument, ``--verify``, which will also verify the partition table during conversion (checking for overlapping partitions, unaligned partitions, etc.)