Что такое вектор прерывания

от admin

6. Прерывания

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

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

  • Прерывание от данного события должно быть разрешено. Если в устройстве используется только UART, возникновение прерываний от всяких таймеров нам не интересно. Более того, некоторые устройства (в основном, ножки ввода-вывода) генерируют прерывания не импульсно (один раз на событие), а непрерывно. Например, все время пока на ножке высокий уровень. Без возможности запрета таких прерываний контроллер будет постоянно висеть в обработчике.
  • Прерывание должно быть разрешено глобально. Дело в том, что некоторые группы команд прерывать нельзя — собьются тайминги, возникнут нежелаельные импульсы или что-то в этом роде. Такие операции называются атомарными (неделимыми) и в простейшем случае реализуются именно сбросом глобального разрешения прерываний и его последующим восстановлением. Также глобальное разрешение сбрасывается при заходе в прерывание, чтобы не было циклической обработки.
  • Контроллер прерываний вообще-то должен быть настроен. Выставлен адрес обработчика, разрешения и т.п.
  • При наличии системы приоритетов прерываний (в некоторых контроллерах, например AVR, ее нет, но в нашем gd32vf103 — есть, хотя работать с ней мы пока не будем) приоритет пытающегося запуститься прерывания должен быть выше того, что обрабатывается сейчас. “Приоритет” основного кода, естественно, ниже, чем у любого прерывания. Таким способом можно обойти предыдущий пункт и все-таки реализовать обработку прерывания в обработчике прерывания.

Поскольку прерывания генерируются внешними устройствами и не привязаны к выполняющейся в данный конкретный момент инструкции, обработчик обязан после завершения работы вернуть контроллер ровно в то же состояние, что было до его вызова. Это значит восстановить все регистры, включая временные, регистр возврата ra, стек. В случае архитектур с регистрами флагов (к RISC-V это не относится) — и их тоже. При этом возникает два очевидных вопроса:

Как писать код, если все регистры заняты? В общем-то, примерно так же, как и для обычных подпрограмм: нужные регистры сохраняются на стеке, а перед выходом из обработчика восстанавливаются. В простейшем случае (на котором мы остановимся) стек будет общий как для пользовательского кода, так и для прерываний. Но вообще-то RISC-V поддерживает переключение стеков при смене привилений. Скажем, основной код выполняется в U-mode (пользовательский с низкими привилегиями), а прерывания — в M-mode (машинный уровень с максимальными привилегиями). Для этого служит интересный CSR регистр mscratchcsw, но его мы проверить не сможем, пока не доберемся до режимов работы контроллера.

Откуда брать адрес возврата из прерывания, если ra использовать нельзя? Для этого существует специальный CSR-регистр mepc. Возврат по его значению осуществляется специальной инструкцией mret. Помимо прочего она умеет переключать уровни привилегий, если контроллер настроен правильно.

6.1. ECLIC и его настройка

Контроллер прерываний в нашем микроконтроллере называется ECLIC (Enhanced Core Local Interrupt Controller). Управляется он частично через CSR-регистры, частично через MMIO. Регистры у него следующие:

регистр размер смещение описание
cliccfg 4(8) 0x0 Глобальные настройки приоритетов
clicinfo 25(32) 0x4 Разнообразная информация о прерываниях конкретного контроллера
mth 8(8) 0xB Порог срабатывания прерываний
clicintip[i] 1(8) 0x1000+4*i Флаг ожидающего прерывания
clicintie[i] 1(8) 0x1001+4*i Флаг разрешения прерывания
clicintattr[i] 3(8) 0x1002+4*i Настрока фронта прерывания и режим
clicintctl[i] 8(8) 0x1003+4*i Приоритет

Тут же встает вопрос относительно чего расчитывается смещение и где про это написано. И тут у меня ответа к сожалению нет: я не нашел упоминаний этого адреса ни в одной документации. Только изучая примеры кода от производителя, был обнаружен базовый адрес 0xD200’0000.

