Диспетчер прерываний

Особое значения при разработке обработчиков прерываний и драйверов устройств (портов ввода/вывода) имеет библиотека подпрограмм signal.h. В ней содержатся процедуры для инициализации, разрешения, запрещения и прерываний и, собственно, сам программный диспетчер прерываний.

Диспетчер прерываний анализирует условия (аппаратные сигналы, состояния флагов и т.п.), которые могут иметь место во время выполнения программы и приводить к возникновению прерываний. Он определяет, каким способом С-программа будет реагировать на эти условия. Обработчики прерываний предназначены в первую очередь для обработки внешних прерываний, прерываний от таймера и от портов ввода/вывода. Для настройки диспетчера прерываний используются две функции: interrupt() и signal().

int interrupt(int SIG, void (*func)(int)) (int);

Функция interrupt() определяет способ обработки (вызываемую функцию-обработчик) сигнала прерывания с номером SIG, получаемого каждый раз в ходе выполнения программы, например:

#include <signal.h>

...

void SPORT0_Receive_Handler(int sig)

...

interrupt(SIG_SPR0I, SPORT0_Receive_Handler); // прерывание по приему от SPORT0

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

Функция возвращает значение SIG_ERR=0x02, если условный код переданного прерывания не существует. В противном случае – возвращает 0. Перечень условных кодов прерываний приведен в файле signal.h.

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

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

int signal(int SIG, void (*func)(int)) (int);

Функция raise() "посылает" сигнал определенного прерывания в исполняемую программу (в диспетчер прерываний), т.е. фактически устанавливает соответствующий бит в регистре IRPTL.

int raise(int SIG);

Функция clear_interrupt() сбрасывает бит соответствующего прерывания в регистре защелки IRPTL.

int clear_interrupt(int SIG);

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

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

1) Стандартный (normal) диспетчер прерываний (функции interrupt() и signal()). Сохраняет все Scratch-регистры и стек цикла. Вложенные прерывания разрешены. Нет ограничений на вложенность циклов (программная поддержка). Передает номер прерывания в подпрограмму-обработчик в качестве параметра. Переход на обработчик занимает от 125 до 160 тактов. Обработчик является обычной процедурой. Когда происходит сигнал прерывания управление передается на таблицу векторов прерываний, затем на диспетчер прерываний, который вызывает установленную командой interrupt() или signal() С-процедуру (не прерывание и не asm-процедуру! – т.е. командой jump). По завершении обработки выход из обработчика осуществляется как из обычной подпрограммы на С (не из прерывания не из процедуры, т.е. не по rti и не по rts, а командой jump). Таким образом, диспетчер полностью берет на себя обработку прерывания и фактически после сохранения регистров просто вызывает процедуру-обработчик, а затем после возвращения – восстанавливает регистры.

2) Быстрый (fast) диспетчер прерываний (функции interruptf() и signalf()). Количество вложенных циклов не должно превышать 6 уровней (т.е. аппаратная поддержка циклов). Стек цикла не сохраняется. Вложенность прерываний не запрещается (до 20 уровней). Диспетчер не передает обработчику номер прерывания. Переход на обработчик занимает приблизительно 50-60 тактов. Код самого обработчика представляет собой обычную С-подпрограмму с присущими ей правилами вызова и возврата. Диспетчер выполняет сохранение/восстановление меньшего числа регистров.

3) Очень быстрый (super fast) диспетчер прерываний (функции interrupts() и signals()). Количество вложенных циклов не должно превышать 6 уровней (т.е. аппаратная поддержка циклов). Стек цикла не сохраняется. Вложенные прерывания запрещены путем запрещения в диспетчере бита глобального разрешения прерываний IRPTEN (в регистре MODE1). Диспетчер не передает обработчику номер прерывания. Код самого обработчика представляет собой обычную С-подпрограмму с присущими ей правилами вызова и возврата. Перед вызовом обработчика диспетчер для сохранения контекста задачи не сохраняет регистры в стеке, а просто переключается на альтернативный набор регистров (при этом в регистрах работы со стеком поддерживаются правильные значения – копируются через R-регистры). Поэтому переход на обработчик занимает всего 30-40 тактов.

4) Диспетчер прерываний для обработчиков на ассемблере (функции interruptss() и signalss()) или С-функций, скомпилированных с использованием директивы #pragma interrupt. Диспетчер сохраняет только 5-6 регистров, определяющих среды выполнения и режим работы процессора (MODE1, ASTAT), возлагает на компилятор (или asm-подпрограмму) сохранение и восстановление регистров. Количество вложенных циклов не должно превышать 6 уровней (т.е. аппаратная поддержка циклов). Стек цикла не сохраняется. Вложенные прерывания не запрещаются (до 20 уровней вложенности). Диспетчер не передает обработчику номер прерывания. Переход на обработчик занимает около 30 тактов. Вызывается как asm-процедура (т.е. вызов/возврат идет через аппаратный стек программного секвенсора).

Для сокращения длительности процесса вызова обработчика и уменьшения объема кода диспетчера процедура установки (регистрации) обработчика прерывания построена в виде самомодифицируемого кода. Немодифицируемые версии диспетчеров реализованы в виде функций interruptXXnsm() и signalXXnsm(), которые функционально идентичны соответствующим функциям interruptXX() и signalXX().

Реализованы также варианты стандартного диспетчера interruptcb() и signalcb() с поддержкой явного запрещения круговых буферов (путем обнуления) регистров L0-L5 и L8-L15. Для работы диспетчера требуется примерно на 50 тактов больше для сохранения и восстановления L-регистров.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: