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

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

Источники

  1. 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) выводят символ и строку на консоль хоста
  2. 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
  3. Arm. CMSIS 6.4.0: CMSIS/Core/Include/core_cm3.h — ITM_SendChar(): символ пишется в порт 0, только если установлены ITM_TCR.ITMENA и бит 0 ITM_TER, иначе функция сразу возвращается

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