Шапка

14.3 (II) Шинные сигналы и процесс обмена данными

14.3.7 Прерывания

Использование флагов состояния - один из трёх возможных способов информирования процессора о событиях, требующих внимания. Этого вполне достаточно для многих простых задач, однако есть и серьёзные ограничения: периферийное устройство не может привлечь внимание, но вынужденно ждать, пока ЦПУ прочитает регистр состояния по команде «IN». Флаги состояния в устройствах, требующих быстрой реакции ( дисковые накопители или устройства ввода-вывода реального времени ) должны опрашиваться часто. И, если таких устройств в системе несколько, скоро окажется, что основное время процессор тратит на опрос состояния, в точности как в предыдущем примере.

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

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

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

Вот как они работают. В PC104 есть набор из шести линий запроса прерывания с именами «IRQ3...IRQ7» и «IRQ9». Все они активные ВЫСОКИЕ. Чтобы вызвать прерывание надо подать ВЫСОКИЙ уровень на одну из линий. Если прерывания разрешены, и нужная линия «IRQ» имеет ВЫСОКИЙ уровень, процессор заканчивает выполнение текущей инструкции, сохраняет регистр флагов и адрес возврата в стеке и переходит по адресу «обработчика прерывания» - некоторого участка где-то в программном коде. В обработчике есть всё, что проектировщик счёл нужным в него поместить ( например, считывание данных из буфера клавиатуры ). Размещать обработчик можно где угодно: для перехода на него процессор использует 4-байтные адреса [* т.е. может при желании попасть в любую точку адресуемого пространства ] , собранные в специализированную таблицу – «таблицу векторов прерываний». Выбираемый процессором адрес обработчика зависит от номера активного прерывания. Для x86 он берётся по смещению [* по «вектору» ]   20h + 4n байт от начала таблицы, где n - номер линии прерывания. Скажем, для прерывания «IRQ2» 4 байта адреса обработчика будут извлекаться из таблицы со смещением 28h...2Bh от начала. Способ очень похож на косвенную адресацию, но, в отличие от последней, адрес располагается в памяти, а не в регистре. Понятно, такая организация процесса требует некоторых вспомогательных действий, гарантирующих, что «вектор» в таблице смотрит на первый байт нужного кода. В конце обработчика исполняется команда «IRET», которая заставляет процессор восстановить прежнее состояние регистра флагов и возвратиться к исполнению прерванного кода.

[* Тут важно понимать, как выглядит процесс «восстановления состояния». Процессор выбирает из стека два верхних элемента - сначала то, что он считает адресом возврата, а затем то, что должно быть содержимым регистра флагов. Если в ходе работы указатель стека случайно или намеренно изменён ( или «подправлены» данные в стеке ), результат может быть немного непредсказуем ].

14.3.7.A Пример: клавиатурный интерфейс с прерыванием

Дополним сказанное примером и добавим прерывание ( рис. 14.16 ) в уже знакомый клавиатурный интерфейс ( рис. 14.15 ). Флаг «character_ready» и базовая схема оставлена без изменений, но появился сигнал «RESET» ( активный ВЫСОКИЙ ), сбрасывающий триггер и прочую логику в определённое состояние при подаче питания. Очевидно, что при начальной инициализации надо сбрасывать флаг, рапортующий о наличии новых данных ( а в новом варианте схемы он ещё и прерывание активизирует ).

Рис. 14.16   Клавиатурный интерфейс с флагом состояния и прерыванием

В схему добавляется и буфер с Z-состоянием, через который передаётся сигнал прерывания «IRQ3», когда появляется новый код клавиши. Это, собственно, всё, что требуется. На всякий случай, добавлена возможность запрещать прерывания через запись ( «OUT» ) по адресу KBFLAG байта с нулевым битом «D0». Такая опция может потребоваться, если хочется использовать ту же линию прерывания в другом периферийном устройстве. В каждый момент времени к линии может быть подключено не более одного источника ( этот вопрос будет дополнительно рассматриваться чуть позднее ).

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

