Программирование DSP Конфигурирование и отладка проектов VDK Fri, March 29 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

Конфигурирование и отладка проектов VDK Печать
Добавил(а) microsin   

В этой статье приведен перевод раздела "CONFIGURATION AND DEBUGGING OF VDK PROJECTS" документации VDK от Analog Devices. Все непонятные термины и сокращения см. в статье [1] и разделе Словарик этой статьи.

[Конфигурирование проектов VDK]

Система разработки VisualDSP++ была расширена таким образом, чтобы дать возможность управлять всеми компонентами VDK. Вы начинаете разрабатывать приложение на основе VDK путем создания набора файлов исходного кода. Среда разработки автоматически генерирует исходный код для каждого запрашиваемого пользователем объекта ядра (например, код шаблона для типа класса потока). Используйте интерфейс закладки Kernel проекта VDK для настройки параметров этих объектов.

Чтобы получить специфическую информацию о том, как настроить системные параметры VDK или как создавать, изменять или удалять компонент VDK, обращайтесь к системе онлайн Help среды разработки VisualDSP++. Следование стандартным процедурам настройки Ваших проектов VDK гарантирует целостную сборку проекта и минимальные затраты усилий на его поддержку. Это уменьшает время разработки и позволяет Вам сконцентрироваться на разработке алгоритма приложения.

Linker Description File. Когда созданный новый проект использует kernel, то к проекту будет автоматически добавлен файл для конфигурирования линкера (Linker Description File, файл с расширением .ldf). Этот файл содержит настройки оп умолчанию, учитывающие специфику использования библиотеки VDK. LDF-файл копируется в корневой каталог Вашего проекта, и его можно изменить так, чтобы он соответствовал индивидуальным настройкам используемой в проекте аппаратуры.

Thread-Safe Libraries. Чтобы обеспечить возможность реентерабельности (reentrant) для потоков, в проект VDK должна быть подключены специальные потокобезопасные (thread-safe) версии стандартных библиотек C и C++. Созданный по умолчанию файл .ldf проекта VDK содержит ссылки на использование этих библиотек. Если Вы измените Linker Description File, то убедитесь, что эти ссылки используют thread safe библиотеки. Ваш .ldf файл проекта находится в папке Linker Files, и он доступен на закладке Project окна браузера проекта среды VisualDSP++.

Заголовочные файлы для VDK API. Когда проект VDK создается в среде VisualDSP++, то одним из автоматически генерируемых файлов проекта будет VDK.h. Это заголовочный файл, который содержит перечисления (enum) для каждого созданного пользователем объекта в среде разработки, и все декларации VDK API. Ваши файлы исходного кода должны подключить файл VDK.h, чтобы получить доступ к любым сервисам VDK.

[Отладка проектов VDK]

Отладка программного обеспечения для встраиваемых приложений всегда была непростой задачей. Чтобы помочь отстраниться от начальной сложности отладки проектов на основе VDK, ядро kernel предоставляет так называемые инструментальные сборки проекта (instrumented builds).

Информация Instrumented Build. Когда осуществляется сборка проекта VDK, у Вас есть опция подключения к исполняемому коду инструментария отладки путем выбора Full Instrumentation. Это делается на закладке Kernel окна активного проекта VDK (Project window). Опция Full Instrumentation устанавливается по умолчанию после создания проекта VDK для включения уровня поддержки отладки. Инструментальная сборка (instrumented build) отличаются от не инструментальной (non-instrumented build), потому что добавляется дополнительный код для сбора в лог статистики выполнения потока (thread statistic logging). Дополнительно инструментальная сборка создает кольцевой буфер для системных событий (system events). Дополнительная запись в лог делается ценой незначительных затрат ресурсов при переключении между потоками (осуществляемого планировщиком) и при определенных вызовах API, но зато помогает трассировать активность процессов в системе.

VDK State History Window. VDK записывает в лог события, определенные пользователем, и изменения состояния системы (system state changes) в специальный кольцевой буфер, так называемый буфер истории состояний (VDK State History). Событие (event) записывается в буфер истории вызовом API-функции LogHistoryEvent(). Вызов LogHistoryEvent() записывает в буфер 4 значения данных: ThreadID потока (вызывавшего LogHistoryEvent), тик (когда был сделан вызов LogHistoryEvent), перечисление и значение, относящееся к конкретному перечислению. Перечисления меньше 0 зарезервированы для использования библиотекой VDK. Для дополнительной информации по типу энумерации истории (history enumeration) см. описание HistoryEnum.

API предоставляет возможность получить большинство данных, отображаемых в окне VDK Status, во время выполнения приложения (run time). Подробное описание этого API находится в руководстве VisualDSP++ Kernel (VDK) User's Guide, Часть 5 "VDK API Reference" (также см. [3]).

С использованием лога истории среда разработки отображает граф работающих потоков и изменения состояния системы (system state changes) в окне State History. Имейте в виду, что отображение данных в этом окне происходит только при остановке отладки. Окно истории состояний (State History), состоянии потоков (Thread Status) и легенда событий потоков (Thread Event) подробно описаны в онлайн Help.

