Программирование ARM ESP32-C3: выделение прерывания Thu, April 25 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

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.
2. Аппаратура MSPI должна поддерживать функцию auto-suspend (железо может автоматически посылать команду приостановки suspend).

Если Вы используете эту функцию на чипе, который это не поддерживает, то это может привести к серьезному сбою. Поэтому строго рекомендуется предварительно изучить даташит на используемую микросхему flash. Убедитесь, что чип как минимум удовлетворяет следующим условиям.

1. Бит SUS в регистрах статуса должен находиться в SR2 bit7 (или SR bit15). Это вызвано ограничением реализации программного обеспечения.
2. Команда suspend 75H, команда resume 7AH. Это вызвано ограничением реализации программного обеспечения.
3. Когда flash успешно приостановлена (suspended), могут быть корректно прочитаны все адреса flash, кроме стертой секции/блока. И команда resume может быть немедленно отправлена в этом состоянии.
4. Когда flash успешно возобновила работу по команде (resumed), в этом состоянии может быть немедленно отправлена команда suspend.

Когда разрешена опция 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.

Функция Описание
esp_intr_mark_shared Помечает прерывание CPU (0 .. 31) как совместно используемое (shared interrupt). При этом определенное прерывание на указанном CPU помечается как прерывание, которое можно использовать для подключения нескольких совместно используемых обработчиков прерываний (shared interrupt handlers).
esp_intr_reserve Резервирует прерывание CPU (0 .. 31) для использования вне фреймворка ESP-IDF. Это пометит определенное прерывание на указанном CPU как зарезервированное, которое не будет выделяться по какой-либо причине.
esp_intr_alloc Выделяет прерывание с указанными параметрами. Вызов найдет прерывание, которое соответствует ограничением, указанным в параметре flags, привяжет к нему указанный источник сигнала прерывания от периферийного устройства, а также подцепит к нему указанный обработчик прерывания (в опциональном аргументе). Если необходимо, вызов также может вернуть дескриптор (handle) для прерывания. Прерывание будет всегда выделено на том ядре CPU, которое запустило эту функцию. Если используется флаг ESP_INTR_FLAG_IRAM, то адрес дескриптора не находится в IRAM или RTC_FAST_MEM, и будет возвращено значение ESP_ERR_INVALID_ARG.
esp_intr_alloc_intrstatus Выделяет прерывание с указанными параметрами. Фактически делает то же самое, что и esp_intr_alloc, но также позволяет указать комбинацию регистра статуса прерывания и маски прерывания. Для shared interrupts обработчик вызывается только если результат чтения из указанного регистра, на который наложена по AND маска прерывания, не равен 0. Передачей адреса регистра статуса прерывания и маски это может ускорить обработку прерывания в случае, если сработало shared-прерывание. С помощью предварительной проверки статуса прерывания код может принять решение, какой обработчик можно пропустить.
esp_intr_free Запретит и освободит прерывание с освобождением связанных с ним ресурсов. В качестве параметра указывается дескриптор прерывания. Если текущее ядро не то, на котором было зарегистрировано это прерывание, то эта функция будет назначена на ядро, которое выделило это прерывание, с блокировкой и ожиданием, пока ресурсы не будут успешно освобождены. Замечания: когда обработчик использует свой сигнал прерывания совместно с другими обработчиками, его биты статуса прерывания должны быть перед освобождением корректно обработаны. Подробности см. в описании esp_intr_disable. Не вызывайте эту функцию в esp_ipc_call_blocking.
esp_intr_get_cpu Вернет номер ядра CPU, к которому привязано прерывание.
esp_intr_get_intno Вернет номер выделенного прерывания по указанному дескриптору.
esp_intr_disable Запретит прерывание по указанному дескриптору. Замечания: (a) Для локальных прерываний (источники ESP_INTERNAL_*) эта функция должна быть вызвана на том ядре CPU, на котором было выделено прерывание. Для других прерываний нет такого ограничения. (b) Когда несколько обработчиков используют один и тот же источник прерывания, биты статуса прерывания, которые обрабатываются в обработчике, должны маскироваться перед запретом, или должны быть правильно обработаны в другом разрешенном прерывании. Пропуск обработки статуса прерывания может привести к бесконечным вызовам прерывания и краху системы.
esp_intr_enable Разрешит прерывание по указанному дескриптору. Замечание: для локальных прерываний (источники ESP_INTERNAL_*) эта функция должна быть вызвана на том ядре CPU, на котором было выделено прерывание. Для других прерываний нет такого ограничения.
esp_intr_set_in_iram Установит статус обработчика "in IRAM". Замечание: не работает на shared-прерываниях.
esp_intr_noniram_disable Запрещает прерывания, которые не были специально промаркированы работающими из IRAM.
esp_intr_noniram_enable Снова разрешает прерывания, которые были запрещены вызовом esp_intr_noniram_disable.
esp_intr_enable_source Разрешает источник источник прерывания по указанному номеру (0 .. 31).
esp_intr_disable_source Запрещает источник источник прерывания по указанному номеру (0 .. 31).
esp_intr_flags_to_level Получает самый низкий уровень прерывания по флагам (параметр flags).

Подробное описание этих функций, их параметров, макросов и определений типов см. в [1].

[Ссылки]

1. ESP32-C3 Interrupt allocation site:docs.espressif.com.
2. ESP32-C3 Concurrency Constraints for flash on SPI1 site:docs.espressif.com.
3. ESP32-C3 SPI Flash API site:docs.espressif.com.
4. ESP32-C3 SPI Features site:docs.espressif.com.
5. ESP32-C3 Memory Types site:docs.espressif.com.
6. espressif / esp-idf/examples/system/flash_suspend/ site:github.com.

 

Добавить комментарий


Защитный код
Обновить

Top of Page