В PC104 сигналы шины, унаследованные от оригинального IBM PC, сильно упрощают обработку прерываний, хотя и несколько ограничивают возможности по сравнению с более универсальным ( и более сложным ) методом, описанным в §14.3.9 . Шина PC104 предполагает наличие не только самого ЦПУ, но и специальной схемы управления прерываниями 25 . Эта «поддержка» делает большую часть работы, а именно: приоритизацию, маскирование и выборку «вектора». ( Подробности будут раскрыты после завершения разбора примера ). Процессор отвечает за свою часть: реагирует на прерывание, приостанавливает исполнение программы, сохраняет указатель на прерванный участок, сохраняет регистр флагов, запрещает дальнейшие прерывания [* именно в этом состоит отличие перехода по вектору от простого вызова подпрограммы ]   и переходит по адресу в соответствующей позиции таблицы векторов. Остальное делает программа-обработчик.

  1. Сохраняет в стеке ( «PUSH» ) все используемые кодом обработчика регистры ( прерванная программа не может подготовиться к прерыванию и даже не подозревает, что оно произошло, а нужные ей данные могут храниться в любом регистре - здесь не в курсе уже обработчик ).
  2. Выясняет, что надо делать, считывая регистр состояния, если требуется.
  3. Таки делает, что надо.
  4. Восстанавливает сохранённые регистры из стека ( «POP» ).
  5. Информирует контроллер прерываний о завершении обработки ( посылкой кода 20h – «конец прерывания» по адресу ввода-вывода 20h , все совпадения случайны ) [* оно требуется для восстановления исходного «ожидающего» состояния схемы приоритизации, которое было заблокировано в ходе реакции на прерывание ] .
  6. И, наконец, исполняет инструкцию выхода из прерывания «IRET», что вынуждает процессор восстановить прежнее состояние регистра флагов и вернуться к прерванному коду программы ( воспользовавшись адресом возврата, сохранённым в стеке ) [* и разрешить прерывания - в восстановлении флагов и разрешении прерываний состоит отличие от инструкции «RET» ] .
  7. Где-то в программе должна быть процедура инициализации аппаратуры, загружающая адрес обработчика в таблицу векторов и сообщающая контроллеру прерываний о необходимости разрешить прерывания нужного уровня [* имеется однозначное соответствие номера линии «IRQ» и приоритетом ] .

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

Program 14.5

;keyboard handler -- uses interrupts
KBVECT    equ word ptr 002CH      ;INT3 vector
KBDATA    equ ***H                ;put kbd data port adr here
KBFLAG    equ ***H                ;put kbd flag port adr here
buflg     DB  0                   ;allocates "end-of-line" flag
bufpos    DW  0                   ;allocates buffer pointer
charbuf   DB  100 dup(0)          ;allocates 100-byte char buf

    CLI                           ;disable interrupts
    MOV   bufpos, offset charbuf  ;initialize buf pntr
    MOV   buflg, 0                ;clear end-of-line flag
    MOV   KBVECT, offset KBINT    ;hndlr adr->vec area
    STI                           ;enable interrupts
    MOV   AL, 1
    OUT   KBFLAG, AL              ;enable hardware 3-state drvr

PROMPT:
    MOV   AL, '*'
    CALL  TYPE                    ;type prompt "*"

    IN    AL, 21H                 ;existing int ctrl   mask
    AND   AL, 0F7H                ;clear bit 3 to enable INT3
    OUT   21H, AL                 ;and send back to intctrl OCW1

LNCHK:
    MOV   AL, buflg
    OR    AL, AL                  ;needed to set zero flag
    JZ    LNCHK                   ;loop til end-of-line flag set

LINE:
    MOV   bufpos, offset charbuf  ;reset   pointer
    MOV   buflg, 0                ;clear line flag
    o                             ;do something with   line
    o
    o
    JMP   PROMPT                  ;and wait for another line

; *** this ends the main program. ***
; *** the code below is completely independent ***

;keyboard interrupt handler
;an INT3 lands you here, via vect we loaded
KBINT:
    STI                           ;enable interrupts
    PUSH  AX                      ;save AX register, used here
    PUSH  SI                      ;save SI, possible other users
    MOV   SI, bufpos              ;and copy buffer pointer there
    IN    AL, KBDATA              ;get data byte from keyboard
    MOV   [SI], AL                ;put it in line buffer
    INC   SI                      ;and advance pointer
    MOV   bufpos, SI              ;and copy to bufpos
    CALL  TYPE                    ;echo to screen

    CMP   AL, 0DH                 ;check for carriage return
    JNZ   HOME                    ;not a CR -- return

    MOV   buflg, 0FFH             ;CR -- set end-of-line flag
    IN    AL, 21H                 ;existing int ctrl mask
    OR    AL, 08H                 ;set bit 3 to disable INT3
    OUT   21H, AL                 ;and send back to intctrl OCW1

HOME:
    MOV   AL, 20H
    OUT   20H, AL                 ;end-of-int signal to int ctlr
    POP   SI                      ;restore SI
    POP   AX                      ;restore old AX
    IRET                          ;and return 

Разберём детали. Текст начинается с констант - задания имени и смещения используемого прерывания ( IRQ3 ) в таблице векторов ( 2Ch = 20h + 4×3 ), символических имён и адресов портов клавиатуры. [* Пока это пары имя-число, которые просто запоминаются ассемблером. Что-либо значить они начнут, только когда появятся в качестве аргументов машинной инструкции ] . Далее идёт сообщения ассемблеру необходимости отведения 100 байт для буфера строки. [* В данной программе начальное состояние отводимой памяти не имеет значения, но, если память должна содержать конкретные данные, например нули, об инициализации придётся побеспокоиться дополнительно ] . Исполняемый код начинается с инициализации указателя позиции в буфере строки ( он используется в связке с индексным регистром SI ), обнуления флага - признака конца строки и инициализации вектора прерывания по смещению вектора IRQ3 адресом KBINT . [* Именно здесь абстрактное число, эквивалентное имени KBINT и лежащее во внутренней таблице ассемблера, превратилось в абсолютный адрес, отсчитываемый от начала адресного пространства процессора. Т.е. именно здесь ассемблер «узнал», что объявленная выше константа является адресом ] . Далее разрешаются прерывания в процессоре, а затем прерывания в клавиатурном контроллере ( посылкой 1 в KBFLAG , которая включает Z-буфер ’125 на передачу ). Чтобы разрешить прерывания уровня 3 в контроллере прерываний, надо сбросить бит 3 в рабочей маске прерываний ( «IN», «AND», «OUT» ) в контроллере прерываний. Теперь мы крутимся в основном цикле, ожидая появления ненулевого значения в переменной «buflg», который внезапно возникает из ниоткуда непостижимым для основного цикла образом. Cразу после обнаружения ненулевого значения «buflg» программа инициализирует указатель буфера и сбрасывает флаг, чтобы подготовиться к появлению новых прерываний, после чего переходит к обработке принятой строки. Строку следует как можно быстрее скопировать в другой буфер, потому что новое прерывание ( т.е. новый байт данных ) может возникнуть уже через несколько миллисекунд. За это время можно выполнить несколько тысяч инструкций, чего более чем достаточно для копирования строки.

Обработчик прерывания выделен в отдельный кусок кода, куда нет доступа из основного цикла. Обработчик получает управление при возникновении прерывания уровня 3 через адрес, загруженный в таблицу векторов. Выполняемая задача вполне конкретна, и обработчик выполняет её без рассуждений. Он сохраняет AX и SI , потому что использует их, считывает из порта данных клавиатуры символ, кладёт его в буфер, передвигает указатель, дублирует символ на экране, устанавливает флаг «buflg». Если получен код «CR» ( 0Dh ), отправляет в контроллер прерываний признак конца прерывания, восстанавливает регистры и возвращает управление прерванному коду.

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

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

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

[* Читателю должно быть понятно, что свободное место не берётся из ниоткуда само собой. Его требуется освобождать. Здесь наглядно видно, как работает буферизация. Есть медленный, но постоянно действующий процесс, заполняющий место, и быстрый процесс, который стартует время от времени, разгребая место для первого. Это самая обычная инженерная задача, которая решается для самых разных направлений разработки на протяжении всей книги. У вас есть много одного ресурса, но мало другого, а вы заняты разменом первого на второй. Например, есть низкое напряжение, но много мощности, а нужно слабый источник но с более высоким потенциалом ( §9.6.6 ), есть высота, нужна скорость, или, как здесь, время и пространство, ну, или мало солдат, но много артиллерии... ].

Ещё одной заботой является сам обработчик прерывания. Должной практикой является всемерное сокращение и упрощение его кода. В идеале это должна быть простая установка флага в расчёте на ведение всей обработки в основной программе. Если обработчик прерывания слишком долго занимает процессор, появляется опасность потери данных от других устройств, т.к. прерывания запрещены ( на уровне, который обрабатывается и менее приоритетных ). Решением может быть разрешение прерываний внутри обработчика ( инструкция «STI» ) по завершении чувствительных к задержкам мероприятий. В таком случае новое прерывание сможет прервать такой обработчик. Флаги и адрес возврата лежат в стеке, поэтому на обратном пути процессор сначала вернётся в прерванный обработчик, а уже из него в основную программу. [* Такая схема называется «вложенными прерываниями». На этом пути главное не впасть в рекурсию, когда одно и то же прерывание перебивает обработку самого себя ] .

14.3.9 Общее описание прерываний

Пример с клавиатурой демонстрирует основное свойство прерываний: внешний запрос от периферийной аппаратуры вызывает переход программы к специальному участку кода - обработчику, который обычно выполняет какие-то операции ввода-вывода, а затем возвращает управление прерванной программе. Другим примером аппаратных прерываний являются часы реального времени, которые информируют систему, что пора обновлять текущие показания. Обычно это 10 событий в секунду, но в оригинальном IBM PC, из коего выросла шина PC104, их 18.2 в секунду. Ещё пример - принтер, сообщающий о готовности принять новый символ. Прерывания позволяют компьютеру перемежать текущие действия с обслуживанием периферии и печатать файл, принимая одновременно символ с клавиатуры и обновляя системное время.

Но PC104/ISA не может служить примером универсальной системы прерываний. В 8-разрядном варианте есть 6 линий «IRQ» ( ещё 5 находятся в 16-разрядном расширении ), каждая из которых может использоваться только одним периферийным устройством. Линии пронумерованы в соответствии со своим приоритетом: при одновременном возникновении нескольких прерываний первым будет обслужена линия с наименьшим номером. Несколько прерываний распределяется среди внутренних периферийных устройств системы, уменьшая число доступных для использования картами расширения. Кроме того, прерывания работают в режиме срабатывания по фронту , не допускающем комбинирования источников по схеме «ПРОВОДНОЕ_ИЛИ». Инженеры IBM, разрабатывавшие оригинальный IBM PC ошиблись с оценкой потребности в разделяемых линиях прерывания и использовали далёкую от оптимума схему.

14.3.9.A Разделяемые линии прерываний

