Обработка прерываний в MIPS-процессорах
Теперь - про "устранение причины". Звучит не очень внятно. Поясню, опять же, на примере. Все тот же мультикор, все тот же таймер.
Таймер установлен с некоторыми настройками. Когда он досчитывает до конца - он в своем регистре статуса/управления сбрасывает бит ENABLE и выставляет бит INT. Бит INT транслируется в соответствующий бит регистра QSTR. А единица в регистре QSTR (при единице в соответствующем разряде MASKR) - это исключение прерывания. То есть, если мы по прерыванию таймера выполним свою обработку и сразу сделаем ERET - то единица в QSTR у нас останется, и мы сразу попадем обратно в исключение. И так - до бесконечности. Поэтому в обработчике мы записываем в бит INT ноль, а в бит ENABLE - единицу (конечно, если нам надо перезапустить таймер). Аналогичная картина - со всеми остальными прерываниями. Для внутреннего устройства необходимо сбросить бит в регистре статуса, для программного прерывания - записать ноль, для аппаратного... С аппаратным сложнее. Оно либо в виде короткого импульса (нажатие кнопки) - тогда, конечно, этот абзац можно пропустить. Либо же "источник" внешнего прерывания как-то связан с процессором помимо этой линии - тогда надо как-то сообщить "источнику"о том, что прерывание обрабатывается и можно его "сбросить".
То есть, в общем виде обработчик прерывания будет выглядеть так:
void int_handler() { unsigned int reg_Cause, reg_Status; unsigned int ExcCode; unsigned int IP; unsigned int IM; // маска прерываний (в регистре Status CP0) unsigned int ActiveIRQ; unsigned int ActiveQST; asm("la $3,reg_Cause"); // адрес переменной reg_Cause asm("mfc0 $4,$13"); // копируем в $4 содержимое регистра Cause (13й регистр CP0). asm("sw $4,0($3)"); // сохраняем это значение в переменную asm("la $3, reg_Status"); // адрес переменной reg_Status asm("mfc0 $4,$12"); // копируем в $4 содержимое регистра Status asm("sw $4,0($3)"); // сохраняем в переменную ExcCode = (reg_Cause >> 2) & 0x1F; // выделяем для удобства поле ExcCode в отдельную переменную IP = (reg_Cause >> 8) & 0xFF; // аналогично - с полем IP IM = (reg_Status >> 8) &0xFF; // и с полем IM поступаем также безжалостно ActiveIRQ = IP & IM; if ( ExcCode == 0 ) { // исключение прерывания ................ if (ActiveIRQ & (1<<4) ) { // внешнее прерывание nIRQ[2] // TO DO } if (ActiveIRQ & (1<<5) ) { // внешнее прерывание nIRQ[3] // TO DO } ................ if ( ActiveIRQ & (1<<7) ) { // прерывание от внутреннего устройства ActiveQST = MASKR & QSTR; ................ if ( ActiveQST & (1<<29) ) { // прерывание от таймера // TO DO ITCSR = 1; // ENABLE = 1, INT = 0 - то есть, сбрасываем прерывание и стартуем таймер заново } ................ } }
На месте многоточий может быть проверка на остальные прерывания. А может и ничего не быть. Это уже зависит от конкретных условий.
Размещение обработчика
Собственно, размещение - вещь несложная. Секцию .text обработчика надо просто разместить по нужному адресу. Как это сделать - разумеется, зависит от инструментов и IDE. Важный нюанс - на обработчики отводится не так уж много места в данной архитектуре. Например, обработчик исключения TLB Refill находится по адресу 0x8000_0000, а вектор исключения прерывания - по адресу 0x8000_0180. То есть, вроде бы на обработку TLB Refill у нас аж 0x180 байт. Это не мало, но не так уж и много. Иногда может быть необходимо и больше места. Да и отслеживать это бывает сложно. Поэтому проще по вектору обработчика разместить только некий минимальный обработчик, который будет выполнять только сохранение контекста и переход на основную функцию, которая располагается в далеких от вектора областях и поэтому может раскинуться хоть на мегабайт. Разумеется, решать каждому персонально, какой подход избрать. Но так как в компиляторе для мультикоров не предусмотрены ключевые слова, чтобы объявить функцию обработчиком - приходится на векторе располагать такой код, делающий jmp на сишную функцию-обработчик.
Почему нельзя объявить обычную функцию обработчиком? В других компиляторах - можно. Но не в данном конкретном случае. Функция на C в ассемблере будет состоять из сохранения используемых регистров, собственно рабочей части (то, что пишет программист), восстановления использованных регистров и возврата управления. Казалось бы, те же части, из которых состоит обработчик прерываний. За одним маленьким исключением. Обработчик должен заканчиваться инструкцией ERET. А обычная функция заканчивается инструкцией JR $31. В регистре $31 по MIPS-овому соглашению содержится адрес возврата из процедуры. Можно вставить ERET в тело функции. Но тогда она будет выполняться ДО восстановления регистров, что приведет вообще к непредсказуемым последствиям. Поэтому наилучшим пока считаю данный вариант. Разумеется, с использованием других компиляторов ситуация может меняться.
Разрешение прерываний
Казалось бы, мелочь. Однако, стоит ее упомянуть. Сколько каждый эмбеддер в своей жизни потратил времени на "отладку", когда вот не входит в прерывание, и все, хоть ты тресни! А потом оказывалось, что они тупо запрещены. Не стану говорить за всех. Но за себя скажу однозначно и честно - таких моментов было очень много.
Сначала надо разрешить прерывания вообще. За это отвечает бит IE регистра Status сопроцессора CP0 (Status[0]). Далее, необходимо разрешить программные, внешние и внутренние прерывания (разумеется, не все, а те, которые нужны. За это, как мы уже говорили, отвечает поле IM[7:0] регистра Status сопроцессора CP0 (Status[15:8]). Но бит IM[7] отвечает за все прерывания от внутренних устройств разом. Поэтому, чтобы разрешить прерывания от конкретных устройств, необходимо внести соответствующее значение в регистр MASKR. И вот лишь после этого прерывания будут доступны.
Ну, пожалуй, на сегодня и все. Посмотрим, что скажут благодарные читатели... Отзывы важны, жду с нетерпением...
- блог пользователя lost_embedder
- 23806 просмотров
Новые записи в блогах
- Устранение дребезга контактов на основе вертикальных счетчиков
- Диагностика Imprecise Bus Faults в микроконтроллерах Cortex-M3/M4/M4F
- Self-powered камера
- Фоновый модулятор: беспроводная связь из ничего (перевод)
- Texas Instruments Analog Applications Journal SLYT612 "Снижение искажений в аналоговых КМОП ключах" (перевод)
- USB MSD. Часть 6. Команды SCSI (перевод)
- USB MSD. Часть 3. USB класс накопителей данных (перевод)
- Texas Instruments Application Report SBAA042 "Кодовые схемы, используемые в аналогово-цифровых преобразователях" (перевод)
- 10 принципов правильного интерфейса
- Релиз SDK на русский микропроцессор КРОЛИК
Recent comments
18 часов 56 минут назад
1 день 22 часа назад
2 дня 2 часа назад
2 дня 10 часов назад
2 дня 19 часов назад
3 дня 21 час назад
4 дня 3 часа назад
6 дней 11 часов назад
1 неделя 11 часов назад
1 неделя 2 дня назад