ФЭНДОМ


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

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

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

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

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

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

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

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

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

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

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

Номера и векторы исключений

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

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

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

Приоритеты исключений

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

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

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

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

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

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

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

Система приоритетов в версии ARMv6-M намного проще: в ней поддерживается только четыре уровня приоритетов без возможности их группировки (каждый уровень образует отдельную группу).

Каждое из трёх исключений с фиксированным приоритетом образует отдельную группу.

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

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

На минимально возможном приоритете процессор работает в режиме потока. «Арифметически» можно считать, что этот приоритет равен минимально возможному приоритету исключения плюс 1. Например, если реализация поддерживает все 8 разрядов приоритета исключения (максимальный — 0, минимальный – 255), минимально возможным приоритетом выполнения будет 256. Эта величина называется в документации базовым уровнем приоритета выполнения (base level of execution priority) или просто базовым приоритетом.

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

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

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

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

  • –1, если установлен бит FM регистра FAULTMASK;
  • 0, если установлен бит PM регистра PRIMASK;
  • приоритету, заданному в регистре BASEPRI, если его содержимое отлично от нуля и оно численно меньше, чем самый высокий из приоритетов активных исключений;
  • высшему из приоритетов активных исключений;
  • базовому приоритету выполнения потока.

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

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

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

Повышение приоритета запроса исключения

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

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

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

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

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

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

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

Управление исключениями

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

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

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

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

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

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

Вход в обработчик исключения

Перед входом в обработчик исключения производится сохранение контекста прерванного процесса. Для этого:

  • в текущем стеке путём уменьшения значения указателя стека резервируется место под область сохранения контекста. Длина этой области определяется по следующим правилам:
  • если использование FPU разрешено (бит FPCA регистра CONTROL установлен), резервируется 104 байта (26 слов). Кроме того, если SP не кратен 8, для обеспечения выравнивания на границу двойного слова он дополнительно уменьшается на 4;
  • если процессор не имеет FPU либо если его использование запрещено, резервируется 32 байта. Кроме того, если установлен бит STKALIGN регистра CCR, а SP не кратен 8, он дополнительно уменьшается на 4. В версии ARMv6-M бит STKALIGN всегда установлен; конкретные реализации версии ARMv7-M также могут иметь его постоянно установленным;
  • в выделенной области, начиная с её младшего слова (новой вершины стека), сохраняются регистры R0–R3, R12 и LR прерванного процесса, адрес возврата к нему (см. ниже) и содержимое регистра XPSR, всего 8 слов. В сохранённом значении XPSR дополнительно устанавливается бит 9, если при резервировании места потребовалось дополнительное уменьшение SP на 4 для обеспечения выравнивания;
  • если использование FPU разрешено, то:
  • при отключенном отложенном сохранении контекста FPU (бит LSPEN регистра FPCCR равен 0) выполняется его немедленное сохранение, начиная со смещения 32 от новой вершины стека (сразу вслед за сохранёнными на предыдущем этапе значениями). В область сохранения записываются регистры S0–S15 и FPSCR, всего 17 слов;
  • при включенном отложенном сохранении контекста FPU (бит FPCCR.LSPEN равен 1) немедленное сохранение не выполняется. Вместо этого в регистр FPCAR заносится адрес новой вершины стека плюс 32, т. е. адрес начала области сохранения контекста FPU, а в регистре FPCCR устанавливается бит LSPACT (признак активности отложенного сохранения контекста FPU). Кроме того, в регистре FPCCR устанавливаются или сбрасываются биты USER, THREAD, HFRDY, BFRDY, MMRDY, MONRDY, отражающие состояние процессора на момент сохранения контекста, т. е. перед входом в новый обработчик исключения. Если окажется необходимым, контекст FPU будет сохранён позже.

Формат сохранения контекста в зависимости от поддержки FPU и необходимости выравнивания приведён на следующих рисунках.

ARMv6-M and ARMv7-M context saving without FPU

Формат области сохранения контекста без FPU

ARMv6-M and ARMv7-M context saving with FPU

Формат области сохранения контекста с FPU

