Кадр стека исключения
Обработчик прерывания на Cortex-M — обычная функция на C, без ассемблерного пролога. Это возможно потому, что ядро само сохраняет в стек часть регистров перед входом и само восстанавливает их при выходе. Эти восемь слов называются кадром стека исключения, и по ним же разбирают причины HardFault.
Обновлено Редакция GAW
Кадр стека исключения (exception stack frame) — восемь 32-битных слов, которые ядро Cortex-M автоматически кладёт в текущий стек при входе в исключение: R0, R1, R2, R3, R12, LR, PC и xPSR. Arm называет сам процесс stacking (DUI 0553B, разд. 2.3.7). При выходе из обработчика ядро снимает кадр и восстанавливает регистры, поэтому обработчик на C не нуждается в особом прологе.
Что лежит в кадре
| Смещение | Регистр | Зачем |
|---|---|---|
| 0x00 | R0 | аргумент и результат функций |
| 0x04 | R1 | аргумент |
| 0x08 | R2 | аргумент |
| 0x0C | R3 | аргумент |
| 0x10 | R12 | временный регистр |
| 0x14 | LR | адрес возврата прерванной функции |
| 0x18 | PC | адрес возврата из исключения |
| 0x1C | xPSR | флаги и состояние |
Порядок — по рис. 2-3 DUI 0553B: после сохранения указатель стека указывает на R0, младший адрес кадра. Если в ядре есть FPU и он использовался, выше этих восьми слов ложатся S0–S15, FPSCR и одно зарезервированное слово — расширенный кадр размером 0x68 байт, или 26 слов (DDI 0403E, рис. B1-4). Выравнивание кадра по 8 байтам задаёт бит STKALIGN регистра CCR; у Cortex-M0 кадр всегда выровнен по двойному слову (DUI 0497A).
EXC_RETURN
Одновременно с сохранением кадра ядро выбирает вектор и записывает в LR специальное значение EXC_RETURN. Оно говорит, из какого стека брать кадр при возврате и в какой режим вернуться (DUI 0553B, табл. 2-17):
| EXC_RETURN | Возврат в | Стек | Кадр с FPU |
|---|---|---|---|
| 0xFFFFFFF1 | режим обработчика | MSP | нет |
| 0xFFFFFFF9 | режим потока | MSP | нет |
| 0xFFFFFFFD | режим потока | PSP | нет |
| 0xFFFFFFE1 | режим обработчика | MSP | да |
| 0xFFFFFFE9 | режим потока | MSP | да |
| 0xFFFFFFED | режим потока | PSP | да |
Когда обработчик загружает это значение в PC (командой BX LR, POP или LDR), ядро понимает, что исключение закончено, и снимает кадр.
Когда кадр не сохраняется
При хвостовой цепочке и позднем приходе прерывания ядро не снимает и не кладёт кадр заново — это экономит такты, подробнее в статье о задержке прерывания. В остальных случаях каждое вложенное прерывание добавляет свой кадр, и глубину стека считают с учётом всех уровней вложенности.
Практическое применение
Разбор кадра — главный инструмент поиска HardFault: сохранённый PC указывает на команду, которая вызвала отказ, а LR — на функцию, из которой её вызвали. Как устроены режимы потока и обработчика, стеки MSP и PSP, рассказано в статье о ядре Cortex-M3, а поведение прерываний в программе на C — в статье о прерываниях и параллельности.
Частые вопросы
Как по кадру стека найти место HardFault?
В обработчике HardFault определить, какой стек был активен (бит 2 значения EXC_RETURN в LR: 0 — MSP, 1 — PSP), и прочитать седьмое слово кадра — сохранённый PC (смещение 0x18 от начала кадра). Это адрес команды, на которой случился отказ или к которой ядро вернулось бы. Его ищут в map-файле или дизассемблере.
Почему R4–R11 не входят в кадр?
По соглашению о вызовах функция на C сама сохраняет R4–R11, если их использует, поэтому обработчику-функции хватает аппаратного сохранения R0–R3, R12, LR, PC и xPSR. Планировщику RTOS этого мало — он сохраняет R4–R11 программно, обычно в обработчике PendSV.
Сколько места в стеке занимает кадр?
Без FPU — 8 слов, то есть 32 байта, плюс до 4 байт выравнивания. С сохранением состояния FPU кадр становится расширенным: 0x68 байт, то есть 26 слов — восемь основных, S0–S15, FPSCR и одно зарезервированное слово, плюс необязательное выравнивающее слово (DDI 0403E, разд. B1.5.7, рис. B1-4). Каждое вложенное прерывание кладёт свой кадр, это нужно учитывать при расчёте глубины стека.
Источники
- Arm. Cortex-M4 Devices Generic User Guide, ARM DUI 0553B (ID012616) — разд. 2.3.7, «Exception entry»: при входе в исключение, кроме хвостовой цепочки и позднего прихода, процессор кладёт в текущий стек восемь слов — stacking, stack frame; при работе с FPU сохраняется и состояние плавающей точки; рис. 2-3: кадр без FPU — R0, R1, R2, R3, R12, LR, PC, xPSR, с FPU — ещё S0–S15 и FPSCR, выравниватель; после сохранения SP указывает на младший адрес кадра, выравнивание задаёт бит STKALIGN регистра CCR; параллельно идёт выборка вектора; в LR записывается EXC_RETURN; «Exception return»: табл. 2-17 — значения EXC_RETURN 0xFFFFFFF1, 0xFFFFFFF9, 0xFFFFFFFD, 0xFFFFFFE1, 0xFFFFFFE9, 0xFFFFFFED, у всех биты 31:5 равны 1
- Arm. Armv7-M Architecture Reference Manual, ARM DDI 0403E.e (ID021621) — разд. B1.5.6, псевдокод PushStack: размер кадра 0x20 без FP-контекста и 0x68 с ним; разд. B1.5.7, рис. B1-4: расширенный кадр — R0–R3, R12, LR, адрес возврата, xPSR, S0–S15, FPSCR и зарезервированное слово, при выравнивании ещё одно слово (вершина стека до исключения на 0x68 или 0x6C); при ленивом сохранении место под S0–S15 и FPSCR резервируется, но не заполняется
- Arm. Cortex-M0 Devices Generic User Guide, ARM DUI 0497A — разд. 2.3.7: кадр стека при входе в исключение выровнен по двойному слову
Нашли ошибку?
Смотрите также
- HardFaultИсключение Cortex-M при необработанной ошибке
- СтекОбласть ОЗУ для адресов возврата и локальных переменных
- Задержка прерыванияВремя от запроса до обработчика: Cortex-M3 и AVR
- FPU (блок плавающей точки)Аппаратные операции с float в Cortex-M4F: регистры, CPACR, ленивое сохранение
- Cortex-M3Регистры, карта памяти, bit-banding, NVIC, SysTick, MPU, старт, пример
- Прерывания и общие данныеISR на AVR и Cortex-M, атомарность, критические секции, кольцевой буфер