Регистры clicintip, clicintie, clicintattr и clicintctl привязаны каждый к своему прерыванию, поэтому и объединены в массив. В нашем случае используется прерывание от USART0, за которым производителем закреплен номер 56, соответственно использоваться будут clicintip[56], clicintie[56], clicintattr[56] и clicintctl[56] с адресами (0x1000 + 4*56 =) 0x10E0, 0x10E1, 0x10E2 и 0x10E3. Посмотреть номера и список всех доступных в данном конкретном контроллере прерываний можно в его User Manual’е в разделе, посвященном прерываниям. В нашем случае это здоровенная табличка из 86 элементов.

Немного расшифрую что написано в таблице. Регистр mth: я пока точно не знаю за что он отвечает, поэтому подробностей не будет, изучайте документацию. Регистр clicintip содержит всего один значащий бит. Если прерывание уже готово выполниться, он выставляется в 1, что, при выполнении остальных условий, приводит к собственно переходу на прерывание. Насколько я понял, он оставлен только для совместимости со старыми версиями контроллеров векторов прерываний и в ECLIC практически не используется. Регистр clicattr: некоторая периферия (особенно ножки ввода-вывода GPIO) умеют генерировать прерывание по высокому уровню (все время пока на ножке лог.1 будет вызываться прерывание), по нарастающему фронту (лог.0 -> лог.1) и по спадающему фронту (лог.1 -> лог.0). Для большей же части периферии эта настройка бесполезна. Что такое режим (векторный / не-векторный) рассмотрим чуть позже.

Собственно настройка ECLIC для простейшего случая работы с прерываниями сводится всего лишь к разрешению прерываний от интересующей нас периферии, то есть выставлении clicintie[56] в 1:

Обратите внимание, что для записи использована инструкция sb: размер регистров 8 бит, и трогать соседние мы не хотим.

6.2. Настрока периферии

Периферии мы пока изучили немного, поэтому работать будем с UART. Как мы уже выяснили, за все события, происходящие с UART’ом отвечает одно прерывание, с номером 56. Самих же событий может быть несколько, и прописаны они в регистре USART_CTL0:

PERRIE, Parity error interrupt enable — прерывание по ошибке приема. У UART есть простенькая система защиты от сбоев при обмене, и это прерывание возникает при ее срабатывании.

TBEIE, Transmitter buffer empty interrupt enable — прерывание по опустошению буфера передачи

TCIE, Transmission complete interrupt enable — прерывание по фактическому окончанию передачи

RBNEIE, Read data buffer not empty interrupt and overrun error interrupt enable — прерывание по приему байта

IDLEIE, IDLE line detected interrupt enable — прерывание по таймауту. Если данные не приходили слишком долго.

Продемонстрировать работу прерываний будет проще всего на передаче. Контроллер передаст байт, после чего должно произойти событие и мы окажемся в обработчике. Но в регистре USART_CTL0 этих прерываний два. Дело в том, что передача байта происходит в два этапа: сначала байт записывается в USART_DATA, потом автоматически копируется во внутренний буфер, из которого бит за битом передается в линию TX. И пока он передается, в регистр USART_DATA можно положить еще один байт, он там будет лежать, пока предыдущий не освоболит место во внутреннем регистре. Так вот, прерывание TBEIE возникает когда байт покинул регистр USART_DATA и начал передаваться. А TCIE — когда покинул внутренний регистр, и передача полностью завершилась. Соответственно, TBEIE надо использовать когда передаются байт за байтом, чтобы не было задержки между фактическим окончанием передачи, пока отработает прерывание, пока положат следующий байт и т.д. А TCIE — когда надо отключить модуль UART, то есть дождаться фактического окончания передачи. Поскольку отключать UART мы не будем, воспользуемся TBEIEб его нужно добавить к прочим флагам USART_CTL0.

6.3. Настройка контроллера

Итак, модуль ECLIC мы настроили, периферию настроили. Осталось написать собственно обработчик прерывания, положить его адрес в какой-нибудь регистр и разрешить прерывания глобально. Начнем, как ни странно, с регистра хранения адреса обработчика, mtvec:

устройство mtvec

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

Из кода довольно очевидно, что прерывание всего лишь мигает зеленым светодиодом и возвращается по адресу mepc при помощи команды mret. Вот именно адрес этой подпрограммы надо записать в mtvec:

И разрешить прерывания глобально. Очевидно, что делать это надо когда периферия и прерывания уже настроены, то есть обычно перед бесконечным рабочим циклом. За глобальное разрешение прерываний отвечает регистр mstatus, а точнее его бит MIE:

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

В том, что с прерыванием мы ничего не сделали: оно как ждало обработки, так и продолжает ждать. Вот и тыкается в обработчик в надежде, что хоть теперь его обработают. Но полноценно мы его обрабатывать пока не будем, просто скажем “хорошо, мы поняли, что данные переданы, можешь больше не следить за UART’ом”. То есть просто запретим данное прерывание:

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

6.4. Исключения

В микроконтроллерах источником неожиданных событий почти всегда оказывается именно периферия. Но в компьютерах, где программы куда больше и сложнее, чаще бывают и чисто программные ошибки. Плюс при наличии операционной системы появляется и необходимость к ней обращаться из пространства пользователя. Эти задачи также решаются контроллером прерываний. Тут я сразу вынужден уточнить терминологию: прерывания это события от внешних устройств; исключения — от выполнения определенных инструкций кода; исключительные ситуации — от обоих. Разница в том, что прерывание возникает когда ему хочется, и к коду не привязано. Поэтому после обработки надо вернуться в то же самое место, на котором оно случилось. Исключение же возникает в строго отведенных местах: код попытался выполнить несуществующую инструкцию; попытался обратиться к недоступному адресу; попытался работать с невыровненными данными (кстати, наш контроллер к невыровненным данным равнодушен, и ошибок не выдает); попытался сделать системный вызов. Следовательно, при обработке исключений надо сначала определиться что же собственно произошло — штатное событие или ошибка. Если ошибка, то можно ли ее обработать или лучше прибить процесс, пока хуже не стало.

Для примера напишем три инструкции, приводящие к исключениям:

При выполнении такого кода контроллер начинает яростно мигать зеленым светодиодом и спамить плюсики в UART. Логично, ведь при выполнении инструкции 0xFFFFFFFF возникает исключительная ситуация, обрабатывается нашим trap_entry, после чего управление передается опять на 0xFFFFFFFF. Что снова приводит к исключению.

В первую очередь надо отделить исключения от прерываний. Для этого служит регистр mcause, а точнее, его 31-й бит. Если он сброшен в 0, то перед нами исключение, а если выставлен в 1 — прерывание. Соответственно обработчик прерываний остается неизменным, но при обнаружении нуля в 31-м бите mcause надо перейти на обработчик исключения. Отличаться он будет тем, что мигать в нем будем красным диодом, а возвращаться не на ту же инструкцию, которая привела к исключению, а на следующую. Просто-напросто считаем mepc, увеличим на 4 (размер инструкции) и запишем обратно:

Но тут из-под воды возникают интересные грабли: контроллер наш поддерживает расширение C (Compressed) — сжатые инструкции. То есть часть инструкций у него 32-битная, а часть — 16-битная. А перепрыгивать 16-битную инструкцию через 4 байта это плохая идея. К счастью, разработчики RISC-V предусмотрели замечательный способ определить длину инструкции. У 32-битных два младших бита всегда равны 0b11, а в 16-битных — любому другому числу. То есть нам надо всего лишь проверить эти два бита и в зависимости от этого решить, прибавлять 4 или все же 2:

Как я говорил в самом начале, исключения в контроллерах используются достаточно редко, поэтому пока на этом и остановимся.

6.5. Разделение прерываний и исключений

Но если исключения штука редкая, но возможная, можно ли убрать проверку mcause из обработчика прерываний? Оказывается, можно. Для этого используется еще один CSR-регистр mtvt2, который в стандарт RISC-V не входит, и является специфичным для нашего контроллера. Его младший бит отвечает за то, использоать ли его вообще, а оставшиеся биты хранят адрес обработчика прерываний. То есть в mtvec будет адрес обработчика исключений, а в mtvt2 — прерываний:

Естественно, раз уж обработчиков теперь стало два, нужно каждый из них оформить как обработчик — персональная точка входа, работа со стеком, mret.

6.6. Векторный режим

Специфика контроллера вынуждает пойти еще дальше и вместо одного обработчика на все прерывания, в котором нужно было анализировать младшие биты mcause чтобы выяснить какое именно устройство вызвало прерывание, был придуман еще более хитрый механизм. Он заключается в том, что для каждого устройства пишут свой, персональный обработчик прерывания, а их адреса (иногда — прямо команды перехода по адресам) сводят в специальную таблицу — таблицу векторов прерываний. Например, если нас интересует 56-е прерывание, то в 56-ю ячейку надо записать адрес обработчика. Как и с отдельными обработчиками, для хранения адреса таблицы выделен отдельный CSR-регистр mtvt. Причем работа с таблицей реализована очень разумно: в регистре хранится старшая часть адреса таблицы, а вместо младшей подставляется номер прерывания. То есть если адрес самой таблицы равен 0x2000’1000 (где-то в оперативной памяти), и произошло прерывание 56 (поскольку инструкция 4-байтная, то смещение будет 224, оно же 0x0000’00E0), то адрес будет взят из ячейки (0x2000’1000 OR 0x0000’00E0) = 0x2000’10E0. Из этой реализации следует ограничение на выравнивание таблицы. В нашем случае, когда прерываний 86, фактический размер таблицы составляет 344 байта, что помещается в 512-байтную область. Это соответствует выравниванию .align 9.

Здесь надо не забыть, что векторный / не-векторный режим настраивается в регистре clicattr[i], причем для каждого прерывания независимо.

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

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

6.7. Расположение таблицы векторов прерываний

Как мы увидели раньше, расположить ее можно где угодно, лишь бы выравнивание соблюдалось. Можно даже хранить несколько таблиц по разным адресам и переключать их по желанию левой пятки. Но проще всего все же выделить для таблицы постоянное место — в начале прошивки. Мы ведь точно знаем, что адрес 0x0000’0000 (и даже реальный адрес 0x0800’0000) совершенно точно выровнены по 512-байтной границе. Разработчики даже сделали нам подарок, не став использовать 0-й адрес вектора прерываний, на который попадает управление при старте контроллера. В него можно записать безусловный переход на начало основного кода. Ну а чтобы таблица располагалась именно там, где надо, для нее можно выделить специальную секцию памяти, а в *.ld файле указать, что размещается она в самом начале.

6.8. Системные вызовы

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

Как говорит нам документация, причина исключения хранится в младших 12 битах регистра mcause. За выполнением ecall зарезервировано два кода: 11 (если вызов произошел на M-mode) и 4 (если на U-mode). Пока что будем обрабатывать только ecall, а остальные исключения игнорировать.

Номер ecall‘а хранится в регистра a7, и из списка вызовов RARS’а (напоминаю, в других средах системные вызовы другие!) нас интересуют 1 и 4 — вывод числа и вывод строки. Вот так может выглядеть обработчик исключений, поддерживающий эти два системных вызова:

В результате наконец-то начали корректно работать участки кода

Заключение

Вот так в микроконтроллер gd32vf103 настраиваются прерывания и исключения. Возможно, на таком простом примере, как UART, их польза не очевидна, но желающие могут изучить и другую периферию, поработать через polling и прерывания и оценить, что выгоднее для их задачи. Это, кстати, не шутка: не надо пихать прерывания куда попало, в ряде случаев именно опрос регистра является оптимальным решением.

8. Прерывания, вектор прерываний.

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

[Можно ли обрабатывать эти события без использования прерываний?

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

Чем неудобен подобный подход?

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

2) Некоторые события в принципе не могут долго ждать, например, обработка приёма данных по USART – если пропустить хоть один байт, логика работы программы будет нарушена, а повторная пересылка данных может быть не предусмотрена.

Именно поэтому для организации более эффективного использования ресурсов микроконтроллера и используют прерывания.]

Как работают прерывания?

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

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

Контроллер стартует с адреса 0x00 (нулевого адреса памяти программ). Далее мы делаем безусловный переход на метку RESET. Если этого не сделать, то контроллер начнёт выполнять команды из таблицы векторов прерываний, что нам совершенно не нужно.

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

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

// инициализация стека и инициализация периферии

Что происходит в том случае, если одновременно возникает не одно прерывание, а несколько?

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

Обработка прерываний

[ВОПРОС ИЗ КОНТРОЛЬНОЙ – краткое описание того, что ниже.

В билете можно писать это, а потом читать остальное для пояснения]

Выберите правильную последовательность действий при обработке прерывания:

1) Для включения прерывания программа должна установить флаг I регистра SREG в единицу и записать в регистры маски такой код, который разрешит лишь нужные в данный момент прерывания

2) При поступлении запроса на прерывание устанавливается флаг соответствующего прерывания

3) Флаг I автоматически сбрасывается, запрещая обработку других прерываний. Флаг, соответствующий вызванному прерыванию, также сбрасывается, сигнализируя о том, что МК уже приступил к его обработке.

4) После окончания обработки очередного прерывания происходит проверка остальных флагов, и если имеется хоть одно не обработанное прерывание, МК переходит к его обработке.

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

Для каждого блока периферии существует собственный регистр для разрешения локальных прерываний (E/TIMSK). При возникновении прерывания флаг I регистра SREG сбрасывается на аппаратном уровне, запрещая тем самым обработку следующих прерываний. При возврате из подпрограммы обработки прерывания (при выполнении команды RETI) флаг I устанавливается обратно.

Все доступные для работы прерывания можно разделить на два типа.

I тип) возникают при наступлении некоторого события, в результате которого устанавливается флаг прерывания.

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

II тип) Не имеют флагов прерываний и возникают в течение всего времени, пока присутствуют условия, необходимые для их возникновения.

Если условия, вызывающие прерывание, исчезнут до разрешения прерывания, генерации прерывания не произойдет.

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

PUSH R16 // Сохраняем регистр R16

IN R16, SREG // Перемещаем содержимое SREG в R16

PUSH R16 // Сохраняем всё в стек

PUSH R17 // R17 сохраняем туда же

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

POP R17 // Перед выходом из прерывания извлекаем

POP R16 // сохранённые данные

OUT SREG, R16 // Действуем при этом в обратном порядке

RETI // Выходим из прерывания

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

16-разрядный регистр считывается в два приема: сначала считывается младший байт, а потом старший. (ЗАПИСЬ ПРОИСХОДИТ В ОБРАТНОМ ПОРЯДКЕ)

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

Вектор прерывания

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

В зависимости от источника возникновения сигнала прерывания делятся на:

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

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

Содержание

Маскирование

В зависимости от возможности запрета аппаратные прерывания делятся на:

  • Маскируемые — прерывания, которые можно запрещать установкой соответствующих битов в соответствующем регистре маски прерываний;
  • Немаскируемые — обрабатываются всегда, независимо от значения флага IF (в процессорах

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

Приоритезация

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

Перехват прерывания — изменение обработчика прерывания на свой собственный.

Таблица прерываний

Вектор прерывания — закреплённый за устройством номер, который идентифицирует соответствующий обработчик прерываний. Векторы прерываний объединяются в таблицу векторов прерываний. Местоположение таблицы зависит от типа и режима работы микропроцессора.

См. также

Микроядро • Монолитное ядро • Гибридное ядро • Пространство ядра (kernel space) • Модульное ядро • Наноядро • Драйвер • Пространство пользователя • Область пользователя • Экзоядро

Защита памяти • Сегментация памяти • Страничная память (Paging) • Блок управления памятью • Ошибка сегментации • Общая ошибка защиты

Обработка прерываний, векторы прерываний, программные прерывания, IRQ.

Здесь мы разберем такие важные темы, как: обработка прерываний, векторы прерываний, программные прерывания, IRQ, в общем поговорим на темы прерывания.

Идея прерывания была предложена в середине 50-х годов и основная цель введения прерываний – реализация синхронного режима работы и реализация параллельной работы отдельных устройств ЭВМ.

Прерывания и обработка прерываний зависят от типа ЭВМ, поэтому их реализацию относят к машинно-зависимым свойствам операционных систем.

Прерывание (interrupt) – это сигнал, заставляющий ЭВМ менять обычный порядок выполнения команд процессором.

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

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

Обработка прерывания

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

Например прерывание с номером 9 — прерывание от клавиатуры, которое генерируется при нажатии и при отжатии клавиши. Используется для чтения данных с клавиатуры. Обозначается в ОС как IRQ1, где IRQ – обозначение прерывания, а 1 – приоритет прерывания. Данные о запросах на прерывание можно проанализировать в диспетчере устройств:

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

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

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

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

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

Таблица векторов прерываний занимает первый килобайт оперативной памяти — адреса от 0000:0000 до 0000:03FF. Таблица состоит из 256 элементов — FAR-адресов обработчиков прерываний. Эти элементы называются векторами прерываний. В первом слове элемента таблицы записано смещение, а во втором — адрес сегмента обработчика прерывания. Векторами являются просто полные адреса памяти программы (в сегментированной форме), которая должна быть активизирована в случае возникновения прерывания.

Прерыванию с номером 0 соответствует адрес 0000:0000, прерыванию с номером 1 — 0000:0004 и т.д. Адрес такой состоит из пары 2-байтовых слов, поэтому каждый из векторов занимает четыре байта.

Можно просмотреть таблицу векторов прерываний в компьютере, если воспользоваться программой DEBUG. Используйте команду D для вывода содержимого начала памяти: D 0:0. Программа DEBUG покажет вам первые 128 байтов или 32 вектора, которые могут иметь вид наподобие следующего:

0000:0000 E8 4E 9A 01 00 00 00 00-C3 E2 00 F0 00 00 00 00
0000:0010 F0 01 70 00 54 FF 00 F0-05 18 00 F0 05 18 00 F0
0000:0020 2C 08 51 17 D0 0A 51 17-AD 08 54 08 E8 05 01 2F
0000:0030 FA 05 01 2F 05 18 00 F0-57 EF 00 F0 F0 01 70 00
0000:0040 90 13 C7 13 4D F8 00 F0-41 F8 00 F0 3E 0A 51 17
0000:0050 5C 00 B7 25 59 F8 00 F0-E2 0A 51 17 9C 00 B7 25
0000:0060 00 00 00 F6 8E 00 DE 09-6E FE 00 F0 F2 00 7B 09
0000:0070 27 08 51 17 A4 F0 00 F0-22 05 00 00 00 00 00 F0

Векторы хранятся как «слова наоборот»: сначала смещение, а потом сегмент. Например, первые четыре байта, которые программа DEBUG показала выше (E8 4E 9A 01) можно преобразовать в сегментированный адрес 019A:4EE8.

Можно встретить три вида адресов в таблице векторов. Это могут быть адреса, указывающие на ROM-BIOS, которые можно идентифицировать шестнадцатеричной цифрой F, которая предшествует номеру сегмента. Это могут быть адреса, которые указывают на главную память (как в примере: 019A:4EE8). Эти адреса могут указывать на подпрограммы ДОС или на резидентную программу (например, SideKick или Prokey), либо они могут указывать на саму программу DEBUG (поскольку DEBUG должна временно управлять прерыванием). Также векторы могут состоять из одних нулей, когда прерывание с данным номером не обрабатывается в текущий момент.

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

Ниже приведено назначение некоторых векторов:

Описание
0 Ошибка деления. Вызывается автоматически после выполнения команд DIV или IDIV, если в результате деления происходит переполнение (например, при делении на 0).
2 Аппаратное немаскируемое прерывание. Это прерывание может использоваться по-разному в разных машинах. Обычно вырабатывается при ошибке четности в оперативной памяти и при запросе прерывания от сопроцессора.
5 Печать копии экрана. Генерируется при нажатии на клавиатуре клавиши PrtScr. Обычно используется для печати образа экрана.
8 IRQ0 — прерывание интервального таймера, возникает 18,2 раза в секунду.
9 IRQ1 — прерывание от клавиатуры. Генерируется при нажатии и при отжатии клавиши. Используется для чтения данных от клавиатуры.
A IRQ2 — используется для каскадирования аппаратных прерываний в машинах класса AT
B IRQ3 — прерывание асинхронного порта COM2.
C IRQ4 — прерывание асинхронного порта COM1.
D IRQ5 — прерывание от контроллера жесткого диска для XT.
E IRQ6 — прерывание генерируется контроллером флоппи-диска после завершения операции.
F IRQ7 — прерывание принтера. Генерируется принтером, когда он готов к выполнению очередной операции. Многие адаптеры принтера не используют это прерывание.
10 Обслуживание видеоадаптера.
11 Определение конфигурации устройств в системе.
12 Определение размера оперативной памяти в системе.
13 Обслуживание дисковой системы.
14 Последовательный ввод/вывод.
19 Загрузка операционной системы.
1A Обслуживание часов.
1B Обработчик прерывания Ctrl-Break.
70 IRQ8 — прерывание от часов реального времени.
71 IRQ9 — прерывание от контроллера EGA.
75 IRQ13 — прерывание от математического сопроцессора.
76 IRQ14 — прерывание от контроллера жесткого диска.
77 IRQ15 — зарезервировано.

IRQ0 — IRQ15 — это аппаратные прерывания.

Механизм обработки прерываний

При обработке каждого прерывания должна выполняться следующая последовательность действий:

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

Главные функции механизма прерывания:

  1. распознавание или классификация прерываний.
  2. передача управления соответственно обработчику прерываний.
  3. корректное возвращение к прерванной программе (перед передачей управления обработчику прерываний содержимое регистров процессора запоминается либо в памяти с прямым доступом либо в системном стеке).

Типы прерываний

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

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

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

Внешние прерывания возникают по сигналу какого-либо внешнего устройства например:

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

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

Маскируемые и немаскируемые внешние прерывания

Существуют два специальных внешних сигнала среди входных сигналов процессора, при помощи которых можно прервать выполнение текущей программы и тем самым переключить работу центрального процессора. Это сигналы NMI (Non Mascable Interrupt, немаскируемое прерывани) INTR (interrupt request, запрос на прерывание).

Соответственно внешние прерывания подразделяются на два вида: немаскируемые и маскируемые.

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

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

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

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

Программные прерывания

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

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

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

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

Пример (программные прерывания):

  • привилегированная команда в режиме пользователя.
  • адрес вне диапазона.
  • нарушение защиты памяти.
  • арифметическое переполнение, отсутствует страница.
  • нарушение защиты сегмента.
  • выход за границу сегмента.

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

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

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

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

В случае о прерывании самой программы обработки прерывания говорят о вложенном прерывании. Уровни приоритетов обозначаются сокращенно IRQ0 — IRQ15 или IRQ0 – IRQ23 (в зависимости от микросхемой реализации).

Пpepывaнию вpeмeни cутoк дан мaкcимaльный пpиopитeт, пocкoльку ecли oнo будет пocтoяннo тepятьcя, то будут нeвepными пoкaзaния cиcтeмныx чacoв. Пpepывaниe от клaвиaтуpы вызывaeтcя при нaжaтии или oтпуcкaнии клавиши; oнo вызывaeт цепь coбытий, кoтopaя oбычнo зaкaнчивaeтcя тем, что код клавиши пoмeщaeтcя в буфep клaвиaтуpы (oткудa он зaтeм мoжeт быть пoлучeн пpoгpaммными пpepывaниями).

Ну и наконец реализация механизма обработки прерываний

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

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

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

Сохранение и восстановление состояния процессора и содержимого регистров называют операцией контекстного переключения.

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

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

  • при программном прерывании отражается тип вызвавшего его условия, например деление на нуль.
  • при прерывании по вводу-выводу заносится номер канала, вызвавший прерывание.

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

Это поле используется, чтобы не допустить наступления прерываний определенного типа, пока первое из них не будет обработано. В MASK каждый бит соответствует некоторому классу прерываний. Если какой-то бит установлен в 1, то прерывания соответствующего класса разрешены, если в 0, то запрещены. В последнем случае говорят, что они маскированы (их также называют запрещеннымиили закрытыми). Однако маскированные прерывания не теряются, потому что сигнал, вызвавший прерывание, сохраняется аппаратурой. Временно задержанное таким способом прерывание называется отложенным. Когда (вследствие того, что значение MASK сброшено) прерывания соответствующего класса вновь разрешаются, сигнал опознается и происходит прерывание.

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

Читать:
Как открыть домофон филман

Похожие публикации