Миро Самек. Построение простых систем на ARM-контроллерах с использованием инструментов GNU (перевод)
- переводы |
- Miro Samek |
- GNU |
- Bare-Metal |
- arm
Часть 8
В этой части описываются низкоуровневые функции-оболочки "ARM_irq()" и "ARM_fiq()", анонсированные в шестой части статьи. Их задача - обеспечить обработку вложенных прерываний, каковая обработка невозможно при использовании атрибута GNU gcc "__attribute__ ((interrupt ("IRQ")))".
Возможно, самой интересной стороной описываемой в данной статье реализации является её хорошая совместимость с архитектурой нового ядра ARM v7-M (так называемого Cortex-M3) [14]. А именно, вся низкоуровневая "кухня" ядра ARM заключена внутри ассемблерных функций-оболочек, что позволяет обработчикам прерываний, подключаемым к контроллеру прерываний, быть обычными функциями на языке "Си".
"Си"-код обработчиков прерываний вызывается в том же режиме работы ядра (SYSTEM), что и код, вызываемый из "main()" (уровень приложения). Кроме того, ассемблерная оболочка однозначно предотвращает использование IRQ/FIQ стеков, размещая вместо этого прерывания всех типов (IRQ и FIQ) в SYSTEM/USER стеке.
Контекст, сохраняемый в стеке, оптимизировн для использования с высокоуровневыми языками и, как предлагается в спецификации ARM v7-M, оболочка сохраняет только 8 регистров, затираемых при соблюдении стандарта вызовов процедур в архитектуре ARM (ARM Architecture Procedure Calling Standard - AAPCS) [14].
На самом деле, программная оболочка прерывания делает в точности тот же стековый кадр ("SPSR", "PC", "LR", "R12", "R3", "R2", "R1", "R0"), что и аппаратура ядра ARM v7-M [14].
(Тут, возможно, следует заметить, что описываемая здесь методика обработки прерываний отличается от проверенного пути, предлагаемого обычно [8, 9, 10, 13]. Большинство работ рекомендует инициализировать таблицу векторов так, чтобы использовать автовекторизацию приоритетного контроллера (см. шестую часть статьи). )
Обработчики прерываний, адресами которых инициализируется контроллер прерываний, требуют весьма специфическую последовательность при входе и выходе, что не позволяет им быть обычными "Си"-функциями. Кроме того, обработчики работают в режиме IRQ или FIQ и используют IRQ и FIQ стеки (как минимум, для части сохраняемого контекста). Соответственно, стековые кадры и само использование стека в традиционных решениях заметно отличается от реализации ARM v7-M.
8.1 Оболочка обработчика IRQ "ARM_irq()"
Функция-оболочка "ARM_irq()", предназначенная для обработки IRQ прерываний, расположена в файле "arm_exc.s" из состава кода, сопровождающего статью, и приведена здесь целиком.
-
Листинг 8.1 Ассемблерная "обёртка" "ARM_irq()" (arm_exc.s) .equ NO_IRQ, 0x80 /* mask to disable IRQ */ .equ NO_FIQ, 0x40 /* mask to disable FIQ */ .equ NO_INT, (NO_IRQ | NO_FIQ) /*mask to disable IRQ and FIQ */ .equ FIQ_MODE, 0x11 .equ IRQ_MODE, 0x12 .equ SYS_MODE, 0x1F .text (1) .code 32 (2) .section .text.fastcode ... ARM_irq: /* IRQ entry {{{ */ (3) MOV r13,r0 /* save r0 in r13_IRQ */ (4) SUB r0,lr,#4 /* put return address in r0_SYS */ (5) MOV lr,r1 /* save r1 in r14_IRQ (lr) */ (6) MRS r1,spsr /* put the SPSR in r1_SYS */ (7) MSR cpsr_c,#(SYS_MODE | NO_IRQ) /* SYSTEM, no IRQ, but FIQ enabled! */ (8) STMFD sp!,{r0,r1} /* save SPSR and PC on SYS stack */ (9) STMFD sp!,{r2-r3,r12,lr} /* save APCS-regs on SYS stack */ (10) MOV r0,sp /* make the sp_SYS visible to IRQ mode */ (11) SUB sp,sp,#(2*4) /* make room for stacking (r0, r1) */ (12) MSR cpsr_c,#(IRQ_MODE | NO_IRQ) /* IRQ mode, IRQ/FIQ disabled */ (13) STMFD r0!,{r13,r14} /* finish saving the context (r0,r1)*/ (14) MSR cpsr_c,#(SYS_MODE | NO_IRQ) /* SYSTEM mode, IRQ disabled */ /* IRQ entry }}} */ /* NOTE: BSP_irq might re-enable IRQ interrupts (the FIQ is enabled * already), if IRQs are prioritized by an interrupt controller. */ (15) LDR r12,=BSP_irq (16) MOV lr,pc /* copy the return address to link */ (17) BX r12 /* call the C IRQ-handler */ /* IRQ exit {{{ */ (18) MSR cpsr_c,#(SYS_MODE | NO_INT) /* SYSTEM mode, IRQ/FIQ disabled */ (19) MOV r0,sp /* make sp_SYS visible to IRQ mode */ (20) ADD sp,sp,#(8*4) /* fake unstacking 8 regs from sp_SYS */ (21) MSR cpsr_c,#(IRQ_MODE | NO_INT) /* IRQ mode, both IRQ/FIQ disabled */ (22) MOV sp,r0 /* copy sp_SYS to sp_IRQ */ (23) LDR r0,[sp,#(7*4)] /* load the saved SPSR from the stack */ (24) MSR spsr_cxsf,r0 /* copy it into spsr_IRQ */ (25) LDMFD sp,{r0-r3,r12,lr}^ /* unstack all saved SYS registers */ (26) NOP /* can't access banked reg immediately */ (27) LDR lr,[sp,#(6*4)] /* load return address from the SYS stack */ (28) MOVS pc,lr /* return restoring CPSR from SPSR */ /* IRQ exit }}} */
Отметим ключевые моменты реализации:
(1) Низкоуровневые IRQ/FIQ обработчики должны быть написаны с использованием 32-разрядных инструкций ARM, так как ядро автоматически переключается в режим ARM при вхождении в прерывание.
(2) "ARM_irq()" функция помещена в специальную секцию (".text.fastcode"), которая будет загружена в оперативную память (см. третью часть статьи) для ускорения выполнения.
(3) IRQ стек не используется и регистр из переключаемого банка "r13_IRQ" ("sp_IRQ") используется как временный регистр для хранения регистра "r0" из контекста SYSTEM.
Премечание. При вхождении в состояние прерывания IRQ ядро ARM выставляет бит "I" в регистре "CPSR" (CPSR[7] = 1), но оставляет бит "F" (обычно сброшенный) без изменений. Это означает, что возможные прерывания IRQ запрещаются, а прерывания FIQ - нет. Подразумевается, что прерывание FIQ может возникнуть в тот момент, когда ядро ARM находится в режиме IRQ. Обработчик прерываний FIQ "ARM_fiq()", который будет рассмотрен позднее, может безопасно перехватывать контроль у "ARM_irq()" везде, где прерывания FIQ не запрещены.
(4) Теперь регистр "r0" может быть перезаписан адресом возврата из прерывания, который должен сохраняться в стеке режима SYSTEM.
(5) С этого момента регистр из переключаемого банка "lr_IRQ" может использоваться для временного хранения регистра "r1" из контекста SYSTEM.
(6) Теперь "r1" может быть перезаписан значением регистра "spsr_IRQ" (Saved Program Status Register), который должен быть сохранён в стеке SYSTEM.
(7) Режим ядра переключается в SYSTEM с запрещёнными IRQ и разрешёнными FIQ перрываниями. Переключение производится для получения доступа к регистрам контекста SYSTEM.
Примечание. На этом этапе бит "F" регистра "CPSR" сбрасывается в нуль (что означает, что FIQ разрешены). В числе прочего это позволяет обойти вторую проблему, описанную в технической заметке фирмы ARM "Что произойдёт, если прерывание возникнет в момент его запрещения ?" (см. седьмую часть статьи).
(8) Регистр "SPSR" и адрес возврата сохраняются в стеке SYSTEM.
(9) Все регистры (кроме "r0" и "r1") используемые по стандарту AAPCS (ARM Architecture Procedure Call Standard) [14] сохраняются в стеке SYSTEM.
(10) Указатель стека SYSTEM помещается в регистр "r0", чтобы он был доступен в режиме IRQ.
(11) Указатель стека SYSTEM корректируется, чтобы дать место для двух дополнительных регистров сохраняемого контекста IRQ. Скорректировав указатель, обработчик IRQ прерывания может сохранять FIQ прерывания в разрешённом состоянии, не рискуя повредить область стека SYSTEM, зарезервированную под контекст IRQ.
(12) Ядро переключается обратно в режим IRQ с запрещёнными IRQ и разрешёнными FIQ прерываниями. Это делается для получения доступа к содержимому остальных переключаемых регистров режима IRQ.
(13) Остатки контекста в составе регистров "r0" и "r1" сохранены в стеке SYSTEM (их копии по-прежнему находятся в регистрах переключаемого банка режима IRQ "r14_IRQ" и "r13_IRQ" соответственно). С настоящего момента сохранённый стековый кадр режима SYSTEM содержит 8 регистров и выглядит как показано ниже (в точности как стековый кадр прерывания в ARM v7-M [14]):
-
Старшие адреса SPSR PC (адрес возврата) | направление LR | роста R12 V стека R3 R2 R1 R0 <-- SP_SYS Младшие адреса
(14) Ядро опять переключается в режим SYSTEM с запрещёнными IRQ и разрешёнными FIQ прерываниями. Отметим, что указатель стека "sp_SYS" смотрит на вершину стекового кадра, так как был скорректирован после первого переключения в режим SYSTEM в точке (11).
(15-17) Аппаратно зависимая функция "BSP_irq()" вызывается для обработки прерывания на уровне приложения. Обратите внимание, что "BSP_irq()" - обычная "Си"-функция, которая может быть скомпилирована под набор инструкций ARM или THUMB. Обычно она использует аппаратный контроллер прерываний (такой, как AIC фирмы Atmel) для выбора вектора текущего прерывания (см. шестую часть статьи).
Примечание. Функция "BSP_irq()" получает управление с запрещёнными IRQ (и разрешёнными FIQ) прерываниями, но она может разблокировать IRQ, если ядро снабжено контроллером прерываний, приоритизирующим IRQ прерывания аппаратно.
(18) IRQ и FIQ прерывания запрещаются на время модификации указателя стека.
(19) Текущее значение указателя стека "sp_SYS" копируется в регистр "r0", чтобы сделать его видимым в режиме IRQ.
(20) Перед выходом из режима SYSTEM проводится корректировка значения регистра "sp_SYS" и из стека целиком выбрасывается стековый кадр прерывания, состоящий из 8 регистров. Это действие возвращает стек SYSTEM в состояние, предшествующее прерыванию [а в регистре "r0" содержится некорректированное значение].
Премечание. Несмотря на то, что указатель стека исправлен, его содержимое всё ещё не восстановлено. Полная блокировка прерываний в данной точке критически важна для сохранения содержимого памяти ниже скорректированного указателя стека [стек растёт в сторону младших адресов памяти].
(21) Ядро переводится в режим IRQ с запрещёнными IRQ и FIQ прерываниями для завершающего возврата из прерывания.
(22) Сохранённый некорректированный указатель стека SYSTEM копируется в "sp_IRQ" регистрового банка режима IRQ, устанавливая его тем самым на "старую" вершину стека SYSTEM.
(23-24) Значение регистра "SPSR" выбирается из "пустой" памяти на 7 слов ниже вершины стека и записывается в регистр "SPSR_irq".
(25) 6 регистров восстанавливаются из стека SYSTEM. Отметим специальную форму инструкции "LDM" (с символом "^" на конце), которая означает, что регистры восстанавливаются из стека SYSTEM/USER. Отметим также, что специальная инструкция "LDM(2)" не позволяет проводить обратную запись, и указатель стека не меняется. (За дополнительной информацией обратитель к секции "LDM(2)" в руководстве "ARM Architecture Reference Manual" [13]).
(26) Очень важно не обращаться к регистровому банку сразу после специальной инструкции "LDM(2)".
(27) Из стека выбирается адрес возврата. Отметим, что он расположен на 6 слов ниже "старой" вершины стека.
(28) Для возврата из прерывания предполагается загрузка указателя команд (PC) адресом возврата, а регистра "CPSR" - содержимым "SPSR", каковые действия осуществляются специальным ваниантом инструкции "MOVS pc, lr".
8.2 Оболочка обработчика FIQ "ARM_fiq()"
Функция-оболочка "ARM_fiq()" предназначена для обработки FIQ прерываний, расположена в файле "arm_exc.s" в составе кода, сопровождающего статью, и приведена здесь целиком.
-
Листинг 8.2 Ассемблерная "обёртка" "ARM_fiq()" (arm_exc.s) (1) ARM_fiq: /* FIQ entry {{{ */ MOV r13,r0 /* save r0 in r13_FIQ */ SUB r0,lr,#4 /* put return address in r0_SYS */ MOV lr,r1 /* save r1 in r14_FIQ (lr) */ MRS r1,spsr /* put the SPSR in r1_SYS */ (2) MSR cpsr_c,#(SYS_MODE | NO_INT) /* SYSTEM mode, IRQ/FIQ disabled */ STMFD sp!,{r0,r1} /* save SPSR and PC on SYS stack */ STMFD sp!,{r2-r3,r12,lr} /* save APCS-regs on SYS stack */ MOV r0,sp /* make the sp_SYS visible to FIQ mode */ SUB sp,sp,#(2*4) /* make room for stacking (r0, SPSR) */ MSR cpsr_c,#(FIQ_MODE | NO_INT) /* FIQ mode, IRQ/FIQ disabled */ STMFD r0!,{r13,r14} /* finish saving the context (r0,r1)*/ MSR cpsr_c,#(SYS_MODE | NO_INT) /* SYSTEM mode, IRQ/FIQ disabled */ /* FIQ entry }}} */ /* NOTE: NOTE: BSP_fiq must NEVER enable IRQ/FIQ interrrupts! */ LDR r12,=BSP_fiq MOV lr,pc /* store the return address */ (3) BX r12 /* call the C FIQ-handler (ARM/THUMB) /* FIQ exit {{{ */ /* both IRQ/FIQ disabled (see NOTE above) */ MOV r0,sp /* make sp_SYS visible to FIQ mode */ ADD sp,sp,#(8*4) /* fake unstacking 8 registers from sp_SYS */ MSR cpsr_c,#(FIQ_MODE | NO_INT) /* FIQ mode, IRQ/FIQ disabled */ MOV sp,r0 /* copy sp_SYS to sp_FIQ */ LDR r0,[sp,#(7*4)] /* load the saved SPSR from the stack */ MSR spsr_cxsf,r0 /* copy it into spsr_FIQ */ LDMFD sp,{r0-r3,r12,lr}^ /* unstack all saved SYSTEM registers */ NOP /* can't access banked reg immediately */ LDR lr,[sp,#(6*4)] /* load return address from the SYS stack */ MOVS pc,lr /* return restoring CPSR from SPSR */ /* FIQ exit }}} */
Функция "ARM_fiq()" очень похожа на обработчик IRQ, обсуждавшийся выше, за исключением режима FIQ, используемого вместо режима IRQ. Комментарии поясняют только небольшие, но существенные отличия в способе запрещения прерываний и взаимодействия с обработчиком, написанным на языке "Си" - "BSP_fiq()".
(1) Вхождение в обработчик FIQ производится с запрещёнными IRQ и FIQ прерываниями и, таким образом, режим FIQ не виден из любого другого режима работы ядра.
(2) Режим ядра переключается на SYSTEM для получения доступа к указателю на стек SYSTEM. Обращаем внимание на то, что запрет на IRQ и FIQ прерывания сохраняется всё время работы "ARM_fiq()".
(3) "Си"-функция "BSP_fiq()" вызывается для обработки прерывания с уровня приложения. Отметим, что "BSP_fiq()" - обычная функция на языке "Си" и может быть скомпилирована под набор инструкций ARM или THUMB. В отличие от IRQ, FIQ прерывания не проходят через приоритетный контроллер и функция "BSP_fiq()" обязана сохранять блокировку прерываний.
Примечание. "BSP_fiq()" получает управление в момент когда оба типа прерываний зпарещены и ни при каких обстоятельствах не должна их разрешать. Обычно линия FIQ в ядре ARM не приоритизируется, несмотря на то, что проходит через контроллер прерываний.
- блог пользователя teap0t
- 154437 просмотров
Новые записи в блогах
- Устранение дребезга контактов на основе вертикальных счетчиков
- Диагностика 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
4 часа 12 минут назад
11 часов 10 минут назад
11 часов 19 минут назад
2 дня 6 часов назад
3 дня 9 часов назад
3 дня 13 часов назад
3 дня 21 час назад
4 дня 6 часов назад
5 дней 8 часов назад
5 дней 14 часов назад