Skip to content

Latest commit

 

History

History
290 lines (172 loc) · 17.2 KB

spi_flash.rst

File metadata and controls

290 lines (172 loc) · 17.2 KB

SPI Flash API

:link_to_translation:zh_CN:[中文]

Overview

The spi_flash component contains API functions related to reading, writing, erasing, memory mapping for data in the external flash. The spi_flash component also has higher-level API functions which work with partitions defined in the partition table </api-guides/partition-tables>.

Different from the API before IDF v4.0, the functionality of esp_flash_* APIs is not limited to the "main" SPI flash chip (the same SPI flash chip from which program runs). With different chip pointers, you can access external flash chips connected to not only SPI0/1 but also other SPI buses like SPI2.

Note

Instead of going through the cache connected to the SPI0 peripheral, most esp_flash_* APIs go through other SPI peripherals like SPI1, SPI2, etc. This makes them able to access not only the main flash, but also external flash.

However, due to limitations of the cache, operations through the cache are limited to the main flash. The address range limitation for these operations are also on the cache side. The cache is not able to access external flash chips or address range above its capabilities. These cache operations include: mmap, encrypted read/write, executing code or access to variables in the flash.

Note

Flash APIs after ESP-IDF v4.0 are no longer atomic. If a write operation occurs during another on-going read operation, and the flash addresses of both operations overlap, the data returned from the read operation may contain both old data and new data (that was updated written by the write operation).

Note

Encrypted flash operations are only supported with the main flash chip (and not with other flash chips, that is on SPI1 with different CS, or on other SPI buses). Reading through cache is only supported on the main flash, which is determined by the HW.

Support for Features of Flash Chips

Quad/Dual Mode Chips

Features of different flashes are implemented in different ways and thus need speical support. The fast/slow read and Dual mode (DOUT/DIO) of almost all 24-bits address flash chips are supported, because they don't need any vendor-specific commands.

Quad mode (QIO/QOUT) is supported on following chip types:

  1. ISSI
  2. GD
  3. MXIC
  4. FM
  5. Winbond
  6. XMC
  7. BOYA

Optional Features

spi_flash_optional_feature

There are some features that are not supported by all flash chips, or not supported by all Espressif chips. These features include:

esp32s3

  • OPI flash - means that flash supports octal mode.
  • 32-bit address flash - usually means that the flash has higher capacity (equal to or larger than 16 MB) that needs longer addresses.

esp32s3

  • High performance mode (HPM) - means that flash works under high frequency which is higher than 80MHz.
  • Flash unique ID - means that flash supports its unique 64-bits ID.

esp32c3

  • Suspend & Resume - means that flash can accept suspend/resume command during its writing/erasing. The {IDF_TARGET_NAME} may keep the cache on when the flash is being written/erased and suspend it to read its contents randomly.

If you want to use these features, please ensure both {IDF_TARGET_NAME} and ALL flash chips in your product support these features. For more details, refer to spi_flash_optional_feature.

You may also customise your own flash chip driver. See spi_flash_override_driver for more details.

Custom Flash Driver <spi_flash_override_driver>

Initializing a Flash Device

To use the esp_flash_* APIs, you need to initialise a flash chip on a certain SPI bus, as shown below:

  1. Call :cppspi_bus_initialize to properly initialize an SPI bus. This function initializes the resources (I/O, DMA, interrupts) shared among devices attached to this bus.
  2. Call :cppspi_bus_add_flash_device to attach the flash device to the bus. This function allocates memory and fills the members for the esp_flash_t structure. The CS I/O is also initialized here.
  3. Call :cppesp_flash_init to actually communicate with the chip. This will also detect the chip type, and influence the following operations.

Note

Multiple flash chips can be attached to the same bus now.

SPI Flash Access API

This is the set of API functions for working with data in flash:

  • :cppesp_flash_read reads data from flash to RAM
  • :cppesp_flash_write writes data from RAM to flash
  • :cppesp_flash_erase_region erases specific region of flash
  • :cppesp_flash_erase_chip erases the whole flash
  • :cppesp_flash_get_chip_size returns flash chip size, in bytes, as configured in menuconfig

Generally, try to avoid using the raw SPI flash functions to the "main" SPI flash chip in favour of partition-specific functions <flash-partition-apis>.

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 setting CONFIG_ESPTOOLPY_FLASHSIZE in the project configuration.

If it is necessary to override the configured flash size at runtime, it is possible to set the chip_size member of the g_rom_flashchip structure. This size is used by esp_flash_* functions (in both software & ROM) to check the bounds.

Concurrency Constraints for Flash on SPI1

spi_flash_concurrency

Attention

The SPI0/1 bus is shared between the instruction & data cache (for firmware execution) and the SPI1 peripheral (controlled by the drivers including this SPI flash driver). Hence, calling SPI Flash API on SPI1 bus (including the main flash) will cause significant influence to the whole system. See spi_flash_concurrency for more details.

Partition Table API

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 can be found in Partition Tables </api-guides/partition-tables>.

This component provides API functions to enumerate partitions found in the partition table and perform operations on them. These functions are declared in esp_partition.h:

  • :cppesp_partition_find checks a partition table for entries with specific type, returns an opaque iterator.
  • :cppesp_partition_get returns a structure describing the partition for a given iterator.
  • :cppesp_partition_next shifts the iterator to the next found partition.
  • :cppesp_partition_iterator_release releases iterator returned by esp_partition_find.
  • :cppesp_partition_find_first is a convenience function which returns the structure describing the first partition found by esp_partition_find.
  • :cppesp_partition_read, :cppesp_partition_write, :cppesp_partition_erase_range are equivalent to :cppesp_flash_read, :cppesp_flash_write, :cppesp_flash_erase_region, but operate within partition boundaries.

Note

Application code should mostly use these esp_partition_* API functions instead of lower level esp_flash_* API functions. Partition table API functions do bounds checking and calculate correct offsets in flash, based on data stored in a partition table.

SPI Flash Encryption

It is possible to encrypt the contents of SPI flash and have it transparently decrypted by hardware.

Refer to the Flash Encryption documentation </security/flash-encryption> for more details.

Memory Mapping API

{IDF_TARGET_CACHE_SIZE:default="64 KB",esp32c2=16~64 KB}

{IDF_TARGET_NAME} 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 a mapped memory region.

Mapping happens in {IDF_TARGET_CACHE_SIZE} pages. Memory mapping hardware can map flash into the data address space and the instruction address space. See the technical reference manual for more details and limitations about memory mapping hardware.

Note that some pages are used to map the application itself into memory, so the actual number of available pages may be less than the capability of the hardware.

Reading data from flash using a memory mapped region is the only way to decrypt contents of flash when flash encryption </security/flash-encryption> is enabled. Decryption is performed at the hardware level.

Memory mapping API are declared in spi_flash_mmap.h and esp_partition.h:

  • :cppspi_flash_mmap maps a region of physical flash addresses into instruction space or data space of the CPU.
  • :cppspi_flash_munmap unmaps previously mapped region.
  • :cppesp_partition_mmap maps part of a partition into the instruction space or data space of the CPU.

Differences between :cppspi_flash_mmap and :cppesp_partition_mmap are as follows:

  • :cppspi_flash_mmap must be given a {IDF_TARGET_CACHE_SIZE} aligned physical address.
  • :cppesp_partition_mmap may be given any arbitrary offset within the partition. It will adjust the returned pointer to mapped memory as necessary.

Note that since memory mapping happens in pages, it may be possible to read data outside of the partition provided to esp_partition_mmap, regardless of the partition boundary.

Note

mmap is supported by cache, so it can only be used on main flash.

SPI Flash Implementation

The esp_flash_t structure holds chip data as well as three important parts of this API:

  1. The host driver, which provides the hardware support to access the chip;
  2. The chip driver, which provides compatibility service to different chips;
  3. The OS functions, provide support of some OS functions (e.g. lock, delay) in different stages (1st/2nd boot, or the app).

Host driver

The host driver relies on an interface (spi_flash_host_driver_t) defined in the spi_flash_types.h (in the hal/include/hal folder). This interface provides some common functions to communicate with the chip.

In other files of the SPI HAL, some of these functions are implemented with existing {IDF_TARGET_NAME} memory-spi functionalities. However, due to the speed limitations of {IDF_TARGET_NAME}, the HAL layer cannot provide high-speed implementations to some reading commands (so the support for it was dropped). The files (memspi_host_driver.h and .c) implement the high-speed version of these commands with the common_command function provided in the HAL, and wrap these functions as spi_flash_host_driver_t for upper layer to use.

You can also implement your own host driver, even with the GPIO. As long as all the functions in the spi_flash_host_driver_t are implemented, the esp_flash API can access the flash regardless of the low-level hardware.

Chip Driver

The chip driver, defined in spi_flash_chip_driver.h, wraps basic functions provided by the host driver for the API layer to use.

Some operations need some commands to be sent first, or read some status afterwards. Some chips need different commands or values, or need special communication ways.

There is a type of chip called generic chip which stands for common chips. Other special chip drivers can be developed on the base of the generic chip.

The chip driver relies on the host driver.

OS Functions

Currently the OS function layer provides entries of a lock and delay.

The lock (see spi_bus_lock) is used to resolve the conflicts among the access of devices on the same SPI bus, and the SPI Flash chip access. E.g.

  1. On SPI1 bus, the cache (used to fetch the data (code) in the Flash and PSRAM) should be disabled when the flash chip on the SPI0/1 is being accessed.
  2. On the other buses, the flash driver needs to disable the ISR registered by SPI Master driver, to avoid conflicts.
  3. Some devices of SPI Master driver may require to use the bus monopolized during a period (especially when the device doesn't have a CS wire, or the wire is controlled by software like SDSPI driver).

The delay is used by some long operations which requires the master to wait or polling periodically.

The top API wraps these the chip driver and OS functions into an entire component, and also provides some argument checking.

OS functions can also help to avoid a watchdog timeout when erasing large flash areas. During this time, the CPU is occupied with the flash erasing task. This stops other tasks from being executed. Among these tasks is the idle task to feed the watchdog timer (WDT). If the configuration option CONFIG_ESP_TASK_WDT_PANIC is selected and the flash operation time is longer than the watchdog timeout period, the system will reboot.

It's pretty hard to totally eliminate this risk, because the erasing time varies with different flash chips, making it hard to be compatible in flash drivers. Therefore, users need to pay attention to it. Please use the following guidelines:

  1. It is recommended to enable the CONFIG_SPI_FLASH_YIELD_DURING_ERASE option to allow the scheduler to re-schedule during erasing flash memory. Besides, following parameters can also be used.
  • Increase CONFIG_SPI_FLASH_ERASE_YIELD_TICKS or decrease CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS in menuconfig.
  • You can also increase CONFIG_ESP_TASK_WDT_TIMEOUT_S in menuconfig for a larger watchdog timeout period. However, with larger watchdog timeout period, previously detected timeouts may no longer be detected.
  1. Please be aware of the consequences of enabling the CONFIG_ESP_TASK_WDT_PANIC option when doing long-running SPI flash operations which will trigger the panic handler when it times out. However, this option can also help dealing with unexpected exceptions in your application. Please decide whether this is needed to be enabled according to actual condition.
  2. During your development, please carefully review the actual flash operation according to the specific requirements and time limits on erasing flash memory of your projects. Always allow reasonable redundancy based on your specific product requirements when configuring the flash erasing timeout threshold, thus improving the reliability of your product.

See Also

  • Partition Table documentation <../../api-guides/partition-tables>
  • Over The Air Update (OTA) API <../system/ota> provides high-level API for updating app firmware stored in flash.
  • Non-Volatile Storage (NVS) API <nvs_flash> provides a structured API for storing small pieces of data in SPI flash.

Implementation Details

In order to perform some flash operations, it is necessary to make sure that both CPUs are not running any code from flash for the duration of the flash operation: - In a single-core setup, the SDK needs to disable interrupts or scheduler before performing the flash operation. - In a dual-core setup, the SDK needs to make sure that both CPUs are not running any code from flash.

When SPI flash API is called on CPU A (can be PRO or APP), start the spi_flash_op_block_func function on CPU B using the esp_ipc_call API. This API wakes up a high priority task on CPU B and tells it to execute a given function, in this case, spi_flash_op_block_func. This function disables cache on CPU B and signals that the cache is disabled by setting the s_flash_op_can_start flag. Then the task on CPU A disables cache as well and proceeds to execute flash operation.

While a flash operation is running, interrupts can still run on CPUs A and B. It is assumed that all interrupt code is placed into RAM. Once the interrupt allocation API is added, a flag should be added to request the interrupt to be disabled for the duration of a flash operations.

Once the flash operation is complete, the 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), you need to disable both caches, so that no inter-CPU communication can take place.

API Reference - SPI Flash

inc/esp_flash_spi_init.inc

inc/esp_flash.inc

inc/spi_flash_mmap.inc

inc/spi_flash_types.inc

inc/esp_flash_err.inc

API Reference - Partition Table

inc/esp_partition.inc

API Reference - Flash Encrypt

inc/esp_flash_encrypt.inc