SPI Flash APIs¶
Overview¶
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 partitions defined in the partition table.
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.
SPI flash access APIs¶
This is the set of APIs for working with data in flash:
spi_flash_read
used to read data from flash to RAMspi_flash_write
used to write data from RAM to flashspi_flash_erase_sector
used to erase individual sectors of flashspi_flash_erase_range
used to erase range of addresses in flashspi_flash_get_chip_size
returns flash chip size, in bytes, as configured in menuconfig
Generally, try to avoid using the raw SPI flash functions in favour of partition-specific functions.
SPI Flash Size¶
The SPI flash size is configured by writing a field in the software bootloader image header, flashed at offset 0x1000.
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).
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.
Concurrency Constraints¶
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.
Refer to the application 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¶
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 interrupt handler is registered.
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 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 here.
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 iteratoresp_partition_get
returns a structure describing the partition, for the given iteratoresp_partition_next
advances iterator to the next partition foundesp_partition_iterator_release
releases iterator returned byesp_partition_find
esp_partition_find_first
is a convenience function which returns structure describing the first partition found by esp_partition_findesp_partition_read
,esp_partition_write
,esp_partition_erase_range
are equivalent tospi_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 Flash Encryption documentation for more details.
Memory mapping APIs¶
ESP32 features memory hardware which allows regions of flash memory to be mapped into instruction and data address spaces. This mapping works only for read operations, it is not possible to modify contents of flash memory by writing to mapped memory region. Mapping happens in 64KB pages. Memory mapping hardware can map up to 4 megabytes of flash into data address space, and up to 16 megabytes of flash into instruction address space. See the technical reference manual for more details 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.
Memory mapping APIs are declared in esp_spi_flash.h
and esp_partition.h
:
spi_flash_mmap
maps a region of physical flash addresses into instruction space or data space of the CPUspi_flash_munmap
unmaps previously mapped regionesp_partition_mmap
maps part of a partition into the instruction space or data space of the CPU
Differences between spi_flash_mmap
and esp_partition_mmap
are as follows:
spi_flash_mmap
must be given a 64KB aligned physical addressesp_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
.
其它¶
- 分区表文档
- 空中升级(OTA)API 提供了更新存储在 flash 中的应用程序的顶层 API。
- 非易变存储器(NVS) API 提供了在 SPI flash 中存储小数据项的结构化 API。
API 参考手册¶
头文件¶
宏¶
-
ESP_ERR_FLASH_BASE
¶
-
ESP_ERR_FLASH_OP_FAIL
¶
-
ESP_ERR_FLASH_OP_TIMEOUT
¶
-
SPI_FLASH_SEC_SIZE
¶ SPI Flash sector size
-
SPI_FLASH_MMU_PAGE_SIZE
¶ Flash cache MMU mapping page size
-
ESP_PARTITION_SUBTYPE_OTA
(i)¶ Convenience macro to get esp_partition_subtype_t value for the i-th OTA partition.
-
SPI_FLASH_CACHE2PHYS_FAIL
¶
类型定义¶
-
typedef uint32_t
spi_flash_mmap_handle_t
¶ Opaque handle for memory region obtained from spi_flash_mmap.
-
typedef struct esp_partition_iterator_opaque_ *
esp_partition_iterator_t
¶ Opaque partition iterator type.
枚举¶
-
enum
spi_flash_mmap_memory_t
¶ Enumeration which specifies memory space requested in an mmap call.
Values:
-
SPI_FLASH_MMAP_DATA
¶ map to data memory (Vaddr0), allows byte-aligned access, 4 MB total
-
SPI_FLASH_MMAP_INST
¶ map to instruction memory (Vaddr1-3), allows only 4-byte-aligned access, 11 MB total
-
-
enum
esp_partition_type_t
¶ Partition type.
- Note
- Keep this enum in sync with PartitionDefinition class gen_esp32part.py
Values:
-
ESP_PARTITION_TYPE_APP
= 0x00¶ Application partition type.
-
ESP_PARTITION_TYPE_DATA
= 0x01¶ Data partition type.
-
enum
esp_partition_subtype_t
¶ Partition subtype.
- Note
- Keep this enum in sync with PartitionDefinition class gen_esp32part.py
Values:
-
ESP_PARTITION_SUBTYPE_APP_FACTORY
= 0x00¶ Factory application partition.
-
ESP_PARTITION_SUBTYPE_APP_OTA_MIN
= 0x10¶ Base for OTA partition subtypes.
-
ESP_PARTITION_SUBTYPE_APP_OTA_0
= ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 0¶ OTA partition 0.
-
ESP_PARTITION_SUBTYPE_APP_OTA_1
= ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 1¶ OTA partition 1.
-
ESP_PARTITION_SUBTYPE_APP_OTA_2
= ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 2¶ OTA partition 2.
-
ESP_PARTITION_SUBTYPE_APP_OTA_3
= ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 3¶ OTA partition 3.
-
ESP_PARTITION_SUBTYPE_APP_OTA_4
= ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 4¶ OTA partition 4.
-
ESP_PARTITION_SUBTYPE_APP_OTA_5
= ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 5¶ OTA partition 5.
-
ESP_PARTITION_SUBTYPE_APP_OTA_6
= ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 6¶ OTA partition 6.
-
ESP_PARTITION_SUBTYPE_APP_OTA_7
= ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 7¶ OTA partition 7.
-
ESP_PARTITION_SUBTYPE_APP_OTA_8
= ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 8¶ OTA partition 8.
-
ESP_PARTITION_SUBTYPE_APP_OTA_9
= ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 9¶ OTA partition 9.
-
ESP_PARTITION_SUBTYPE_APP_OTA_10
= ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 10¶ OTA partition 10.
-
ESP_PARTITION_SUBTYPE_APP_OTA_11
= ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 11¶ OTA partition 11.
-
ESP_PARTITION_SUBTYPE_APP_OTA_12
= ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 12¶ OTA partition 12.
-
ESP_PARTITION_SUBTYPE_APP_OTA_13
= ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 13¶ OTA partition 13.
-
ESP_PARTITION_SUBTYPE_APP_OTA_14
= ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 14¶ OTA partition 14.
-
ESP_PARTITION_SUBTYPE_APP_OTA_15
= ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 15¶ OTA partition 15.
-
ESP_PARTITION_SUBTYPE_APP_OTA_MAX
= ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 16¶ Max subtype of OTA partition.
-
ESP_PARTITION_SUBTYPE_APP_TEST
= 0x20¶ Test application partition.
-
ESP_PARTITION_SUBTYPE_DATA_OTA
= 0x00¶ OTA selection partition.
-
ESP_PARTITION_SUBTYPE_DATA_PHY
= 0x01¶ PHY init data partition.
-
ESP_PARTITION_SUBTYPE_DATA_NVS
= 0x02¶ NVS partition.
-
ESP_PARTITION_SUBTYPE_DATA_COREDUMP
= 0x03¶ COREDUMP partition.
-
ESP_PARTITION_SUBTYPE_DATA_ESPHTTPD
= 0x80¶ ESPHTTPD partition.
-
ESP_PARTITION_SUBTYPE_DATA_FAT
= 0x81¶ FAT partition.
-
ESP_PARTITION_SUBTYPE_DATA_SPIFFS
= 0x82¶ SPIFFS partition.
-
ESP_PARTITION_SUBTYPE_ANY
= 0xff¶ Used to search for partitions with any subtype.
结构体¶
-
struct
esp_partition_t
¶ partition information structure
This is not the format in flash, that format is esp_partition_info_t.
However, this is the format used by this API.
函数¶
-
void
spi_flash_init
()¶ Initialize SPI flash access driver.
This function must be called exactly once, before any other spi_flash_* functions are called. Currently this function is called from startup code. There is no need to call it from application code.
-
size_t
spi_flash_get_chip_size
()¶ Get flash chip size, as set in binary image header.
- Note
- This value does not necessarily match real flash size.
- Return
- size of flash chip, in bytes
-
esp_err_t
spi_flash_erase_sector
(size_t sector)¶ Erase the Flash sector.
- Return
- esp_err_t
- Parameters
sector
: Sector number, the count starts at sector 0, 4KB per sector.
-
esp_err_t
spi_flash_erase_range
(size_t start_address, size_t size)¶ Erase a range of flash sectors.
- Return
- esp_err_t
- Parameters
start_address
: Address where erase operation has to start. Must be 4kB-alignedsize
: Size of erased range, in bytes. Must be divisible by 4kB.
-
esp_err_t
spi_flash_write
(size_t dest_addr, const void *src, size_t size)¶ Write data to Flash.
- Note
- If source address is in DROM, this function will return ESP_ERR_INVALID_ARG.
- Return
- esp_err_t
- Parameters
dest_addr
: destination address in Flash. Must be a multiple of 4 bytes.src
: pointer to the source buffer.size
: length of data, in bytes. Must be a multiple of 4 bytes.
-
esp_err_t
spi_flash_write_encrypted
(size_t dest_addr, const void *src, size_t size)¶ Write data encrypted to Flash.
- Note
- Flash encryption must be enabled for this function to work.
- Note
- Flash encryption must be enabled when calling this function. If flash encryption is disabled, the function returns ESP_ERR_INVALID_STATE. Use esp_flash_encryption_enabled() function to determine if flash encryption is enabled.
- Note
- Both dest_addr and size must be multiples of 16 bytes. For absolute best performance, both dest_addr and size arguments should be multiples of 32 bytes.
- Return
- esp_err_t
- Parameters
dest_addr
: destination address in Flash. Must be a multiple of 16 bytes.src
: pointer to the source buffer.size
: length of data, in bytes. Must be a multiple of 16 bytes.
-
esp_err_t
spi_flash_read
(size_t src_addr, void *dest, size_t size)¶ Read data from Flash.
- Return
- esp_err_t
- Parameters
src_addr
: source address of the data in Flash.dest
: pointer to the destination buffersize
: length of data
-
esp_err_t
spi_flash_read_encrypted
(size_t src, void *dest, size_t size)¶ Read data from Encrypted Flash.
If flash encryption is enabled, this function will transparently decrypt data as it is read. If flash encryption is not enabled, this function behaves the same as spi_flash_read().
See esp_flash_encryption_enabled() for a function to check if flash encryption is enabled.
- Return
- esp_err_t
- Parameters
src
: source address of the data in Flash.dest
: pointer to the destination buffersize
: length of data
-
esp_err_t
spi_flash_mmap
(size_t src_addr, size_t size, spi_flash_mmap_memory_t memory, const void **out_ptr, spi_flash_mmap_handle_t *out_handle)¶ Map region of flash memory into data or instruction address space.
This function allocates sufficient number of 64k MMU pages and configures them to map request region of flash memory into data address space or into instruction address space. It may reuse MMU pages which already provide required mapping. As with any allocator, there is possibility of fragmentation of address space if mmap/munmap are heavily used. To troubleshoot issues with page allocation, use spi_flash_mmap_dump function.
- Return
- ESP_OK on success, ESP_ERR_NO_MEM if pages can not be allocated
- Parameters
src_addr
: Physical address in flash where requested region starts. This address must be aligned to 64kB boundary (SPI_FLASH_MMU_PAGE_SIZE).size
: Size of region which has to be mapped. This size will be rounded up to a 64k boundary.memory
: Memory space where the region should be mappedout_ptr
: Output, pointer to the mapped memory regionout_handle
: Output, handle which should be used for spi_flash_munmap call
-
void
spi_flash_munmap
(spi_flash_mmap_handle_t handle)¶ Release region previously obtained using spi_flash_mmap.
- Note
- Calling this function will not necessarily unmap memory region. Region will only be unmapped when there are no other handles which reference this region. In case of partially overlapping regions it is possible that memory will be unmapped partially.
- Parameters
handle
: Handle obtained from spi_flash_mmap
-
void
spi_flash_mmap_dump
()¶ Display information about mapped regions.
This function lists handles obtained using spi_flash_mmap, along with range of pages allocated to each handle. It also lists all non-zero entries of MMU table and corresponding reference counts.
-
size_t
spi_flash_cache2phys
(const void *cached)¶ Given a memory address where flash is mapped, return the corresponding physical flash offset.
Cache address does not have have been assigned via spi_flash_mmap(), any address in flash map space can be looked up.
- Return
- SPI_FLASH_CACHE2PHYS_FAIL If cache address is outside flash cache region, or the address is not mapped.
- Otherwise, returns physical offset in flash
- Parameters
cached
: Pointer to flashed cached memory.
-
const void *
spi_flash_phys2cache
(size_t phys_offs, spi_flash_mmap_memory_t memory)¶ Given a physical offset in flash, return the address where it is mapped in the memory space.
Physical address does not have to have been assigned via spi_flash_mmap(), any address in flash can be looked up.
- Note
- Only the first matching cache address is returned. If MMU flash cache table is configured so multiple entries point to the same physical address, there may be more than one cache address corresponding to that physical address. It is also possible for a single physical address to be mapped to both the IROM and DROM regions.
- Note
- This function doesn’t impose any alignment constraints, but if memory argument is SPI_FLASH_MMAP_INST and phys_offs is not 4-byte aligned, then reading from the returned pointer will result in a crash.
- Return
- NULL if the physical address is invalid or not mapped to flash cache of the specified memory type.
- Cached memory address (in IROM or DROM space) corresponding to phys_offs.
- Parameters
phys_offs
: Physical offset in flash memory to look up.memory
: Memory type to look up a flash cache address mapping for (IROM or DROM)
-
bool
spi_flash_cache_enabled
()¶ Check at runtime if flash cache is enabled on both CPUs.
- Return
- true if both CPUs have flash cache enabled, false otherwise.
-
esp_partition_iterator_t
esp_partition_find
(esp_partition_type_t type, esp_partition_subtype_t subtype, const char *label)¶ Find partition based on one or more parameters.
- Return
- iterator which can be used to enumerate all the partitions found, or NULL if no partitions were found. Iterator obtained through this function has to be released using esp_partition_iterator_release when not used any more.
- Parameters
type
: Partition type, one of esp_partition_type_t valuessubtype
: Partition subtype, one of esp_partition_subtype_t values. To find all partitions of given type, use ESP_PARTITION_SUBTYPE_ANY.label
: (optional) Partition label. Set this value if looking for partition with a specific name. Pass NULL otherwise.
-
const esp_partition_t *
esp_partition_find_first
(esp_partition_type_t type, esp_partition_subtype_t subtype, const char *label)¶ Find first partition based on one or more parameters.
- Return
- pointer to esp_partition_t structure, or NULL if no partition is found. This pointer is valid for the lifetime of the application.
- Parameters
type
: Partition type, one of esp_partition_type_t valuessubtype
: Partition subtype, one of esp_partition_subtype_t values. To find all partitions of given type, use ESP_PARTITION_SUBTYPE_ANY.label
: (optional) Partition label. Set this value if looking for partition with a specific name. Pass NULL otherwise.
-
const esp_partition_t *
esp_partition_get
(esp_partition_iterator_t iterator)¶ Get esp_partition_t structure for given partition.
- Return
- pointer to esp_partition_t structure. This pointer is valid for the lifetime of the application.
- Parameters
iterator
: Iterator obtained using esp_partition_find. Must be non-NULL.
-
esp_partition_iterator_t
esp_partition_next
(esp_partition_iterator_t iterator)¶ Move partition iterator to the next partition found.
Any copies of the iterator will be invalid after this call.
- Return
- NULL if no partition was found, valid esp_partition_iterator_t otherwise.
- Parameters
iterator
: Iterator obtained using esp_partition_find. Must be non-NULL.
-
void
esp_partition_iterator_release
(esp_partition_iterator_t iterator)¶ Release partition iterator.
- Parameters
iterator
: Iterator obtained using esp_partition_find. Must be non-NULL.
-
esp_err_t
esp_partition_read
(const esp_partition_t *partition, size_t src_offset, void *dst, size_t size)¶ Read data from the partition.
- Return
- ESP_OK, if data was read successfully; ESP_ERR_INVALID_ARG, if src_offset exceeds partition size; ESP_ERR_INVALID_SIZE, if read would go out of bounds of the partition; or one of error codes from lower-level flash driver.
- Parameters
partition
: Pointer to partition structure obtained using esp_partition_find_first or esp_partition_get. Must be non-NULL.dst
: Pointer to the buffer where data should be stored. Pointer must be non-NULL and buffer must be at least ‘size’ bytes long.src_offset
: Address of the data to be read, relative to the beginning of the partition.size
: Size of data to be read, in bytes.
-
esp_err_t
esp_partition_write
(const esp_partition_t *partition, size_t dst_offset, const void *src, size_t size)¶ Write data to the partition.
Before writing data to flash, corresponding region of flash needs to be erased. This can be done using esp_partition_erase_range function.
Partitions marked with an encryption flag will automatically be written via the spi_flash_write_encrypted() function. If writing to an encrypted partition, all write offsets and lengths must be multiples of 16 bytes. See the spi_flash_write_encrypted() function for more details. Unencrypted partitions do not have this restriction.
- Note
- Prior to writing to flash memory, make sure it has been erased with esp_partition_erase_range call.
- Return
- ESP_OK, if data was written successfully; ESP_ERR_INVALID_ARG, if dst_offset exceeds partition size; ESP_ERR_INVALID_SIZE, if write would go out of bounds of the partition; or one of error codes from lower-level flash driver.
- Parameters
partition
: Pointer to partition structure obtained using esp_partition_find_first or esp_partition_get. Must be non-NULL.dst_offset
: Address where the data should be written, relative to the beginning of the partition.src
: Pointer to the source buffer. Pointer must be non-NULL and buffer must be at least ‘size’ bytes long.size
: Size of data to be written, in bytes.
-
esp_err_t
esp_partition_erase_range
(const esp_partition_t *partition, uint32_t start_addr, uint32_t size)¶ Erase part of the partition.
- Return
- ESP_OK, if the range was erased successfully; ESP_ERR_INVALID_ARG, if iterator or dst are NULL; ESP_ERR_INVALID_SIZE, if erase would go out of bounds of the partition; or one of error codes from lower-level flash driver.
- Parameters
partition
: Pointer to partition structure obtained using esp_partition_find_first or esp_partition_get. Must be non-NULL.start_addr
: Address where erase operation should start. Must be aligned to 4 kilobytes.size
: Size of the range which should be erased, in bytes. Must be divisible by 4 kilobytes.
-
esp_err_t
esp_partition_mmap
(const esp_partition_t *partition, uint32_t offset, uint32_t size, spi_flash_mmap_memory_t memory, const void **out_ptr, spi_flash_mmap_handle_t *out_handle)¶ Configure MMU to map partition into data memory.
Unlike spi_flash_mmap function, which requires a 64kB aligned base address, this function doesn’t impose such a requirement. If offset results in a flash address which is not aligned to 64kB boundary, address will be rounded to the lower 64kB boundary, so that mapped region includes requested range. Pointer returned via out_ptr argument will be adjusted to point to the requested offset (not necessarily to the beginning of mmap-ed region).
To release mapped memory, pass handle returned via out_handle argument to spi_flash_munmap function.
- Return
- ESP_OK, if successful
- Parameters
partition
: Pointer to partition structure obtained using esp_partition_find_first or esp_partition_get. Must be non-NULL.offset
: Offset from the beginning of partition where mapping should start.size
: Size of the area to be mapped.memory
: Memory space where the region should be mappedout_ptr
: Output, pointer to the mapped memory regionout_handle
: Output, handle which should be used for spi_flash_munmap call
-
static bool
esp_flash_encryption_enabled
(void)¶ Is flash encryption currently enabled in hardware?
Flash encryption is enabled if the FLASH_CRYPT_CNT efuse has an odd number of bits set.
- Return
- true if flash encryption is enabled.
实现细节¶
为了执行某些 flash 操作,我们需要确保两个 CPU 在 flash 操作期间都没有从 flash 运行任何代码。在单核中,这非常简单:禁止中断/调度器,然后执行 flash 操作。在双核中,所谓有点复杂。我们需要确保其它 CPU 没有从 flash 上面运行任何代码。
当 SPI flahs API 在 CPU A(可以是 PRO 或者 APP)上被调用,我们使用 API esp_ipc_call 在 CPU B 上启动函数 spi_flash_op_block_func。这个 API 会唤醒 CPU B 上的高优先级任务,告诉它取执行所给函数,即 spi_flash_op_block_func。该函数子啊 CPU B 上 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.