OSDev Wiki
Advertisement

Общие сведения

Обработка прерываний в M-профиле кардинально отличается от таковой в других профилях архитектуры ARM.

Документация на M-профиль различает несколько видов прерываний.

  • Сброс (reset) является особым видом прерывания, немедленно прекращающим выполнение текущей операции и переводящим процессор в предопределённое состояние без возможности возвращения управления ранее выполнявшемуся коду.
  • Синхронный вызов супервизора (SVCall) выполняется по команде SVC.
  • Асинхронный вызов супервизора (PendSV) выполняется с помощью механизма отложенного вызова супервизора через регистр ICSR.
  • Отказ (fault) возникает при обнаружении какой-либо ошибки при выборке или выполнении команды. Отказы могут быть синхронными (прямо связаны с определённой командой) или асинхронными (вызываться какой-либо из ранее выполненных команд, если ошибка не могла быть выявлена сразу). Асинхронным в M-профиле является лишь неточный отказ шины (imprecise BusFault), возникающий при неудачной попытке записи в память. Процессор ради увеличения производительности может не дожидаться фактического завершения записи и продолжать выполнять команды дальше, поэтому при позднем обнаружении отказа установить, какая именно команда стала причиной ошибки, и выполнить «откат» к ней в общем случае невозможно.
К отказам технически относятся также синхронные события, связанные с отладкой (асинхронные отладочные события обрабатываются как внешние прерывания).
  • Внешнее прерывание (interrupt) вызывается внешним по отношению к программе событием, например, изменением состояния какой-либо из входных линий микроконтроллера или завершением передачи байта через последовательный интерфейс. Внешние прерывания возникают асинхронно по отношению к выполняемому потоку команд. Технически к ним также относятся асинхронные отладочные события.

Каждое прерывание имеет следующие характеристики:

  • номер прерывания;
  • приоритет;
  • вектор в памяти, где хранится адрес точки входа в обработчик прерывания.

Все прерывания, кроме сброса, могут находиться в одном из следующих состояний:

  • неактивно (inactive) — прерывание не запрошено и не обслуживается;
  • ожидает (pending) — прерывание запрошено, но его обработка ещё не начата;
  • активно (active) — прерывание обрабатывается в данный момент либо его обработка была начата, но в настоящее время прервана для обработки более приоритетного прерывания;
  • активно и ожидает (active and pending) — обработка прерывания была начата, однако запрос на это же прерывание продолжает присутствовать и ожидать обслуживания. В таком состоянии могут находиться лишь асинхронные прерывания.

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

При вызове обработчика ожидающего прерывания признак ожидания сбрасывается и устанавливается признак активности данного прерывания. Последний будет сброшен при завершении обработчика данного прерывания.

Выполняемый процессором код имеет некоторый приоритет выполнения. Сразу после сброса устанавливается самый низкий, так называемый базовый приоритет, на котором выполняется только код в режиме потока. Когда появляется запрос прерывания, процессор сравнивает его приоритет с текущим приоритетом выполнения и в случае, если приоритет запроса существенно выше, приостанавливает текущий выполняемый код и переходит к обработке прерывания. Для этого состояние прерванного кода сохраняется в стеке, текущий приоритет выполнения повышается до уровня приоритета данного прерывания и управление передаётся его обработчику. Привилегированный код может повысить свой приоритет выполнения, записав те или иные значения в регистры масок. Непривилегированный код не может влиять на приоритет выполнения, который для него всегда будет минимальным.

Примечание. «Существенность» превосходства приоритета запроса прерывания над приоритетом выполнения определяется механизмом группирования приоритетов.

M-профиль оптимизирован для «цепочечной» обработки прерываний: если при завершении текущего обработчика окажется, что своей обработки ожидает запрос примерно такого же или несколько более низкого приоритета (существенно выше, чем приоритет ранее прерванного кода, но не существенно выше, чем приоритет обработчика, который завершает своё выполнение), то процессор сразу перейдёт к обработке ожидающего запроса, при необходимости скорректировав текущий приоритет выполнения. Возврат к прерванному ранее коду произойдёт лишь тогда, когда не останется достаточно приоритетных запросов, ожидающих обработки. Благодаря такому подходу исключается время, необходимое на восстановление и повторное сохранение контекста прерванного процесса между завершением одного и началом следующего обработчика.

Номера и векторы прерываний

Таблица векторов прерываний имеет размер N+1 слов, где N — число различных прерываний, поддерживаемых данной реализацией архитектуры. По смещению 0 относительно начала таблицы находится значение, загружаемое в указатель основного стека MSP при сбросе (если процессор поддерживает расширение безопасности, загрузка производится в MSP безопасного режима). За ним располагаются собственно векторы, т. е. адреса точек входа в соответствующие обработчики прерываний (заметим, что младший бит адреса точки входа всегда должен быть равен единице, задавая использование системы команд Thumb). Для прерывания с номером N вектор в таблице располагается по смещению 4*N. Базовый адрес таблицы векторов в памяти находится в регистре VTOR.

Архитектурой определены следующие стандартные номера прерываний:

  • Сброс (reset) — номер 1. Имеет высший приоритет, численно равный –3 (ARMv6-M и ARMv7-M) или –4 (ARMv8-M) и не может быть запрещён. Возобновление выполнения прерванного сбросом кода невозможно. Хотя формально сброс отнесён к прерываниям, реально таковым он не является; по этой причине описание выполняемых по нему действий вынесено в отдельный раздел.
  • Немаскируемое прерывание (NMI, Non Maskable Interrupt) — номер 2. Имеет фиксированный приоритет –2 (наибольший, не считая сброса и, в некоторых случаях, прерывания HardFault, см. ниже) и не может быть запрещено. Немаскируемые прерывания могут запрашиваться аппаратно либо программно через регистр ICSR.
  • Серьёзный отказ (HardFault) — номер 3. Не может быть запрещён и обычно имеет приоритет –1 (третий после сброса и NMI).
Если имеется расширение безопасности, то при установленном бите BFHFNMINS регистра AIRCR серьёзный отказ в безопасном режиме имеет приоритет –3 (самый высокий, не считая сброса). При сброшенном бите BFHFNMINS, а также в небезопасном режиме серьёзный отказ имеет приоритет –1. Серьёзные отказы могут обрабатываться независимо для каждого из режимов безопасности или же вся обработка может выполняться только обработчиком безопасного режима.
Серьёзный отказ фиксируется каждый раз, когда невозможно вызвать «родной» обработчик возникшего синхронного прерывания, и обычно является признаком невосстановимой ошибки. В частности, серьёзный отказ возникает, если синхронное прерывание происходит в момент, когда его обработка запрещена.
В архитектурах ARMv8-M без основного расширения и ARMv6-M многие другие прерывания отсутствуют, и вместо них сразу возникает серьёзный отказ.
  • Управление памятью (MemManage) — номер 4. Приоритет этого прерывания настраивается и оно может быть запрещено, однако его возникновение в запрещённом состоянии приведёт к серьёзному отказу. Это прерывание происходит, если программа обращается к той или иной ячейке памяти недопустимым образом (нарушает права доступа либо стандартные ограничения на доступ к определённым областям адресного пространства).
Это прерывание может быть запрошено также путём установки соответствующего бита в регистре SHCSR. Такая возможность имеется у отладчика, однако доступность этого регистра для программы определяется версией архитектуры и конкретной реализацией.
В версиях ARMv8-M без основного расширения и ARMv6-M это прерывание отсутствует, вместо него возникает серьёзный отказ.
При наличии расширения безопасности для каждого режима безопасности имеется свой обработчик этого прерывания и свой набор управляющих регистров.
  • Отказ шины (BusFault) — номер 5. Приоритет этого прерывания настраивается и оно может быть запрещено, однако его возникновение в запрещённом состоянии приведёт к серьёзному отказу. Это прерывание возникает при ошибках во время попытки доступа к памяти, не связанных с нарушением прав доступа и иных чётко определённых правил, приводящим к возникновению прерывания MemManage. Например, к отказу шины может привести ошибка чётности в считанной из памяти информации. Прерывания этого типа могут быть как синхронными, так и асинхронными.