Обычная схема прерываний, реализованная во многих микрокомпьютерах, свободна от подобных ограничений. Взгляните на рис. 14.17 . Есть несколько линий прерываний с разным приоритетом и активным НИЗКИМ уровнем. Для возбуждения прерывания надо подтянуть линию к земле с помощью элемента с открытым коллектором ( или его имитацией на буфере с Z-состоянием, как показано на рис. 14.17 ). Так организуются разделяемые линии «IRQ'» с одной резистивной подтяжкой, допускающие подключение любого числа источников ( на схеме два источника на линии «IRQ1» ). Устройства, не допускающие задержек в обслуживании, подключаются к линиям с высоким приоритетом.

Рис. 14.17   Совместное использование чувствительной к уровню линии прерывания с помощью схемы «ПРОВОДНОЕ_ИЛИ»

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

Автовекторная система с опросом

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

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

Если обслуживаемое устройство было единственным источником прерывания, то линия «IRQ'» перейдёт в ВЫСОКОЕ состояние, и после выхода из обработчика процессор вернётся к исполнению прерванной программы. Если же на линии есть ещё устройства, ожидающие обслуживания, «IRQ'» останется в НИЗКОМ состоянии ( за счёт «ПРОВОДНОГО_ИЛИ» ), и сразу после выхода из обработчика процессор вновь пройдёт через таблицу векторов и вернётся в начало того же обработчика. Теперь опрос выявит другое устройство, и обслуживаться будет уже оно. Отметим, что порядок, в котором проводится опрос источников на линии, позволяет произвести приоритизацию устройств, но теперь не аппаратную, а программную.

Прерывания с подтверждением

Есть ещё более сложная процедура идентификации источников прерывания - подтверждение прерывания . Здесь процессору не требуется считывать регистры состояния, потому что источник прерывания сообщает свои данные в ответ на запрос. Идентификатор ( обычно 8-разрядное число ) выставляется на линиях данных в цикле «подтверждения прерывания» в ответ на сигнал «подтверждения прерывания» от ЦПУ.

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

  1. Процессор фиксирует сигнал прерывания.
  2. Процессор завершает исполнение текущей инструкции и формирует
    1. сигналы шины, сообщающие о прерывании,
    2. уровень обслуживаемого прерывания ( через младшие линии ADDRESS ) и
    3. выдаёт строб, позволяющий источнику прерывания назвать себя.
  3. Устройство, нуждающееся в обслуживании, отвечает на всю эту активность процессора, выставляя на линии DATA свой идентификатор ( вектор ) [* идентификатор записи в таблице векторов прерываний ] .
  4. Процессор считывает по вектору [* смещению в таблице ]   адрес и переходит на код обработчика прерывания.
  5. Обработчик, подобно коду последнего примера, проверяет флаги, посылает и отправляет данные и выполняет прочие действия, зависящие от ситуации и приводящие к снятию сигнала прерывания.
  6. Наконец, обработчик возвращает управление прерванной программе.

Внимательный читатель может заметить слабость предложенного алгоритма. Данный протокол предполагает, что вектор будет выставлять только одно устройство, а на разделяемой линии «IRQ» может находиться несколько активных источников. Обычным методом обхода проблемы является линия «INTP» , которая, в отличие от прочих параллельных сигналов, проходит через каждое устройство. Такая схема называется «daisy chain» и работает следующим образом. У самого ближнего к ЦПУ абонента линия имеет ВЫСОКИЙ уровень. Если устройство не возбуждало прерывание, обслуживаемое в данный момент уровня, оно должно передать «INTP» дальше без изменений. Если устройство возбуждало прерывание обслуживаемого в данный момент уровня, оно устанавливает на своём выходе «INTP» НИЗКИЙ уровень. Вектор на линях DATA выдаётся при совпадении следующих условий:

  1. устройство возбудило прерывание на обслуживаемом уровне и
  2. его вход «INTP» находится в ВЫСОКОМ состоянии.

Такая схема гарантирует, что вектор будет только один, и обеспечивает «последовательный приоритет» в цепочке: устройство, которое электрически ближе к процессору, будет обслужено раньше. [* Цепочка «INTP» строится на логических элементах и исполняемого кода для своего функционирования не требует. Выше просто описана логика работы ] .

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

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

14.3.9.B Маскирование прерываний

В клавиатурный интерфейс был добавлен триггер, который позволил запретить прерывания, хотя контроллер прерываний позволяет заблокировать ( «маскИровать» ) каждую линию «IRQ» в отдельности. Аппаратный запрет был добавлен, чтобы позволить другому устройству использовать линию. Для шин с разделяемыми ( чувствительными к уровню ) входами «IRQ'» особенно важно иметь механизм запрета прерывания через командный регистр. Например, принтерный порт возбуждает прерывание всякий раз, когда его буфер освобождается ( «Дайте данных!» ), но когда печать завершена, прерывания становятся не нужны. Очевидное решение - выключить принтерное прерывание. Но могут существовать другие источники на том же уровне приоритета, поэтому маскировать всю линю - не выход. Зато можно переключить бит в порту принтера и запретить именно его прерывания.

