Перейти к содержанию

Кадр стека исключения

Обработчик прерывания на 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 не нуждается в особом прологе.

Что лежит в кадре

СмещениеРегистрЗачем
0x00R0аргумент и результат функций
0x04R1аргумент
0x08R2аргумент
0x0CR3аргумент
0x10R12временный регистр
0x14LRадрес возврата прерванной функции
0x18PCадрес возврата из исключения
0x1CxPSRфлаги и состояние

Порядок — по рис. 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). Каждое вложенное прерывание кладёт свой кадр, это нужно учитывать при расчёте глубины стека.

Источники

  1. 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
  2. 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 резервируется, но не заполняется
  3. Arm. Cortex-M0 Devices Generic User Guide, ARM DUI 0497A — разд. 2.3.7: кадр стека при входе в исключение выровнен по двойному слову

Нашли ошибку?