Это прерывание может быть запрошено также путём установки соответствующего бита в регистре SHCSR. Такая возможность имеется у отладчика, однако доступность этого регистра для программы определяется версией архитектуры и конкретной реализацией.
В версиях ARMv8-M без основного расширения и ARMv6-M это прерывание отсутствует, вместо него возникает серьёзный отказ.
  • Ошибка использования (UsageFault) — номер 6. Приоритет этого прерывания настраивается и оно может быть запрещено, однако его возникновение в запрещённом состоянии приведёт к серьёзному отказу. Это прерывание связано с разного рода ошибками в программе:
  • недопустимым кодом команды или попыткой выполнить привилегированную команду в непривилегированном режиме;
  • неверным состоянием выполнения (например, попыткой перейти на систему команд ARM, которая в M-профиле не поддерживается);
  • ошибкой, возникшей при возврате из обработчика прерывания;
  • попыткой доступа к отключённому или отсутствующему сопроцессору;
  • попыткой невыровненного доступа, если включён контроль выравнивания;
  • попыткой выполнить деление на 0.
Это прерывание может быть запрошено также путём установки соответствующего бита в регистре SHCSR. Такая возможность имеется у отладчика, однако доступность этого регистра для программы определяется версией архитектуры и конкретной реализацией.
В версиях ARMv8-M без основного расширения и ARMv6-M это прерывание отсутствует, вместо него возникает серьёзный отказ.
При наличии расширения безопасности для каждого режима безопасности имеется свой обработчик этого прерывания и свой набор управляющих регистров.
  • Нарушение безопасности (SecureFault) – номер 7. Приоритет этого прерывания настраивается и оно может быть запрещено, однако его возникновение в запрещённом состоянии приведёт к серьёзному отказу. Это прерывание имеется лишь в процессорах, поддерживающих расширение безопасности.
  • Прерывания с номерами 8—10 зарезервированы.
  • Вызов супервизора (SVCall) — номер 11. Это прерывание имеет настраиваемый приоритет и происходит при выполнении команды SVC, а поэтому не может быть запрещено.
Документация на архитектуру ARMv6-M, перечисляя прерывания, не упоминает о том, что данное прерывание (вместе с вызывающей его командой SVC) может отсутствовать. Однако исходные тексты ядра Cortex-M1 подтверждают, что данное прерывание имеется лишь в случае, если при сборке ядра выбрана поддержка ОС. То же самое относится к версии ARMv8-M без основного расширения.
При наличии расширения безопасности для каждого режима безопасности имеется свой обработчик этого прерывания и свой набор управляющих регистров.
  • Отладочный монитор (Debug monitor) — номер 12. Приоритет этого прерывания настраивается. В общем случае прерывания, генерируемые отладочным монитором, являются синхронными и обрабатываются как отказы, однако точки слежения (debug watchpoints) порождают асинхронные события, обрабатываемые аналогично внешним прерываниям.
В версиях ARMv8-M без основного расширения и ARMv6-M это прерывание возникать не может.
  • Прерывание с номером 13 зарезервировано.
  • Отложенный вызов супервизора (PendSV) — номер 14. Это прерывание имеет настраиваемый приоритет и всегда разрешено. Оно управляется с помощью регистра ICSR и происходит асинхронно по отношению к выполняемому программному коду.
Документация на архитектуру ARMv6-M, перечисляя прерывания, не упоминает о том, что данное прерывание может отсутствовать. Однако исходные тексты ядра Cortex-M1 подтверждают, что данное прерывание имеется лишь в случае, если при сборке ядра выбрана поддержка ОС. То же самое относится к версии ARMv8-M без основного расширения.
При наличии расширения безопасности для каждого режима безопасности имеется свой обработчик этого прерывания и свой набор управляющих регистров.
  • Системный таймер (SysTick) — номер 15. Это прерывание с настраиваемым приоритетом всегда разрешено. Его аппаратная генерация может быть запрещена, однако его всегда можно вызвать программно через регистр ICSR. Оно связано со стандартным таймером, входящим в процессоры M-профиля, и является асинхронным.
В версии ARMv6-M это прерывание является необязательным (отсутствует, если в конкретной реализации нет системного таймера). В частности, оно отсутствует в ядре Cortex-M1, если при сборке последнего поддержка ОС не включена. То же самое относится к версии ARMv8-M без основного расширения.
При наличии расширения безопасности для каждого режима безопасности имеется свой обработчик этого прерывания и свой набор управляющих регистров (при условии, что для данного режима безопасности таймер вообще имеется).
  • Внешние прерывания (external interrupts). Нумерация источников этих прерываний является двоякой: номера отведённых для них векторов, а соответственно, и номера прерываний с точки зрения процессора, начинаются с 16, однако номера источников запросов, поступающих в контроллер прерываний NVIC, начинаются с нуля (это объясняется тем, что логически NVIC работает исключительно с внешними прерываниями, в то время как все описанные выше прерывания обрабатываются непосредственно процессором). Эти прерывания имеют настраиваемый приоритет и могут запрещаться. Все они являются асинхронными.
Максимальный номер прерывания определяется реализацией, но не может превышать 511, что ограничивает количество возможных внешних прерываний 496-ю. В версии ARMv6-M может быть реализовано не более 32 внешних прерываний, однако в ARMv8-M их может быть 496 независимо от наличия основного расширения.

Приоритеты прерываний

Приоритет прерывания тем выше, чем алгебраически меньше обозначающее его число. Три самых приоритетных прерывания — сброс, NMI и серьёзный отказ — имеют фиксированные приоритеты, выражаемые отрицательными числами. Все остальные прерывания имеют программно назначаемые приоритеты (стандартные прерывания – через регистры SHPR1–SHPR3, внешние прерывания – через регистры IPR), причём наименьшее значение, а соответственно, наибольший приоритет будет равен нулю. Благодаря последнему обстоятельству все три прерывания с фиксированными приоритетами всегда являются более приоритетными, чем любые другие прерывания.

Когда одновременно возникает несколько прерываний с разными приоритетами, управление получит обработчик прерывания с наибольшим (численно наименьшим) приоритетом. Если имеется несколько запросов с одинаковым приоритетом, первым будет обслужено прерывание с наименьшим номером.

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

Сброс устанавливает все программно настраиваемые приоритеты в нуль.

Программируемые приоритеты

Как уже отмечалось, почти все прерывания имеют программируемый приоритет. В версиях ARMv8-M с основным расширением и ARMv7-M эта величина в зависимости от реализации занимает от 3 до 8 разрядов; в версиях ARMv8-M без основного расширения и ARMv6-M – всегда два бита. Для обеспечения максимальной программной совместимости реализованные биты приоритета всегда являются старшими разрядами байта, при этом младшие разряды, соответствующие нереализованным битам, считаются равными нулю. Таким образом, минимальный приоритет прерывания в зависимости от реализованного числа разрядов может быть равен 192, 224, 240, 248, 252, 254 или 255 (C0, E0, F0, F8, FC, FE или FF); максимальный программируемый приоритет всегда равен нулю. Запись единиц в нереализованные разряды игнорируется; при чтении они будут равны нулю.

При программном изменении приоритета в версиях ARMv8-M без основного расширения и ARMv6-M соответствующее прерывание должно быть запрещено и не обрабатываться в момент изменения, иначе последствия будут непредсказуемыми (требование запрещённости не распространяется на прерывания SVCall и PendSV, которые не могут быть запрещены, но и не могут возникнуть неожиданно, без явного запроса со стороны программы). Версии ARMv8-M с основным расширением и ARMv7-M позволяют изменять приоритеты прерываний в любое время, однако в общем случае после этого необходимо выполнить следующие подряд команды DSB и ISB, обеспечивающие синхронизацию изменения приоритета с выполнением кода.

Приоритеты стандартных прерываний задаются в регистрах SHPR, приоритеты внешних прерываний — в регистрах контроллера прерываний IPR.

Группирование приоритетов

Приоритеты (не считая прерываний с фиксированными приоритетами) делятся на группы в соответствии со значением поля PRIGROUP регистра AIRCR. Полное 8-разрядное поле приоритета делится на две части: приоритет группы и подприоритет (приоритет внутри группы). Приоритет группы имеет длину от 7 до 0 битов для значений PRIGROUP от 0 до 7 соответственно; оставшуюся часть поля приоритета (1–8 разрядов) занимает подприоритет.

Приоритет группы используется для того, чтобы определить, достаточно ли приоритетен запрос, чтобы прервать выполнение текущего кода: приоритет запроса существенно выше текущего приоритета выполнения, если приоритет группы данного запроса численно меньше приоритета группы текущего выполняющегося кода. Подприоритет используется только для того, чтобы выбрать порядок обработки одновременно поступивших запросов прерываний, относящихся к одной группе приоритетов.

В версиях ARMv8-M без основного расширения и ARMv6-M поле PRIGROUP не реализовано и считается равным нулю. Поскольку сами приоритеты прерываний в этих версиях включают лишь два старших разряда, группирование приоритетов фактически отсутствует.

Каждое из трёх прерываний с фиксированным приоритетом считается образующим свою собственную отдельную группу (а HardFault в процессоре с расширением безопасности может образовывать две независимых группы – для уровней приоритетов –1 и –3).

Приоритет выполнения

Приоритет выполнения (приоритет текущего выполняемого процессором кода) зависит от следующих факторов:

  • выполняется ли в текущий момент поток или какой-либо обработчик прерывания;
  • если выполняется обработчик, то каков приоритет самого приоритетного из активных обработчиков;

Минимально возможным является так называемый базовый уровень приоритета выполнения (base level of execution priority) или просто базовый приоритет. Можно считать, что численно он равен приоритету самого низкоприоритетного из возможных прерываний плюс 1 (или, например, числу 256, которое гарантированно больше любого возможного уровня приоритета независимо от особенностей реализации процессора).

Численное значение приоритета выполнения, если регистры масок содержат нули, т. е. не влияют на приоритет, определяется по следующим правилам, перечисленным в порядке убывания их приоритета.

  • Если в данный момент активен сброс, приоритет выполнения равен –4. Реально на обработку прерываний этот случай не влияет, поскольку активный сигнал сброса полностью блокирует работу процессора и сбрасывает любые запросы прерываний, а после снятия сигнала начинается выполнение обработчика сброса. Формальное внесение этого пункта в документацию на архитектуру связано с тем, что сброс рассматривается авторами документации как самое приоритетное прерывание.
  • Если в данный момент активно прерывание HardFault безопасного режима и установлен бит AIRCR.BFHFNMINS, приоритет выполнения равен –3.
  • Если в данный момент активно немаскируемое прерывание (NMI), приоритет выполнения равен –2.
  • Если в данный момент активно прерывание HardFault небезопасного режима (сюда относится также случай, когда процессор не имеет расширения безопасности), а также если активно это прерывание безопасного режима и бит AIRCR.BFHFNMINS сброшен, приоритет выполнения равен –1.
  • Если в данный момент имеется хотя бы одно активное прерывание, за исключением сброса, NMI и HardFault, приоритет выполнения будет равен групповому приоритету самого приоритетного из активных прерываний (см. ниже).
  • Если же никаких активных прерываний нет, приоритет выполнения будет равен базовому приоритету, т. е. числу 256.

Групповой приоритет некоторого активного прерывания определяется следующим образом.

  • Если это прерывание обрабатывается в безопасном режиме или если процессор не имеет расширения безопасности, берётся полный приоритет этого прерывания (содержимое соответствующего регистра) и из него выделяются биты, задающие групповой приоритет; младшие биты, задающие приоритет внутри группы, обнуляются. Значения для этих вычислений на процессоре с расширением безопасности извлекаются из соответствующего регистра приоритета и регистра AIRCR безопасного режима.
  • Если это прерывание обрабатывается в небезопасном режиме, сначала производится определение его группового приоритета, как в предыдущем случае, только необходимые значения извлекаются из соответствующих регистров небезопасного режима. Однако затем анализируется состояние бита AIRCR_S.PRIS. Если этот бит сброшен, уже полученный приоритет будет приоритетом выполнения обработчика данного прерывания. Однако в случае, если этот бит установлен, полученный приоритет сдвигается вправо на 1 бит (численно делится на 2), после чего устанавливается его старший бит. Таким образом, установленный бит AIRCR_S.PRIS отображает весь диапазон значений приоритетов прерываний небезопасного режима (0–255) на младшую половину набора приоритетов (128–255).

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

В архитектурах ARMv8-M с основным расширением и ARMv-7 (а возможно, и ARMv8-M без основного расширения), если некоторый активный обработчик был вытеснен в связи с появлением более приоритетного запроса, он уже не может получить управление до тех пор, пока вытеснивший его обработчик не закончит своё выполнение, даже если в процессе выполнения приоритеты будут изменены, и вызванный позднее обработчик в итоге снизит свой приоритет до уровня, уступающего приоритету предыдущего обработчика. Однако снижение приоритета может обеспечить вытеснение текущего обработчика новым обработчиком, если приоритет нового обработчика после понижения приоритета текущего обработчика окажется в достаточной степени высоким.

В версии ARMv6-M (а возможно, и в ARMv8-M без основного расширения) изменение приоритета активного или разрешённого прерывания приведёт к непредсказуемым последствиям, а поэтому производиться не должно.

Регистры масок позволяют повысить текущий приоритет выполнения, ограничив тем самым возможность прерывания текущего кода независимо от приоритета самого этого кода.

  • Если расширение безопасности отсутствует, ненулевое значение в регистре BASEPRI (отсутствует в версиях ARMv8-M без основного расширения и ARMv6-M) задаёт приоритет, который будет использован в качестве приоритета выполнения, если он выше (численно меньше), чем самый высокий приоритет среди активных прерываний. В частности, если выполняется поток, BASEPRI повышает его приоритет до содержащегося в BASEPRI значения. Нулевое значение в BASEPRI означает, что этот регистр в определении текущего приоритета выполнения не участвует.
Если имеется расширение безопасности, у каждого из режимов безопасности есть свой регистр BASEPRI. Ненулевое значение безопасной версии регистра используется в неизменном виде, а небезопасной версии – в зависимости от состояния бита AIRCR.PRIS. Если этот бит сброшен, значение BASEPRI_NS также используется в неизменном виде. Если же этот бит установлен, исходное значение BASEPRI_NS сдвигается вправо на 1 бит (т. е. делится на 2), после чего производится установка старшего бита (исходный приоритет 0 превращается в 128, 64 – в 192, и т. д.).
  • Если расширение безопасности отсутствует, установленный бит PM регистра PRIMASK повышает текущий приоритет выполнения до нуля, т. е. запрещает все прерывания с программируемым приоритетом (разрешёнными остаются лишь HardFault, NMI и сброс). Если этот бит сброшен, он не влияет на определение текущего приоритета выполнения. Заметим, что установленный бит PM не препятствует прерыванию вывести процессор из ожидания, начатого командой WFI, но запрещает после пробуждения переходить к обработке данного прерывания: вместо этого процессор продолжит выполнять команды, следующие за WFI.
Если расширение безопасности имеется, то бит PM безопасной версии регистра PRIMASK ведёт себя так, как описано выше. Влияние небезопасной версии этого бита зависит от состояния бита AIRCR.PRIS: если последний сброшен, то установка PRIMASK_NS.PM повышает приоритет выполнения до нуля, если установлен – до 128.
  • Если расширение безопасности отсутствует, установленный бит FM регистра FAULTMASK (отсутствует в версиях ARMv8-M без основного расширения и ARMv6-M) повышает текущий приоритет до –1, запрещая все прерывания, кроме сброса и NMI. Он может быть установлен лишь при текущем уровне приоритета, численно равным или большим нуля; попытка установить этот бит в обработчике HardFault или NMI игнорируется. Если этот бит сброшен, он не участвует в определении текущего приоритета. Завершение обработки прерывания, за исключением возврата из NMI, автоматически сбрасывает бит FM.
Если расширение безопасности имеется, то процессор располагает двумя версиями регистра FAULTMASK. Установка безопасной версии бита FM повышает приоритет выполнения до –1, если бит AIRCR.BFHFNMINS сброшен, и до –3, если указанный бит установлен. Влияние установленной небезопасной версии бита FM зависит от состояния двух управляющих битов: AIRCR.BFHFNMINS и AIRCR.PRIS. Если первый из них установлен, приоритет выполнения повышается до –1; если он сброшен, то при сброшенном бите AIRCR.PRIS приоритет повышается до 0, а при установленном – до 128.