14.3.9.C Как прерывания в PC104 дошли до жизни такой

Шина ISA была создана инженерами IBM и без каких-либо изменений, кроме формы разъёма, принята консорциумом PC104. Процессор i8086/88, стоявший в оригинальном PC, реализует полную автовекторную схему с подтверждением, но разработчики решили упростить аппаратуру и поставили на материнскую плату контроллер прерываний i8259. В IBM PC он получил набор линий, проходящих через все разъёмы расширений параллельно, и был подключён к шине данных процессора. Получив запрос по линии IRQ, i8259 определяет приоритет и выполняет все действия по выдаче на шину вектора. Его внутренний регистр маски ( порт 21h ) позволяет запретить прерывания в любом сочетании.

Сам i8259 позволяет выбрать срабатывание по фронту или по уровню на входах «IRQ» ( в согласии с командным словом, отправленным в порт 20h ). К сожалению, разработчики выбрали режим «по фронту», возможно, потому что его было чуть проще реализовать ( например, достаточно просто подключить вывод генератора в часах реального времени к линии «IRQ0» ). Если бы был выбран режим «по уровню», можно было бы вешать на каждую линию прерывания по несколько источников и использовать программный опрос.

[* С часами реального времени становится понятнее разница между прерываниями по уровню ( общие линии с «ПРОВОДНЫМ-ИЛИ» ) и по фронту ( индивидуальные линии ). Прерывание по фронту фиксируется триггером внутри схемы обработки. Далее линию можно возвращать в пассивное состояние: обработчик всё равно будет вызван и отработает. В варианте срабатывания по уровню ( «ПРОВОДНОЕ-ИЛИ» на разделяемой линии ) активный уровень требуется удерживать в источнике прерывания до тех пор, пока со стороны ЦПУ не будут проведены все мероприятия по его возврату в пассивное состояние. В случае простого генератора срабатывание по фронту выглядит гораздо привлекательнее ].

У проблемы есть частичное решение. Пока линия «IRQ» доступна целиком [* на ней не висит неотключаемый источник ] , можно комбинировать несколько источников на одной материнской плате, используя только ту логику, которая уже есть. Но все источники прерываний должны знать о существовании соседей, поэтому такой вариант не подходит для посторонних периферийных устройств. А с другой стороны использование отдельной линии «IRQ» для каждого устройства быстро приводит к исчерпанию свободных прерываний в сложных системах.

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

В процессорах x86 есть инструкция «INT n», где n - число из диапазона 0...255 , которая позволяет инициировать программное прерывание - переход по вектору в таблице прерываний, подобно тому, как это происходит с аппаратным прерыванием. Среди 256 доступных векторов есть и программные копии 16 аппаратных прерываний по линиям «IRQ0...IRQ15». В результате становятся возможными вызовы «программных прерываний» непосредственно из кода программы [* только не следует забывать, что у обработчика нет возможности узнать, вызван ли он аппаратно или программно ] .

Программные прерывания надо отличать от событий, вызванных аппаратными запросами на обслуживание, о которых шла речь до настоящего момента. Программные прерывания - удобный способ реализации векторизованных переходов к системному программному обеспечению [* с программно настраиваемыми адресами, лежащими в определённой строке таблицы ] . В отличие от аппаратных прерываний здесь известно место и причина вызова, что позволяет передавать дополнительные аргументы через регистры и требует меньше усилий при кодировании ( хотя технически это те же прерывания ). «Программные прерывания» - хитрый способ «расширения» набора инструкций процессора [* , где «n» из инструкции «INT n» служит «кодом операции». Либо, как это реализовано в DOS, выбрать одно из софтовых прерываний для «расширителя команд» и передавать ему «код операции» в виде аргумента в регистре ] .

14.3.10 Прямой доступ к памяти

Бывают ситуации, требующие быстрого перемещения данных из одной точки в другую. Классический пример - дисковые накопители и сетевые соединения. Использование прерываний для передачи каждого блока данных будет слишком накладным по логике работы и слишком медленным. Например, данные с типичного магнитного диска легко достигают установившейся скорости 500 Mb/s . Накладные расходы по обслуживанию прерываний делают ситуацию безнадёжной даже в отсутствие других источников прерываний в системе. Дисковые и ленточные накопители не могут остановиться посреди передачи потока данных ( не говоря уже о сигналах и данных реального времени ). Техническая организация должна обеспечивать быструю реакцию и высокую скорость передачи. Кстати, даже устройства с низким средним потоком данных могут оговаривать низкую латентность - малое время между инициирующим передачу событием и реальным началом передачи данных.

