ESP32: SPI Flash API |
![]() |
Добавил(а) microsin | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Компонент spi_flash содержит API-функции для чтения, записи, стирания и отображения на адресное пространство (memory mapping) данных, находящихся во внешней памяти SPI flash. Компонент spi_flash также содержит высокоуровневые API-функции, которые работают с разделами, определенными в таблице разделов [2]. Существует отличие API для версий ESP-IDF до v4.0, функционал API-функций esp_flash_* не ограничивается "основной" микросхемой SPI flash (микросхема SPI flash, с которой запускается программа). С указателями на разные микросхемы можно получить доступ к их содержимому, не только к SPI0/SPI1, но также и к другим шинам SPI наподобие SPI2. Замечания: • Вместо доступа через кэш к подключенному перириферийному устройству SPI0, большинство esp_flash_* API передают данные через другие периферийные устройства SPI наподобие SPI1, SPI2, и т. д. Это позволяет получить доступ не только к основной flash, но также и к внешней SPI flash. Kconfig-опция CONFIG_SPI_FLASH_USE_LEGACY_IMPL может использоваться для переключения функций spi_flash_* обратно в реализацию до ESP-IDF v4.0. Однако в этом случае размер кода может быть больше, если одновременно используется новый и старый API. Чтение и запись с шифрованием используют старую реализацию, даже если не разрешена опция CONFIG_SPI_FLASH_USE_LEGACY_IMPL. Так что шифрованные операции с flash поддерживаются только для основной микросхемы flash (но не с другими микросхемами flash, т. е. на SPI1 с другими сигналами выборки CS, или на других шинах SPI). Чтение через кэш поддерживается только на основной памяти flash, которая определяется аппаратно. [Поддержка особенностей микросхем flash] Микросхемы с режимами Quad/Dual. Функции разных микросхем flash реализуются разными способами, что требует специальной поддержки. Быстрое/медленное чтение (fast/slow read) и двойной режим (Dual mode, DOUT/DIO) почти всех микросхем с 24-битным адресом, потому что они не требуют любых специфичных, зависящих от производителя команд. Счетверенный режим (Quad mode, QIO/QOUT) поддерживается для микросхем следующих типов: ISSI Опциональные функции. Существуют некоторые функции, которые поддерживаются не всеми микросхемами flash, или не поддерживаются всеми микросхемами Espressif. Эти функции включают: • flash с 32-битным адресом - обычно это значит, что у микросхемы повышенная емкость (равная или больше чем 16 MB), которая требует больших адресов. Если Вы хотите использовать такие функции, то убедитесь, что и ESP32, и все микросхемы flash в Вашем изделии поддерживают эти функции. Более подробную информацию см. документации [3]. Также Вы можете переписать драйвер для своей микросхемы flash, см. [4]. [Инициализация устройства flash] Чтобы использовать esp_flash_* APIs, Вам нужно инициализировать микросхему flash на определенной шине certain SPI, как показано ниже: 1. Вызовите spi_bus_initialize() для правильной инициализации шины шины SPI. Эта функция инициализирует ресурсы (I/O, DMA, прерывания), совместно используемые этой шиной. Примечание: в настоящее время к одной шине может быть подключено несколько микросхем. [SPI Flash Access API] Для работы с данными на микросхеме flash существует набор API-функций: esp_flash_read() считывает данные и помещает из в RAM. Старайтесь как правило избегать использования функций для "сырого" физического доступа через SPI к основной микросхеме SPI flash, преимущественно применяйте функции, работающие с разделами (см. далее "API таблицы разделов"). [Размер SPI Flash] Размер SPI flash конфигурируется путем записи поля в заголовке образа программного загрузчика, прошиваемого со смещением 0x1000. По умолчанию размер SPI flash детектируется утилитой esptool.py, когда загрузчик второй стадии [5] записывается во flash, и заголовок обновляется корректным размером flash. Альтернативно можно сгенерировать размер фиксированным путем установки CONFIG_ESPTOOLPY_FLASHSIZE в конфигурации проекта. Если необходимо отменить сконфигурированный размер flash во время работы кода (runtime), то можно установить поле chip_size структуры g_rom_flashchip. Этот размер используют функции esp_flash_* (как в коде приложения и загрузчика, так и в коде ROM) для проверки границ адресов. [Ограничения по конкурентному доступу для flash на шине SPI1] Важное замечание: шина SPI0/1 совместно используется кэшем инструкций и данных (для выполнения кода firmware) и периферийным устройством SPI1 (управляется драйверами, включая этот драйвер SPI flash). Следовательно, вызов SPI Flash API на шине SPI1 (включая main flash) значительно повлияет на всю систему, подробности см. в следующей врезке. Операции на шине SPI1 включают вызов SPI Flash API или других драйверов шины SPI1, любые операции наподобие read/write/erase, либо другие определяемые пользователем операции SPI, независимо от основной памяти flash или от других подчиненных устройств SPI. На ESP32 кеширование шин SPI0/1 должно быть запрещено во время операций read/write/erase. Когда кэши запрещены. В таких условиях все ядра CPU всегда выполняют код из RAM и осуществляют доступ только к данным во внутреннем ОЗУ (IRAM). API-вызовы для запрета кэшей будут сделаны автоматически и прозрачно. Способ, которым эти API-вызовы запрещают кэш, будет приостанавливать все другие задачи. Кроме того, все прерывания, не связанные с IRAM, будут запрещены. Другое ядро будет ждать завершения операций с flash в цикле опроса занятости (busy loop). Также см. далее "Функции операционной системы" и врезку "Блокировка шины SPI". Подобных ограничений нет на других шинах SPI, отличающихся от SPI0/SPI1. Для получения информации по отличиям внутреннего ОЗУ (например IRAM, DRAM) и кэша flash см. документацию по типам памяти [7]. IRAM-Safe ISR. Для обработчиков прерываний (ISR), которые должны выполняться, когда кэш запрещен (например для операций с низкой латентностью), установите флаг ESP_INTR_FLAG_IRAM при регистрации таких ISR. Вы должны гарантировать, что все данные и функции, к которым осуществляют доступ эти ISR, включая те, которые вызывают обработчики, размещены в IRAM или в DRAM. См. "How to place code in IRAM" в статье [7]. Если функция или символ в настоящее время не размещен корректно в IRAM/DRAM, и ISR делает чтение из кэша flash во время операции с flash, то произойдет общий сбой из-за исключения недопустимой инструкции Illegal Instruction exception (для кода, который должен находиться в IRAM), или будут прочитаны мусорные данные (для данных константы, которые должны находиться в DRAM). Замечание: при работе в ISR со строками не рекомендуется использовать printf и другие функции вывода. Для целей отладки используйте ESP_DRAM_LOGE() и другие подобные макросы, когда необходим вывод в лог из ISR. Убедитесь для этого случая, что и TAG, и строка форматирования размещены в DRAM. Non-IRAM-Safe ISR. Если при регистрации ISR не установлен флаг ESP_INTR_FLAG_IRAM, то ISR не будет выполнен, когда кеширование отключено. Как только работа кэша восстановится, non-IRAM-safe прерывания будут снова разрешены. После этого момента ISR снова запустится как обычно. Это значит, что пока кэши запрещены, пользователи не увидят соответствующее аппаратное событие. Для реализации мультиплексирования различных устройств от разных драйверов (SPI Master, SPI Flash, и т. д.) применяется блокировка SPI (bus lock) на каждой шине SPI. Драйверы могут присоединить свои драйверы к шине с арбитражем блокировки. Каждая блокировка шины инициализируется в фоне зарегистрированной службой (BG), и все устройства, запрашивающие выполнение транзакции на шине, должны ожидать момента успешного завершения фоновой блокировки. • Для шины SPI1 такой BG-блокировкой будет кэш, блокировка шины поможет запретить кеширование перед запуском работы устройства, и снова разрешить кеширование после того, как устройство освободит блокировку. Использование ISR не позволяет использовать устройства на SPI1 (нет смысла для задачи уступать свое процессорное время для других задач, когда кэш запрещен). Существует довольно много ограничений при использовании драйвера мастера SPI Master на шине SPI1, см. "Notes on Using the SPI Master driver on SPI1 Bus" [8]. • Для других шин драйвер может зарегистрировать свой собственный ISR как BG. Блокировка шины заблокирует задачу устройства, когда она запрашивает исключительный доступ к шине, попытается запретить ISR, и разблокирует задачу устройства, позволяя ей эксклюзивно использовать шину, когда ISR был успешно запрещен. Затем задача освобождает блокировку, блокировка будет также пытаться возобновить работу ISR, если в ISR остались транзакции, ожидающие своего завершения. [API таблицы разделов] Проекты ESP-IDF используют таблицу разделов, чтобы обслуживать информацию по содержимому различных регионов памяти SPI flash memory (bootloader, различные бинарники приложений, данные, файловые системы). Дополнительную информацию по таблице разделов см. в документации [2]. Этот компонент предоставляет API-функции для просмотра информации по разделам, которые находятся в таблице разделов, и для выполнения операций с разделами. Эти функции декларированы в заголовочном файле esp_partition.h: esp_partition_find() проверяет таблицу разделов на наличие в ней записей определенного типа, возвращая непрозрачный итератор по таблице. Замечание: код приложения должен в большинстве случаев использовать именно API-функции esp_partition_* вместо низкоуровневых API-функций level esp_flash_*. API-функции таблицы разделов делают проверку границ и вычисляют корректные смещения по памяти flash, основываясь на данных, содержащихся в таблице разделов. [SPI Flash Encryption] Существует возможность зашифровать содержимое SPI flash, и оно будет прозрачно расшифровываться аппаратурой. Подробности см. в документации [9]. [Memory Mapping API] У ESP32 есть функция аппаратного отображения регионов памяти flash на адресные пространства инструкций и данных. Это отображение работает только для операций чтения. Нельзя поменять содержимое памяти flash путем записи в отображенный регион памяти. Отображение происходит страницами по 64 KB. Подробную и информацию и существующие ограничения этой технологии см. в техническом руководстве по используемому процессору. Обратите внимание, что некоторые страницы используются для отображения самого приложения, поэтому реальное количество доступных для отображения страниц может быть меньше, чем может обеспечить аппаратура. Чтение данных из flash с использованием отображенного региона памяти - единственный способ расшифровать содержимое flash, когда разрешено шифрование (flash encryption). Расшифровка происходит прозрачно на уровне аппаратуры. API-функции отображения памяти декларированы в заголовочных файлах esp_spi_flash.h и esp_partition.h: spi_flash_mmap() отобразит регион физических адресов flash на область инструкций или данных CPU. Различия между spi_flash_mmap() и esp_partition_mmap() следующие: • spi_flash_mmap() должна использовать физические адреса, выровненные на 64 KB. Обратите внимание, что поскольку отображение памяти происходит страницами, то для esp_partition_mmap может произойти чтение данных вне раздела, независимо от расположения границ раздела. Замечание: отображение поддерживается кэшем, поэтому отображение можно использовать только на основной памяти flash. [Реализация SPI Flash] Структура esp_flash_t хранит данные микросхем, а также 3 основные части этого API: 1. Драйвер хоста (host driver), который предоставляет аппаратную поддержку доступа к микросхеме. Host driver. Драйвер хоста использует интерфейс (spi_flash_host_driver_t), определенный в заголовке spi_flash_types.h (находится в папке components\hal\include\hal). Этот интерфейс предоставляет некоторые общие функции для обмена с микросхемой. В других файлах SPI HAL некоторые из этих функций реализованы с существующим функционалом ESP32 memory-spi. Однако из-за ограничений по скорости ESP32, слой HAL не может предоставить высокоскоростные реализации некоторых команд чтения (поэтому поддержка для этого была удалена). Файлы исходного кода драйвера хоста memspi_host_driver.h и memspi_host_driver.c реализуют высокоскоростную версию этих команд с функцией common_command, предоставленной в HAL, и создает обертку для этих функций как spi_flash_host_driver_t для использования на верхнем уровне. Также Вы можете реализовать свой собственный драйвер хоста, даже через GPIO. Пока реализованы все функции в spi_flash_host_driver_t, функции esp_flash API могут получить доступ к flash независимо от оборудования на нижнем уровне. Chip Driver. Драйвер микросхемы, определенный в заголовочном файле spi_flash_chip_driver.h (находится в папке components\spi_flash\include), делает обертку базовых функций, определенных драйвером хоста, для использования на уровне API. Для некоторых операций необходимо, чтобы сначала были отправлены некоторые команды, или впоследствии было считано некоторое состояние. Некоторым микросхемам нужны разные команды или значения, либо требуется применить специальные способы реализации обмена. Существует тип микросхем, который называется generic chip, он обозначает обычные микросхемы памяти. Другие драйверы для специальных микросхем могут быть определены на основе драйвера generic chip. Драйвер микросхемы использует драйвер хоста. Функции операционной системы. В настоящее время слой функций операционной системы предоставляет функции блокировки и задержки. Блокировка, lock (см. выше врезку "Блокировка шины SPI") используется для разрешения конфликтов доступа к устройствам, подключенным к одной и той же шине SPI, и для доступа к микросхеме SPI Flash. Например: 1. На шине SPI1 должен быть запрещен кэш (используемый для захвата данных data/code в Flash и PSRAM), когда происходит доступ к микросхеме flash на шине SPI0/1. Задержка используется некоторыми длительными операциями, которые требуют от мастера ожидания или периодического опроса. API верхнего уровня оборачивает эти функции драйвера и операционной системы в единый компонент, который также предоставляет некоторую проверку аргументов функций. Функции операционной системы также помогают избежать срабатывания сторожевого таймера (watchdog timeout) во время стирания больших областей flash. В течение этого времени CPU занят задачей стирания flash. Этот процесс останавливает выполнение других задач. К этим задачам относится также задача ожидания FreeRTOS (idle task), которая сбрасывает сторожевой таймер (watchdog timer, WDT). Если выбрана опция конфигурации CONFIG_ESP_TASK_WDT_PANIC, и операция с памятью flash происходит по времени дольше, чем таймаут сторожевого таймера, то произойдет (нежелательная) перезагрузка системы. Довольно трудно полностью устранить риск возникновения такого события, потому что время стирания меняется в зависимости от используемых микросхем драйвера, что затрудняет обеспечение совместимости для драйверов flash. Таким образом, пользователям нужно уделить этому моменту особое внимание, рекомендуется следующее: 1. Разрешить опцию CONFIG_SPI_FLASH_YIELD_DURING_ERASE, чтобы планировщик мог восстанавливать переключение задач во время стирания памяти flash. Кроме того, могут использоваться следующие параметры. • Увеличение CONFIG_SPI_FLASH_ERASE_YIELD_TICKS или CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS в menuconfig. 2. Помните о последствиях разрешения опции CONFIG_ESP_TASK_WDT_PANIC при выполнении длительных операций с памятью SPI flash, которые приведут к срабатыванию таймаута. Однако эта опция может также помочь решить проблему возникновения неожиданных исключений (unexpected exceptions), т. е. ошибок в Вашем приложении. Необходимо принять обдуманное решение для реальной рабочей ситуации. 3. Во время разработки своих проектов внимательно изучите фактическую работу памяти flash в соответствии с требуемыми и ограничениями по времени стирания. Всегда допускайте разумное резервирование в зависимости от специфических требований для изделия, когда конфигурируете порог таймаута стирания flash, что позволяет повысить надежность конечной продукции и отсутствие заметных зависаний. [Технические подробности реализации] Для выполнения некоторых операций с памятью flash необходимо убедиться, что оба ядра CPU не выполняют любой код из flash во время операции с flash: - В одноядерной системе SDK должен запретить прерывания или планировщик перед выполнением операции с flash. Когда API-функция SPI flash вызывается на CPU A (это может быть ядро PRO или APP), запускается функция spi_flash_op_block_func на CPU B с использованием esp_ipc_call API. Это API выводит из сна высокоприоритетную задачу на CPU B, и указывает выполнить функцию, для этого случая это функция spi_flash_op_block_func. Эта функция запретит кэш на CPU B и сигнализирует о запрете кэша установкой флага s_flash_op_can_start. Затем задача на CPU A также запретит кэш и приступит к выполнению операции с flash. Когда осуществляется процесс операции над flash, прерывания все еще запускаются на ядрах CPU A и CPU B. При этом подразумевается, что весь код прерываний помещен в RAM. После добавления interrupt allocation API должен быть добавлен флаг для запроса запрета прерывания на время длительности операций flash. Как только операция с flash завершится, функция на CPU установит другой флаг s_flash_op_complete, чтобы задача на CPU B смогла узнать об этом и снова разрешить работу кэша и освободить CPU. Затем функция на CPU A снова разрешит кэш на CPU A, а также вернет управление в вызывающий код. Дополнительно все API-функции защищены мьютексом (s_flash_op_mutex). В одноядерном рабочем окружении (когда разрешена опция CONFIG_FREERTOS_UNICORE), Вам нужно запретить оба кэша, чтобы не было возможности обмена данными между ядрами CPU. [Справочник по API SPI Flash] Заголовочный файл: components/spi_flash/include/esp_flash_spi_init.h.
Заголовочный файл: components/spi_flash/include/esp_flash.h.
Заголовочный файл: components/spi_flash/include/esp_spi_flash.h.
[Справочник по API таблицы разделов] Заголовочный файл: components/spi_flash/include/esp_partition.h.
[Справочник по API шифрования flash] Заголовочный файл: components/bootloader_support/include/esp_flash_encrypt.h.
Подробное описание типов данных, макросов и API-функций см. документацию [1]. [Словарик] APP CPU, PRO CPU условное обозначение двух ядер ESP32, на которые в примерах среды разработки ESP-IDF возлагаются различные задачи приложения (PRO это ядро для обработки кода протоколов, APP это ядро для обработки кода приложения). MMU Memory Management Unit, блок управления памятью. OTA Over The Air, технология загрузки (обновления) по радиоканалу. [Ссылки] 1. ESP32 SPI Flash API site:docs.espressif.com. |