Итоговый приоритет выполнения равен численно наименьшему из всех значений, сформированных описанными выше способами.

Повышение приоритета запроса прерывания

Когда текущий приоритет выполнения ниже, чем HardFault (–1), процессор может поднять приоритет до этого уровня и войти в обработчик серьёзного отказа, если:

  • произошёл синхронный или асинхронный отказ с настраиваемым приоритетом, обработка которого запрещена;
  • групповой приоритет возникшего синхронного отказа или вызова супервизора меньше либо равен текущему приоритету выполнения, что не позволяет вызвать соответствующий обработчик обычным образом (прервав текущий выполняющийся код). Под этот случай подпадает также выполнение команды BKPT, но не другие прерывания, вызываемые отладочным монитором.

Если любое из этих событий произошло в обработчике HardFault или NMI, вызов обработчика серьёзного отказа невозможен; вместо этого процессор входит в состояние блокировки. Предполагается, что при нормальной работе подобные ситуации возникать не должны.

Заметим, что приоритет запросов внешних прерываний никогда не повышается: если прерывание запрещено, его запрос запоминается, но не влияет на выполнение программы, а если разрешено – ожидает, пока текущий приоритет выполнения не окажется существенно ниже приоритета данного запроса, после чего последний начинает обрабатываться.

Возникающие асинхронные отказы (к которым относится лишь неточный отказ шины) вызывают повышение приоритета запроса и вход в обработчик HardFault лишь в случае, если их обработка запрещена. Если обработка разрешена, но блокируется высоким текущим приоритетом выполнения, асинхронный отказ будет ожидать понижения приоритета выполнения, после чего произойдёт нормальный вызов его обработчика.

Адрес возврата из обработчика прерывания HardFault, вызванного в результате повышения приоритета запроса другого прерывания, определяется по правилам, относящимся к исходному запросу, а не к серьёзному отказу (например, для UsageFault он будет соответствовать команде, вызвавшей отказ, а для SVCall – команде, следующей за командой SVC).

Если процессор располагает сопроцессором с плавающей запятой, правила повышения приоритета во время сохранения контекста сопроцессор отличаются от вышеописанных (см. ниже).

Управление прерываниями

Управление обработкой прерываний осуществляется с помощью ряда регистров, которые можно разделить на несколько групп.

Первую группу образуют входящие в состав ядра процессора регистры IPSR, PRIMASK, FAULTMASK и BASEPRI (два последних в версиях ARMv8-M без основного расширения и ARMv6-M отсутствуют). Все они доступны привилегированному коду с помощью команд MSR и MRS; кроме того, состояние битов масок в регистрах PRIMASK и FAULTMASK может изменяться командой CPS. Первый из перечисленных регистров доступен только для чтения и содержит номер текущего обрабатываемого прерывания, три остальных позволяют изменить текущий приоритет выполнения и тем самым разрешить или запретить обработку прерываний с определёнными приоритетами.

Во вторую группу входят регистры пространства управления системой (SCS), из которых к управлению прерываниями относятся следующие:

  • VTOR — содержит базовый адрес таблицы векторов прерываний;
  • ICSR — позволяет получить информацию об активных и ожидающих прерываниях, а также программно запросить прерывания NMI, SysTick и PendSV и сбросить запросы двух последних прерываний;
  • AIRCR — управляет группированием приоритетов и поведением при сбросе, а также позволяет узнать порядок следования байтов, используемый для хранения информации в памяти;
  • SCR — управляет влиянием прерываний на режимы сна;
  • CCR — позволяет разрешить или запретить генерацию прерываний при делении на 0 и нарушении выравнивания, а также BusFault при текущем уровне привилегий –1 и выше;
  • SHPR1–SHPR3 — управляют приоритетами стандартных прерываний;
  • SHCSR — хранит информацию об активных и ожидающих прерываниях, а также разрешает или запрещает прерывания UsageFault, BusFault и MemManage;
  • STIR — позволяет программно вызвать появление запроса внешнего прерывания.

Третья группа представлена регистрами контроллера прерываний NVIC, а четвёртая — регистрами системного таймера SysTick. Они применяются для управления прерываниями от внешних устройств и от таймера соответственно.

Обработчики прерываний и расширение безопасности

Синхронные прерывания HardFault, MemManage, UsageFault, SVCall, PendSV и SysTick имеют отдельные обработчики для безопасного и небезопасного режимов. Как правило, вызывается обработчик того режима, в котором возникло данное прерывание. Однако из этого правила имеются описываемые далее исключения.

  • Если использование некоторого сопроцессора запрещено только соответствующим битом регистра NSACR, но не регистром CPACR, попытка небезопасного кода обратиться к этому сопроцессору вызовет прерывание UsageFault безопасного режима.
  • Если доступ к некоторому сопроцессору запрещён регистром CPPWR, возникающее при попытке доступа прерывание UsageFault адресуется либо всегда безопасному режиму (если соответствующий бит SUS указанного регистра установлен), либо тому режиму безопасности, в котором была предпринята данная попытка доступа.
  • Прерывания, возникающие при попытке выборки команды, адресуются состоянию безопасности, связанному с адресом команды, а не текущему состоянию безопасности.
  • Если разрешена небезопасная обработка прерываний HardFault (бит AIRCR.BFHFNMINS установлен), то:
  • HardFault, являющийся результатом возникновения другого синхронного прерывания, которое не может быть обработано (см. выше), обрабатывается в том режиме безопасности, в котором возникло исходное прерывание;
  • HardFault, возникший в результате невозможности выборки вектора прерывания, адресуется тому состоянию безопасности, к которому относится прерывание, чей вектор не удалось выбрать.
  • Отказы, вызванные сохранением регистров вызываемой программы, всегда передаются безопасному обработчику.
  • Если небезопасная обработка HardFault запрещена (бит AIRCR.BFHFNMINS сброшен), возникновение небезопасного HardFault приводит к возникновению безопасного HardFault, при этом биты ожидающих прерываний небезопасного режима ведут себя так, как будто они сброшены, за исключением случаев их программного считывания или записи.
  • Если данная реализация имеет два таймера, прерывание от таймера адресуется соответствующему ему режиму безопасности, а не текущему режиму.

Прерывания NMI и BusFault передаются режиму безопасности, указанному битом AIRCR.BFHFNMINS (0 – всегда безопасному, 1 – текущему).

Прерывание SecureFault всегда адресуется безопасному обработчику.

Прерывание DebugMonitor адресуется безопасному режиму, если бит DEMCR установлен, и небезопасному режиму, если этот бит сброшен.

Каждое внешнее прерывание адресуется тому режиму безопасности, который задан соответствующим битом соответствующего регистра ITNS.

Если любое из прерываний NMI, BusFault, DebugMonitor или внешнее, а также прерывание от таймера, если имеется только таймер безопасного режима, адресуется безопасному режиму, то связанные с этим прерыванием поле приоритета и биты разрешения, ожидания и активности в небезопасном режиме будут равны нулю, а любая попытка их изменения игнорируется.

Изменение адресации прерываний должно выполняться, когда соответствующие прерывания не находятся в активном состоянии или состоянии ожидания обработки.

Начало и завершение обработки прерывания

Вход в обработчик прерывания

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

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

Вход в обработчик прерывания производится, когда ожидающий запрос прерывания не запрещён и его фактический (с учётом возможного влияния расширения безопасности на приоритеты небезопасных прерываний) групповой приоритет выше (численно – меньше), чем текущий приоритет выполнения. В целом для входа в обработчик выполняются следующие действия.

  • В текущем стеке сохраняется контекст прерванного кода. Объём сохраняемых данных зависит от особенностей процессора и от условий, в которых произошло прерывание.
  • В процессорах с расширением безопасности может быть выполнена очистка регистров процессора и FPU.
  • В регистр LR заносится код EXC_RETURN, обеспечивающий возврат из вызываемого обработчика.
  • Регистры управления и состояния модифицируются для обеспечения выполнения обработчика.
  • В процессорах с расширением безопасности устанавливается режим безопасности, нужный для выполнения вызываемого обработчика.
  • Сбрасывается признак ожидания для данного прерывания и устанавливается признак его активности.
  • Из таблицы векторов загружается адрес точки входа в обработчик, информация из которого заносится в PC и EPSR.T, после чего начинается выполнение обработчика.

