Данная статья описывает создание дебаг интерфейса для различных микроконтроллеров.
На настоящий момент вы можете отлаживать свою программу через jtag, имеея специальный отладчик (JLINK, FT232 JTAG, STLINK и.т.д). Моя идея отлаживать программу через любой интерфейс микроконтроллера, даже эмулированный, включая удаленный интерфейсы такие как GPRS, Wifi, Lora и.т.д. Типичная схема отладки выглядит так. 

         Моя схема отладки выглядит так.  

        Похожая схема применяется для отладки микроконтроллеров Xtensa l106(ESP8266) там отладка ведется через интерфейс uart. Моя идея более обширная я придумал, как реализовать данную схему не только на микроконтроллерах Xtensa, Cortex-M которые имеют аппаратную поддержку, но и микроконтроллеры, которые такой поддержки не имеют, например, AVR. Общий алгоритм выглядит так прошиваем микроконтроллер и отлаживаем свою программу через интерфейс, который реализован в прошивке. Также возможна перепрививка микроконтроллера через этот же интерфейс на мой взгляд в этом есть смысл если интерфейс удаленный, если интерфейс проводной лучше перепрашивать через программатор, а он все равно вам хотя бы один раз да понадобиться. Исходники интерфейса и само ядро отладчика компилируются вместе с программой пользователя.
       Основная идея заключается в создании 2х поточного приложения, каждый поток имеет свой стек, от использования кучи в потоке отладчика я отказался (т.к. это создает дополнительные проблемы, связанные с пересечением указателя конца кучи и текущего указателя на стек из вариантов постоянно переключаться на user thread при выделении памяти или писать дополнительно свою кучу для debugger thread, а главное при возникновении проблем типа stack overflow будет не известно какой поток это сделал, по собственному опыту я проверял на практике данный вариант, лучше просто кучу при написании ядра отладки не использовать) потоки переключаются с помощью таймера. Один поток для отладки, другой основная программа. 

 

        Для реализации точек останова (Breakpoints) можно использовать 2 варианта: 1 аппаратные точки останова, 2 эмулятор инструкций данного микроконтроллера. К счастью все инструкции можно не эмулировать, а эмулировать только те инструкции, которые используют Program Counter, а остальные инструкции просто перемещать в другое место и там исполнять. В качестве точек останова можно использовать инструкции типа break, если можно настроить микроконтроллер на генерацию Exception при попытке исполнения инструкции break (Cortex-M, Xtensa). Я использую инструкции которые реализуют логику while(1); т.е. процессор останавливается на этой инструкции до срабатывания таймера. Затем инструкция, предварительно скопированная при установке Breakpoint, по команде пользователя эмулируется выше описанным способом.

  

 

 

       Для реализации instruction single step есть 3 варианта:1 аппаратный поставить Breakpoint на следующую инструкцию, 2 сделать задержку между прерываниями за которую может исполниться только одна инструкция, 3 эмулировать или переместить инструкцию в другое место и там исполнить. Второй вариант идеально работает на микроконтроллерах AVR, там идеальная ситуация минимальная задержка между прерываниями 1 такт, любая инструкция выполниться до конца не зависимо от того сколько тактов надо. 3 вариант подходит для микроконтроллеров Cortex-M, 2 вариант не подходит т.к. все инструкции выполняются за разное время, если инструкции тактов не хватило она обрывается и выполняется заново или такие инструкции как push, pop могут прервать свое выполнение на середине, но эта проблема обходиться просто увеличением тактов между прерыванием таймера пока процессор не перескочит на следующую инструкцию, но есть инструкции, которые могут с другими выполняться одновременно вот именно из-за этого 2 вариант не подходит.
       При исполнении инструкции из другого места между прерываниями таймера, есть 2 варианта: задать время между прерываниями равное времени исполнения инструкции, 2 добавить после инструкции инструкцию реализуют логику while(1);. 

 
        Реализация команд RUN, PAUSE. Существуют 3 варианта: 1 переключиться на пользовательский поток и ждать прерывания от отладочного интерфейса, не всегда возможен, 2 переключиться на пользовательский поток и вызывать прерывания через равные промежутки по таймеру, 3 постоянно слать от GDB Server команду RUN на заданный промежуток времени. 1 вариант всем хорош, если он возможен, 2 хороший вариант для удаленных интерфейсов, 3 вариант хорош для проводных интерфейсов (особенно для эмулированных). 

  

        Реализация загрузчика прошивки, моя идея заключается в том, что контроллеру посылаются команды для прошивки flash памяти, которая не занята программой, каждая команда содержит другие команды уже для прошивки памяти программы. То есть сначала пересылаем все что хотим записать в flash, затем посылаем команду контролеру на выполнение команд, записанных в flash память. Чтобы снизить размер пересылаемых данных, посылаем только разницу между предыдущей прошивки и новой. Чтобы минимизировать эту разницу, я решил через linker script отделить часть программы пользователя, от части этой же программы реализующую отладочный интерфейс. На приведенной на изображении схемы Map файла, название секций может быть другим. Ну и соответственно написал функцию инициализации секции user data (копирование всех переменных из flash в RAM), функцию запуска массива user global constructors (для языка C++).  

 

  
        Для переименования секций .text, .data, .bss, .contructors array, в .user text, . user data, . user bss, .user contractors array я написал программу которая переименовывает их во всех объектных файлах(расширение .o) находящихся в определенной папке, для вызова данной программой между процессом компиляции объектных файлов и линковкой я подменяю .exe программу компилятора, на мою которая затем вызывает программу компилятора и программу замены секций.


Комментарии  

# RE: Отладка микроконтроллеров через любой интерфейс, как через jtag dmitriy_1_b 10.07.2020 10:49
:-| Тестовый комментарий :lol:

Site language: