Почему прошивка с semihosting зависает без отладчика?
Обновлено Редакция GAW
Semihosting — способ, которым программа на микроконтроллере просит компьютер разработчика что-то сделать: вывести строку, прочитать файл, узнать время. Канал связи — отладчик. По спецификации Arm на M-профиле вызов делается командой BKPT #0xAB, а номер операции лежит в регистре: например, SYS_WRITE0 (0x04) печатает строку. Если библиотека ввода-вывода собрана с semihosting, каждый printf в итоге выполняет эту команду.
Что происходит без отладчика
BKPT — команда точки останова. Под отладчиком ядро останавливается, отладчик видит код 0xAB, выполняет операцию и отпускает программу. Без отладчика по разделу B1.5.14 ARMv7-M Architecture Reference Manual точка останова эскалируется в HardFault:
| Состояние | Что делает BKPT 0xAB | Результат |
|---|---|---|
| отладчик подключён, semihosting включён | остановка, отладчик выполняет вызов | вывод в консоль отладчика |
| отладчик подключён, semihosting выключен | остановка в отладчике | программа стоит на BKPT |
| отладчика нет, DebugMonitor выключен | эскалация в HardFault, HFSR.DEBUGEVT = 1 | программа в обработчике HardFault |
| BKPT внутри HardFault или NMI | невосстановимая ошибка | lockup до сброса |
Типичный симптом: плата работает, пока к ней подключён программатор с открытым сеансом отладки, и «зависает» при питании от блока. Как найти место отказа по кадру стека, рассказано в статье о HardFault, а что такое точка останова — в статье о точке останова.
Чем заменить
- UART — работает без отладчика, нужен свободный порт и переходник.
- SWO и ITM на Cortex-M3, M4 и M7 — вывод через отладочный разъём. Если отладчик не включил ITM, функция CMSIS
ITM_SendCharпросто возвращается, и программа идёт дальше; подробности — в статье об SWO. - SEGGER RTT — кольцевой буфер в ОЗУ, который читает J-Link.
Сравнение способов вывода и их цену по времени разбирает статья «Логирование в прошивке».
Похожие вопросы
Как понять, что виноват именно semihosting?
Остановите зависшую программу отладчиком без сброса: если она в обработчике HardFault, посмотрите регистр HFSR. Флаг DEBUGEVT означает, что в HardFault превратилось отладочное событие — точку останова BKPT. По адресу из кадра стека в дизассемблере будет команда BKPT 0xAB.
Почему под отладчиком всё работает?
Отладчик с включённой поддержкой semihosting сам ловит BKPT 0xAB, выполняет запрошенную операцию — например, печатает строку в своей консоли — и продолжает программу. Без него поймать точку останова некому, и архитектура отправляет её в HardFault.
Можно ли оставить semihosting в отладочной сборке?
Можно, если такая сборка запускается только под отладчиком. Для всего, что работает автономно, вывод заменяют на UART, SWO или SEGGER RTT: они не требуют отладчика для продолжения работы программы.
Источники
- Arm. Semihosting for AArch32 and AArch64, release 2025Q4 (репозиторий ARM-software/abi-aa) — таблица Semihosting Trap Instructions and Encodings: на M-профиле вызов — BKPT #0xAB (код 0xBEAB) в T32; операции SYS_WRITEC (0x03) и SYS_WRITE0 (0x04) выводят символ и строку на консоль хоста
- Arm. ARMv7-M Architecture Reference Manual, DDI 0403E.e, 2021 — B1.5.14 Fault behavior (с. B1-553) — HardFault on breakpoint (BKPT) escalation: BKPT при выключенной остановочной отладке и выключенном или менее приоритетном DebugMonitor эскалируется в HardFault, флаг HFSR.DEBUGEVT; C1.5 Debug event behavior — остановочная отладка выключена при DHCSR.C_DEBUGEN = 0; BKPT в обработчике HardFault или NMI без отладчика — lockup
- Arm. CMSIS 6.4.0: CMSIS/Core/Include/core_cm3.h — ITM_SendChar(): символ пишется в порт 0, только если установлены ITM_TCR.ITMENA и бит 0 ITM_TER, иначе функция сразу возвращается
Нашли ошибку?
Смотрите также
- Логирование и трассировкаprintf через UART, ITM и SWO, SEGGER RTT, уровни журнала, метки времени
- HardFaultИсключение Cortex-M при необработанной ошибке
- SWO (вывод трассировки)Однопроводный вывод трассировки и отладочных сообщений Cortex-M
- Точка остановаАдрес, на котором отладчик останавливает выполнение программы