Сохранение контекста прерванного кода

Сохраняемый при входе в обработчик прерывания контекст в общем случае состоит из четырёх частей:

  • основной контекст процессора (state context);
  • дополнительный контекст процессора (additional state context, только ARMv8-M с расширением безопасности);
  • основной контекст FPU (FP context, только процессоры с FPU или MVE);
  • дополнительный контекст FPU (additional FP context, только ARMv8-M с расширением безопасности и FPU/MVE).

Сохранение контекста производится в стеке прерванного кода. Место под него выделяется путём уменьшения текущего значения SP на число байтов, необходимое для сохранения контекста. Если значение SP на момент начала входа в обработчик прерывания не кратно 8, может быть выполнено его принудительное выравнивание, для чего SP дополнительно уменьшается на 4 (два младших бита SP всегда нулевые, поэтому кратность меньше 4 невозможна). Такое выравнивание выполняется в двух случаях:

  • если бит CCR.STKALIGN установлен (это всегда так в версиях ARMv6-M и ARMv8-M; версия ARMv7-M разрешает программно изменять значение этого разряда, но конкретная реализация может использовать фиксированное значение 1);
  • если необходимо сохранить контекст FPU или зарезервировать место под него.

Основной контекст процессора, занимающий восемь слов, сохраняется всегда. В эту область записывается содержимое регистров R0–R3, R12 и LR прерванного кода, адрес возврата к нему и используемое при возврате из обработчика значение PSR; последнее в документации на ARMv8-M носит обозначение RETPSR.

Адрес возврата, сохраняемый в области основного контекста, зависит от причины прерывания:

  • для любого синхронного прерывания, кроме SVCall (и HardFault, произошедшего вместо SVCall из-за необходимости повышения приоритета SVCall до HardFault), сохраняется адрес команды, вызвавшей данное прерывание;
  • для SVCall (и HardFault, произошедшего вместо SVCall) сохраняется адрес команды, следующей за SVC;
  • для любых асинхронных прерываний сохраняется адрес следующей подлежащей выполнению команды.

Младший бит в сохранённом адресе возврата всегда равен нулю.

RETPSR представляет собой копию значения XPSR (т. е. объединённого регистра состояния программы, включающего значения APSR, IPSR и EPSR). Кроме того, в RETPSR используются ещё два бита, которые в «настоящем» XPSR не используются:

  • бит 9 (SPREALIGN) устанавливается, если при выделении места в стеке под область сохранения SP был дополнительно уменьшен на 4 для обеспечения выравнивания. Если дополнительное уменьшение на 4 не производилось, этот бит будет сброшен;
  • бит 20 (SFPA) используется для сохранения значения бита CONTROL_S.SFPA, если был прерван безопасный код. Если был прерван небезопасный код, этот бит будет равен нулю.

Дополнительный контекст процессора, занимающий десять слов, сохраняется, если на процессоре с расширением безопасности прерывается безопасный код и производится вызов небезопасного обработчика. Кроме того, в зависимости от реализации он может сохраняться, если прерывается безопасный код и вызывается безопасный разработчик. При прерывании небезопасного кода дополнительный контекст не сохраняется.

В области дополнительного контекста процессора сохраняются регистры R4–R11 прерванного кода и так называемая сигнатура целостности (integrity signature).

Основной контекст FPU (18 слов) сохраняется или место под него резервируется, если процессор имеет FPU или MVE и бит CONTROL.FPCA установлен (т. е. если с момента последнего сохранения содержимое регистров FPU изменялось и разрешено автоматическое сохранение контекста FPU, что задаётся установленным битом FPCCR.ASPEN).

Область основного контекста FPU предназначена для сохранения регистров S0–S15, FPSCR и, если имеется MVE, VPR.

Процессор с расширением безопасности выполняет проверку на разрушение предыдущего контекста FPU. Для этого он проверяет состояние бита LSPACT версии регистра FPCCR, соответствующей состоянию безопасности, заданному битом FPCCR_S.S (т. е. если последний установлен, проверяется бит FPCCR_S.LSPACT, а если сброшен – бит FPCCR_NS.LSPACT). Бит FPCCR.LSPACT устанавливается при входе в обработчик прерывания, если в это время планируется отложенное сохранение (при этом бит CONTROL.FPCA сбрасывается), а сбрасывается при фактическом сохранении контекста FPU или при возврате из данного обработчика прерывания, если нужды в сохранении контекста так и не возникло. Одновременно установленными биты FPCCR.LSPACT и CONTROL.FPCA быть не должны, так как это означает, что подлежащие сохранению регистры FPU были изменены без выполнения сохранения, т. е. что контекст FPU был разрушен. В такой ситуации место под контекст FPU выделяется, однако его сохранение не выполняется; вместо этого возникает прерывание SecureFault с установкой бита SFSR.LSERR. Заметим, что в процессорах архитектуры ARMv7-M, а также в ARMv8-M без расширения безопасности такая проверка не выполняется.

Если при установленном бите CONTROL.FPCA процессор находится в небезопасном режиме и бит NSACR.CP10 сброшен (доступ небезопасного кода к FPU запрещён), место под контекст FPU не выделяется и возникает безопасное прерывание UsageFault с установкой бита UFSR_S.NOCP. Эта проверка не выполняется в версии ARMv7-M, поскольку там нет ни регистра NSACR, ни разделения на безопасный и небезопасный режимы. В версии ARMv8-M без расширения безопасности данная проверка формально всё равно выполняется, однако она всегда будет успешной, поскольку в таких процессорах регистр NSACR доступен только для чтения и разрешает небезопасному коду свободное использование сопроцессоров.

Если описанных выше ошибок обнаружено не было, а бит FPCCR.LSPEN установлен, планируется отложенное сохранение, фактически выполняемое уже после входа в обработчик прерывания и лишь в случае, если в нём действительно возникнет нужда. Если же указанный бит сброшен, процессор пытается выполнить немедленное сохранение контекста FPU. Если это невозможно из-за того, что регистр CPACR запрещает доступ к FPU, сохранение не производится и формируется прерывание UsageFault с установкой бита UFSR.NOCP.

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

Дополнительный контекст FPU (16 слов) сохраняется только процессором с расширением безопасности, если был прерван безопасный код и биты CONTROL.FPCA и FPCCR_S.TS установлены. Область под дополнительный контекст FPU не выделяется, если не удалось выделить место под основной контекст FPU из-за отсутствия доступа небезопасного кода к сопроцессору, как описано выше.

В области дополнительного контекста FPU сохраняются его регистры S16–S31. Их сохранение выполняется одновременно с сохранением основного контекста FPU, т. е. может производиться либо в момент вызова обработчика, либо позже.

Сохранение всех частей контекста ведётся с использованием режимов и стека прерванного кода. Это относится и к отложенному сохранению, для чего в регистре FPCCR предусмотрены биты, отражающие состояние процессора в момент выделения памяти под сохранение контекста.

Контекст не сохраняется или сохраняется лишь частично, если попытка сохранения вызывает отказ; в таком случае порождается соответствующий запрос на прерывание (MemFault, BusFault или HardFault; кроме того, возможна блокировка процессора). Однако, если отказ фиксируется из-за нарушения пределов стека, то, согласно псевдокоду в документации на ARMv8-M, контекст всё равно сохраняется в полном объёме (т. е. происходит затирание информации, размещающейся ниже нижней границы стека, если сама по себе запись по этим адресам не вызывает отказа BusFault или MemManage) и лишь после этого формируется запрос на прерывание UsageFault с установкой бита UFSR.STKOF, при этом в SP заносится значение, равное нижнему пределу стека, а не истинному пололжению сформированного и записанного в память кадра стека. Заметим, что в процессоре с основным расширением контроль границ стека может быть отключён для прерываний NMI и HardFault установкой бита CCR.STKOFHFNMIGN соответствующего режима безопасности.

