Как известно, совместимость с инструментарием GNU и поддержка GDB делают практически любую популярную среду разработки пригодной для отладки широкого спектра встраиваемых платформ, чаще всего бесплатно и легально. В теории.
Что получается на практике при попытке подружить STM32 и NetBeans, и возможно ли в принципе получить работоспособную систему с поддержкой новейших камней — под катом.
Спойлер
Да. Но нет.
Немного лирики
Съезжать с Микрочипа очень не хотелось. Однако после покупки компанией Атмела, сначала прикрыли, возможно, одно из самых перспективных семейств в портфолио компании — PIC32MM, а затем и всю линейку MIPS. Стало очевидно, что в обозримом будущем переход на ARM неизбежен, а т. к. Микрочип за два года так и не интегрировал поддержку атмеловских контроллеров в свою экосистему, никаких преимуществ «Оставайтесь с нами» не давало. Скорее наоборот — скорректированная вверх ценовая политика и традиционные организационные трудности слияния компаний сделали Атмеловские АРМы менее привлекательными. Одновременно подвернулся проект, об который PIC32MZ просто споткнулся. Критическая масса была набрана.
Почему STM: широкий охват рынка, бюджетная отладка, бесплатная полнофункциональная среда SW4STM32 на базе опенсорса, ну и политический аспект — ST Microelectronics поддерживается правительством Франции как стратегический ресурс, поэтому внезапный уход с рынка или поглощение, вроде бы, не грозят.
Отладка — первые впечатления
SW4STM32 установился традиционным способом — многократным нажатием кнопочки Next > (* здесь и далее все эксперименты происходят на Win7 x64). Подходящий для тестирования нужной функции демо-проект был вынут из пакета STM32Cube Firmware, все сработало более-менее сразу из коробки. Первый запуск JTAG эмулятора оставил впечатление: весь цикл входа в режим отладки, начиная от подключения и заканчивая остановкой в начале main () с обновлением контекста, оказывается, может укладываться в 1-2 с. По сравнению с микрочиповскими отладчиками (даже REAL ICE за полтыщи) разница в скорости — кратная!
И все же, кое-что неприятно удивило.
Что не так в Eclipse / SW4STM32
Мириады настроек и нелогичная организация, скрытые пункты меню, перспективы, баги в интерфейсе и визуальные артефакты, отсутствующие часто используемые горячие кнопки и функции на тулбаре, мелкие корявые нечитаемые пиктограммы и маркеры, отсутствие «Find Usages» — отчасти субъективны, и при желании можно приспособиться. Но: регулярно забывает сохранить измененные файлы перед сборкой, хотя все галочки где надо проставлены; при принудительном ручном сохранении не видит изменений, и инкрементальная сборка получается неактуальной; полной пересборки (Clean and Build) как единой команды нет, при этом после принудительной очистки через Clean Project сборка происходит с ошибками (файлового доступа ?), и успешно завершается только с 4-й попытки — это разумному объяснению уже не поддается. Даже у ранних Бета-релизов MPLAB X на базе древнего NetBeans 6.xx не было таких проблем 10 лет назад, какие есть у официально поддерживаемой среды разработки под STM32 сегодня.
Кроме того, с SW4STM32 набирается уже 3 копии типовых IDE в системе, т. к. помимо него стоят еще намертво прибитый к NetBeans 8.0.1 и некоторым образом отгороженный MPLAB X (т. е. использовать его для других языков/платформ невозможно), и NetBeans 8.2 для Джавы и С/С++.
Получается, что настройка NetBeans 8.2 для работы с STM32 устранит описанные практические проблемы Eclipse, сократит кол-во копий IDE, и сведет к одной платформе, пусть и немного разных версий.
NetBeans 8.2 и GNU ARM Tools
NetBeans лучше использовать 32-битный, т. к. помимо удвоенного расхода памяти, отличий 64-битной версии обнаружить не удалось.
Гуглом быстро нашлось руководство по настройке. Принципиальная разница заключалась только в ОС (Linux у них против Win7 x64 у меня), поэтому призовой игрой стала установка *nix-окружения MSYS, которое входит в пакет MinGW. Настройки тулчейна должны получиться примерно такие:
Важный момент — при добавлении тулчейна GNU ARM выбрать семейство «GNU MinGW», в таком случае NetBeans будет правильно вызывать MSYS. Если на машине уже установлен Cygwin, логично будет использовать его, соответственно семейство для GNU ARM надо выбрать «GNU Cygwin».
Добиться успешной компиляции оказалось не просто, а очень просто. Поскольку SW4STM32 использует этот же компилятор, подсмотрев командную строку вызова компилятора в SW4STM32 и скопировав недостающие ключи в Project Properties → C Compiler → Additional Options
получаем в точности такой же выходной результат, но с важным практическим отличием — все собирается с первого раза, Clean and Build есть и отлично работает:
Но и этот результат можно улучшить, добавив опциональную Post-Build обработку. Открываем Makefile, и в раздел .build-post: .build-impl дописываем:
.build-post: .build-impl
cp ${CND_DISTDIR}/${CONF}/${CND_ARTIFACT_NAME_${CONF}} ${CND_ARTIFACT_NAME_${CONF}}
arm-none-eabi-objcopy -O ihex ${CND_ARTIFACT_NAME_${CONF}} ${CND_ARTIFACT_NAME_${CONF}}.hex
arm-none-eabi-size ${CND_ARTIFACT_NAME_${CONF}}
(важно — отступ должен быть одинарным символом табуляции, не пробелами)
Построчно: 1 — копирует объектный файл (.elf) из выходной папки в корень проекта, для упрощения доступа; 2 — генерирует HEX из elf (можно закомментить, если HEX не нужен); 3 — выводит объем занятой памяти по сегментам.
Конечный результат:
Пока все прекрасно.
OpenOCD – первые сложности
В упомянутых онлайн-руководствах программирование кристалла через OpenOCD протекает просто и буднично. Устанавливается последняя версия (0.10.0), берется конфигурационный файл (из комплекта OpenOCD или из папки проекта SW4STM32), в Project Properties → Run прописывается команда вида:
и все сразу работает. Действительно, так дело и обстоит для младших семейств типа STM32F103 и F407, но меня интересуют F7 и H7. С первыми официальная версия OpenOCD 0.10.0 вылетает с ошибками «auto_probe failed» и «undefined debug reason 7»; вторые не поддерживаются вообще. Перепробованы все доступные официальные сборки 0.10.0 от Янв 2017 и Янв 2018 — результат идентичный. Поиск по ключевым словам подтверждает существование проблемы, хотя массовой ее не назовешь; разбора и решения нет.
Но ведь существует версия, которая гарантированно работает — из комплекта SW4STM32. Естественно, оказывается улучшенной и дополненной, с новыми скриптами и поддержкой семейства H7. Также в ней изменена файловая структура, и в плагине ресурсы хранятся в отдельном модуле, поэтому, чтобы консолидированная в единой папке утилита увидела свои скрипты, потребовался ключ -s.
Board.cfg для NUCLEO-F767ZI, за вычетом комментов, и уплотненный:
set CHIPNAME STM32F767ZITx
source [find interface/stlink-v2-1.cfg]
transport select hla_swd
source [find target/stm32f7x.cfg]
set WORKAREASIZE 0x10000
set ENABLE_LOW_POWER 1
set STOP_WATCHDOG 1
reset_config srst_only srst_nogate connect_assert_srst
set CONNECT_UNDER_RESET 1
Наконец запуск через Run Main Project:
Прошивка успешна, код выполняется.
Отладка
Схема подразумевается самая традиционная: локальный GDB-сервер на OpenOCD, NetBeans подключается к нему через localhost:3333 по TCP. Cоответственно, для NetBeans потребуется плагин Gdbserver.
Упростить запуск OpenOCD можно через bat-скрипт, а поскольку после завершения сеанса он выходит в консоль, имеет смысл бесконечно зациклить перезапуск:
:start
openocd -f debug.cfg -s d:/Prog/openocd/scripts
goto start
Запуск:
В версии из SW4STM32 явно не пишется, но сервер ожидает подключения по TCP к порту 3333. В NetBeans необходимо выбрать Debug → Attach Debugger…, и установить:
Сессия активна. Терминал OpenOCD:
С виду все выглядит неплохо — отладочная сессия активна, код выполняется. Однако проблема все-таки существует.
Проблема
В режиме свободного прогона выполнение невозможно остановить.
Если перед запуском сессии поставить брейкпоинт, при входе в отладку произойдет остановка с обновлением контекста, будет работать пошаговое выполнение и просмотр / изменение переменных, т. е. в принципе — все базовые функции, необходимые для полноценной отладки:
Но только до следующего свободного запуска, после которого останется только закрыть и перезапустить сессию.
Еще одна неприятная мелочь связана с программными брейкпоинтами: ф-ция SYSTEM_Halt () определена как __asm__ («bkpt»), и ее срабатывание приводит к отображению ненужного диалога:
При нажатии Discard and Pause работает как требуется (т. е. останавливает выполнение), однако установить эту опцию по умолчанию и отключить отображение окна стандартными средствами невозможно.
До кучи, хотелось бы автоматизировать запуск OpenOCD и подключение отладчика прямо через NetBeans.
Однако, объективно, единственная функция, которой не хватает для более-менее полноценной отладки, — это остановка выполнения (она же требуется и для установки брейкпоинта на лету).
Отладка отладки
Поиск гуглом показал, что похожие проблемы с неработающей остановкой GDB в NetBeans существовали, но были исправлены несколько лет назад. За неимением лучшей идеи, были скачаны исходники NetBeans в надежде пройтись отладчиком по живому. На удивление, довольно быстро удалось локализовать проблему до вызова внешней утилиты GdbKillProc.exe, которая по сути является оберткой для DebugBreakProcess ( pid ) из WinAPI. Принцип работы утилиты сводится к неинтрузивному прерыванию процесса (аналог «kill -SIGINT [pid]» под *nix, или Ctrl+C в консоли).
Но она не работает.
Что опробовано
В консольном режиме GDB-клиент (arm-none-eabi-gdb.exe) на Ctrl+C реагирует правильно, т. е. останавливает выполнение кода без закрытия сессии, и ждет дальнейших указаний.
ps -W и Windows Process Explorer отображают процесс правильно, и PID совпадает с внутренней переменной в NetBeans.
Ручной вызов «kill -SIGINT [pid]» из пакета MSYS выдает ошибку «No such process».
Проверка через «taskkill /pid [pid]» выдает «The process … could not be terminated… This process can only be terminated forcefully...», что, похоже, свидетельствует о системной блокировке. С ключом /f процесс закрывается полностью, что тоже не годится.
По ходу дела выяснилось, что в Windows дела с генерацией сигналов прерывания обстоит неважно, а точнее — никак: стандартно поддерживается только аналог SIGTERM, что соответствует грубому вырубанию процесса, и общепринятого решения как бы нет.
На просторах интернета обнаружилась утилита windows-kill, созданная для эмуляции Ctrl+C и Ctrl+Brk. Процесс находит, прерывание отправляет без ошибок, но GDB-клиент по-прежнему не реагирует.
Эксперименты проводилась с использованием всех 32-битных версий (NetBeans, ARM Tools, MSYS 1.0), кроме windows-kill, которая 32-битная запускаться отказалась («...unable to start correctly...»). Возможно, проблема именно в этом, т. к., по обрывочным данным с форумов и багтрекеров, разрядности утилиты и процесса должны совпадать. Сложность здесь в том, что ARM не предлагает x64 версию тулчейна под Windows, т. ч. единственный путь устранить разнородность — заставить работать x32 версию windows-kill, что тоже не ясно, возможно ли под Win x64 в принципе.
В середине процесса была с нуля переустановлена ОС, и никаких изменений в поведении подопытных замечено не было, т. ч. с большой уверенностью особенности конкретной системы можно исключить.
Требуется помощь зала
Собственно, все вышенаписанное можно считать вступлением к этому параграфу.
Остался последний шаг, чтобы сделать отладку STM32 под NetBeans реальной:
требуется работающий программный механизм передачи сигнала прерывания SIGINT (Ctrl+C) в процесс GDB-клиента под Windows
Ниже приведена рекомендация по настройке минимальной конфигурации, достаточной для проверки / отладки вышеозначенной проблемы. Если / когда ее удастся разрешить, статья будет переделана в простое пошаговое руководство по настройке NetBeans + OpenOCD под несколько разных семейств и отладочных плат. Дальнейшую функциональность можно будет допиливать по желанию, уже имея работоспособное базовое решение.
Тестовая установка
В качестве аппаратной платформы предлагается использовать плату Blue Pill на основе STM32F103C8T6 и клон ST-Link V2.
Необходимо:
1. Установить GNU Arm Embedded Toolchain
2. Установить OpenOCD 0.10.0 (сборка под Win)
3. Прописать папки bin обоих пакетов в PATH (Control Panel → System → Advanced System Settings → Environment Variables… → Path).
4. В удобном месте создать файл board.cfg, скопировать содержимое:
source [find interface/stlink-v2.cfg]
source [find target/stm32f1x.cfg]
transport select "hla_swd"
reset_config none separate
set WORKAREASIZE 0x5000
set CHIPNAME STM32F103C8T6
set ENABLE_LOW_POWER 1
set STOP_WATCHDOG 1
set CLOCK_FREQ 4000
set CONNECT_UNDER_RESET 1
5. Подобрать подходящую тестовую прошивку (test.elf), главный критерий — чтобы явно было различимы выполнение и остановка. Скопировать в удобное место.
6. Из удобного места прошить плату:
openocd -f board.cfg -c "program test.elf verify reset exit"
Прошивка должна запуститься. Примерный вывод OpenOCD в консоль:
7. Из удобного места запустить GDB-сервер OpenOCD:
openocd -f board.cfg
Код все еще выполняется; примерный вывод (консоль остается заблокирована OpenOCD):
8. В другой консоли или напрямую запустить arm-none-eabi-gdb.exe из пакета GNU ARM Embedded Toolchain и выполнить команды:
target extended-remote localhost:3333
(код все еще выполняется)continue
(код выполняется, консоль заблокирована)Ctrl+C
(код остановлен, консоль активна)continue
(снова выполняется, консоль заблокирована)Задача — вывести GDB-клиент из состояния выполнения (т. е. по сути сэмулировать Ctrl+C) программным способом.
Для выяснения Process ID использовать «ps -W» из *nix-окружения, или Windows Process Explorer (устанавливается дополнительно).
Возможно, у кого-то отладка заработает правильно сразу после начальной настройки NetBeans – подобная информация тоже будет нелишней, особенно с подробным описанием системы и возможных особенностей.
Просьба делиться идеями в комментах, и, надеюсь, общими усилиями удастся превратить смелый эксперимент в полезное руководство по настройке еще более полезного инструмента.