Почему отладчик показывает PC = 0xFFFFFFFE и ядро Cortex-M не работает?
Обновлено Редакция GAW
Обычно отказ ядра Cortex-M ведёт в обработчик HardFault. Но что, если отказ случился в самом обработчике HardFault или процессор не может даже прочитать его адрес? Для таких случаев архитектура предусматривает lockup: по разделу B1.5.15 ARMv7-M Architecture Reference Manual ядро перестаёт нормально выполнять программу и бесконечно выбирает одну команду с фиксированного адреса — адреса lockup. Отладчик видит этот адрес в PC и бит S_LOCKUP в регистре DHCSR.
Что означает адрес
| Причина | Приоритет | Адрес lockup |
|---|---|---|
| ошибка чтения начального SP или PC при сбросе | −1 | 0xFFFFFFFE |
| ошибка чтения вектора HardFault | −1 | 0xFFFFFFFE |
| ошибка чтения вектора NMI | −2 | 0xFFFFFFFE |
| отказ по биту Thumb T = 0 (INVSTATE) внутри HardFault или NMI | −1 или −2 | всегда виден как 0xFFFFFFFE |
| ошибка шины при выборке команды в HardFault | −1 | адрес этой выборки |
| другой UsageFault, SVC или BKPT в HardFault | −1 | адрес команды |
Так что 0xFFFFFFFE указывает на таблицу векторов или на переход в состояние, отличное от Thumb, внутри обработчика с приоритетом −1 и выше, а не на конкретную строку программы. В обычном коде тот же INVSTATE — не lockup, а обычный HardFault. Адрес 0xFFFFFFFF в некоторых отладчиках — то же самое: выборка команд идёт по полусловам.
Частые причины на практике
- Неверная таблица векторов. Прошивка собрана под адрес после загрузчика, а запускается с начала флеш-памяти, или наоборот, — и при сбросе ядро берёт SP и адрес Reset_Handler не оттуда. Если у адреса из вектора сброса бит 0 равен нулю, первая же команда вызывает INVSTATE, который по архитектуре эскалируется в HardFault; до lockup доходит, только если испорчен и вектор HardFault (наш вывод из B1.5.15). Как устроена таблица векторов и при чём здесь VTOR, рассказывает статья «Код запуска микроконтроллера».
- Ошибка в самом обработчике HardFault. Например, обработчик читает кадр стека по испорченному указателю стека — тогда в PC будет адрес этой команды обработчика.
Lockup — крайний случай HardFault, поэтому его разбор начинается так же: со статьи о HardFault. Если программатор после прошивки перестал видеть кристалл, порядок подключения под сбросом описан в ответе «Почему ST-LINK перестал подключаться к STM32 после прошивки?».
Похожие вопросы
Почему lockup возникает сразу после прошивки?
Частая причина — неверная таблица векторов: программа собрана под другой адрес флеш-памяти, загрузчик не перенёс VTOR, или файл прошит не с того адреса. Ядро при сбросе читает из таблицы начальное значение SP и адрес Reset_Handler; если там не то, что ожидалось, программа не стартует, а ядро может оказаться в lockup.
Выйдет ли микроконтроллер из lockup сам?
Ядро — только через системный сброс, NMI или вмешательство отладчика. Arm настоятельно рекомендует выводить состояние lockup сигналом наружу, и в некоторых микроконтроллерах он сам вызывает сброс — это смотрят в справочном руководстве конкретного кристалла. Если такого сброса нет, выручит включённый сторожевой таймер; без него устройство будет стоять до отключения питания.
Что делать, если отладчик не подключается к ядру в lockup?
Подключаться под сбросом: отладчик удерживает NRST, подключается и останавливает ядро до выполнения первой команды. Потом стирают флеш-память или записывают исправленную прошивку.
Источники
- Arm. ARMv7-M Architecture Reference Manual, DDI 0403E.e, 2021 — B1.5.15 Unrecoverable exception cases (с. B1-556 — B1-559): lockup при отказе на приоритете −1 и выше; ядро повторно выбирает команду с адреса lockup, ставит DHCSR.S_LOCKUP; выход — NMI (для lockup в HardFault), системный сброс или остановка отладчиком; ошибка чтения вектора при сбросе, при входе в NMI или HardFault и ошибка шины при сохранении контекста на входе в NMI с приоритета −1 — адрес lockup 0xFFFFFFFE; BusFault при выборке команды — адрес этой выборки; UsageFault, SVC и точка останова в HardFault — адрес команды; при сброшенном бите T видимый адрес lockup всегда 0xFFFFFFFE, адреса 0xFFFFFFFE и 0xFFFFFFFF равнозначны; Arm настоятельно рекомендует выводить состояние lockup внешним сигналом; B1.5.3 — если бит 0 вектора равен 0, первая команда вызывает INVSTATE; при сбросе он эскалируется в HardFault, так как UsageFault после сброса выключен
- Arm. Cortex-M0 Devices Generic User Guide, DUI 0497A, 2009 — 2.4.1 Lockup — ядро входит в lockup при отказе в обработчике NMI или HardFault и не выполняет команды до сброса, NMI или остановки отладчиком
Нашли ошибку?
Смотрите также
- HardFaultИсключение Cortex-M при необработанной ошибке
- Таблица векторов прерыванийАдреса обработчиков прерываний в начале флеш-памяти
- Код запускаТаблица векторов, Reset_Handler, .data и .bss, VTOR, FPU, .initN avr-libc
- Почему ST-LINK перестал подключаться к STM32 после прошивки?Программа заняла SWD или ушла в сон — подключаться под сбросом