Если при сохранении контекста возник какой-либо отказ при доступе к памяти (MemFault/BusFault) и одновременно была обнаружена ошибка, связанная с разрушением контекста FPU (SecureFault) или с невозможностью доступа к FPU со стороны небезопасного кода (UsageFault), в зависимости от реализации могут быть приняты либо оба возникших запроса прерывания, либо лишь запрос на SecureFault/UsageFault. Запрос на UsageFault по переполнению стека принимается всегда.

Архитектура задаёт порядок следования сохраняемых значений в памяти, но не порядок их фактической записи.

Структура области сохранения, сформированная при вызове данного обработчика, отражается значением EXC_RETURN, передаваемым обработчику через регистр LR (см. ниже).

На следующем рисунке представлена область сохранения контекста, включающая все четыре описанные выше части для архитектуры ARMv8-M, по этой причине поле, отведённое в ARMv8.1-M с MVE для сохранения регистра VPR, отмечено на нём как зарезервированное (смещение 8C). Заметим, что указатель стека прерванного кода, обновлённый в процессе входа в обработчик для выделения места под сохраняемый контекст, в данном случае указывает на начало области дополнительного контекста процессора. Если эта область не сохраняется, он будет указывать на область основного контекста процессора.

ARM M-profile exception context saving area

Инициализация регистров для вызываемого обработчика

Если при сохранении контекста было выполнено в том числе фактическое сохранение контекста FPU (т. е. если бит CONTROL.FPCA на момент вызова обработчика был установлен, а бит FPCCR.LSPEN – сброшен), был прерван безопасный код и бит FPCCR_S.TS установлен, выполняется обнуление регистров S0–S31, FPSCR и, при наличии MVE, регистра VPR. Если был прерван небезопасный код либо если бит FPCCR_S.TS сброшен, регистры S16–S31 не изменяются, а состояние остальных перечисленных регистров не определено (в частности, оно может остаться неизменным). В процессорах без расширения безопасности (в частности, в любых процессорах архитектуры ARMv7-M) любой код считается небезопасным. Изменение регистров не производится, если выполнить сохранение не удалось из-за отсутствия доступа к FPU.

Если же при сохранении контекста место под контекст FPU было успешно выделено, а само сохранение было отложено из-за установленного бита FPCCR.LSPEN, производится запоминание информации, необходимой для последующего отложенного сохранения контекста FPU:

  • в регистр FPCAR заносится адрес начала выделенной области сохранения контекста FPU;
  • если область сохранения контекста FPU нарушает нижнюю границу стека, устанавливается бит FPCCR.SPLIMVIOL; если нарушения нет, этот бит сбрасывается. Данная проверка осуществляется только в ARMv8-M. Она может быть заблокирована для NMI и HardFault установкой бита CCR.STKOFHFNMIGN. Заметим, что нарушение границы стека областью основного или дополнительного контекста процессора не проверяется;
  • биты FPCCR_S.S, FPCCR.USER и FPCCR.THREAD регистра FPCCR устанавливаются в соответствии с режимами выполнения прерванного кода;
  • бит FPCCR.HFRDY (он общий для обоих режимов безопасности) устанавливается, если на момент прерывания вход в обработчик HardFault возможен, т. е. если текущий приоритет выполнения численно больше, чем –1. Если вход невозможен, этот бит сбрасывается;
  • бит FPCCR.BFRDY (он общий для обоих режимов безопасности) устанавливается, если на момент прерывания вход в обработчик BusFault возможен, и сбрасывается в противном случае. Возможность входа определяется маской этого прерывания (бит SHCSR.BUSFAULTENA, общий для обоих режимов безопасности) и соотношением приоритета выполнения прерванного кода и приоритета прерывания BusFault для того режима безопасности, в котором оно должно обрабатываться в соответствии с состоянием бита AIRCR.BFHFNMINS;
  • бит FPCCR.MMRDY устанавливается, если на момент прерывания вход в обработчик MemManage возможен, и сбрасывается в противном случае. Возможность входа определяется состоянием бита маски SHCSR.MEMFAULTENA и приоритетом обработчика MemManage для режима безопасности прерванного кода;
  • изменяются биты FPCCR.UFRDY для обоих режимов безопасности. Каждый из них устанавливается, если в соответствующем режиме на момент прерывания вход в обработчик UsageFault возможен, и сбрасывается в противном случае. Возможность входа определяется битом маски SHCSR.USGFAULTENA и приоритетом обработчика UsageFault для соответствующего режима безопасности;
  • если процессор имеет расширение безопасности, то бит FPCCR_S.SFRDY устанавливается, если на момент прерывания вход в обработчик SecureFault возможен, и сбрасывается в противном случае. Возможность входа определяется битом маски SHCSR_S.SECUREFAULTENA и приоритетом обработчика SecureFault;
  • бит FPCCR.MONRDY (он общий для обоих режимов безопасности) устанавливается, если на момент прерывания вход в обработчик DebugMon возможен, и сбрасывается в противном случае. Вход в этот обработчик невозможен в следующих случаях:
  • отладочный монитор в данной реализации не поддерживается;
  • в момент прерывания разрешён отладочный останов процессора (с учётом режимов привилегированности и безопасности прерванного кода);
  • вызов монитора запрещён вообще (в процессоре без UDE сброшен бит DEMCR.MON_EN; в процессоре с UDE сброшен и этот бит, и бит DEMCR.UMON_EN) или только для безопасного состояния (бит DEMCR.SDME сброшен), а процессор находится в этом состоянии;
  • приоритет обработчика прерывания DebugMon ниже или равен текущему приоритету выполнения, т. е. обработчик DebugMon не может быть вызван (проверка проводится с учётом того, в безопасном или небезопасном режиме должен выполняться обработчик DebugMon, что управляется битом DEMCR.SDME).

Указатель стека прерванного кода уменьшается на размер выделенной под сохранение всех элементов контекста области. Однако в случае процессоров архитектуры ARMv8-M (т. е. процессоров с контролем выхода за пределы стека), если фиксируется нарушение границ стека, SP устанавливается равным нижней границе стека, что, по сути, лишает возможности определить местоположение не только начала текущего сохранённого контекста (сохранение основного контекста процессора, судя по документации на ARMv8-M, выполняется независимо от нарушения границ стека), но и любых предыдущих данных в стеке.

В регистр LR заносится код возврата из прерывания – так называемый EXC_RETURN. Он имеет следующий вид:

ARM M-profile EXC RETURN
  • Биты 31:24 все равны 1, что определяет, что данный код – EXC_RETURN.
  • Биты 23:7 устанавливаются равными 1, но сами по себе они являются зарезервированными и при возврате из прерывания могут игнорироваться.

Возникновение прерываний во время входа в обработчик прерывания

Во время выполнения входа в обработчик прерывания могут появиться следующие запросы прерываний:

  • асинхронное внешнее прерывание от какого-либо устройства ввода-вывода;
  • отладочная точка наблюдения (при запрещённом отладочном останове);
  • прерывания MemManage и BusFault (или HardFault, если первые два не поддерживаются), связанные с попыткой сохранения контекста прерванного кода в его стеке;
  • в процессорах с расширением безопасности и FPU/MVE – прерывание SecureFault или UsageFault, возникающее при анализе состояния FPU перед немедленным выполнением сохранения его контекста или перед активацией отложенного сохранения;
  • в процессорах версии ARMv8-M – прерывание UsageFault (или HardFault, если UsageFault не поддерживается) по переполнению стека;
  • прерывание BusFault (или HardFault), связанное с попыткой выборки адреса точки входа из таблицы векторов прерываний.

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

  • немедленно после входа в обработчик возникнет новое прерывание, в результате чего вход в обработчик будет повторён уже для нового прерывания. Оба прерывания получат статус активных. Работа первоначального обработчика начнётся, когда завершится выполнение нового обработчика;
  • после сохранения контекста прерванного кода произойдёт вход не в первоначальный, а в новый обработчик. Прерывание, послужившее первопричиной начала процедуры входа, сохранит статус ожидающего; активным станет лишь новое, более приоритетное прерывание. После того, как его обработчик завершится, произойдёт вызов обработчика для ожидающего прерывания (как правило, с помощью сцепления прерываний).

Поведение процессора при остальных прерываниях зависит от текущего приоритета выполнения прерванного (вытесненного) кода, приоритета исходного прерывания (которое послужило причиной начала процедуры входа в обработчик и чей приоритет, следовательно, существенно выше, чем приоритет прерванного кода) и приоритета порождённого прерывания (возникшего вследствие выполнения процедуры входа в обработчик исходного прерывания). Кроме того, если одновременно возникли условия прерываний из-за отказа при сохранении контекста процессора и из-за недопустимого состояния FPU, что влечёт за собой SecureFault или UsageFault, то в зависимости от реализации либо сохраняются оба запроса на прерывание, либо только запрос, вызванный состоянием FPU (в последнем случае запрос прерывания MemManage или BusFault теряется).

Точка наблюдения во время выполнения входа в обработчик может сработать, если она установлена для слежения за областью памяти, в которой выполняется сохранение контекста, или в других подобных случаях. Ошибкой это прерывание не является. Если приоритет обработчика отладочного прерывания (оно отвечает в том числе за обработку событий, связанных с точками наблюдения) ниже или равен приоритету исходного прерывания, то прерывание по точке наблюдения просто игнорируется и управление получает обработчик исходного прерывания. Если же приоритет прерывания по точке наблюдения выше приоритета исходного прерывания, то исходное прерывание получит статус ожидающего, а отладочное прерывание станет активным.

Два последних случая указывают на ошибку в процессе входа в обработчик. Ошибкам MemManage и BusFault, связанным с сохранением контекста, будет присвоен класс MSTKERR и STKERR соответственно, что найдёт своё отражение в регистрах состояния прерываний. Прерывание BusFault, связанное с попыткой считывания адреса обработчика из таблицы векторов прерываний, всегда рассматривается как серьёзный отказ и обрабатывается не как BusFault, а как HardFault.

Если приоритет прерывания, порождённого в результате ошибки, ниже или равен приоритету прерванного кода, т. е. если обработчик порождённого прерывания в соответствии с обычными правилами приоритетов немедленно вызван быть не может, произойдёт его повышение до уровня HardFault (приоритет –1). Исходное прерывание получит статус ожидающего и произойдёт вход в обработчик серьёзного отказа.

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

Наконец, если приоритет порождённого прерывания выше приоритета исходного прерывания, последнее получит статус ожидающего, после чего произойдёт вход в обработчик порождённого прерывания. Обработчик исходного прерывания получит управление лишь после завершения обработчика порождённого прерывания.


Если прерывание является небезопасным, однако выбранный вектор указывает на безопасную область памяти, возникает прерывание SecureFault, при этом устанавливается бит INVEP. Этого, однако, не происходит, если вектор указывает на команду SG, находящуюся в безопасной области, допускающей вызов из небезопасного кода.

Если прерывание является безопасным, а выбранный вектор указывает на небезопасную область памяти, возникает прерывание SecureFault, при этом устанавливается бит INVTRAN.

Завершение обработчика прерывания

Завершение обработчика прерывания и возврат к прерванному коду происходит, когда в режиме обработчика значение, содержащее 1 в четырёх старших разрядах, загружается в счётчик команд с помощью одной из следующих команд: POP, LDM, LDR или BX. Такое специальное значение носит название EXC_RETURN. Загрузка численно идентичного значения любым другим способом или в режиме потока не является завершением обработчика прерывания, а трактуется как обычный переход. Поскольку все адреса в диапазоне от F0000000 до FFFFFFFF не являются обычной памятью и не допускают выборку команд для исполнения, переход на любой подобный адрес вызывает прерывание UsageFault (HardFault в архитектуре ARMv6-M).

Разряды EXC_RETURN имеют следующие функции:

  • биты 31:28, равные 1, являются признаком операции возврата из обработчика прерывания;
  • биты 27:5 зарезервированы и должны быть равны 1. Если какой-либо из них будет равен нулю, результат окажется непредсказуемым;
  • бит 4 равен нулю, если при сохранении контекста в стеке выделялось место под регистры FPU, и равен единице, если место выделялось лишь под основной контекст;
  • бит 3 указывает, в каком режиме должен находиться процессор после завершения обработчика: 0 соответствует режиму обработчика, 1 — режиму потока;
  • бит 2 выбирает стек, в котором был сохранён контекст и который будет использован после выхода из данного обработчика: 0 соответствует основному стеку, 1 — стеку процесса. Если возврат происходит в режим обработчика, этот бит должен быть равен нулю;
  • бит 1 всегда равен нулю;
  • бит 0 всегда равен единице.

При завершении обработчика процессор версии ARMv7-M выполняет ряд проверок корректности возврата:

  • текущее прерывание (его номер находится в регистре IPSR) должно быть отмечено в соответствующем системном регистре как активное;
  • если активно несколько прерываний, после возврата из текущего обработчика процессор обычно должен оставаться в режиме обработчика. Если необходимо нарушить это правило, необходимо сначала установить бит NONBASETHRDENA регистра CCR;
  • при возврате в код режима потока восстанавливаемый IPSR должен иметь нулевое значение, а при возврате в код режима обработчика — ненулевое;
  • младшие четыре разряда EXC_RETURN должны быть выставлены корректно: допустимыми являются только комбинации 0001, 1001 и 1101.

Нарушение любого из этих правил вызывает прерывание UsageFault, при этом прерывание, нормально завершить обработчик которого не удалось, всё равно считается завершённым.

В версии ARMv6-M проверки не выполняются, а нарушение правил возврата из обработчика имеет непредсказуемые последствия.

После успешного возврата из обработчика прерывания:

  • регистры R0–R3, R12, LR, PC и XPSR восстановлены из области сохранения контекста в стеке. Если считанное содержимое PC (адрес возврата) не выровнено на границу полуслова, поведение процессора непредсказуемо;
  • регистры FPU S0–S15 и FPSCR восстановлены из стека, если под них выделялась память (EXC_RETURN[4] равен нулю) и сохранение действительно имело место (бит LSPACT регистра FPCCR сброшен). Если бит LSPACT на момент выхода из обработчика установлен, это означает, что сохранение контекста не потребовалось, поскольку при выполнении завершающегося обработчика обращений к FPU не было; в этом случае LSPACT просто сбрасывается. Если же EXC_RETURN[4] равен единице, это означает, что память в стеке под контекст FPU не выделялась;
  • указатель стека, активного после выхода из обработчика, скорректирован таким образом, чтобы удалить область сохранения контекста, из которой были восстановлены другие регистры, при этом учитывается факт дополнительного уменьшения SP на 4, когда оно выполнялось (бит 9 сохранённого значения XPSR установлен);
  • бит FM регистра FAULTMASK сброшен, за исключением возврата из обработчика NMI, когда он не изменяется;
  • разряды регистра CONTROL находятся в состоянии, соответствующем значению EXC_RETURN: FPCA = not EXC_RETURN[4], SPSEL = EXC_RETURN[2]. Состояние nPRIV не изменяется;
  • локальный монитор синхронизации, используемый командами LDREX и STREX, сброшен;
  • выполнен барьер синхронизации команд;
  • если необходимо, процессор переведён в сон (см. описание команды WFI и бита SLEEPONEXIT регистра CCR).

Возникновение прерывания при выходе из обработчика прерывания

Прерывание может возникнуть и во время выполнения процедуры возврата из обработчика. Как и в случае со входом в обработчик, его причиной может послужить внешнее асинхронное прерывание, срабатывание точки наблюдения либо какая-либо ошибка, связанная с попыткой восстановления контекста.

В случае возникновения внешнего прерывания, чей приоритет выше, чем приоритет выполнения кода, к которому происходит возврат, вместо возврата имеет место сцепление прерываний, в результате чего управление получит обработчик нового прерывания.

Прерывание по точке наблюдения будет проигнорировано, если его приоритет ниже либо равен приоритету кода, к которому производится возврат; в противном случае произойдёт сцепление прерываний и управление получит обработчик отладочного прерывания.

Для прерываний MemManage и BusFault, связанных с попыткой восстановления контекста, в регистрах состояния прерываний будет установлен класс MUNSTKERR и UNSTKERR соответственно. Если приоритет соответствующего обработчика выше, чем приоритет кода, к которому осуществляется возврат, произойдёт сцепление прерываний и управление получит обработчик ошибки. Если же его приоритет ниже или равен приоритету кода, к которому выполняется возврат, произойдёт повышение приоритета до уровня HardFault (–1), после чего с помощью сцепления прерываний будет вызван обработчик серьёзного отказа.

Сцепление прерываний

Сцепление прерываний (tail-chaining) — это механизм, позволяющий сократить время переключения с одного обработчика прерывания на другой в момент завершения первого обработчика. Это достигается тем, что, обнаружив наличие ожидающего запроса прерывания с групповым приоритетом более высоким, чем приоритет выполнения кода, к которому осуществляется возврат (с учётом состояния регистров масок), процессор не выполняет возврат из прерывания с немедленным повторным входом в новый обработчик, а сразу передаёт управление новому обработчику, при этом в LR на момент входа в обработчик нового прерывания будет находиться значение EXC_RETURN, заданное при завершении предыдущего обработчика.

Дополнительная информация

Блокировка при невозможности входа в обработчик прерывания

Если при выполнении кода с приоритетом –1 или выше (обработчика серьёзного отказа или NMI, а также любого кода при установленном бите FM регистра FAULTMASK) возникнет прерывание, оно не сможет быть обработано, поскольку приоритет его обработчика не может быть выше 0, а с помощью повышения его приоритета нельзя получить приоритет выше –1. В этом случае процессор выполняет следующие действия:

  • устанавливает бит S_LOCKUP в регистре состояния и управления отладочными остановами DHCSR;
  • устанавливает бит регистра состояния прерываний, отвечающий за причину, приведшую к возникновению прерывания, которое невозможно обработать;
  • зацикливается на команде, послужившей причиной возникновения прерывания, а если причиной была не команда, а выполнение какого-либо другого действия, например, попытка сохранения или восстановления контекста, — на «команде», выбираемой по адресу FFFFFFFE. Адрес, на котором произошло зацикливание, называется адресом блокировки (lockup address).
В архитектуре ARMv6-M зацикливание всегда происходит на адресе FFFFFFFE.

Блокировка с зацикливанием по адресу FFFFFFFE происходит по следующим причинам:

  • неудачная попытка считывания адреса или начального значения SP при сбросе;
  • неудачная попытка считывания вектора NMI или HardFault;
  • BusFault при попытке сохранения или восстановления контекста;
  • MemManage при попытке сохранения или восстановления контекста;
  • UsageFault при возврате из прерывания в связи с недопустимым значением PC.

Блокировка с зацикливанием по адресу команды, вызвавшей ошибку, происходит по следующим причинам:

  • BusFault при попытке выборки команды. Если через некоторое время причина возникновении ошибки будет устранена (например, первопричиной ошибки шины послужило «сонное» состояние памяти), эта ошибка будет автоматически устранена, и процессор, выбрав команду, продолжит нормальную работу;
  • точный BusFault при обращении к данным. Эта ошибка будет проигнорирована, если установлен бит BFHFNMIGN в регистре CCR (процессор ограничится лишь установкой индикатора ошибки в соответствующем регистре состояния). Как и в предыдущем случае, блокировка может быть снята, если ошибка через некоторое время исчезнет;
  • MemManage при попытке выборки команды. Эта ошибка всегда приведёт к зацикливанию по адресу команды, если производится попытка её выборки из адресного пространства, недоступного для исполнения, однако может быть проигнорирована в остальных случаях, если установлен бит HFNMIENA регистра MPU_CTRL;
  • MemManage при попытке доступа к данным. Эта ошибка игнорируется, если установлен бит HFNMIENA регистра MPU_CTRL;
  • попытка выполнения команды SVC. Сама команда в этом случае рассматривается как неопределённая и никаких действий, кроме зацикливания, не вызывает;
  • UsageFault, связанный с какой-либо конкретной командой;
  • срабатывание точки останова.

Вывод из блокировки, за исключением автоматического продолжения выполнения при устранении причины отказа шины при выборке команды или обращении к данным, возможен лишь следующими способами:

  • если зацикливание имеет место при приоритете –1, NMI вызовет обычный вход в обработчик NMI, имеющий более высокий приоритет (–2). Адресом возврата из этого обработчика будет адрес блокировки;
  • с помощью сброса, в том числе от сторожевого таймера;
  • путём останова внешним отладчиком.

Прерываемые команды

Некоторые команды могут быть реализованы как прерываемые. В случае поступления прерывания во время выполнения такой команды она будет прервана, причём информация о том, какая часть уже проделана, будет сохранена в поле ICI регистра EPSR. К числу потенциально прерываемых относятся команды LDM, STM, POP, PUSH, VLDM, VSTM, VPOP и VPUSH (последние четыре относятся к набору команд FPU). После возврата из прерывания выполнение прерываемой команды возобновится с точки, где оно было прервано. В других реализациях при возникновении прерывания выполнение перечисленных команд может прекращаться, а после возврата из прерывания — начинаться с самого начала.

Если прерываемая команда встречается внутри блока IT, в регистре EPSR будет сохранено состояние выполнения этого блока (поле IT), а не состояние прерываемой команды. В этом случае выполнение последней после возврата из обработчика прерывания начнётся с самого начала.

Из-за того, что способ возобновления перечисленных команд зависит от места их появления и конкретной реализации, использовать их для доступа к строго упорядоченной памяти или памяти устройств нельзя.

Отложенное сохранение контекста FPU

Если в момент входа в обработчик прерывания бит CONTROL.FPCA установлен (его установленное состояние означает, что после последнего сохранения контекста FPU содержимое его регистров изменилось, т. е. выполнялись какие-либо команды FPU или векторного расширения), процессор выделяет в стеке прерванного кода место под сохранение основного и, возможно, дополнительного контекста FPU. Если при этом сброшен бит FPCCR.LSPEN (т. е. если отложенное сохранение контекста FPU запрещено), при сохранении контекста процессора будет выполнено и сохранение контекста FPU. Если был прерван безопасный код и установлен бит FPCCR_S.TS, сохраняется также расширенный контекст FPU.

Если бит FPCCR.LSPEN установлен (отложенное сохранение разрешено), то в момент входа в обработчик в стеке выделяется место под контекст FPU (и под расширенный контекст FPU, если прерван безопасный код и установлен бит FPCCR_S.TS), однако само сохранение не производится. Вместо этого в регистре FPCAR сохраняется адрес начала области контекста FPU (адрес слова, в которое должен сохраняться регистр S0), а бит FPCCR.LSPACT устанавливается, отмечая активное состояние отложенного сохранения. Кроме того, в регистре FPCCR запоминается информация о режимах и разрешённых отказах на момент возникновения прерывания; эти сведения будут использованы в случае выполнения отложенного сохранения контекста FPU.

Если позднее при установленном бите FPCCR.LSPACT будет предпринята попытка выполнить какую-либо команду обработки вещественных чисел (FPU или MVE, включая коды команд, относящиеся к CP10, но не выделенные конкретным командам), перед её фактическим выполнение производится отложенное сохранение контекста FPU.

  • Основываясь на информации о режимах, активных на момент входа в обработчик прерывания, при котором было выделено место для отложенного сохранения контекста (она была занесена в соответствующие биты регистра FPCCR), проверяется возможность использования FPU для прерванного кода (для этого анализируются соответствующие разряды регистров CPACR и NSACR).
Если окажется, что использование FPU было запрещено, его контекст не сохраняется и возникнет прерывание UsageFault по недоступности сопроцессора. Однако, если программа пытается выполнить одну из команд FPU VLLDM, VLSTM или VSCCLRM, возникает прерывание UsageFault по неопределённой команде вместо прерывания по недоступности сопроцессора.
  • Если доступ к FPU возможен, выполняется сохранение его контекста по адресу, находящемуся в регистре FPCAR. Основной контекст сохраняется всегда, дополнительный – только если установлены биты FPCCR.TS и FPCCR_S.S.
Если в процессе сохранения контекста FPU были обнаружены какие-либо отказы, возникает соответствующее прерывание.
  • Если сохранение контекста выполнено успешно, бит FPCCR.LSPACT сбрасывается.
  • Если команда, инициировавшая выполнение сохранения контекста, выполняется в небезопасном состоянии, регистры FPU устанавливаются в неизвестное состояние, если в безопасном – очищаются.
  • Производится фактическое выполнение команды, вызвавшей сохранение контекста.
Advertisement