После сохранения контекста прерванного процесса производится переход к обработчику исключения. На момент входа в него:

  • процессор находится в режиме обработчика;
  • используется основной стек, указатель которого (SP_main, он же MSP) содержит последнее занесённое в него значение: если контекст сохранялся в стеке процесса (указатель SP_process, он же PSP), SP_main не изменился по сравнению с моментом начала перехода к обработке исключения, если же контекст сохранялся в основном стеке, последний был уменьшен на размер области сохранения контекста, как описывалось выше;
  • регистры R4–R11 содержат значения, находившиеся там на момент начала входа в обработчик. Если последний желает использовать эти регистры, он должен сначала сохранить их;
  • счётчик команд содержит адрес точки входа в обработчик, считанный из таблицы векторов исключений. Младший бит в нём принудительно сброшен;
  • регистр LR содержит значение, определяющее способ возврата к прерванному коду (так называемый EXC_RETURN). Оно формируется по следующим правилам:
  • биты 31:5 всегда содержат единицы;
  • бит 4 равен 0, если у процессора есть FPU и он используется (бит CONTROL.FPCA установлен), и равен 1 в противном случае;
  • бит 3 равен 0, если на момент начала перехода к обработке исключения процессор находился в режиме обработчика, и равен 1, если в режиме потока;
  • бит 2 равен 0, если на момент начала перехода к обработке исключения использовался основной стек, и равен 1, если использовался стек процесса;
  • биты 1:0 всегда содержат 01;
  • если было выполнено сохранение контекста FPU, то содержимое его регистров S0–S15, FPSCR и FPCAR не определено, иначе они содержат значения на момент начала входа в обработчик исключения. Регистры S16–S31 не изменяются;
  • регистр IPSR содержит номер исключения;
  • регистр EPSR равен нулю, за исключением бита T, который равен младшему биту адреса точки входа, считанной из таблицы векторов. Если этот бит равен нулю, то сразу после входа в обработчик возникнет исключение UsageFault (HardFault для архитектуры ARMv6-M);
  • биты регистра CONTROL содержат следующие значения:
  • nPRIV не меняется и находится в состоянии на момент начала входа в обработчик исключения. Поскольку процессор к данному времени уже находится в режиме обработчика, значение этого бита игнорируется (выполняемый код считается привилегированным);
  • бит SPSEL равен нулю (активен основной стек);
  • бит FPCA, если имеется FPU, установлен (работа FPU разрешена);

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

  • для NMI, неточного серьёзного отказа или отказа шины, вызова супервизора, асинхронного отладочного события и внешнего прерывания (включая PendSV и SysTick) он равен адресу команды, с которой должно продолжиться выполнение;
  • для точного серьёзного отказа или отказа шины, MemManage, UsageFault и точного отладочного события он равен адресу команды, ставшей причиной исключения.

Заметим, что в случае вызова супервизора командой SVC её адрес может быть получен вычитанием 2 из адреса возврата. Неточные отказы не позволяют определить адрес команды, выполнение которой стало первопричиной ошибки, и обычно приводят к невозможности корректного продолжения работы.

Завершение обработчика исключения

Завершение обработчика исключения и возврат к прерванному процессу происходит, когда в режиме обработчика значение, содержащее 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 должен иметь нулевое значение, а при возврате в код режима обработчика — ненулевое;
  • младшие четыре разряда значения, загруженного в PC, должны быть выставлены корректно: допустимыми являются только комбинации 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).

Сцепление исключений

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

Возникновение исключения во время входа в обработчик исключения

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Если при выполнении кода с приоритетом –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), а не состояние прерываемой команды. В этом случае выполнение последней после возврата из обработчика исключения начнётся с самого начала.

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

Энергосбережение

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

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

Если в процессоре реализованы средства энергосбережения, управление ими осуществляется с помощью следующих разрядов регистра SCR:

  • SEVONPEND — если установлен, появление ожидающего исключения будет считаться пробуждающим событием для команды WFE, даже если само по себе это исключение замаскировано;
  • SLEEPONEXIT — если установлен, вызывает переход процессора в сон, если производится выход из последнего активного обработчика исключения. Функционально аналогично исполнению сразу после возврата из последнего обработчика команды WFI;
  • SLEEPDEEP — когда установлен, разрешает переход в глубокий сон с большим временем возвращения к режиму работы, чем когда сброшен. В частности, при переходе в глубокий сон может быть автоматически выключен главный тактовый генератор. Точный смысл глубокого сна и его отличия от обычного сна определяются реализацией.

Событиями, выводящими процессор из сна после выполнения команды WFE, являются:

  • сброс;
  • выполнение команды SEV на каком-либо другом процессоре многопроцессорной системы;
  • появление ожидающего исключения, если установлен бит SEVONPEND регистра SCR;
  • появление асинхронного прерывания с более высоким групповым приоритетом, чем текущий приоритет «спящего» кода;
  • возникновение отладочного события, если отладка разрешена.

Событиями, выводящими процессор из сна после выполнения команды WFI, являются:

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

Исключения при наличии FPU