ESP32-C3: выделение прерывания |
![]() |
Добавил(а) microsin | ||||||||||||||||||||||||||||||||
У чипа ESP32-C3 одно ядро CPU, у которого существует 31 прерывания. Каждому прерыванию может программно назначен уровень приоритета. Из-за того, что может быть несколько аппаратных сигналов периферии на одного прерывание CPU, иногда имеет смысл использовать одно прерывание CPU для нескольких драйверов. Существует абстракция esp_intr_alloc(), которая скрывает все эти детали реализации. Драйвер может выделить прерывание (interrupt allocation) для определенного периферийного устройства путем вызова esp_intr_alloc() (или esp_intr_alloc_intrstatus()). Передаваемые в эту функцию флаги могут использоваться для установки типа выделяемого прерывания, указания уровня прерывания или метода запуска. Код interrupt allocation затем найдет соответствующее прерывание, использует мультиплексор прерывания для привязки к периферийному устройству, и инсталлирует указанный обработчик прерывания и ISR для него. Этот код представляет 2 различных типа прерывания, обрабатываемых по-разному: совместно используемые прерывания (shared interrupts) и индивидуальные прерывания (non-shared interrupts). Самые простые (и низколатентные) non-shared interrupts: здесь выделяется отдельное прерывание на вызов esp_intr_alloc(), и это прерывание исключительно используется подсоединенным к нему периферийным устройством, с только одним вызываемым ISR. С другой стороны, shared interrupts могут иметь несколько привязанных к ним сигналов прерывания от разных периферийных устройств, с несколькими обработчиками, вызываемыми по сигналу прерывания от определенного периферийного устройства. Таким образом обработчики, предназначенные для shared interrupts, должны проверять статус прерывания периферийного устройства, которое они обрабатывают, чтобы определить необходимое действие по обработки прерывания. Примечание переводчика: насколько я понял, в контексте этого описания термины "обработчик прерывания" и "ISR" имеют разный смысл. Здесь обработчик прерывания это код пользователя, который будет вызван из кода ISR (может быть несколько обработчиков для одного и того же ISR), а ISR это обработчик на ассемблере одно из прерываний CPU (с идентификаторами 1 .. 31), который реализован на ассемблере библиотеками ESP-IDF. Non-shared interrupts могут быть срабатывать либо по уровню (level-triggered), либо по перепаду (edge-triggered). Shared interrupts могут быть только level-triggered, поскольку существует шанс пропуска прерываний, когда используются прерывания edge-triggered. Для примера представим, что DevA и DevB совместно используют одно прерывание CPU (share interrupt). DevB сигнализирует о прерывании, сигнал INT переходит в лог. 1. ISR вызывает код для обработчика DevA, но код обработчика DevA ничего не делает. Затем ISR вызывает код обработчика для DevB, но когда он это делает, DevA сигнализирует о прерывании. Обработчик DevB завершается, очищает статус прерывания для DevB и производит выход из кода прерывания. Теперь прерывание для DevA все еще ожидает обработки, но поскольку сигнал INT никогда не перейдет в лог. 0, поскольку DevA удерживает его в лог. 1 даже когда было очищено прерывание DevB, прерывание DevA никогда не будет обработано. [IRAM-Safe обработчики прерывания] Флаг/параметр ESP_INTR_FLAG_IRAM регистрирует обработчик прерывания, который всегда работает из IRAM (и читает все свои данные из DRAM). По этой причине его не надо запрещать на время выполнения операция erase (стирание) и write (запись) памяти FLASH. Это полезно для прерываний, которые требуют гарантированной минимальной задержки выполнения, поскольку операции flash write и flash erase могут быть медленными (стирание может занимать десятки и сотни миллисекунд до своего завершения). Также полезно держать обработчик прерывания в IRAM, если он вызывается очень часто, что позволяет избежать промахов кэша flash. Дополнительные подробности см. в документации SPI flash API [2] (а также следующую врезку "Конкурентные ограничения для FLASH на SPI1"). Шина SPI0/SPI1 совместно используется между кэшем инструкций и данных (для выполнения firmware) и периферийным устройством SPI1 (управляемым драйверами, включая драйвер SPI Flash). Как следствие операции SPI1 будут оказывать значительное влияние на всю систему в целом. Операции такого вида включают вызовы SPI Flash API [3] или других драйверов шины SPI1, любые операции наподобие read/write/erase, или другие операции пользователя над SPI, независимо для основной flash или других устройств SPI. На ESP32-C3 опция конфигурации CONFIG_SPI_FLASH_AUTO_SUSPEND (которая по умолчанию разрешена) позволяет кешировать чтение flash конкурентно с операциями SPI1. Подробности см. в далее, секция "Функция Flash Auto Suspend". Если эта опция запрещена, то кэши должны быть запрещены во время операций read/write/erase. Существуют некоторые ограничения на использование драйвера по шине SPI1, см. далее секцию "Когда кэши запрещены". Эти ограничения приводят к повышению использования IRAM/DRAM. [Когда кэши запрещены] В этих условиях CPU должен всегда выполнять код и обращаться к данным, когда и код и данные находятся во внутреннем ОЗУ. Соответствующие API-функции автоматически и прозрачно запрещают кэширование. Однако, когда разрешена опция CONFIG_SPI_FLASH_AUTO_SUSPEND, эти API-функции не запрещают кэши. Аппаратура будет обрабатывать арбитражи между ними. Способ, которым эти API-функции запрещают кэши, также будет запрещать non-IRAM-safe прерывания. Они будут восстановлены, когда операция над Flash завершится. См. также секцию "OS Functions" документации [3] и SPI Bus Lock [4]. Нет таких ограничений и последствий для flash-чипов на других шинах SPI, отличающихся от SPI0/SPI1. Для отличий между внутренним ОЗУ (например IRAM, DRAM) и кэшем flash, см. документацию по распределению памяти [5]. IRAM-Safe Interrupt Handlers. Для обработчиков прерывания, которые должны выполняться, когда кэш запрещена (например для низколатентных операций), установите флаг/параметр ESP_INTR_FLAG_IRAM, когда регистрируете обработчик прерывания. Вам нужно гарантировать, что все данные и функции, к которым обращаются эти обработчики прерывания, включая те, которые вызывают обработчики, также размещены в IRAM или DRAM. См. секцию "How to place code in IRAM" документации [5]. Если функция или символ в настоящий момент не размещен в IRAM/DRAM, и обработчик прерывания читает из кэша flash во время операции с памятью flash, то это приведет к краху из-за исключения недопустимой инструкции (Illegal Instruction exception) для кода, который должен бы находиться в IRAM, или приведет к мусорным данным при чтении для данных констант, которые должны были бы находиться в DRAM. Примечание: когда в ISR работают со строками, не рекомендуется использовать printf и другие подобные функции вывода. Для целей отладки используйте макрос ESP_DRAM_LOGE() и подобные, чтобы организовать лог операций из ISR. Для этого случая убедитесь, чтобы и параметр TAG, и строка формата обе размещались в DRAM. Non-IRAM-Safe Interrupt Handlers. Если во время регистрации обработчика прерывания не был установлен флаг ESP_INTR_FLAG_IRAM, то обработчик прерывания не выполнится, когда кэши запрещены. Как только кэширование восстановится, автоматически будут разрешены прерывания non-IRAM-safe. После этого момента обработчик прерывания будет снова нормально запускаться. Это значит, что пока кэши запрещены, пользовательское firmware не узнает о возникновении аппаратного события. [Функция Flash Auto Suspend] Важные замечания: 1. Чип flash, который Вы используете, должен поддерживать функцию приостаsuspend/resume. Если Вы используете эту функцию на чипе, который это не поддерживает, то это может привести к серьезному сбою. Поэтому строго рекомендуется предварительно изучить даташит на используемую микросхему flash. Убедитесь, что чип как минимум удовлетворяет следующим условиям. 1. Бит SUS в регистрах статуса должен находиться в SR2 bit7 (или SR bit15). Это вызвано ограничением реализации программного обеспечения. Когда разрешена опция CONFIG_SPI_FLASH_AUTO_SUSPEND, кэши остаются разрешенными (они будут отключены, если выключена опция CONFIG_SPI_FLASH_AUTO_SUSPEND). Аппаратура обрабатывает арбитраж между SPI0 и SPI1. Если операция SPI1 короткая (наподобие операции чтения), то CPU и кэш будут ждать завершения операции SPI1. Однако если происходит стирание (erasing), программирование страницы или запись в регистр статуса (например SE, PP и WRSR), то произойдет auto suspend, прерывающая происходящую операцию flash, чтобы CPU смог прочитать из кэша и flash в ограниченном промежутке времени. Таким образом, некоторый код/переменные могут вводиться во flash/psram вместо IRAM/DRAM, но могут выполняться во время стирания флэш-памяти. Это уменьшает использование IRAM/DRAM. Обратите внимание, что эта функция вносит дополнительные затраты процессорного времени на flash suspend/resume. Стирание flash может происходить очень долго, если стирание слишком часто прерывается. Используйте приоритеты задач FreeRTOS, чтобы гарантировать, что только критические задачи реального времени выполняются с более высоким приоритетом, чем flash erase, чтобы обеспечить разумное время стирания flash. Другими словами, существует 3 вида кода: Critical code: код находится в IRAM/DRAM. Этот вид кода обычно требует максимальной производительности, связанной с cache/flash/psram, или он вызывается очень часто (например, обработчик прерывания таймера). Cached code: код находится в flash/psram. У этого кода требования к производительности ниже, или он вызывается не очень часто. Он будет вызываться во время операции erase, с некоторыми накладными расходами по производительности. Low priority code: находится в flash/psram, и его выполнение запрещено во время стирания flash. Должно быть запрещена работа этого кода во время стирания, чтобы он не влиял на процесс стирания. Обычно это реализуется путем понижения приоритета задачи для этого кода - приоритет должен быть ниже, чем у задачи, которая выполняет стирание. Независимо от использования функции flash suspend и соответствующей задержки времени реакции, см. пример system/flash_suspend [6]. [Несколько обработчиков, совместно использующих ISR] Несколько обработчиков можно назначить на один и тот же источник сигнала прерывания, если все они выделены с флагом/параметром ESP_INTR_FLAG_SHARED. Все они будут выделены на одно прерывание, к которому подключен источник, и вызываться последовательно, когда источник активен. Обработчики могут быть запрещены и освобождены по отдельности. Источник (разрешенный) подключен к прерыванию (enabled), если разрешено один или несколько обработчиков, иначе он отключен. Обработчик никогда не будет вызван, когда он запрещен, в то время как его источник прерывания все еще срабатывает, если какой-либо его обработчик разрешен. Источник сигналов прерывания, подсоединенные к non-shared interrupt, не поддерживают эту функцию. Хотя фреймворк поддерживает эту функцию, следует использовать её очень осторожно. Обычно есть 2 способа остановить срабатывание прерывания: запрет источника или маскирование статуса прерывания периферийного устройства. ESP-IDF поддерживает только разрешение и запрет самого источника прерывания, оставляя биты статуса и маскирования на обработку пользователем. Биты статуса должны быть либо маскированы перед отключением соответствующего обработчика, либо замаскированы, а затем правильно обработаны в другом разрешенном прерывании. Имейте в виду, что если оставить некоторые биты статуса не обработанными без их маскирования, при этом отключив их обработчики, то это может привести к тому, что прерывание (прерывания) будет срабатывать бесконечно, что приведет к краху системы. [Описание API-функций] Файл заголовка components/esp_hw_support/include/esp_intr_alloc.h.
Подробное описание этих функций, их параметров, макросов и определений типов см. в [1]. [Ссылки] 1. ESP32-C3 Interrupt allocation site:docs.espressif.com. |