Отладочные средства🔗

Создание и настройка отладочной сессии Vitis🔗

Отладочная конфигурация🔗

Для осуществления процесса отладки с возможностью доступа ко внутренним ресурсам SoC необходимо создать отладочную сессию. Для этого требуется создать и настроить отладочную конфигурацию. Все действия выполняются средствами GUI Vitis.

СОВЕТ

Графическая оболочка Vitis при запуске использует системную тему оформления, и если эта тема тёмная, то отображение окон программы оказывается очень некомфортным – многие места является нечитаемыми из-за низкого контраста текста и фона. Исправить это путём настройки цветовой схемы в полной мере не удаётся, намного более простым и эффективным путём является указание темы при загрузке программы, например:

GTK_THEME=Adwaita `<path-to-xilinx-tools>/Vitis/<version>/bin/vitis&

Для загрузки битстрима PL и исполняемого файла программы PS используется Tcl консоль xsct:

<path-to-xilinx-tools>/Vitis/<version>/bin/xsct

ВНИМАНИЕ

Для того, чтобы консоль xsct была способна подключаться к SoC, нужно предварительно запустить hw_server (опция -d запускает сервер в режиме службы (daemon)):

<path-to-xilinx-tools>/Vitis/<version>/bin/hw_server -d

Создание отладочной конфигурации🔗

Диалог отладочных сессий🔗

Оболочка Vitis позволяет создавать различные вариаты сессий, начиная от полноценного проекта со сборой и загрузкой исполняемого в SoC и заканчивая подключением к уже загруженной программе. Ниже показан вариант подключения к уже загруженной программе, т.к. Vitis используется только в качестве front-end отладчика, а сборка и загрузка исполняемого осуществляются другими средствами.

Первым действием является активация диалога отладочных сессий:


Далее, нужно создать отладочную конфигурацию, указав имя и тип:



После этого можно просто подключиться к SoC – будут доступны внутренности микросхемы.

Пример загрузки с помощью консоли xsct показан ниже.

Загрузка в режиме отладки🔗

В процессе разработки значительно удобнее и быстрее запускать код путём непосредственной загрузки образов (ELF) в память SoC. Для корректной работы необходимо проимитировать процесс штатной загрузки, который состит из двух этапов:

  1. запуск загрузчика (это часть, которой посвящен данный раздел);
  2. загрузка битстрима PL (если нужно);
  3. запуск целевой программы.

В результате работы загрузчика и непосредственно перед запуском целевой программы SoC находится в следующем состоянии:

  • низкоуровневая инициализация (SCU, MMU, кэши) выполнена;
  • все четыре сегмента OCM размещены по адресу 0xfffc0000 в виде непрерывной области памяти.
  • таблица векторов прерываний размещена по адресу 0xfffc0000;
  • таблица трансляции MMU находится по адресу 0xffffc000;
  • инициализация периферийных устройств PS части SoC выполнена загрузчиком (файл ps7_init.c).

Всю эту работу осуществил загрузчик, и проэмулировать его работу средствами отладки весьма проблематично. С другой стороны, программировать флешь память при каждой отладочной итерации является неудачным решением: это затратно по времени, это расходует ресурс флешь памяти и, главное, теряется (или сильно затрудняется) возможность иметь доступ к отладочным средствам в виде инспекции объектов программы, установки точек останова и т.п.

Поэтому для организации отладочной сессии применяется следующий сценарий. Хост:

  1. производит сброс SoC;
  2. загружает (через JTAG) образ загрузчика (ELF);
  3. устанавливает точку останова на функции load_img;
  4. запускает программу загрузчика;
  5. дожидается останова работы программы, что означает, что загрузчик остановился в точке перед непосредственно загрузкой целевой программы из флешь памяти;
  6. удаляет точки останова;
  7. загружает образ целевой программы (вместо load_img);
  8. запускает работу программы.

Описанный сценарий в виде Tcl скрипта, пригодный для запуска из консоли xsct:

proc rl { } {

    set timeout 10

    bpadd 0xc800;                   # address of "_start" function
    puts "-> set bp at _start"
    puts [bplist]
    puts "-> reset!"
    rst

    puts "-> wait for stop at _start"
    while { $timeout } {

        if { [state] == "Running" } {
            after 10
            incr timeout -1
            if { $timeout == 0 } {
                puts "no _start breakpoint, timeout expired"
                break
            }
        } else {
            break
        }

    }
    puts "-> remove breakpoint at _start"
    bpremove -all

    puts "---------------------------------------"
    puts "-> load bld.elf"
    dow build/prj/${::bv}/ps/bld/bld.elf
    puts "-> loading bld.elf done"
    puts "-> verify bld.elf"
    verify build/prj/${::bv}/ps/bld/bld.elf
    puts "-> verification of bld.elf loading done"

    puts "---------------------------------------"
    puts "-> add breakpoint at 'load_img'"
    bpadd load_img
    bplist
    puts "-> run bld.elf"
    con
    puts "-> wait for bld.elf complete"
    while { [state] == "Running" } {

    }
    puts "-> remove breakpoints"
    bpremove -all

    puts "---------------------------------------"
    puts "-> load fpga"
    fpga build/prj/${::bv}/syn/${::bv}.runs/impl_1/lwircam.bit
    puts "-> loading FPGA done"

    puts "---------------------------------------"
    puts "-> load cam.elf"
    dow build/prj/${::bv}/ps/cam/cam.elf
    puts "-> loading cam.elf done"
    puts "---------------------------------------"
    con

Таким образом, описанный алгоритм эмулирует работу связки "загрузчик-целевая программа", позволяя при этом иметь все возможности пользоваться отладочными средствами благодаря тому, что в SoC заргужен полноценный образ ELF.

В коде скрипта можно увидеть ещё одну точку останова – в самом начале на адрес 0xc800. Это связано с особенностями запуска, когда в загрузочной флеши присутствует валидный образ загрузчика – подробнее эта ситуация описана на странице описания стартапа.