Можно либо заменить подпрограммы записи в лог истории VDK, либо добавить в существующий механизм логирования предоставленную пользователем функцию записи в лог истории (см. раздел "Custom VDK History Logging").

Target Load Graph Window. Инструментальные сборки VDK позволяют Вам анализировать среднюю нагрузку на процессор за определенный период времени. Вычисленная загрузка отображается в графе окна Target Load. Хотя это не точно вычисленная нагрузка на процессор, но граф помогает оценить, насколько поток загружает вычислительные ресурсы процессора. Имейте в виду, что информация по загрузке обновляется при остановке отладки. Для получения более точного измерения нагрузки см. пример LoadMeasurement среди примеров инсталляции VisualDSP++.

Граф Target Load показывает процент времени, которое отлаживаемый целевой процессор находится в потоке ожидания (Idle thread). Загрузка 0% означает, что приложение VDK тратит все свое время в потоке Idle (т. е. система ничем не загружена). Загрузка 100% означает, что целевой процессор никогда не попадает в поток Idle, потому что этого не позволяют другие потоки, отнявшие все процессорное время. Данные загрузки обрабатываются с помощью перемещающегося окна усреднения. Процент загрузки вычисляется для каждого системного тика, и результаты по всем тикам усредняются. Для вычисления нагрузки в каждом тике используется следующая формула:

Load = 1 – (Nidle / Nall)

Здесь Nidle это количество тактов, которое процессор провел в потоке Idle для этого тика, а Nall это количество тактов, проведенная процессором во всех потоках для этого тика). Для получения дополнительной информации по графу Target Load см. онлайн Help.

VDK Status Window. Помимо информации лога истории и нагрузки процессора инструментальная сборка собирает статистические данные по соответствующим компонентам VDK, таким как когда был создан поток, последний запуск Run, количество запусков Run и т. д. Эти данные отображаются в окне состояния (Status window), и обновляется оно при остановке отладки.

API предоставляет доступ к получению большинства данных, отображаемых в окне VDK Status, во время выполнения приложения (runtime, т. е. без остановки приложения). Подробное описание этого API находится в руководстве VisualDSP++ Kernel (VDK) User's Guide, Часть 5 "VDK API Reference" (также см. [3]).

Для получения дополнительной информации по VDK Status window см. онлайн Help.

Основные советы по отладке. Даже с фичами сбора данных, встроенными в VDK, отладка кода потока может быть непростой задачей. Из-за того, что многочисленные потоки в системе асинхронно взаимодействуют с драйверами устройств, прерываниями и потоком Idle, иногда трудно найти место ошибки. 

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

if (VDK_GetThreadID() == < поток_где_есть_подозрение_на_ошибку >)
{
   < какой_нибудь_оператор >; /* Здесь вставьте точку останова */
}

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

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

Обстоятельства, при которых вызывается KernelPanic(), могут включать следующее:

• Ошибки в создании элемента загрузки VDK при старте приложения
• Ошибки времени выполнения, которые не могут быть обработаны через C/C++ Thread error handler
• Внутренние ошибки VDK

См. PanicCode для получения полного списка причин вызова  KernelPanic(). Чтобы помочь пользователям определить причину "паники", VDK устанавливает следующие переменные:

VDK::PanicCode VDK::g_KernelPanicCode;
VDK::SystemError VDK::g_KernelPanicError;
int VDK::g_KernelPanicValue;
int VDK::g_KernelPanicPC;

g_KernelPanicCode показывает причину, из-за которой VDK запускает KernelPanic. Для дополнительной информации по значениям этой переменной см. PanicCode.

g_KernelPanicError более подробно показывает причину ошибки. Например, если g_KernelPanicCode показывает ошибку загрузки (boot error), то g_KernelPanicError указывает, есть ли проблема загрузки в семафоре, флаге устройства и т. д. Для дополнительной информации см. SystemError.

g_KernelPanicValue это значение, смысл которого определяется через энумератор ошибки. Например, если проблема в создании потока загрузки (boot thread) с идентификатором ID равным 4, то g_KernelPanicValue будет равно 4. Дополнительную информацию по значениям этой переменной см. раздел "VDK Error Codes and Error Values" документации по VDK.

g_KernelPanicPC предоставляет адрес кода, который послужил причиной KernelPanic.

Если необходимо, то можно предоставить свою собственную версию функции KernelPanic(). Однако VDK не ожидает восстановление работы приложения, как только хотя бы однажды была вызвана KernelPanic().

Нет никакой поддержки для систем, которые продолжают выполнение после того, как выполнение достигло KernelPanic, и вызов KernelPanic не должен игнорироваться пользовательской версией KernelPanic().

Для создания KernelPanic() на языке C++ используется следующий прототип:

extern "C" void KernelPanic(VDK::PanicCode, VDK::SystemError, const int );

Прототип KernelPanic() на языке C:

void KernelPanic( VDK_PanicCode, VDK_SystemError, const int );

Эти прототипы определены в подключаемых файлах VDK, так что когда VDK.h подключен в файл исходного кода, будет использоваться корректный прототип.

В версии KernelPanic(), предоставленной VDK, переменные g_KernelPanicCode, g_KernelPanicError и g_KernelPanicValue установлены в параметры функции, перед тем как она перейдет в бесконечный цикл.

[Использование KernelPanic для вызова исключений пользователя]

Функцию KernelPanic можно вызывать из приложения для обработки ошибок при отладке. Для этого в первом параметре следует передать значение VDK::kThreadError (ошибка потока), во втором параметре нужно передать доопределенное значение VDK::SystemError, а в третьем параметре нужно передать значение типа u32 или int, которое содержит дополнительную информацию об ошибке. Пример:

VDK::KernelPanic(VDK::kThreadError,             //Параметр типа VDK::PanicCode
                (VDK::SystemError)myError1,     //Параметр типа VDK::SystemError
                 0xBADCAB1E);                   //Произвольное 32-битное значение

Давайте теперь подробнее рассмотрим передаваемые параметры функции из этого примера. 

VDK::PanicCode. Поскольку в основном все ошибки в коде приложения происходят в потоках, то в этом первом параметре можно передавать kThreadError. Также в исключительных случаях можно использовать и другие значения из перечисления PanicCode (см. файл VDK_Public.h). 

VDK::SystemError. Во втором параметре следует передать код ошибки приложения. Вызов KernelPanic в приложении может быть в нескольких местах, и в каждом месте вызова возможно будет происходить какая-то особенная ошибка. Поэтому целесообразно создать перечисление с кодами ошибок приложения, первый код ошибки в котором начинается с кода kFirstUserError (см. перечисление SystemError в файле VDK_Public.h). Это позволит сохранить коды ошибок глобальными, так что они не будут пересекаться с кодами ошибок библиотек VDK, SSL и других библиотек. Пример:

// Коды ошибок приложения error-codes-app.h.
#include "VDK.h"
 
typedef enum
{
   kFirstUserError = VDK::kFirstUserError,
   //Различные ошибки приложения:
   myError1,
   myError2,
   myError3,
   //Ошибки модуля чтения АЦП:
   myErrorAD7680spi_dev_Open,
   myErrorAD7680spi_dev_Control1,    //Настройка параметров SPI2
   myErrorAD7680spi_dev_Control2,    //Разрешение потока данных
   myErrorAD7680spi_dev_Read,
   myErrorAD7680spi_dev_callback,    //Ошибка в функции обратного вызова
} TAppError;

Теперь если ошибка произойдет, выполнение программы в отладчике автоматически остановится, и Вы сможете в окне VDK Status (открывается через меню View -> VDK Windows -> Status) отследить код ошибки SystemError и соответствующее ей значение, описывающее ошибку (переданное в третьем параметре).

Значение, описывающее ошибку. Это третий параметр, который нужно передать в функцию KernelPanic. Можно передать какое угодно значение, лишь бы оно было 32-битным (указатель, константа, какая-то переменная). Целесообразно передавать такое значение, которое дополнительно описывало бы ошибку. Например, если ошибка обрабатывается в callback-фукнции, то можно передать код произошедшего события. Пример функции обратного вызова драйвера SPI:

static void spiCallbackFunction (void *hArg, u32 Event, void *pArg)
{
   /* Приведение указателя к буферу данных */
   ADI_DEV_1D_BUFFER *buffer = (ADI_DEV_1D_BUFFER *)pArg;
   
   /* обработка типа сообщения */
   switch (Event)
   {
   case ADI_SPI_EVENT_READ_BUFFER_PROCESSED:
      // Произошло ожидаемое событие поступления данных.
      accvoltage16 = *(u16*)rxbuffer.Data;
      // Настройка ножки тактов:
      *pPORTDIO_FER   |= portSCKpwr;   //переключение ножки SCLK в режим GPIO
      *pPORTDIO_DIR   |= portSCKpwr;   // SCK выход
      *pPORTDIO_SET    = portSCKpwr;   // SCK=1   
      // Снять выборку АЦП:
      *pPORTDIO_FER   |= portCSpwr;
      *pPORTDIO_DIR   |= portCSpwr;
      *pPORTDIO_SET    = portCSpwr; //~CS=1 (выборка AD7680 не активна)
      break;
   default:
      // Это событие было неожиданным и означает ошибку.
      // Следует вызвать исключение пользователя.
      VDK::KernelPanic( VDK::kThreadError,
                       (VDK::SystemError)myErrorAD7680spi_dev_callback,
                       Event );
      break;
   }
}

[Ссылки]

1. Обзор VisualDSP++ Kernel RTOS (VDK).
2. EE-307: советы по отладке для Blackfin.
3. VDK API для получения информации инструментальной сборки.

 

Добавить комментарий


Защитный код
Обновить

Top of Page