Проблему решает прямой доступ к памяти ( DMA ). С его помощью периферийное устройство может отправлять данные сразу в память. В некоторых микрокомпьютерах прямой доступ к памяти обслуживается центральным процессором, но в данном случае это не важно. Самое главное - при передаче данных по шине нет программно-управляемых действий. Единственным минусом является некоторое замедление исполнения всех программ на компьютере, потому что ПДП «крадёт» циклы шины, «подвешивая» процессор и замедляя программное обращение к памяти. ПДП обычно существенно усложняет схему интерфейсной части, поэтому используется только там, где это необходимо. Тем не менее, знать, как работает такой интерфейс полезно, поэтому здесь будут в сокращённой форме описаны способы его использования. Подобно ситуации с прерываниями PC104/ISA использует типовой протокол ПДП, где всю работу делает контроллер i8237 - отдельная микросхема на материнской плате, что заметно упрощает процесс. Сначала рассмотрим исходный полный протокол, а затем упрощённую версию, применяемую в PC104/ISA.

14.3.10.A Протокол ПДП

Для работы через прямой доступ к памяти периферийное устройство запрашивает доступ к шине по специальным линиям «запроса на прямой доступ». Каналы прямого доступа, подобно прерываниям, разбиты по приоритетам. ЦПУ отдаёт управление, освобождая линии адреса, данных и стробирования. Периферийное устройство [* или специальный контроллер ПДП вроде упоминавшегося i8237 ]   выдаёт на шину адрес памяти и отправляет или принимает данный побайтно в сопровождении стробирующих сигналов, т.е. берёт управление шиной на себя ( становится «задатчиком на шине» ), управляя обменом подобно ЦПУ. Задатчик на шине отвечает за генерацию адресов ( обычно блок последовательно расположенных чисел, создаваемых двоичным счётчиком ) и подачу данных. Проще всего включать счётчики адресов и данных прямо в интерфейс шины. Исходные значения, требуемые для обмена, загружаются процессором через операции ввода-вывода. Затем по команде того же ЦПУ ( через изменение бита в контрольном регистре ), интерфейс переводится в режим ПДП и начинает передавать данные. Обмен может происходить побайтно с освобождением шины, чтобы и процессор мог урвать инструкцию-другую, а может поблочно, надолго блокируя прочих потребителей. Когда передачи завершена, устройство окончательно отдаёт управление шиной, сообщая об этом изменением регистра состояния и, возможно, выставление сигнала прерывания, а процессор решает, что будет происходить далее.

Стандартным примером обмена с использованием ПДП является передача данных с дискового накопителя. Программа запрашивает «файл» по его символическому имени. Операционная система преобразует этот запрос в некоторый набор команд «OUT», передавая в контрольные регистры дискового интерфейса нужное место на диске, начальный адрес в памяти и число передаваемых байт. Закончив передачу, дисковый интерфейс взводит флаг и активирует прерывание. Процессор, который в этот момент может исполнять другие операции или ждать данных с диска, отвечает на прерывание, считывает регистр состояния дискового интерфейса, выясняет, что данные уже в памяти, и переходит к следующей задаче. Таким образом, программируемый ввод-вывод используется для подготовки ПДП, сам ПДП быстро передаёт данные ( придерживая ЦПУ, если требуется ), а прерывание позволяет сообщить, что задание выполнено. Такая последовательность программных этапов очень популярна в области работы с аппаратурой и в частности с дисковыми накопителями. В современных компьютерах с шиной PCI express скорости передачи достигают сотен мегабайт в секунду.

14.3.10.B Прямой доступ к памяти в шине PC104/ISA

PC104/ISA появилась в старое доброе время, когда солнце светило ярче, а протоколы прямого доступа были проще. Аппаратная поддержка включает контроллер ПДП i8237, имеющий счётчики адреса и данных, а также логику блокировки процессора и доступа к шине. Периферийным устройствам, использующим режим ПДП, не требуется формировать адрес и сигналы на шине. Вместо этого надо активировать одну из линий «запроса ПДП» «DRQ1...DRQ3», каждая из которых имеет сигнал подтверждения «DACK1...DACK3» . Дальше всем процессом обмена периферийного устройства с памятью, включая выдачу адреса и стробирование данных, управляет i8237. Всё происходящее со стороны ничем не отличается от обычного функционирования шины. Обычно адресными линиями и стробами памяти ( «MEMW'» и «MEMR'» ) управляет ЦПУ, а в циклах ПДП его замещает контроллер прямого доступа. Разница в том, что для циклов передачи в память источником данных является периферийное устройство, которое как раз в курсе, что цикл не вполне обычный. Для периферийного устройства цикл регулируется сочетанием «DACK» и стробом ввода-вывода ( «IOR'» и «IOW'» ). Вместе с каждым новым стробом данных периферийное устройство выдаёт или получает очередной байт данных. Можно поинтересоваться, почему другая периферия не вмешивается в процесс, ведь на шине появляются как адреса памяти , так и стробы данных не только для памяти, но и для пространства ввода-вывода [* при этом сочетание младших линий «A9...A0» может совпасть с каким-либо реальным посторонним периферийным адресом ] . Секрет в состоянии сигнала «AEN», который введён именно для решения указанной проблемы. Во время циклов ПДП сигнал «AEN» имеет ВЫСОКИЙ уровень, а все обычные адреса ввода-вывода должны сопровождаться НИЗКИМ уровнем на нём.

Но, даже используя специализированную микросхему, требуется настраивать стартовый адрес, объём данных и направление передачи. Все эти сведения процессор пишет в специальные регистры контроллера ПДП в обычных операциях ввода-вывода. Логика действий достаточно очевидна, исключая некоторые сложности с выбором «режимов» ПДП ( однократная передача, блочная и т.д. ). В PC104/ISA скорость передачи через прямой доступ невелика: 2 μs на байт и, как и в ситуации с прерываниями, каналов ПДП явно недостаточно.

14.3.11 Общий обзор сигналов 8-разрядной шины PC104/ISA

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

Рис. 14.18   Шина PC104, рассчитанная на параллельное включение абонентов имеет долгую и славную историю

A19...A0: Адресные линии
«Однонаправленные» сигналы с третьим состоянием. Управляются задатчиком на шине. Для адресации памяти используются все 20 линий ( в сопровождении стробов «MEMR'», «MEMW'» ), образуя 1M адресов, но только 16 младших могут передавать адрес ввода-вывода - 64K адресов [* 16 младших разрядов - ограничение процессоров x86, а в шине PC104/ISA для адресации используются 10 младших линий «A9...A0», т.е. только 1K адресов ] . Устройства ввода-вывода обязаны проверять адрес, только если он сопровождается НИЗКИМ уровнем на линии «AEN».
D7...D0: Линии данных
Двунаправленные с третьим состоянием. В циклах программного вывода и записи в память задатчиком является ЦПУ, в циклах чтения памяти или ПДП из памяти задатчик - память, в цикле программного ввода или ПДП записи в память задатчик - периферийное устройство.
IOR', IOW', MEMR', MEMW': Стробы данных
Выходы с активным НИЗКИМ уровнем и третьим состоянием. Управляются задатчиком на шине. При записи данные должны защёлкиваться в приёмном устройстве по заднему ( восходящему ) фронту сообразно с выставленным на шине адресом. При чтении данные должны выдаваться при активном уровне строба в соответствии с выставленным адресом и быть в гарантированно установившемся состоянии до прихода заднего ( восходящего ) фронта строба.
AEN : Разрешение адреса
Выход с двумя состояниями. Процессор переводит «AEN» в ВЫСОКОЕ состояние, когда шина работает в режиме ПДП. Порты ввода-вывода не должны учитывать состояние адресных линий в момент прихода стробов «IOR'», «IOW'» при ВЫСОКОМ уровне «AEN». Данные на линии DATA должно выставлять ( или принимать ) периферийное устройство, получившее подтверждение «DACK'» [* Состояние адресных линий учитываться не должно, т.к. на них в этот момент выставлен адрес памяти. При пересылке блока памяти периферийное устройство должно иметь внутренний счётчик и указатель данных, т.к. по шине оно получает строб данных, сопровождающийся ВЫСОКИМ уровнем на «AEN» и НИЗКИМ на соответствующем «DACKn'», и более ничего ] .
IRQ2...IRQ7 : Линии запроса прерывания
Входы с двумя состояниями. Срабатывают по восходящему фронту. Активируются устройством, которое требует внимания со стороны процессора. Приоритизированы: «IRQ2» имеет самый высокий, а «IRQ7» - самый низкий приоритет. Могут маскироваться на уровне контроллера прерываний записью в порт 21h . Каждая линия в каждый момент времени должна быть подключена не более чем к одному выходу.
RESET: Сброс
Выход с двумя состояниями. Активный ВЫСОКИЙ. Выставляется внешней аппаратурой при включении питания и используется для приведения периферийных устройств и системы в целом в определённое исходное состояние.
DRQ1...DRQ3 : Запрос на прямой доступ к памяти
Вход. Активный ВЫСОКИЙ. Выставляется периферийным устройством, требующим доступа к каналу ПДП. Приоритизированы: «DRQ1» имеет самый высокий, а «DRQ3» - самый низкий приоритет. Подтверждаются парными «DACK1'...DACK3'».
DACK1'...DACK3' : Подтверждение по каналу ПДП
Выход с двумя состояниями. Активный НИЗКИЙ. Выставляется ЦПУ или контроллером прямого доступа, чтобы сообщить, что запрошенный канал выделен.
ALE : Адресный строб
Выход с двумя состояниями. Активный ВЫСОКИЙ. i8086/88 использует мультиплексированную шину адреса/данных, и «ALE» позволяет зафиксировать адрес во внешних защёлках. Может использоваться для обозначения начала машинного цикла процессора. На схемотехнику устройства ввода-вывода не влияет [* потому что на шину выдаётся уже защёлкнутый адрес с регистров на материнской плате ] .
CLK : Тактовый сигнал
Выход с двумя состояниями. Асимметричный сигнал: 1/3 ВЫСОКИЙ, 2/3 НИЗКИЙ. В оригинальном IBM PC имел частоту 4.77 Mhz , но сейчас обычны более высокие значения. «CLK» используется для синхронизации тактов ожидания ( через запрос «IOCHRDY» ), позволяющих растянуть цикл ввода-вывода для медленных устройств.
OSC : Генератор
Выход с двумя состояниями. Прямоугольный сигнал частотой 14.31818 MHz , который может использоваться ( после деления на 4 ) в системе цветности PAL.
TC : Завершение счёта
Выход с двумя состояниями. Активный ВЫСОКИЙ. Сообщает периферийному устройству, что передача данных в режиме ПДП завершена. «TC» следует использовать совместно с активным сигналом «DACK'», т.к. «TC» - общий для всех каналов [* каналы по естественным причинам работают строго по одному ] .
IOCHCK' : Проверка канала ввода-вывода
Вход «ПРОВОДНОЕ_ИЛИ». Активный НИЗКИЙ. Вызывает немаскируемое прерывание NMI с наивысшим приоритетом, которое информирует об аварийном событии в каком-то периферийном устройстве. Поиск источника выполняется опросом ( §14.3.9.A ). Каждое периферийное устройство, могущее активировать «IOCHCK'» обязано иметь сигнальный флаг, доступный для считывания процессором.
IOCHRDY' : Готовность канал ввода-вывода
Вход «ПРОВОДНОЕ_ИЛИ». Активный ВЫСОКИЙ. Процессор вставляет «такты ожидания» в цикл обращения [* растягивает активное состояние «IOR'», «IOW'» ] , если того просит медленное периферийное устройство. Запрос на расширение цикла обмена - НИЗКИЙ сигнал на линии - проверяется процессором на втором восходящем фронте сигнала «CLK» машинного цикла ( всего их в обычном цикле четыре ). [* Т.е. для растягивания цикла периферия должна устанавливать «IOCHRDY'» в НИЗКИЙ сразу же, как обнаружит свой адрес на шине, иначе не успеет. Считается активным ВЫСОКИМ из-за названия – «проверка готовности ввода-вывода», см. §10.1.7 . Т.е. когда все готовы и можно двигаться дальше, на линии ВЫСОКИЙ уровень ] .
GND, +5Vdc, -5Vdc, +12Vdc, -12Vdc: Линии питания
Регулируемые источники напряжения, питающие периферийные устройства на платах расширения. Допустимые диапазоны нагрузки можно выяснить из документации на конкретный блок питания. В общем случае тока должно хватать всем потребителям, сидящим на шине PC104.

14.3.12 PC104 в качестве одноплатной встроенной системы

На основе стандартизированной шины PC104 создано достаточное число одноплатных компьютеров ( SBC ) и существует впечатляющее разнообразие периферийных устройств, выпускаемых более чем 100 фирмами. Эти небольшие платы часто используют в качестве встраиваемых систем , которые размещаются в приборах и оборудовании, превращая последние в часть интеллектуальной системы. На рис. 14.19 показано, как может выглядеть внутри блок управления сложным оптическим детектором в астрофизической лаборатории. PC104 работает под управлением embedded Linux, живущем на внешнем твердотельном «диске» из микросхем флэш-памяти. Данный одноплатный компьютер фирмы Diamond Systems имеет на борту Ethernet и последовательные порты ( и много другого добра, которое в данной задаче не требуется ). Коробка слева - переходник с медной жилы на оптический кабель, по которому идёт обмен с пультом управления. Использование оптической связи - хорошая идея для обсерватории в горах, где молнии от постоянных летних гроз способны выжечь всё, к чему подходит обычный провод.

Рис. 14.19   Одноплатный компьютер стандарта PC104, встроенный в научное оборудование

Table 14.2 PC104/ISA 8-bit Bus Signals Signal Qty Active CC Cl) Q. Direction CPU^-I/O Pin # Function A[19..0] 20 H 3S — A12..21 address (A15..0 for I/O) D[7..0] 8 H 3S A2..9 data IOR# 1 L 3S —> B14 I/O read strobe IOW# 1 L 3S —> B13 I/O write strobe MEMR# 1 L 3S —> B12 memory read strobe MEMW# 1 L 3S —> B11 memory write strobe AEN 1 H 2S —> A11 DMA address signal IRQ[7..2] 6 t 2S B21..25, B4 interrupt request RESET 1 H 2S —> B2 power-on reset DRQ[3..1] 3 H 2S B16,6,18 DMA request DACK[3..0]# 4 L 2S - B15,26,17,19 DMA acknowledge ALE 1 H 2S — B28 «address latch enable» CLK 1 - 2S —> B20 CPU clock IOCHCK# 1 L OC A1 I/O error — makes NMI IOCHRDY 1 H OC A10 pull LOW for wait states OSC 1 - 2S —> B30 14.31818 MHz TC 1 H 2S — B27 DMA terminal count GND 4 PS A32;B1,31,32 signal & power gnd +5V 2 PS B3,29 +5V supply +12V 1 PS B9 +12V supply -5V 1 PS B5 -5V supply -12V 1 PS B7 -12V supply Notes: (a) OC = open-collector; PS = power supply; 2S = 2-state (active pullup); 3S = 3-state. (b) address, data, and read/write lines can be driven by bus master (e.g., during DMA).

25 В оригинальном PC эту задачу выполнял аппаратный контроллер прерываний i8259. <-

Previous part:

Next part: