Программирование DSP Blackfin FAQ Thu, March 28 2024  

Поделиться

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

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

Blackfin FAQ Печать
Добавил(а) microsin   

Здесь приведены краткие ответы на вопросы и решения различных проблем, возникающих при программировании процессоров Blackfin компании Analog Devices.

Процессор суффиксом F (если точнее, то F8) имеет встроенную в кристалл память FLASH на 8 мегабит (1 мегабайт), а у процессора без суффикса такой встроенной памяти нет. В процессоре без суффикса F сигналы ~FCE и ~FRESET (это входы) соединены с землей GND внутри кристалла. В процессоре с суффиксом F сигнал ~FCE должен быть на плате разведен на один из сигналов ~AMSx, а сигнал ~FRESET должен использоваться для сброса внутренней памяти FLASH (обычно его вместе с сигналом ~RESET параллельно подключают к выходу микросхемы контроллера сброса).

Это разные типы памяти, отличающиеся друг от друга по скорости доступа со стороны ядра процессора. Цифра означает степень близости к вычислительному ядру: 1 самая близкая к ядру память, у которой меньше всего задержек, и 3 самая дальняя от ядра, т. е. самая медленная по доступу.

L1. Это самая быстрая память (SRAM), с которой ядро процессора работает на полной скорости. Эта память присутствует на всех процессорах семейства Blackfin, но её размер и привязка к физическим адресам может меняться. Память L1 используют для кэширования других областей памяти (память инструкций, память данных уровней L2 и L3) и как сверхбыструю память данных. Обычно кэшируют памяти инструкций и память данных уровня L3.

L2. Внутренняя память, тоже очень быстрая. Она может быть в наличии на кристалле, а может и не быть - все зависит от модели процессора. Может быть использована только под данные.

Примечание: внутренняя память L2 доступна только в двухядерных процессорах ADSP-BF561 Blackfin. Память L2 может быть сконфигурирована только как SRAM.

L3. Внешняя память, самая медленная. Под внешней памятью подразумевается память, которая для организации доступа подключается через специальный аппаратный модуль EBIU и внешнюю шину. Обычно это внешняя память FLASH для хранения программы или SDRAM для данных и программы. У некоторых моделей процессоров, например ADSP-BF538F, внешняя память FLASH встроена в кристалл, и подключается к шине процессора через специальные сигналы выборки (~FCE, ~AMSx).

Подробнее про устройство памяти Blackfin см. [2].

[Уровни памяти и кэширование]

На рис. 1 показана иерархия памяти процессора Blackfin. Память L1 может использоваться полностью как память SRAM, или может быть поделена между SRAM и памятью кэша.

EE 306 Blackfin memory hierarchy

Рис. 1. Иерархия памяти процессора Blackfin.

Кэш нужен для того, чтобы уменьшить задержки, связанные с обращениями к медленной памяти. Подробнее про кэширование в процессорах Blackfin см. даташит Using Cache Memory on Blackfin® Processors EE-271 (или его перевод в статье [12].

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

По умолчанию вложенность прерываний запрещена, т. е. выполняющееся прерывание прервать нельзя (очень похоже на принцип обработки прерываний у микроконтроллеров AVR).

Однако есть возможность использования вложенных прерываний. Чтобы разрешить вложенность, нужно в обработчике прерывания немедленно обратиться на чтение к регистру RETI (например, протолкнуть в стек). После этого такой обработчик может быть прерван любым прерыванием, у которого приоритет выше. Для того, чтобы отменить вложенность, в конце обработчика прерывания нужно обратиться к RETI на запись (например, восстановить из стека). Подробнее см. [3].

Ядро процессора Blackfin и его вычислительные операции построены по принципу конвейера - чтобы можно было оптимизировать скорость выполнения программы с минимальными задержками. Такое функционирование может привести к тому, что иногда некоторые инструкции могут быть выполнены не в том порядке, в котором присутствуют в коде. Аналогично операции с памятью на чтение и запись могут быть выполнены не в том порядке, в каком их видит программист, если специально не позаботится от этом. Для синхронизации конвейера потока выполнения и синхронизации содержимого внутренних буферов ядра и хранилищ данных служат инструкции CSYNC и SSYNC. Подробнее см. [3].

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

1. Настроить нужный порт в режим GPIO, если он еще не настроен. Это делается битами регистра PORTxIO_FER (здесь x это имя порта, например C, D, E).

Примечание: у некоторых портов некоторых моделей процессоров Blackfin (например у порта F микроконтроллера ADSP-BF538) нет специального регистра для настройки режима ножки порта. Вместо этого режим определяется конфигурированием периферийных устройств процессора. Дикая путаница - нет определенной системы, поэтому подробности см. в руководстве по аппаратуре Вашего процессора.

2. Настроить нужный бит порта для работы в режиме входа битами регистра PORTxIO_DIR (здесь x это имя порта, например C, D, E, F).

3. Разрешить входной буфер для этого порта битами регистра PORTxIO_INEN (здесь x это имя порта, например C, D, E, F).

4. Прочитать состояние ножки в битах регистра PORTxIO, наложив нужную маску (здесь x это имя порта, например C, D, E, F).

Есть также возможность настроить генерацию прерывания по изменению лог. уровня на входе ножки порта. Обратите внимание, что выводы GPIO в режиме входа не имеют встроенных подтягивающих резисторов (pull-up, pull-down). Ниже показан простой пример чтения входного уровня (опрос кнопки выключения).

/*********************/
/*    Файл pins.h    */
/*********************/
#ifndef __PINS__
#define __PINS__
 
#include < cdefBF538.h >
 
//Входы:
#define portBTN_OFF PD5    //0: сигнал от кнопки: ВЫКЛючить питание.
 
//Выходы:
#define portON33V PD8      //1: ВКЛючение 3.3V, силовое питание.
#define portON15V PC6      //1: ВКЛючение 15V, питание индикатора OLED.
 
#define ON33V()   (*pPORTDIO_SET = portON33V)
#define OFF33V()  (*pPORTDIO_CLEAR = portON33V)
#define ON15V()   (*pPORTCIO_SET = portON15V)
#define OFF15V()  (*pPORTCIO_CLEAR = portON15V)
#define POWER_BTN_PRESSED() (0 == (*pPORTDIO & portBTN_OFF))
 
void ConfigureOutputs (void);
void ConfigureInputs (void);
void PowerON(void);
void PowerOFF(void);
 
#endif

Обратите внимание, что в заголовочном файле pins.h подключается файл cdefBF538.h. Благодаря этому в коде становятся доступны мнемонические имена для выводов процессора (PD8, PC6 и т. д.) и адресов регистров MMR (pPORTCIO_FER и т. п.). Имя подключаемого файла зависит от модели процессора (в данном примере использовался процессор ADSP-BF538F).

/************************/
/*    Файл pins.cpp     */
/************************/
#include "pins.h"
#include "delay.h"
 
void ConfigureOutputs (void)
{
   //Настройка выхода PC6:
   *pPORTCIO_FER |= portON15V;      //настроить ножку как GPIO.
   *pPORTCIO_DIR |= portON15V;      //настроить ножку как выход GPIO.
   *pPORTCIO_CLEAR = portON15V;     //==0, выключение 15V
   //Настройка выхода PD8:
   *pPORTDIO_FER |= portON33V;      //настроить как GPIO.
   *pPORTDIO_DIR |= portON33V;      //настроить как выход GPIO.
   *pPORTDIO_CLEAR = portON33V;     //==0, выключение 3.3V
}
 
void ConfigureInputs (void)
{
   //Настройка входа для кнопки:
   *pPORTDIO_FER |= portBTN_OFF;    //настроить как GPIO.
   *pPORTCIO_DIR &= ~portBTN_OFF;   //настроить как вход GPIO.
   *pPORTDIO_INEN |= portBTN_OFF;   //разрешить работу входа GPIO.
}
 
void PowerON ()
{
   ON33V();
   Delay_ms(1);
   ON15V();
}
 
void PowerOFF ()
{
   OFF15V();
   Delay_ms(100);
   OFF33V();
}

 

/************************/
/*    Файл main.cpp     */
/************************/
#include "pins.h"
 
int main()
{
   ...
   ConfigureInputs();
   ConfigureOutputs();
   PowerON();
   while(1)
   {
      ...
      if (POWER_BTN_PRESSED())
         PowerOFF();
   }
} 

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

1. Настроить нужный порт в режим GPIO, если он еще не настроен. Это делается битами регистра PORTxIO_FER.

Примечание: у некоторых портов некоторых моделей процессоров Blackfin (например у порта F микроконтроллера ADSP-BF538) нет специального регистра для настройки режима ножки порта. Вместо этого режим определяется конфигурированием периферийных устройств процессора. Дикая путаница - нет определенной системы, поэтому подробности см. в руководстве по аппаратуре Вашего процессора.

2. Настроить нужный бит порта для работы в режиме выхода битами регистра PORTxIO_DIR.

3. Записью в регистры  можно менять состояние выходных ножек процессора. Это регистры PORTFIOPORTFIO_SETPORTFIO_CLEARPORTFIO_TOGGLE.

Имейте в виду, что некоторые выводы порта работают в режиме выхода как двухтактные (т. е. дают выходной ток и для лог. 0, и для лог. 1), а некоторые как выходы с открытым стоком. Никакой системы нет, например ножки PC1 и PC4 процессора ADSP-BF538 имеют выходы с открытым стоком, а остальные ножки порта C двухтактные. Подробности см. в руководстве по аппаратуре Вашего процессора. Ниже показаны простые примеры управления выходами процессора.

/*********************/
/*    Файл pins.h    */
/*********************/
#ifndef __PINS__
#define __PINS__
 
#include < cdefBF538.h >
 
//Входы:
#define portBTN_OFF PD5    //0: сигнал от кнопки: ВЫКЛючить питание.
 
//Выходы:
#define portON33V PD8      //1: ВКЛючение 3.3V, силовое питание.
#define portON15V PC6      //1: ВКЛючение 15V, питание индикатора OLED.
 
#define ON33V()   (*pPORTDIO_SET = portON33V)
#define OFF33V()  (*pPORTDIO_CLEAR = portON33V)
#define ON15V()   (*pPORTCIO_SET = portON15V)
#define OFF15V()  (*pPORTCIO_CLEAR = portON15V)
#define POWER_BTN_PRESSED() (0 == (*pPORTDIO & portBTN_OFF))
 
void ConfigureOutputs (void);
void ConfigureInputs (void);
void PowerON(void);
void PowerOFF(void);
 
#endif

 

/************************/
/*    Файл pins.cpp     */
/************************/
#include "pins.h"
#include "delay.h"
 
void ConfigureOutputs (void)
{
   //Настройка выхода PC6:
   *pPORTCIO_FER |= portON15V;      //настроить ножку как GPIO.
   *pPORTCIO_DIR |= portON15V;      //настроить ножку как выход GPIO.
   *pPORTCIO_CLEAR = portON15V;     //==0, выключение 15V
   //Настройка выхода PD8:
   *pPORTDIO_FER |= portON33V;      //настроить как GPIO.
   *pPORTDIO_DIR |= portON33V;      //настроить как выход GPIO.
   *pPORTDIO_CLEAR = portON33V;     //==0, выключение 3.3V
}
 
void ConfigureInputs (void)
{
   //Настройка входа для кнопки:
   *pPORTDIO_FER |= portBTN_OFF;    //настроить как GPIO.
   *pPORTCIO_DIR &= ~portBTN_OFF;   //настроить как вход GPIO.
   *pPORTDIO_INEN |= portBTN_OFF;   //разрешить работу входа GPIO.
}
 
void PowerON ()
{
   ON33V();
   Delay_ms(1);
   ON15V();
}
 
void PowerOFF ()
{
   OFF15V();
   Delay_ms(100);
   OFF33V();
}

 

/************************/
/*    Файл main.cpp     */
/************************/
#include "pins.h"
 
int main()
{
   ...
   ConfigureInputs();
   ConfigureOutputs();
   PowerON();
   while(1)
   {
      ...
      if (POWER_BTN_PRESSED())
         PowerOFF();
   }
}

Пример лога, который появляется в окне Build среды VisualDSP:

Linking...
[Warning pp0037] "///CL":0 Incompatible redefinition of macro 'USER_CRT'
Preprocessor finished with 0 error(s) 1 warning(s)

Предупреждение появляется потому, что из-за переноса проекта, переключения конфигурации (из Debug в Release или наоборот), или по какой-то иной причине испортилась настройка "LDF Preprocessing", в ней ошибочно дважды указано макроопределение для USER_CRT, которое используется в makefile для передачи компилятору. Чтобы исправить ситуацию, откройте меню Project -> Project Options -> Link -> LDF Preprocessing, и проверьте поле ввода "Preprocessor macro definitions:". Пример ошибочной настройки этого поля, из-за которого появляется предупреждение:

USER_CRT="myproj_basiccrt.doj",USE_FILEIO,__cplusplus,USER_CRT="myproj_basiccrt.doj"

Очевидно, что тут дважды повторяется определение USER_CRT. Удалите последнее определение, чтобы осталось только одно, и предупреждение исчезнет.

Источник: Incompatible redefinition of macro 'USER_CRT' site:ez.analog.com.

Ошибка линкера на совершенно нормально определенную функцию:

..
Linking...
[Error li1021]  The following symbols referenced in processor 'p0' could not be resolved:
        'InitOLED() [_InitOLED__Fv]' referenced from '.\Debug\Main.doj'
Linker finished with 1 error
cc3089: fatal error: Link failed
Tool failed with exit/exception code: 1.
Build was unsuccessful.

Причина ошибки может быть в том, что в проекте имеются модули с расширением *.c и *.cpp. Если одна и та же функция (в нашем примере InitOLED) вызывается и из модуля *.c, и из модуля *.cpp, или модуль, в котором определена функция InitOLED, имеет расширение *.c, но модули, из которых она вызывается, имеют расширение *.cpp, то возникнет такая ошибка. Есть два спосооба решения проблемы.

Способ 1. Удалить модуль *.c из проекта, переименовать в *.cpp, и снова добавить в проект, после этого ошибка исчезнет.

Способ 2. В заголовочном файле модуля *.c сделать стандартное обрамление extern "C" для определения функции, пример:

#pragma once
 
#if defined(__cplusplus)
extern "C" {
#endif
EX_INTERRUPT_HANDLER(TimerISR);
 
#ifdef __cplusplus
}
#endif

Функция clock() часто используется в программе для получения текущего системного времени - аппаратного счетчика тактов ядра. Она возвращает количество тактов, прошедших с момента включения питания или сброса системы. Функция определена в файле %ProgramFiles%\Analog Devices\VisualDSP 5.0\Blackfin\include\time.h следующим образом:

#pragma misra_func(time)
#pragma inline
#pragma always_inline
static clock_t clock(void)
{
  _cycle_t _cnt;
  _GET_CYCLE_COUNT( _cnt );
  return  (clock_t) _cnt;
}

Примеры использования clock() для генерации блокирующих задержек:

#include < time.h >
 
#define CCK 327680                   //тактовая частота ядра, кГц
#define MKS_TICKS (((CCK/100)+5)/10) // сколько тиков в 1 мкс
#define MS_TICKS CCK                 // сколько тиков в 1 мс
 
void delay_mks(clock_t mks)
{
   clock_t end_tick = clock() + mks*MKS_TICKS;
   while (end_tick > clock())
      ;
}
 
void delay_ms(clock_t ms)
{
   clock_t end_tick = clock() + ms*MS_TICKS;
   while (end_tick > clock())
      ;
}

Отсчет не блокирующих задержек основан на запоминании в статической или глобальной переменной текущего значения счетчика ядра, и последующих проверок этого счетчика. Пример формирования не блокирующей задержки на 1000 миллисекунд:

//Код, запускающий отсчет задержки:
static clock_t tick_timeout = clock() + MS_TICKS * 1000;
...
 
//Код, который постоянно проверяет, истекла ли задержка:
if (tick_timeout < clock())
{
   //Задержка 1000 мс истекла.
   ...
}
else
{
   //Задержка 1000 мс еще не закончилась
   ...
}

Sequence переводится как "последовательность". В данном контексте секвенсер означает специальный блок ядра, управляющий последовательностью выполнения программы. Регистры секвенсера содержат адреса возврата из различных видов подпрограмм (на какой адрес перейдет управление после выполнения инструкции RTS, RTI, RTX, RTE, RTN).

RETS возврат из обычной подпрограммы
RETI возврат из прерывания
RETX возврат из исключения
RETE возврат из режима отладки
RETN возврат из немаскируемого прерывания (NMI)

Текущие значения регистров секвенсера можно посмотреть в режиме отладки VisualDSP через меню Register -> Core -> Sequencer.

ADSP BF538 Sequencer

Иногда ошибка в программе приводит к тому, что выполнение в отладчике останавливается по метке __unknown_exception_occured или __fatal_exeption. Что можно предпринять, чтобы быстро найти в программе возможное место ошибки?

Регистры секвенсера. В режиме отладки, когда произошла ошибка, зайдите в меню Register -> Core -> Sequencer. Вы увидите содержимое регистров RETS, RETI, RETX, RETE, RETN, которые покажут адрес кода, в котором возможно произошла ошибка. Особое внимание нужно уделить адресу в RETS (возврат из подпрограммы), RETI (возврат из прерывания), RETX (здесь может быть адрес не выровненной инструкции).

ADSP BF538 Sequencer

Регистры состояния секвенсера. Зайдите в меню Register -> Core -> Status -> Sequencer Status. Здесь проверьте содержимое регистров HWERRCAUSE (статус аппаратной ошибки) и EXCAUSE (программная ошибка, связанная с ходом выполнения кода). Подробнее см. [3].

ADSP BF538 Sequencer Status

EXCAUSE 0x2A. На скриншоте показан случай программной ошибки с кодом 0x2A - Instruction fetch misaligned address violation [3] (EXCAUSE == 0x2A). Была выполнена попытка выборки из кэша не выровненной инструкции. Когда возникло такое исключение, адрес возврата, который находится в регистре RETX, является адресом возврата с ошибочным выравниванием, а не адресом инструкции, которая привела к ошибке. Например, если предпринято косвенное ветвление (indirect branch) на не выровненный адрес, содержащийся в P0 (один из регистров-указателей), то не выровненный адрес в RETX будет равен P0, а не адресу инструкции ветвления. Имейте в виду, что исключение 0x2A никогда не будет сгенерировано инструкциями ветвления относительно счетчика инструкций (PC-relative branches), исключение 0x2A генерируется только инструкциями косвенного ветвления (indirect branch).

ADSP BF538 EXCAUSE 0x2A

Чтобы определить место в коде, где произошла ошибка EXCAUSE 0x2A, в отладчике посмотрите значение регистра RETX (меню Register -> Core -> Sequencer), и в контекстном меню окна Disassembly введите адрес для "Go To...", равный RETX.

Подробнее расшифровку кодов аппаратных и программных ошибок см. в статье "ADSP-BF538: обработка событий (прерывания, исключения)" [3], глава "Аппаратные ошибки и обработка исключений".

Иногда при попытке запуска отладчика (в момент загрузки программы в память чипа) появляется диалог запроса такого рода: "Trying to load an executable built for 0.5 silicon into a 0.4 silicon target. Continue? Да/Нет".

VisualDSP load 04silicon to 05silicon

Ничего страшного в этом сообщении нет, можно просто нажать "Да". Для эстетики можно скорректировать ревизию кремния, указав компилятору опцию -si-revision. Добавить опцию  -si-revision можно в диалоге настроек проекта VisualDSP, раздел Compile, строка ввода "Additional options:". Добавьте туда опцию наподобие следующей:

-si-revision 0.4

Опция  -si-revision версия (silicon revision, версия кремния) указывает компилятору собрать программу для специальной ревизии кристалла (разные ревизии кристалла могут иметь исправленные или не исправленные ошибки, отраженные в anomalies и errata). С указанной ревизией будут разрешены методы обхода ошибок и аномалий кристалла. Пример командной строки компилятора, где задана версия кремния:

cc21k -proc ADSP-21161 -si-revision 0.1 prog.c

Если в качестве версии указано none, то для компилятора не будет разрешено использование обходных путей для ошибок кристалла. Если указать all, то будут разрешены все способы обхода ошибок для целевого процессора (процессор указывается опцией -proc командной строки, что соответствует выбору из выпадающего списка Processor начального диалога опций проекта VisualDSP).

Если опция -si-revision не указана, то будет произведена компиляция для последней известной ревизии кремния, и будет разрешено использование методов обхода ошибок, соответствующих последней ревизии кристалла.

Подробнее про опцию -si-revision можно прочитать в руководстве C/C++ Compiler Manual for SHARC® Processors, раздел "Using the -si-revision Switch".

Линковщик может генерировать карту памяти приложения (memory map) в формате XML. Этот файл по умолчанию будет находиться в выходной папке компилятора (обычно в папках Debug или Release, находящихся в корневом каталоге проекта), и файл будет называться имяпроекта.map.xml. Чтобы задать генерацию карты памяти, зайдите в настройки опций проекта, меню Project -> Project Options... -> Link -> General, поставьте галочки Generate symbol map (будет генерироваться карта памяти в файле имяпроекта.map.xml) и Generate xref (в файле XRef.xml будет генерироваться информация о перекрестных ссылках).

VisualDSP Link memory map options

Чтобы узнать, сколько памяти осталось в куче, см. вопрос Q057.

[Установка VisualDSP++ 5.0]

1. Установка начинается запуском пакета инсталлятора VisualDSP++5.0.exe.

2. После завершения установки запустите ярлычком программу VisualDSP++ Environment, на вопрос об установке лицензии нужно ответить Yes.

3. На закладке About -> Licenses скопируйте в буфер обмена Host ID, там должно быть число наподобие F884CDEA.

4. Если у Вас есть лицензия, то этот шаг пропускается. Иначе запустите кейген 5_0\Analog.Devices.Visual.DSP.Plus.Plus.v5.0.Incl.Keygen-Lz0\Lz0\keygen.exe, нажмите кнопку Generate. В поле ввода Host ID появится то же число, что и на шаге 3, только буквы там будут маленькие. В текущем каталоге keygen.exe появится новый файл license.dat.

5. Копируйте файл license.dat в папку программы C:\Program Files\Analog Devices\VisualDSP 5.0\System\, запустите VisualDSP заново, запроса о лицензии больше не будет.

6. Закройте VisualDSP, запустите обновлялку Maintain this installation. Выберите Apply a downloaded Update, нажмите Next, выберите файл C:\aaa\5_0\VisualDSP++5.0_Update8.vdu (сразу 8-ю версию обновления, 7-ю пропускаем). Сначала обновится сам обновляльщик, перезапустится, а потом пойдет обновление самой IDE VisualDSP.

[Установка VisualDSP++ 5.0 Update 10]

1. Запустите пакет инсталлятора VisualDSP++ 5.1.2.exe.

VisualDSP50 install01

2. После распаковки инсталлятор сделает необходимые обновления Windows.

VisualDSP50 install02

Пример лога инсталлятора на Windows XP:

~Install_CL v1.2.14.2. (c) 2003-2006 Analog Devices, Inc.
~Testing Windows System files...
#ERROR One or more Windows system files must be updated.
~Install_CL v1.2.14.2. (c) 2003-2006 Analog Devices, Inc.
~~Beginning at 28/11/2012 11:23 UTC
~~Installed operating system is reported to be 5.1 ("Service Pack 3").
~~Installed version of Internet Explorer is reported to be "8.0.6001.18702".
~Testing Windows System files...
~~"C:\WINDOWS\system32\atl.dll" is currently in use and likely cannot be updated without rebooting the operating system.
~~Skipping update of "C:\WINDOWS\system32\atl.dll" because already-installed version is up-to-date (3.5.2284.2 >= 3.0.8449.0).
~~Skipping update of "C:\WINDOWS\system32\atl71.dll" because already-installed version is up-to-date (7.10.6101.0 >= 7.10.3077.0).
~~Skipping update of "C:\WINDOWS\system32\comcat.dll" because already-installed version is up-to-date (5.0.2600.1 >= 4.71.1460.1).
~~Skipping update of "C:\WINDOWS\system32\MFC42.DLL" because already-installed version is up-to-date (6.2.8081.0 >= 6.0.8665.0).
~~"C:\WINDOWS\system32\MFC42U.DLL" is currently in use and likely cannot be updated without rebooting the operating system.
~~Skipping update of "C:\WINDOWS\system32\MFC42U.DLL" because already-installed version is up-to-date (6.2.8081.0 >= 6.0.8665.0).
~~Skipping update of "C:\WINDOWS\system32\mfc71.dll" because already-installed version is up-to-date (7.10.6101.0 >= 7.10.3077.0).
~~Skipping update of "C:\WINDOWS\system32\MSVCIRT.DLL" because already-installed version is up-to-date (7.0.2600.5512 >= 6.0.8168.0).
~~"C:\WINDOWS\system32\MSVCP60.DLL" is currently in use and likely cannot be updated without rebooting the operating system.
~~Skipping update of "C:\WINDOWS\system32\MSVCP60.DLL" because already-installed version is up-to-date (6.2.3104.0 >= 6.0.8972.0).
~~Skipping update of "C:\WINDOWS\system32\msvcp71.dll" because already-installed version is up-to-date (7.10.6052.0 >= 7.10.3077.0).
~~Skipping update of "C:\WINDOWS\system32\msvcr71.dll" because already-installed version is up-to-date (7.10.7031.4 >= 7.10.3052.4).
~~"C:\WINDOWS\system32\msvcrt.dll" is currently in use and likely cannot be updated without rebooting the operating system.
~~Skipping update of "C:\WINDOWS\system32\msvcrt.dll" because already-installed version is up-to-date (7.0.2600.5512 >= 6.0.8797.0).
~~"C:\WINDOWS\system32\msxml3.dll" is currently in use and likely cannot be updated without rebooting the operating system.
~~Skipping update of "C:\WINDOWS\system32\msxml3.dll" because already-installed version is up-to-date (8.100.1053.0 >= 8.20.8730.1).
~~Installing "C:\WINDOWS\system32\msxml3a.dll" because it does not exist.
~~Bailing at 28/11/2012 11:23 UTC
#ERROR One or more Windows system files must be updated.

VisualDSP50 install03

VisualDSP50 install04

VisualDSP50 install05

VisualDSP50 install06

VisualDSP50 install07

VisualDSP50 install08

VisualDSP50 install09

После последнего нажатия Next инсталлятор задаст вопрос - проверить ли наличие обновлений на сайте Analog Devices? Если ответите положительно (установкой соответствующей галочки), то в браузере откроется ссылка [1], где можно проверить наличие обновлений. Эту операцию можно отложить на потом - в зависимости от Ваших целей и наличия у Вас уже закачанных пакетов обновлений.

3. Установка лицензии. Если у Вас уже есть файл лицензии, то этот шаг можно пропустить. Сначала нужно узнать Host ID компьютера. Для этого запустите VisualDSP++ Environment, перейдите в меню Help -> About VisualDSP++... -> в окне About перейдите на закладку Licenses. В правой нижней части окна увидите этот идентификатор. Скопируйте его в буфер обмена.

VisualDSP50 install09a

Запустите также VisualDSP\5_0\Analog.Devices.Visual.DSP.Plus.Plus.v5.0.Incl.Keygen-Lz0\Lz0\keygen.exe. Вставьте в поле ввода HostID скопированный идентификатор.

VisualDSP50 install10

Примечание: иногда после стандартной операции Ctrl+V визуально идентификатор не вставляется. Не обращайте внимания, это просто глюк. После Cltl+V просто нажмите кнопку Generate.

Нажмите кнопочку Generate. В каталоге, где лежит keygen.exe, появится файл license.dat, скопируйте его в каталог установки c:\Program Files\Analog Devices\VisualDSP 5.0\System.

Для проверки запустите менеджер лицензий через Пуск -> Analog Devices -> VisualDSP++ 5.0 -> Manage Licenses. На закладке Licenses Вы должны увидеть установленные лицензии.

VisualDSP50 install11

4. Установка обновлений. Запустите Менеджер обновлений через Пуск -> Analog Devices -> VisualDSP++ 5.0 -> Maintain this installation. В появившемся окошке Program Maintenance выберите вариант Apply a downloaded Update и нажмите кнопку Next.

VisualDSP50 install12

Выберите для обновления ранее скачанный файл VisualDSP++_5.0_Update10.1.vdu и нажмите кнопку Next. Все файлы обновлений кумулятивные (т. е. они содержат в себе все необходимые обновления, и нет необходимости ставить более ранние обновления) и имеют большой размер, порядка гигабайта.

VisualDSP50 install13

VisualDSP50 install14

Во время процесса распаковки и установки обновления инсталлятор может перезапустить самого себя, если в пакете обновления обнаружится более новая версия.

VisualDSP50 install15

VisualDSP50 install16

VisualDSP50 install17

VisualDSP50 install18

VisualDSP50 install19

Обновления можно скачать по ссылке [5].

[Установка VisualDSP++ 5.1.2]

Версию 5.1.2 можно установить параллельно с версией 5.0, т. е. на компьютере может обновременно существовать и работать независимо друг от друга обе версии. Готовый файл лицензии от версии 5.0 без изменений подходит для версии 5.1.2. Таким образом, чтобы установить версию 5.1.2, нужно скачать её дистрибутив, установить, и после этого подсунуть ей файл лицензии, сгенерированный для версии 5.0.

VisualDSP++ версии 5.1.2 включает в себя поддержку аппаратного JTAG-отладчика ICE-1000 (в версии 5.0 есть только драйвер для отладчика ICE-100B).

Pre-build и Post-build. В VisualDSP до компиляции и после компиляции можно выполнить специальные действия, которые позволяют автоматизировать процесс. Это можно применить для разных целей - для автоматической генерации версии прошивки перед компиляцией, для копирования файлов после компиляции или преобразований файлов и т. д. Выполняемые действия до компиляции доступны для управления через список Pre-build, а после компиляции Post-build. Выполняемые действия - обычная командная строка, которую можно использовать для запуска команд Windows или утилит.

Проблема запуска. По непонятной причине команда следующего вида не работает:

copy /B srcfile.bin /B destfile.bin

Чтобы она заработала корректно, нужно добавлять запуск интерпретатора CMD следующим образом:

cmd /C copy /B srcfile.bin /B destfile.bin

Почему-то это странное правило относится только к командам операционной системы. С обычными исполняемыми файлами не нужно применять запуск интерпретатора CMD. К примеру, следующая команда Post-build отрабатывает нормально:

.\bin2hex.exe ..\bin\firmware.bin ..\bin\firmware.hex

Пути относительно корневого каталога проекта. Как Вы успели заметить, в предыдущем примере применялись в составе пути .\ и ..\, они означают соответственно текущий каталог и родительский каталог для текущего каталога. Для проекта текущий каталог это папка, в которой лежит файл проекта (файл с расширением *.dpj). Такие пути можно использовать в командах Pre-build и Post-build.

Компилятор на не распознаваемые escape-последовательности наподобие \e выдает предупреждение cc0192, например:

".\uart-debug/BfDebugger.h", line 205: cc0192: {D} warning: unrecognized
          character escape sequence
  static char crsuw[] = { '\e', '[', 'A' };

Чтобы избавиться от вывода предупреждающих сообщений cc0192 (код все равно нормально компилируется), не затрагивая при этом другие сообщения, откройте свойства проекта Project Options..., зайдите в раздел настроек Compile -> Warning, и в поле ввода Additional Options добавьте опцию -Wsuppress 192:

VisualDSP suppress warning cc0192

Как Вы уже догадались, в этой опции число 192 это номер предупреждающего сообщения cc0192. Если надо удалить несколько разных предупреждающих сообщений, то в этой опции их номера нужно перечислить друг за другом через запятую.

Подробнее см. раздел "Promoting, Demoting, and Suppressing Error Messages" руководства "VisualDSP++ 5.0 User’s Guide".

Проблема с блокировкой выходного файла. Иногда по непонятной причине блокируется на запись/стирание выходной генерируемый файл среды VisualDSP++ (*.dxe, *.ldr, *.dlb). Блокировка остается даже после того, как закрыты все окна VisualDSP и даже произведен полный выход из неё: заблокированный файл нельзя удалить с диска даже под учетной записью администратора.

Обычно это происходит из-за того, что в памяти висит зависшая копия процесса среды Idde.exe. Она находится в памяти без видимого окна, и блокирует файл. Кроме блокировки файлов это также иногда приводит к проблеме запуска отладчика.

Решается проблема просто: запустите Менеджер Задач (Task Manager) и прибейте зависший процесс Idde.exe.

Предопределенный идентификатор __func__, вместо которого подставляется текстовое имя функции, поддерживается только при компиляции в режиме C99 (с опцией -c99 компилятора, файл модуля имеет расширение *.c). При попытке скомпилировать такой файл в режиме C++ (когда файл имеет расширение *.cpp, опция -c++) компилятор выдаст ошибку, что идентификатор __func__ не найден:

".\uart-debug\ADSP-BF538-UART.cpp", line 146: cc0020:  error: identifier
          "__func__" is undefined
              if (*pDmaIrqStatus & DMA_ERR) { printf("%s(): DMA%d Error\n",__func__,DmaChan);

К счастью, в режиме компиляции C++ доступно ключевое слово __FUNCTION__, которое может заменить __func__. Для этого достаточно добавить макроопределение #define для __func__, и ошибка cc0020 исчезнет:

#define __func__ __FUNCTION__

Если проект скомпилирован с оптимизацией (выбран вариант конфигурации Release), то полноценная пошаговая отладка будет невозможна, после запуска отладки точка останова будет стоять не в модуле main, а в окне машинного кода Disassembly. Чтобы можно было вести нормальную отладку, скомпилируйте проект с настройками для отладки (выберите конфигурацию Debug).

Функции get_vco_hzget_cclk_hzget_sclk_hz вычисляют соответственно частоту тактового генератора, управляемого напряжением (VCO, используется в системе генерации тактовой частоты на PLL), частоту ядра и частоту системной шины. Эти функции определены в модуле system.c. Модуль system.c использует операторы условной компиляции, которые меняют код этих функций. К примеру, если Вы не задали __USEBFSYSCONTROL__, то будет использоваться код, который вычисляет частоты на основе константы CLKIN_Hz (это значение частоты кварцевого резонатора) и содержимого системных регистров PLL_CTL и PLL_DIV. Поэтому прежде всего проверьте __USEBFSYSCONTROL__ и CLKIN_Hz.

При компиляции появляется ошибка следующего вида:

C:\Program Files (x86)\Analog Devices\VisualDSP 5.0\gmake-378: *** No rule to make target `uart-debug/system.h',
needed by `Debug/WEX025664.doj'.  Stop.
No valid command(s) to build.
Build was unsuccessful.

Симптомы: не помогает Clean Project, и не помогает даже удаление модуля WEX025664.cpp из проекта, ошибка возникает снова, только с зависимостью от другого модуля.

Причина и устранение ошибки: ошибка возникнет в том случае, если были перенесены модули или заголовки из одного каталога проекта в другой каталог. Чтобы устранить ошибку, необходимо выполнить пункт меню Project -> Update Dependencies.

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

Debug работает, а Release нет. Скорее всего причина в том, что отличается скорость выполнения кода для критичных ко времени выполнения операций.

Например, в программу встроена задержка на основе времени выполнения определенного участка кода. Часто неопытные программисты организуют такие задержки с помощью пустых циклов. Это неправильный метод, оправданный только в очень редких частных случаях. Правильная привязка к реальному времени должна осуществляться на основе таймеров и обработчиков их прерываний. В среде RTOS привязка к реальному времени (менее точная) может быть реализована с помощью планировщика (средствами API системы RTOS).

Другой вариант подобной проблемы - наличие конвейера Blackfin для выполнения команд, и тот факт, что есть задержка в применении установок для периферии. Ниже показан пример, когда в режиме компиляции Debug программа работает как ожидалось, а в режиме Release нет. Это код обработчика прерывания таймера, когда программа в Debug нормально управляет светодиодом (с помощью макроса LED), а в Release светодиод не мигает:

// Обработчик прерывания таймера 0, который срабатывает
// с частотой 20 кГц.
EX_INTERRUPT_HANDLER(Timer0ISR)
{
   *pTIMER_STATUS = TIMIL0;
   // Счетчик фазы:
   static float phase = 0;
   // Указатель на таблицу синуса в SDRAM:
   s16 *ptr = 0;
   // Временные переменные:
   u16 val16phase;
   u16 val16;
 
   // Запись одной выборки:
   val16phase = (u16)phase;
   val16 = SHIFT + ptr[val16phase] * amplitude;
   // С помощью макроса LED здесь контролируется
   // Время операции записи в аппаратуру U2C/TWI.
   LED(1);     // ВКЛючение светодиода
   twi_write_sample(val16);
   LED(0);     // ВЫКЛючение светодиода
   // Инкремент фазы для 375 Гц:
   phase += 512.0/((float)SAMPLERATE/375);
   if (phase > 512.0)
      phase = phase - 512.0;
}

По замыслу программиста управление ножкой GPIO, к которой подключен светодиод, позволило бы контролировать частоту срабатывания прерываний таймера и время выполнения функции twi_write_sample. Однако это нормально работает в Debug, а в Release нет. Ошибка тут в том, что ход выполнения программы не синхронизирован с состоянием порта GPIO, который должен управлять светодиодом. Т. е. в режиме Release код между двумя вызовами макроса LED(1) и LED(0) выполняется слишком быстро, так что логический уровень на выводе порта не успевает изменить свое состояние. Решить эту проблему позволяет инструкция SSYNC, которая синхронизирует состояние аппаратуры с потоков выполнения кода:

   ...
   LED(1);     // ВКЛючение светодиода
   ssync();
   twi_write_sample(val16);
   LED(0);     // ВЫКЛючение светодиода
   ssync();
   ...

В месте вызова опеатора ssync() происходит задержка до момента времени, когда уровень на выводе ножки порта светодиода реально изменит свое состояние. Теперь оба варианта компиляции Debug и Release будут работать нормально. Подробности см. во врезке "Q004. Для чего нужны инструкции SSYNC и CSYNC?".

Release работает, а Debug нет. Возможно, что неправильно задана область памяти для SDRAM, либо она вообще не проинициализирована. Возможна нехватка памяти для отдельных данных или кусков кода. Есть смысл проанализировать сгенерированные файлы карты памяти для этих двух конфигураций. Используйте директиву #pragma section для управления размещением в памяти отдельных символов (см. [6, 7]).

Если в проекте используется внешняя память SDRAM, проверьте настройки генерируемого кода инициализации контроллера SDRAM - возможно, что в неработающей конфигурации не сделаны соответствующие настройки.

При загрузке проекта на отладку появляется окно "One or more breakpoints could not be set and were disabled":

VisualDSP err disabled breakpoints

Причина в том, что код (или модуль), в котором когда-то была установлена точка останова, был удален, но ссылка на эту точку останова осталась. Чтобы устранить появление этого окна, нужно открыть меню точек останова Settings -> Breakpoints... (Alt+F9), и удалить несуществующие точки останова, помеченные белыми кружочками.

VisualDSP missed breakpoint

Первая странность: автоматически создаваемые точки останова выглядят нормально работающими при сборке проекта. Но когда я делаю Debug -> Reset, все точки останова пропадают, и я не могу их больше добавить, потому что выбор точек останова больше невидим в контекстном меню. Единственный способ вернуть все обратно - перекомпилировать проект. Ниже приведен пример простого кода, с которым проводились эксперименты с точками останова.

unsigned int counter=0;
 
while(1) {
   if (counter > 1000) {    //breakpoint 1
      counter=0;            //breakpoint 2
      sysreg_bit_tgl(sysreg_FLAGS, FLG4);  //переключение светодиода
   }
   else {
      counter++;            //breakpoint 3
      sysreg_bit_tgl(sysreg_FLAGS, FLG5);
   }
}

Приложение нормально работает, если не установлено никаких точек останова. Еще одна странность: если установить точки останова на указанные строки, то только первая из них вызывает остановку, другие точки останова ничего не делают. Например, если нажать F5 (запуск программы на выполнение), то выполнение всегда останавливается на первой точке останова (breakpoint 1), светодиод не переключается и счетчик не изменяется.

Причина: команда Debug -> Reset в VisualDSP++ восстанавливает target (отлаживаемый процессор) в его заранее известное начальное состояние. Эта операция включает в себя очистку памяти процессора, конфигурирование регистров в стартовые значения по умолчанию, и установку счетчика команд PC на адрес сброса. Следовательно, все контрольные точки будут очищены. Однако есть Вы просто перезагрузите готовый DXE, вместо того, чтобы заново пересобрать проект, то увидите, что точки останова появятся снова, и Вы снова получите возможность устанавливать/удалять точки останова выбором в контекстном меню.

Проблема, когда видна работа только первой точки останова, может возникнуть из-за включенной оптимизации. Если Вы собрали проект со включенной оптимизацией, т. е. либо если использовали одну из опций компилятора, влияющих на оптимизацию, либо если установили галочку оптимизации в свойствах проекта (Project -> Project Options -> Compiler -> General -> Enable Optimization), то попробуйте отключить оптимизацию и снова попробуйте запустить проект. После этого все точки останова должны отрабатывать нормально.

Возможно также причина необычного поведения в не обновленной версии среды разработки VisualDSP++.

Дополнительную информацию по точкам останова можно получить в системе помощи VisualDSP++: откройте меню Help и перейдите в раздел Contents -> Graphical Environment -> IDDE -> Debugging -> Program Execution operations -> Breakpoints.

Горячие клавиши для часто повторяющихся действий (например, перекомпиляция, поиск по всем файлам и т. д.) настраиваются через меню Settings -> Preferences... -> раздел Keyboard.

VisualDSP Preferences Hotkeys

Я себе настроил следующие горячие клавиши:

Действие Hotkeys
Edit:Find Ctrl+F
Edit:Find in Files Ctrl+Shift+F
Project: Rebuild Project Ctrl+R

Опция проекта Debug Release
Compile -> Enable Optimization   +
Compile -> Generate debug information +  
Link -> General -> Optimizations -> Individually map functions and data items   +
Link -> Elimination -> Eliminate unused objects   +
Link -> Processor (1) -> Libraries -> Use Debug System libraries +  
Link -> Processor (1) -> Memory usage Internal SRAM and external SDRAM Internal SRAM only
Load -> Options -> Verbose +  

Если Ваш проект генерирует исполняемый файл (это обычно используется для отладки), то двоичный код будет находиться в файле имя_проекта.dxe. Если генерируется файл для загрузчика, то двоичный код будет находиться в файле имя_проекта.ldr. Двоичный код программы создается в папке Debug или Release - в зависимости от того, какая конфигурация была выбрана при компиляции.

Для управления упаковкой и выравниванием используют директивы #pragma pack, pad, align. Подробнее см. [6]. Пример структуры, где отключено выравнивание:

#pragma pack(1)
/* Минимальное выравнивание в структуре теперь 1 байт */
 
struct is_packed {
   char a;
   /* в этом месте обычно компилятор добавил бы 3 лишних байта для выравнивания,
      но теперь этого не будет, потому что ранее была использована pragma pack(1) */
   int b;
} t[2]; /* упакованное определение t займет 10 байт */
 
#pragma pack()
 
/* Теперь минимальное выравнивание восстановлено на значение
по умолчанию (4 байта)
*/

Внимание: будьте осторожны, когда используете 32-битные поля внутри упакованных pack(1) структур, потому что возможно возникновение ошибки доступа (unknown exception, KernelPanic) из-за того, что адрес поля структуры не выровнен на 4 байта (не делится нацело на 4).

Среда VisualDSP автоматически (на основе настроек проекта Project Options -> LDF Settings) генерирует файл имя_проекта.ldf, содержащий директивы управления линковщиком. См. в нем раздел PROCESSOR p0 -> SECTIONS, в нем перечислены имена секций, которые можно использовать в программе (например, L1_code, L1_data, sdram_bsz и т. д.).

Пример вывода сообщения об ошибке линковки:

Linking...
 
[Warning li2060]  The following input section(s) that contain program code
        and/or data have not been placed into the executable for processor 'p0'
        as there are no relevant commands specified in the LDF:
        .\Debug\Main.doj(sdram0_bank0)
 
[Error li1060]  The following symbols are referenced, but not mapped:
        '_HelloWorld' referenced from .\Debug\Main.doj(program)
 
Linker finished with 1 error and 1 warning
cc3089: fatal error: Link failed
Tool failed with exit/exception code: 1.
Build was unsuccessful.

Ошибка происходила в следующем коде:

section ("sdram0_bank0") char HelloWorld[] = "Hello, world!!!";

Цель применения директивы section была в том, чтобы разместить переменную не в памяти L1, а в SDRAM. Причина ошибки в том, что для массива была указана секция памяти, не предназначенная для размещения данных. Чтобы узнать имя секции, которую можно использовать для размещения данных в SDRAM, нужно посмотреть в файле имя_проекта.ldf (он автоматически генерируется на основе настроек проекта) определения секций. Ищите секцию, где в операторах INPUT_SECTIONS($OBJECTS(имя_секции)) указаны соответствующие для SDRAM секции.

К примеру, если коде программы вместо section ("sdram0_bank0") указать section ("sdram0") или ("sdram0_bank3"), то ошибка исчезнет. Сравните определения секций sdram0_bank0 и sdram0_bank3 в файле *.ldf:

...
      sdram0_bank0
      {
         INPUT_SECTION_ALIGN(4)
         INPUT_SECTIONS($OBJECTS(program) $LIBRARIES(program))
      } > MEM_SDRAM0_BANK0
...
      sdram0_bank3
      {
         INPUT_SECTION_ALIGN(4)
         INPUT_SECTIONS($OBJECTS(sdram0) $LIBRARIES(sdram0))
         INPUT_SECTIONS($OBJECTS(sdram0_bank3) $LIBRARIES(sdram0_bank3))
         INPUT_SECTIONS($OBJECTS(noncache_code) $LIBRARIES(noncache_code))
         INPUT_SECTIONS($OBJECTS(program) $LIBRARIES(program))
         INPUT_SECTIONS($OBJECTS(cplb_code) $LIBRARIES(cplb_code))
      } > MEM_SDRAM0_BANK3
...

Теперь становится понятна причина ошибки: секция sdram0_bank0 предназначена только для размещения кода программы.

Дословно Update Dependencies переводится как "обновление информации о зависимостях в программе". Система управления зависимостями проекта (Project dependencies control) анализирует взаимосвязи отдельных модулей кода внутри проекта - какие модули зависят от других модулей, и в зависимости от этого определяет порядок сборки проекта. Этот порядок отражается в makefile проекта (автоматически генерируется системой VisualDSP++ в файле имя_проекта.mak, который находится в корневом каталоге проекта). В makefile генерируются зависимости для каждого модуля проекта. Система VisualDSP++ обновляет информацию о зависимостях каждый раз, когда Вы меняете настройки проекта, добавляете файлы в проект, или выбираете пункт меню Project -> Update Dependencies.

Вручную делать операцию Project -> Update Dependencies нужно в том случае, если Вы вручную манипулировали месторасположением модулей проекта, меняли расширения у модулей, и в других случаях, когда возникают странные, необъяснимые ошибки компиляции из-за сбоя автоматического обновления зависимостей.

Пример сообщения об ошибке:

[Error li1040] "C:\Program Files (x86)\Analog Devices\VisualDSP 5.0\Blackfin/ldf/ADSP-BF538.ldf":
482 Out of memory in output section 'L1_data_b' in processor 'p0'
        Total of 0x142865 word(s) were not mapped.
For more details, see 'linker_log.xml' in the output directory.

Здесь система сообщает о том, что оперативной памяти L1 недостаточно для размещения данных, и предлагает обратиться к логу линкера linker_log.xml, чтобы найти места потребления памяти. 

Обычно причина ошибки в том, что для в размещения памяти переменных не хватает места, что часто бывает, когда проект собран с настройками по умолчанию. Для устранения ошибки нужно либо уменьшить потребление памяти (например, попробовать скомпилировать проект с настройками Release или выкинуть какой-нибудь код, который потребляет память), либо предоставить системе больше памяти (это можно осуществить добавлением памяти SDRAM и рациональным размещением переменных в нужных секциях памяти с помощью директивы section, подробнее см. [6, 7] и вопросы Q021, Q028, Q029).

VisualDSP project out of date

Причина такого сообщения может быть в том, что в группе проектов имеется также и другой проект, который претерпел изменения и пока не был скомпилирован. Чтобы это сообщение больше не появлялось, нужно либо скомпилировать все проекты в группе, либо удалить из группы не используемые проекты.

Пример такого предупреждения в логе Build:

Creating loader file...
[Warning ld0151]: You may need to specify an initialization file (.dxe) to set up the registers
for external memory initialization. Please refer to engineering notes EE-240, EE-314 
(http://www.analog.com/ee-notes) and/or ..\Blackfin\ldr\init_code for more information.
Build completed successfully.

Это предупреждение говорит о том, что в генерируемом исполняемом коде, который запустит загрузчик, может отсутствовать код инициализации внешней памяти, в результате чего система может работать не так, как ожидалось. Если проблема действительно имеет место (т. е. программа не работает), то исправить ситуацию можно добавлением кода настройки - либо в теле основной программы, либо нужно настроить опции проекта, раздел Load -> Options -> поле ввода Initialization file: здесь нужно выбрать двоичный файл *.dxe с кодом инициализации.

[C]

Если у функции на языке C нет параметров, и она ничего не возвращает, то все довольно просто. Нужно в модуле ассемблера перед вызовом функции объявить её ключевым словом .extern, и затем вызвать инструкцией CALL, и не забыть к имени функции добавить префикс '_'. Пример инициализации SDRAM в Init Code на языке ассемблера (подпрограмма InitSDRAM написана на C, и вызывается из кода ассемблера):

//Модуль на языке C
#include < cdefBF532.h >
 
void InitSDRAM (void)
{
   //SDRAM Refresh Rate Control Register:
   *pEBIU_SDRRC = 0xFFF;      //По расчетам сюда надо записать 252, но и MAX прокатывает.
   ssync();
   //SDRAM Memory Bank Control Register:
   *pEBIU_SDBCTL = EBCAW_9 |  //EBCAW = 01 (9 бит ширина адреса столбца)
                   EBSZ_32 |  //EBSZ  = 01 (32 мегабайта размер внешнего банка)
                   EBE;       //EBE   = 1  (разрешен внешний банк SDRAM)
   ssync();
   //SDRAM Memory Global Control Register:
   *pEBIU_SDGCTL = PSSE    |  //Разрешение запуска SDRAM power-up sequence
                   TWR_2   |  //SDRAM tWR = 2 цикла
                   TRCD_3  |  //SDRAM tRCD = 3 цикла
                   TRP_3   |  //SDRAM tRP = 3 цикла
                   TRAS_6  |  //SDRAM tRAS = 6 циклов
                   PASR_ALL|  //Обновляются все банки
                   CL_3    |  //SDRAM CAS latency = 3 цикла
                   SCTLE;     //Разрешить выдачу CLKOUT, SRAS, SCAS, SWE, SDQM[1:0]
                   //0x0091998D;   
   ssync();
}
//Модуль на ассемблере, откуда вызывается подпрограмма на C
#include < defBF532.h >
 
.section program;
 
.EXTERN _InitSDRAM;
 
start:
/*******Pre-Init Section*********************************************/
/*******НЕ ИЗМЕНЯЙТЕ ЭТУ СЕКЦИЮ**************************************/
	[--SP] = ASTAT;	       //сохранение регистров в стек
	[--SP] = RETS;
	[--SP] = (r7:0);
	[--SP] = (p5:0);
	[--SP] = I0;
	[--SP] = I1;
	[--SP] = I2;
	[--SP] = I3;
	[--SP] = B0;
	[--SP] = B1;
	[--SP] = B2;
	[--SP] = B3;
	[--SP] = M0;
	[--SP] = M1;
	[--SP] = M2;
	[--SP] = M3;
	[--SP] = L0;
	[--SP] = L1;
	[--SP] = L2;
	[--SP] = L3;
/********************************************************************/
/*******Init Code Section********************************************/
/** В этой секции вставьте код инициализации             ************/
/*******SDRAM Setup************/
CALL _InitSDRAM;
/********************************************************************/
/*******Post-Init Section********************************************/
/*******НЕ ИЗМЕНЯЙТЕ ЭТУ СЕКЦИЮ**************************************/
	L3 = [SP++];
	L2 = [SP++];	
	L1 = [SP++];
	L0 = [SP++];
	M3 = [SP++];
	M2 = [SP++];
	M1 = [SP++];
	M0 = [SP++];
	B3 = [SP++];
	B2 = [SP++];
	B1 = [SP++];
	B0 = [SP++];
	I3 = [SP++];
	I2 = [SP++];
	I1 = [SP++];
	I0 = [SP++];
	(p5:0) = [SP++];       //восстановление регистров из стека
	(r7:0) = [SP++];
	RETS = [SP++];
	ASTAT = [SP++];
/********************************************************************/
END:
	RTS;

Обратите внимание на особенность вызова и объявления функции InitSDRAM в модуле на языке ассемблера (выделил в коде жирным шрифтом): её имя имеет префикс подчеркивания (_InitSDRAM вместо InitSDRAM). Если не соблюсти это правило, то произойдет ошибка линковки li1021:

Linking...
[Error li1021]  The following symbols referenced in processor 'p0' could not be resolved:
        'InitSDRAM' referenced from '.\Release\Init_code.doj'
Linker finished with 1 error
cc3089: fatal error: Link failed
Tool failed with exit/exception code: 1.
Build was unsuccessful.

[C++]

Если функция реализована на C++ (файл модуля имеет расширение *.cpp), то нужно в модуле ассемблера к имени функции кроме префикса '_' также добавлять еще и суффикс '__Fv':

//Модуль на ассемблере, откуда вызывается подпрограмма на C++
#include < defBF532.h >
 
.section program;
 
.EXTERN _InitSDRAM__Fv; 
	...
CALL _InitSDRAM__Fv;
	...

В некоторых случаях требуется предварительная настройка регистров процессора перед началом выполнения основной программы. Например, когда к процессору подключена внешняя память данных/программ SDRAM, то требуется предварительная настройка контроллера внешней шины EBIU (регистров блока SDC, SDRAM controller [9]). Когда программа работает в рабочей системе, без отладчика, то вся предварительная настройка возлагается на Init Code [8]. Но как настраивать регистры перед запуском сессии отладки? 

Оказывается, все просто: среда VisualDSP позволяет с помощью XML-файла задать предварительную настройку нужных регистров процессора, причем любых. Пример такого файла, который настраивает регистры контроллера SDC, чтобы обеспечить корректную работу внешней памяти SDRAM в сессии отладчика, приведен ниже (MT48LC16M16A2-75 на 32 мегабайта, подключенная процессору ADSP-BF538, см. [9]). Строки, от которых зависит настройка регистров, выделены жирным шрифтом:

< ?xml version="1.0" standalone="yes"?>
< custom-visualdsp-proc-xml
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="\Program Files\Analog Devices\VisualDSP 5.0\System\ArchDef\ADSP-custom-board.xsd"
processor-family="BLACKFIN"
file="My_custom_board_reset_settings.xml">
< !-- ************************************************************************* -->
< !-- ******* My_custom_board_reset_settings.xml -->
< !-- ************************************************************************* -->
< custom-register-reset-definitions>
< register name="EBIU_SDRRC" reset-value="0x00FC" core="Common" />
< register name="EBIU_SDBCTL" reset-value="0x13" core="Common" />
< register name="EBIU_SDGCTL" reset-value="0x00888849" core="Common" />
< /custom-register-reset-definitions>
< /custom-visualdsp-proc-xml>

Подключить этот файл к сессии отладки можно следующим образом: 

1. Запустите сессию отладки через меню Session -> Select Session. 

VisualDSP select debug session

Подождите пару секунд, чтобы отладчик запустился (в строке статуса VisualDSP должна появиться надпись Halted). 

2. Зайдите в меню Settings -> Session..., диалоге поставьте галочку "Enable customizations", в строке ввода "Custom board support file name:" выберите (или введите вручную) путь до XML-файла, котором прописана настройка регистров.

Примечание: путь, который указывается для XML-файла в поле ввода "Custom board support file name:", можно указать относительно корневой папки проекта. Это удобно для того, чтобы не нужно было перенастраивать путь до XML-файла при переносе проекта из одной папки в другую. Путь нужно указать относительно папки Debug или Release (в зависимости от того, для какой конфигурации запускается отладка). Например, если Ваш файл инициализации имеет имя initsdram.xml, и он находится в корневой папке проекта, то для любой секции отладки (и Debug, и Reelase) путь можно настроить как ..\initsdram.xml, см. скриншот:

VisualDSP debug session settings

Нажмите кнопку OK. 

После этого нужно перезапустить сессию отладчика, в свежезапущенной сессии будут проинициализированы регистры процессора в соответствии с файлом XML.

Сценарий: в процессе разрастания размера приложения оно перестало помещаться во встроенную память L1, и пришлось использовать внешнюю оперативную память SDRAM. После этого в среде разработки стали появляться ошибки наподобие 'External Memory is disabled for this region of memory' (для этой области адресов внешняя память запрещена), и в окне дизассемблирования кода стали появляться ошибки 'Illegal opcode' (недопустимый код операции). Код перестал нормально работать.

Причина: когда приложения ведут себя некорректно при доступе к внешней памяти - либо при выполнении кода во внешней памяти, либо при чтении/записи данных внешней памяти - это скорее всего из-за того, что внешняя память не проинициализирована правильно. Попытка выполнения кода в отладчике при такой ситуации будет выдавать некорректные результаты, могут срабатывать исключения, программный счетчик может оказываться в случайных местах окна дизассеблированного кода (Disassembly), или программа может зависнуть. В среде VisualDSP могут происходить ошибки наподобие таких, что в окне просмотра памяти (Window/Memory Browser) все содержимое будет заполнено неожиданными значениями 0xDD (Disabled, запрещено), секции кода будут показывать "Illegal Opcode" в окне дизассемблера, или в окне сообщений консоли будет появляться текст "External memory is disabled for this region of memory". Здесь описаны некоторые распространенные ошибки, о которых следует знать.

[Поведение по умолчанию]

Когда Вы подключаетесь эмулятором к отлаживаемой целевой системе (эмулятор JTAG или Debug Agent), то по умолчанию эмулятор проверяет соответствующий файл XML, чтобы обнаружить в нем любую конфигурацию, которую надо сделать для регистров EBIU / внешних портов GPIO / DDR. Эта фича разрешается/запрещается следующими опциями:

VisualDSP++: Settings -> Target Options -> галочка "Use xml reset values"

CrossCore Embedded Studio (CCES): на закладке "Custom Board Support" меню "Run: Debug Configurations". 

Это инструктирует эмулятор проверять соответствующий файл XML в папке "...\System\Archdef\" каталога инсталляции VisualDSP++ для значений регистров после сброса (register reset definitions). Например, файл "...\System\Archdef\adsp-bf561-proc.xml" для BF561 в среде VisualDSP++, или "...\System\Archdef\adsp-bf609-resets.xml" для BF609 в среде CCES. 

Эти файлы XML содержат секцию < register-reset-definitions >, которая задает значения, которые должны быть назначены регистрам контроллера внешней памяти при сбросе отлаживаемого процессора. Обратите внимание, что файлы XML содержат только значения сброса, которые предназначены для соответствующей платы разработчика EZ-KIT Lite. Например, здесь указаны значения для BF533 EZ-KIT Lite, где значения регистров при сбросе заданы в файле "...\System\Archdef\adsp-bf533-proc.xml" каталога установки VisualDSP++. 

Однако, к примеру, поскольку нет соответствующей платы разработчика EZ-KIT Lite для BF532 или BF608, то может не оказаться настроек register reset definitions в соответствующем файле XML. Эти файлы XML, предоставленные для VisualDSP++ и CrossCore Embedded Studio, не должны быть модифицированы, хотя и можно это сделать. Дополнительно эти файлы XML работают универсально - т. е. XML-файл для BF533 повлияет на все целевые устройства BF533, так что изменения, которые Вы сделаете для своей уникальной платы, могут оказаться некорректными для платы разработчика EZ-KIT Lite, или могут повлиять на цели отладки BF533 от сторонних разработчиков.

[Уникальные системы (или системы от сторонних разработчиков)]

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

Для плат пользователя в средах разработки VisualDSP++ и (CCES предоставлена фича "Custom Board Support" (поддержка уникальных плат), которая позволяет пользователю указать собственный файл XML (Custom Board Support XML file), содержащий настройки регистров процессора при сбросе (register reset definitions), и привязать его к нужной сессии отладки. Шаблоном для такого файла может служить пример для настройки регистров при сбросе процессора BF527 EZ-KIT Lite (Blackfin BF527 EZ-KIT Lite Register Reset Definitions).

[Редактирование Custom Board Support XML file]

Править файл XML можно обычным текстовым редактором - особенно хорош редактор наподобие Notepad2. При редактировании нужно убедиться, что все свойства в файле XML настроены корректно; что 'processor-family' соответствует отлаживаемой цели (процессор Blackfin, SHARC или TigerSHARC), и имя файла соответствует имени, указанном внутри файла XML.

[VisualDSP++]

< custom-visualdsp-proc-xml
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="\Program Files\Analog Devices\VisualDSP 5.0\System\ArchDef\ADSP-custom-board.xsd"
     processor-family="Blackfin"
     file="Custom_Resets.xml">

[CrossCore Embedded Studio]

< custom-cces-proc-xml
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation "\Analog Devices\CrossCore Embedded Studio 1.0.0\System\ArchDef\ADSP-custom-board.xsd"
     processor-family="Blackfin"
     file="Custom_Resets.xml">

Затем просто отредактируйте секцию < register-reset-definitions >, как это необходимо Вам. Может оказаться полезным скопировать готовые register reset definitions от похожего процессора или существующей платы разработчика EZ-KIT Lite (например, если Вы используете BF531, сделайте копию определений из adsp-bf533-proc.xml) в качестве отправной точки для дальнейшего редактирования. Дополнительно, если у Вас инструментарий установлен не по пути по умолчанию, то нужно поменять путь "noNamespaceSchemaLocation".

В среде VisualDSP++ можно связать настроенный файл XML с сессией отладки через меню Settings -> Session (оно становится доступным только после запуска сессии отладки). В среде CrossCore Embedded Studio (CCES) Вы можете связать файлы XML с сессией отладки, перейдя на закладку "Custom Board Support" для Вашей конфигурации отладки через меню Run -> Debug Configurations.

Для настройки значений регистров контроллера внешней шины [9] разработчики предоставили EBIU Calculator, который можно использовать для определения правильных значений регистров EBIU для целей отладки на процессоре Blackfin: BfSdcCalculation_Release.xlsx, см. [10]. 

Больше информации по поддержке пользовательских плат, структуре XML и синтаксису можно найти:

[VisualDSP++] VisualDSP++ Help в меню Help -> Contents -> Graphical Environment -> Custom Board Support.

[CrossCore Embedded Studio] CCES Help в меню Help -> Contents -> CrossCore Embedded Studio™ 1.0.0 -> Graphical Development Environment -> Tasks -> Running and Debugging Programs -> Working with Launch Configurations -> Configuring Custom Board Support.

[Другие общие проблемы]

Общая ошибка - конфигурирование EBIU с не установленным битом PSSE в регистре EBIU_SDGCTL. Это бит, разрешающий запуск процедуры Power-up Sequence, его установка гарантирует, что следующий доступ к SDRAM приведет к тому, что заданные Вами установки в регистрах EBIU будут применены к SDRAM правильно. Этот бит всегда читается как 0, но он все равно должен быть установлен всякий раз, когда Вы меняете значения регистров EBIU. Подробно все это описано в аппаратном руководстве на используемый процессор (Hardware Reference Manual, см. также [9]). 

Другая общая проблема с EBIU происходит, когда Вы отлаживаете DXE в среде IDDE - после окончания отладки Вам нужно подготовить файл загрузки (loader file, файл с расширением *.ldr), записываемый в некое внешнее хранилище (например параллельная или последовательная память FLASH, либо файл может передаваться под управлением внешнего мастера шины SPI, в зависимости от выбранного режима загрузки, см. [8]) с которого система должна загружать программу на выполнение. Как только приложение больше не загружается под управлением отладчика в среде разработки, так что никакие XML-файлы больше не используются, EBIU перестает конфигурироваться, если об этом специально не позаботиться в коде инициализации (Init Code, см. [ , ]). 

Имейте в виду, что Вы не сможете модифицировать настройки PLL через файл XML. Программирование PLL требует специальной последовательности ожидания захвата PLL, чтобы гарантировать стабилизацию тактовой частоты и переключение на новую частоту, чего нельзя добиться простым редактированием настроек файла .xml. Подобным образом программирование встроенного регулятора напряжения инициирует последовательность переустановки захвата PLL, когда система стабилизации напряжения процессора достигнет заданного уровня напряжения питания.

< ?xml version="1.0" standalone="yes"?>
< custom-visualdsp-proc-xml
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="\Program Files\Analog Devices\VisualDSP 5.0\System\ArchDef\ADSP-custom-board.xsd"
     processor-family="Blackfin"
     file="Custom_Resets.xml">
< !-- ************************************************************************* -->
< !-- ****** Custom Register Definitions for a Custom Target                    -->
< !-- ************************************************************************* -->
< !-- ****** Просто измените настройки регистров EBIU в секции                  -->
< !-- ******       "< custom-register-reset-definitions>"                       -->
< !-- ****** Также Вы можете добавить инициализацию других регистров            -->
< !-- ****** (кроме PLL).                                                       -->
< !-- ************************************************************************* -->
< custom-register-reset-definitions>
  < register name="EBIU_SDRRC" reset-value="0x0407" core="Common" /> 
  < register name="EBIU_SDBCTL" reset-value="0x25" core="Common" /> 
  < register name="EBIU_SDGCTL" reset-value="0x0091998D" core="Common" />
< /custom-register-reset-definitions>
< /custom-visualdsp-proc-xml>

Если Вы управляете поведением утилиты elfloader.exe через опции проекта (доступно через меню Project -> Project Options...), то откройте раздел Load -> Options в дереве свойств проекта. Начальный адрес, по которому будет находится программа на внешнем носителе, определятся полем ввода Start address. Это поле станет доступным, когда будет выбран формат загрузки Boot Format -> Intel hex, и снята галочка Use default start address (см. скриншот).

VisualDSP Load start address

Эти настройки соответствуют опции -p address утилиты elfloader.exe (подробнее см. описание опций командной строки утилиты elfloader.exe [1]).

В среде разработки VisualDSP++ можно создавать проекты для библиотек. Это удобно, когда Вам хотелось бы избежать дублирования кода в связанных проектах - например, у Вас есть основная программа, и есть загрузчик для неё. Оба проекта используют одни и те же ресурсы системы (графический индикатор, порты UART и т. п.). Код, который работает с этими общими ресурсами, будет одинаковый для обоих проектов, поэтому во избежание его дублирования можно его скомпилировать в одну общую библиотеку.

Для создания библиотеки кода можно воспользоваться мастером Project Wizard. Процесс по шагам:

1. Через меню File -> New -> Project... запустите диалог Project Wizard. В дереве опций Project -> Select Type выберите вариант Library. В поле ввода Name: введите название проекта для библиотеки (например mylib). В поле ввода Directory выберите папку, где будет размещен корневой каталог проекта библиотеки.

VisualDSP library create project name

Нажмите кнопку Next. 

2. Выберите используемый процессор (Select Processor). Нажмите кнопку Next. 

VisualDSP library create select processor

3. В разделе Library Settings выберите язык проекта (язык, на котором будет создан шаблон библиотеки), например C++. Нажмите кнопку Next. 

VisualDSP library create select language

4. В разделе Add Startup Code/LDF ничего менять не надо, потому что скорее всего стартовый код добавлять не нужно - это же библиотека. Оставьте выбор по умолчанию "Don't add an LDF or startup code", нажмите кнопку Next.

VisualDSP library create skip startup code

5. Нажмите Next и Finish. Создание проекта библиотеки завершено. В каталоге проекта появятся два готовых файла: модуль кода библиотеки имя_проекта.cpp (для нашего примера mylib.cpp) и заголовочный файл для него (для нашего примера mylib.h). Теперь Вы можете создавать в модуле библиотеки mylib.cpp свои функции, или даже добавлять в проект библиотеки другие модули кода и заголовочные файлы. Компилируется библиотека как обычно, через меню Project -> Rebuild Project, после чего в каталоге Debug (или Release, в зависимости от того, какая конфигурация проекта выбрана из двух созданных по умолчанию) появится файл готовой библиотеки имя_проекта.dlb (для нашего примера mylib.dlb).

[Как использовать функции из библиотеки]

Перетащите файл библиотеки в проект, где хотите использовать функции библиотеки. После этого библиотека будет отображаться в папке Linker Files, и Вы можете использовать её функции точно так же, как и функции других модулей. В теле модуля, где нужны функции библиотеки, директивой #include подключите к проекту заголовочный файл функций библиотеки (в нашем примере это заголовочный файл mylib.h), и вызывайте функции библиотеки (в примере ниже вызывается функция библиотеки myFunction).

#include "pins.h"
#include "WEX025664.h"
#include "delay.h"
#include "keyboard.h"
#include "uart-debug/BfDebugger.h"
#include "UARTconsole.h"
#include "..\mylib\mylib.h"
 
int main()
{
   ConfigureInputs();
   ConfigureOutputs();
   
   PowerON();
   DEBUG_OPEN();
   InitOLED();
   Beep(100);
   while(1)
   {
      ReadKeyboardPoll();
      UARTdebugPoll();
      consolePoll();
      myFunction();
   }
}

Чтобы добавить реализацию функции __mulli3, добавьте в проект модуль кода Blackfin\lib\src\libdsp\mul_64.asm.
Чтобы добавить реализацию функции __udiv32, добавьте в проект модуль кода Blackfin\ldr\init_code\asm\src\libdsp.asm.
Чтобы добавить реализацию функции __urem32, добавьте в проект модуль кода Blackfin\lib\src\libdsp\rem_u32.asm.

Примечание: каталог Blackfin находится в корневой папке установленной системы VisualDSP++.

Причина в том, что в конце строки кода ассемблера блока asm должен стоять обратный слеш \. Если же в этой строке применить комментарий //, то этот обратный слеш действовать не будет, в результате чего весь текст кода после комментария не скомпилируется. Компилятор ошибку не выдаст, и никак не сообщит об этом. Пример:

void cycle_counter_enable (void)
{
   asm("\
   [--SP] = R1;\
   R1 = SYSCFG;\
   BITSET(R1,1);  // недопустимый комментарий, он запретит остальной код блока asm\
   SYSCFG = R1;\
   R1 = [SP++];\
   ");
}

Вот что получится на выходе после компиляции функции cycle_counter_enable:

   ...
cycle_counter_enable__Fv: [--SP] = R1; R1 = SYSCFG; BITSET(R1,1); RTS; ...

Как видите, все строки блока asm после комментария // исчезли. Потому, что компилятор ассемблера счел эти строки комментарием, потому что слеш \ также попал в комментарий. Чтобы этого не произошло, всегда в блоках asm используйте комментарии, вставленные с помощью /* */:

   asm("\
   [--SP] = R1;\
   R1 = SYSCFG;\
   BITSET(R1,1);  /* это допустимый комментарий в блоке asm */\
   SYSCFG = R1;\   R1 = [SP++];\
   ");

Функция clock (находится в заголовочном файле Blackfin\include\time.h) возвращает текущее значение счетчика тактов процессора (cycle counter, это пара регистров CYCLES, CYCLES2, обычно используется для формирования задержек в программе). Она всегда возвращает 0, если счетчик тактов (пара регистров CYCLES, CYCLES2) после сброса процессора не считает. В результате задержки не отслеживаются.

Такое может произойти, если в Вашем проекте не подключен код инициализации процессора (например, у Вас проект Init Code на ассемблере). Дело в том, что обычно заботу о взведении бита CCEN берет на себя код инициализации, который генерирует среда VisualDSP++ (обычно это модуль кода на ассемблере, он находится в файле с именем наподобие имяпроекта_basiccrt.s):

   ...
   R1 = SYSCFG;
   R4 = R0;       // Save modified list
   BITSET(R1,1);  // бит 1 это и есть CCEN, который разрешает отсчет тактов в CYCLES, CYCLES2
   SYSCFG = R1;   // Enable the cycle counter
   ...

Следовательно, чтобы счетчик тактов начал считать, и начала работать функция clock(), нужно установить бит CCEN в регистре SYSCFG. Во-первых, можно дать указание среде VisualDSP сгенерировать код инициализации процессора, в нем по умолчанию вставлен код, который взводит бит CCEN. Код инициализации добавляется через свойства проекта, раздел Add Startup Code/LDF. Во-вторых, установку бита CCEN можно сделать самостоятельно (если Вы по каким-то причинам не хотите добавлять код инициализации) отдельной функцией:

//Процедура разрешает работу счетчика CYCLES, CYCLES2 (нужно для работы функции clock()).
void cycle_counter_enable (void)
{
   asm("\
   [--SP] = R1;\
   R1 = SYSCFG;\
   BITSET(R1,1); /* бит 1 это и есть CCEN, который разрешает отсчет тактов в CYCLES, CYCLES2*/\
   SYSCFG = R1; /* Enable the cycle counter*/\
   R1 = [SP++];\ ");
}

Распределение памяти в программе задается с помощью обработки LDF-файла, привязанного к проекту. Привязка может осуществляться двумя способами:

Привязка LDF по умолчанию. Работает в том случае, если в проекте не выбрана опция генерации/добавления индивидуального файла управления линкером (*.ldf). В этом случае применяются настройки LDF-файла, расположенного в каталоге установки VisualDSP++. Например, если в проекте используется процессор ADSP-BF538, то будет использоваться файл линкера "%ProgramFiles%\Analog Devices\VisualDSP 5.0\Blackfin\ldf\ADSP-BF538.ldf".

Отдельный файл LDF. Индивидуальный файл настройки линкера можно добавить, если в настойках проекта выбрать пункт Add Startup Code/LDF. В этом случае в опциях проекта появится отдельный раздел настроек LDF Settings, где можно управлять распределением памяти системы. Также после того, как сделаете все настройки и нажмете кнопку OK, в корневом каталоге проекта появится файл имяпроекта.ldf, который можно редактировать текстовым редактором. В этом случае, если Вы хотите сохранить эти сделанные вручную изменения, то нужно в разделе настроек Remove Startup Code/LDF -> выбрать вариант Leave files in the project, but stop regenerating them.

Типы, функции Подключаемый файл заголовка
u8, s8, u16, s16, u32, s32 services_types.h
uint8_t, int8_t, uint16_t, ... stdint.h
size_t builtins.h, stdlib.h
memcpy, memcmp, strcpy, strncpy, ... string.h
clock_t time.h
FILE stdio.h

//#define SWRST 0xFFC00100
#define SoftwareReset() asm ("\
    /* Issue soft reset */\
    P0.L = LO(0xFFC00100);\
    P0.H = HI(0xFFC00100);\
    R0.L = 0x0007;\
    W[P0] = R0;\
    SSYNC;\
    /* Clear soft reset */\
    P0.L = LO(0xFFC00100);\
    P0.H = HI(0xFFC00100);\
    R0.L = 0x0000;\
    W[P0] = R0;\
    SSYNC;\
    /* Core reset - forces reboot */\
    RAISE 1;\
    ")

Такое случается, когда произошел сбой либо в самой программе VisualDSP, либо в отладчике. Восстановить нужные панели инструментов можно через меню Settings -> Preferences... -> раздел настроек Toolbars:

VisualDSP enable toolbars

Окно браузера проекта можно восстановить через меню View -> Project Window:

VisualDSP View Project Window

Пример кода, на который выдается ошибка, заголовочный файл:

// Заголовок strval.h:
#ifndef __STRVAL__
#define __STRVAL__
 
extern char const mytext1 [];
extern char const mytext2 [];
 
#endif   //__STRVAL__
 
//Модуль strval.cpp:
#define TRIANGLE_UP     0xE3
 
char const mytext1 [] = "это текст";
char const mytext2 [] = {TRIANGLE_DOWN, 0};

Ошибка:

[Error li1021]  The following symbols referenced in processor 'p0' could not be resolved:
        'mytext1 [_mytext1]' referenced from '.\Release\buttlabels.doj'
        'mytext2 [_mytext2]' referenced from '.\Release\buttlabels.doj'
Linker finished with 1 error
cc3089: fatal error: Link failed
Tool failed with exit/exception code: 1.
Build was unsuccessful.

Если удалить из определения строк атрибут const, то ошибка исчезает. На этот вариант линкер ошибки не выдает:

// Заголовок strval.h:
#ifndef __STRVAL__
#define __STRVAL__
 
extern char mytext1 [];
extern char mytext2 [];
 
#endif   //__STRVAL__
 
//Модуль strval.cpp:
#define TRIANGLE_UP     0xE3
 
char mytext1 [] = "это текст";
char mytext2 [] = {TRIANGLE_DOWN, 0};

Причина в том, строки с const попадают в память FLASH, а обычные проекты программ, которые загружаются LDR-загрузчиком в SRAM и SDRAM, не могут спользовать константы во FLASH. Если цель применения строк с атрибутом const была в экономии памяти SRAM, то как метод добиться той же цели можно перенести строки в память SDRAM, вот так:

section ("sdram0") char mytext1 [] = "это текст";

На некоторых файлах или функциях в отладчике почему-то не устанавливаются точки останова.

	Loading: "C:\!pkrc-m-lite\mainapp\Debug\pkrc-m-lite.dxe"...
	Load complete.
        Failed to set breakpoint in "table.cpp" on line 216
	Failed to set breakpoint in "table.cpp" on line 218
	Failed to set breakpoint in "table.cpp" on line 326
	Failed to set breakpoint in "table.cpp" on line 329
	Failed to set breakpoint in "table.cpp" on line 343
	Failed to set breakpoint in "table.cpp" on line 308
	Failed to set breakpoint in "table.cpp" on line 315

Возможные причины:

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

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

3. Не обновлена библиотека, в которую Вы ставите точку останова, либо эта библиотека скомпилирована с оптимизацией (Release).

Команда в списке команд Pre-buld или Post-build не выполняется с ошибкой "CTool::Execute failed to CreateProcess":

copy /Y Release\miscell.dlb /B ..\..\bin\miscell.dlb /B

Эту ошибку можно устранить, если перед строкой команд запустить интерпретатор cmd.exe:

cmd.exe /C copy /Y Release\miscell.dlb /B ..\..\bin\miscell.dlb /B

См. также: CTool: Execute failed to CreateProcess site:ez.analog.com.

Простая ситуация - Вы открыли какой-то проект из примеров VisualDSP++, рассчитанный на использование внешней памяти SDRAM. Настройки проекта рассчитаны на какую-нибудь фирменную плату EZ-KIT Lite, у которой памяти больше, чем в отлаживаемом устройстве. Например, проект рассчитан на 64 мегабайта SDRAM, а у Вас в наличии всего лишь 32. Проект компилируется, но на Вашем устройстве не работает, потому что реальной памяти меньше, чем задано в настройках проекта. Если же поменять объем памяти SDRAM (Project Options... -> LDF Settings -> External Memory), то проект не компилируется из-за нехватки памяти - в нем заданы кучи пользователя (user heap) слишком большого размера. Возникает вопрос - как поменять размеры пользовательских куч?

Пользовательские кучи меняются через Project Options... -> LDF Settings -> UserHeap. Это и есть так называемый LDF Wizard. Интерфейс редактирования куч сделан очень неудобно. Во-первых, если уже созданы несколько куч, и их нужно поменять, то это возможно только вводом имени кучи в поле Heap name. После этого кнопка Add/Update позволяет изменить размер существующей кучи. Итак, если нужно поменять размер пользовательской кучи, нужно выполнить следующие шаги:

1. Откройте свойства проекта, зайдите в раздел Project Options... -> LDF Settings -> UserHeap. Вы увидите список уже имеющихся куч. Пользовательские кучи называются user heap. Ниже на скриншоте показаны пользовательские кучи FssHeap и RamDiskHeap проекта shell_browser из примеров для Blackfin.

LDF Wizard user heap edit01

2. Предположим, нужно поменять абсолютный размер кучи RamDiskHeap с 9 мегабайт на 4 мегабайта SDRAM. Для этого в поле HeapName нужно ввести имя редактируемой кучи RamDiskHeap, выбрать тип памяти Memory types: L3 external memory (SDRAM), переставить переключатель Heap size в положение Absolute size, выбрать размер 4 MB:

LDF Wizard user heap edit02

3. Нажмите на кнопку Add/Update. Мастер выдаст запрос о подтверждении изменения размера существующей кучи:

LDF Wizard user heap edit03

Ответьте утвердительно OK, и у кучи будет установлен новый размер.

LDF Wizard user heap edit04

Нажмите кнопку OK, чтобы подтвердить изменение конфигурации. Будет автоматически сгенерирован новый файл настроек линкера имяпроекта.ldf (в нашем примере shell_browser.ldf) и сохранен в корневом каталоге проекта, а старый LDF-файл будет переименован в vdsp_bak_имяпроекта.ldf (в нашем примере vdsp_bak_shell_browser.ldf).

К примеру, функция adi_dev_Open возвращает код ошибки 0x00060003. Что это означает?

Все функции библиотеки системных служб (System Services Library, SSL), драйверов, менеджеров и другие функции библиотек Analog Devices (эти функции имеют префикс adi_) возвращают код ошибки типа u32 (целое 32-битное число). Старшие 16 бит этого кода означают тип службы или драйвера, и они определяются так называемыми стартовыми значениями для перечислений, которые определяются в заголовочном файле Blackfin\include\services\services.h следующим образом:

...
/*********************************************************************
Здесь определены стартовые значения для перечислений различных служб.
Это гарантирует, что значения перечислений для каждой из служб не будут
пересекаться со значениями от других служб (т. е. все коды возврата
будут получаться уникальными и глобальными для всей системы библиотечных
функций Analog Devices). Это касается только таких элементов, как
идентификаторы команд (command ID), идентификаторы событий (event ID)
и коды возврата. Обратите внимание, что код возврата для каждой службы
имеет нулевой код обычного успешного возврата (код generic success всегда
равен 0), в то время как код возврата из каждой службы для стандартной
ошибки (generic failure) всегда равен 1 (младшие 16 бит кода возврата),
независимо от стартовой точки перечисления.
*********************************************************************/
/* Physical Device Drivers Enumeration start */
#define ADI_DEV_ENUMERATION_START           (0x40000000)
/* Device Class Drivers Enumeration start */
#define ADI_DEV_CLASS_ENUMERATION_START     (0x50000000)
/* Deferred Callkback Manager Enumeration start */
#define ADI_DCB_ENUMERATION_START           (0x00020000)
/* DMA Manager Enumeration start */
#define ADI_DMA_ENUMERATION_START           (0x00030000)
/* EBIU Manager Enumeration start */
#define ADI_EBIU_ENUMERATION_START          (0x00040000)
/* Interrupt Manager Enumeration start */
#define ADI_INT_ENUMERATION_START           (0x00050000)
/* Power Service Enumeration start */
#define ADI_PWR_ENUMERATION_START           (0x00060000)
/* Timer Service Enumeration start */
#define ADI_TMR_ENUMERATION_START           (0x00070000)
/* Flag Control Service Enumeration start */
#define ADI_FLAG_ENUMERATION_START          (0x00080000)
/* Port Control Service Enumeration start */
#define ADI_PORTS_ENUMERATION_START         (0x00090000)
/* Real Time Clock Service Enumeration start */
#define ADI_RTC_ENUMERATION_START           (0x000a0000)
/* File System Service Enumeration start */
#define ADI_FSS_ENUMERATION_START           (0x000b0000)
/* Semaphore Service Enumeration start */
#define ADI_SEM_ENUMERATION_START           (0x000c0000)
/* Reserved Enumeration start */
#define ADI_RES1_ENUMERATION_START          (0x000d0000)
/* Reserved Enumeration start */
#define ADI_RES2_ENUMERATION_START          (0x000e0000)
/* Reserved Enumeration start */
#define ADI_RES3_ENUMERATION_START          (0x000f0000)
/* PWM Module Enumeration start */
#define ADI_PWM_ENUMERATION_START           (0x00100000)
/* ACM Module Enumeration start */
#define ADI_ACM_ENUMERATION_START           (0x00110000)
/* STDIO Module Enumeration start */
#define ADI_STDIO_ENUMERATION_START         (0x00120000)
...

Так что если в нашем примере код ошибки был равен 0x00060003, то это соответствует началу перечисления кодов ошибки службы управления питанием ADI_PWR_ENUMERATION_START (оно равно 0x00060000). Осталось только выяснить, что означает код 0x0003, находящийся в младших 16 битах кода возврата. Для этого нужно найти заголовочный файл службы управления питанием, это проще всего выполнить поиском ADI_PWR_ENUMERATION_START по содержимому в файлах *.h каталога Blackfin\include\. 

SSL search ADI PWR ENUMERATION START 

Результаты поиска показали на 2 файла - adi_pwr.h и services.h, перечисление для кодов ошибок службы управления питанием находится в первом из них, adi_pwr.h:

...
//$RCSfile: adi_pwr.h,v $
//Описание: заголовочный файл Dynamic Power Management (служба управления питанием)
...
/*********************************************************************************
* Коды возврата
*********************************************************************************/
typedef enum ADI_PWR_RESULT {
   ADI_PWR_RESULT_SUCCESS,                        // Успешное завершение
   ADI_PWR_RESULT_FAILED,                         // Общая ошибка
   ADI_PWR_RESULT_START=ADI_PWR_ENUMERATION_START,
    
   ADI_PWR_RESULT_NO_MEMORY,          // (1) Недостаточно памяти для выполнения операции
   ADI_PWR_RESULT_BAD_COMMAND,        // (2) Команда не распознана, или значение её параметра неправильное
   ADI_PWR_RESULT_NOT_INITIALIZED,   // (3) Служба управления питанием не была инициализирована
   ADI_PWR_RESULT_INVALID_CSEL,       // (4) Предоставлено недопустимое значение CSEL
   ADI_PWR_RESULT_INVALID_SSEL,       // (5) Предоставлено недопустимое значение SSEL
   ADI_PWR_RESULT_INVALID_CSEL_SSEL_COMBINATION,  // (6) Предоставлена недопустимая комбинация
                                                      // значений SSEL/CSEL
   ADI_PWR_RESULT_INVALID_VLEV,       // (7) Недопустимый уровень внутреннего напряжения
   ADI_PWR_RESULT_INVALID_VDDEXT,     // (8) Недопустимый уровень внешнего напряжения
   ADI_PWR_RESULT_INVALID_VR_FREQ,    // (9) Недопустимая частота переключения регулятора
   ...
} ADI_PWR_RESULT;

Поскольку значения кодов в перечислении идут по порядку, начиная от ADI_PWR_ENUMERATION_START (равного 0x00060000), то значению 0x00060003 будет соответствовать код ошибки ADI_PWR_RESULT_NOT_INITIALIZED, т. е. причина ошибки в том, что служба управления питанием не была инициализирована (очевидно, для управления портом UART через SSL нужен сервис этой службы). 

Таким образом, поиск причины ошибки по её коду состоит из 3 простых шагов: 

1. Откройте файл services.h, и найдите определение начального кода перечисления по старшим 16 битам кода возврата (в нашем примере это 0x0006, что соответствует искомому определению ADI_PWR_ENUMERATION_START).

2. Найдите заголовочный файл службы, где находится найденный начальный код. Заголовочные файлы служб SSL и драйверов находятся в каталоге Blackfin\include\ (в нашем примере искомый файл - Blackfin\include\services\pwr\adi_pwr.h).

3. Откройте заголовочный файл службы, и найдите перечисление, где используется найденный начальный код. Просмотрите это перечисление по порядку, и найдите тот по порядку код, который соответствует младшим 16 битам кода возврата (в нашем примере это 0x0003, что соответствует значению ADI_PWR_RESULT_NOT_INITIALIZED). Рядом с этим кодом будет описание ошибки (в нашем примере не была инициализирована служба управления питанием).

Окно дерева файлов проекта (Project Window) случайно отцепилось от левого края окна среды разработки VisualDSP и стало плавающим. Обратно прикрепить на старое место не получается. Как исправить?

Оказывается, в контекстном меню закладки Project есть секретная опция Allow Docking. Если галочка там стоит, то окно Project можно прикрепить к левой или правой стороне главного окна VisualDSP++ (см. скриншот).

VisualDSP Project Windows allow docking

Окно можно мышью перетаскивать по экрану, если кликнуть и удерживать левую кнопку мыши на верхней плашке окна. Если прикрепление окна разрешено, то при приближении к месту прикрепления окно можно отпустить, и оно останется прикрепленным. Чтобы предотвратить прикрепление, можно при перетаскивании удерживать клавишу Ctrl.

Ветвь реестра HKEY_CURRENT_USER\Software\Analog Devices\VisualDSP++ 5.0

В сессии отладки произошло неожиданное отключение от отлаживаемого процессора (сбой по питанию), после чего среда разработки стала глючить с завершением по ошибке "APPCRASH Idde.exe".

VisualDSP APPCRASH Idde

Сигнатура проблемы:
  Имя события проблемы:	APPCRASH
  Имя приложения:	Idde.exe
  Версия приложения:	8.0.7.15
  Отметка времени приложения:	4e12e206
  Имя модуля с ошибкой:	ntdll.dll
  Версия модуля с ошибкой:	6.1.7601.17725
  Отметка времени модуля с ошибкой:	4ec49b8f
  Код исключения:	c015000f
  Смещение исключения:	00084621
  Версия ОС:	6.1.7601.2.1.0.256.1
  Код языка:	1049
  Дополнительные сведения 1:	0a9e
  Дополнительные сведения 2:	0a9e372d3b4ad19135b953a78882e789
  Дополнительные сведения 3:	0a9e
  Дополнительные сведения 4:	0a9e372d3b4ad19135b953a78882e789
Ознакомьтесь с заявлением о конфиденциальности в Интернете:
  http://go.microsoft.com/fwlink/?linkid=104288&clcid=0x0419
Если заявление о конфиденциальности в Интернете недоступно, ознакомьтесь
с его локальным вариантом:
  C:\Windows\system32\ru-RU\erofflps.txt

Проблема заключалась в открытом окне VDK Status, которое осталось открытым в отказавшей сессии отладки, но текущее состояние IDDE не позволяло корректно его отобразить. Чтобы исправить ошибку, нужно в настройках VisualDSP++ отключить отображение окна VDK Status. Как исправить ошибку:

1. Закройте VisualDSP++.
2. Запустите regedit с правами администратора.
3. Откройте ветвь реестра HKEY_CURRENT_USER\Software\Analog Devices\VisualDSP++ 5.0\Components\VDK Status\Preferences.
4. Измените значение параметра Enabled в 0 (раньше там было установлено значение 1).
5. Запустите VisualDSP++, окно VDK Status не будет отображаться, и ошибка исчезнет.
6. Закройте VisualDSP++.
7. В regedit верните значение параметра Enabled в 1.
8. Закройте regedit.

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

const char version_full [] = "[DSP] 01.06.2016 10:32:52";

Если оптимизация отключена, и линкер проинструктирован не удалять неиспользуемые объекты (поведение по умолчанию конфигурации Debug проекта), то проблем нет, стока останется в коде программы. Но если оптимизация включена, и инструментарию задано удалять все лишнее (таково поведение по умолчанию конфигурации Release проекта), то строка version_full будет удалена из программы. Как сохранить строку version_full и в конфигурации Debug и в конфигурации Release?

Решить проблему позволяет директива #pragma retain_name. Вот как её надо использовать, чтобы сохранить version_full для нашего примера:

const char version_full [] = "[DSP] 01.06.2016 10:32:52";
#pragma retain_name
const char *pversion = version_full;

Здесь прагма retain_name дает указание сохранить переменную pversion. Но поскольку эта переменная инициализируется ссылкой на version_full, то строка version_full также будет сохранена в коде программы.

Иногда выскакивает предупреждение линкера li2152 о том, что в проекте имеются объектные или библиотечные файлы, которые скомпилированы с несовместимой ревизией кристалла процессора. Т. е. для текущего проекта выбрана ревизия кристалла, которая не совпадает с ревизией объектных / библиотечных файлов, которые были скомпилированы ранее. Пример:

[Warning li2152]  The following input file(s) 'si-revision' is incompatible with 0.4 [ADSP-BF538]
           0.5 [ADSP-BF538]  ..\bin\u8glib.dlb[u8g_circle.doj]
           0.5 [ADSP-BF538]  ..\bin\u8glib.dlb[u8g_com_api.doj]
           0.5 [ADSP-BF538]  ..\bin\u8glib.dlb[u8g_com_blackfin_hw_spi.doj]
           0.5 [ADSP-BF538]  ..\bin\u8glib.dlb[u8g_dev_ssd1322_nhd31oled_gr.doj]
           0.5 [ADSP-BF538]  ..\bin\u8glib.dlb[u8g_font.doj]
           0.5 [ADSP-BF538]  ..\bin\u8glib.dlb[u8g_font_data.doj]
           0.5 [ADSP-BF538]  ..\bin\u8glib.dlb[u8g_line.doj]
           0.5 [ADSP-BF538]  ..\bin\u8glib.dlb[u8g_ll_api.doj]
           0.5 [ADSP-BF538]  ..\bin\u8glib.dlb[u8g_pb8h2.doj]
           0.5 [ADSP-BF538]  ..\bin\u8glib.dlb[u8g_rect.doj]
           0.5 [ADSP-BF538]  ..\bin\u8glib.dlb[WEX025664.doj]

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

VisualDSP project select automatic silicon revision

Таким образом, предупреждение li2152 будет выдано, если какая-то часть кода (например библиотеки, которые используются в проекте) была скомпилирована, когда процессор через отладчик JTAG был недоступен, а другая часть кода была скомпилирована, когда отладчик JTAG мог прочитать из процессора версию. Если версия процессора в каких-то частях скомпилированного кода не совпали, то будет выдано сообщение li2152.

Предупреждение li2152 чаще всего можно проигнорировать. Устранить его можно следующими способами:

1. Перекомпилировать все двоичные, объектные и библиотечные файлы под одну ревизию процессора. Если во всех этих проектах выбрано автоматическое определение ревизии кремния процессора, то подключите Ваше устройство с процессором к отладчику и включите питание устройства.

2. Выбрать в проекте приложения фиксированную ревизию кремния, чтобы она совпадала с ревизией библиотек (для данного примера нужно выбрать ревизию силикона 0.5) и перекомпилировать проект.

Эти макросы предназначены для отладки драйверов устройств (ADI_DEV) и библиотек системных служб (System Services Library, ADI_SSL) соответственно. Оба эти макроса очень полезны, и позволяют вычислить многие ошибки на этапе отладки проекта. Это особенно важно по той причине, что документация по библиотекам Analog Devices весьма скудная и в ней есть досадные пробелы. Например, Вы никогда не узнаете без явного определения макроса ADI_DEV_DEBUG, что синхронный режим работы драйвера (задается командой ADI_DEV_CMD_SET_SYNCHRONOUS Менеджера Устройств) несовместим с методами потока данных ADI_DEV_MODE_CHAINED и ADI_DEV_MODE_CHAINED_LOOPBACK - в документации это не написано. Без этих отладочных макросов вызовы библиотечных функций будет завершаться успешно, но код все равно не будет работать корректно. Если Вы укажете отладочные макросы, то вызов библиотечной функции вернет код ошибки, и Вы узнаете о неправильном использовании библиотеки. Обязательно определяйте макросы ADI_DEV_DEBUG и ADI_SSL_DEBUG в проекте при отладке, это избавит Вас от многих проблем, связанных с непонятным поведением отлаживаемого кода.

Макросы ADI_DEV_DEBUG и ADI_SSL_DEBUG определены в проектах драйверов и библиотеке SSL, и находится они в скомпилированных библиотечных файлах *.dlb (см. каталог %ProgramFiles% \ Analog Devices \ VisualDSP 5.0 \ Blackfin \ lib). Когда проект библиотеки компилируется в конфигурации Release, то макрос ADI_DEV_DEBUG отсутствует, что позволяет условными операторами #ifdef выкинуть отладочный код. Для конфигурации Debug этот макрос в проекте библиотеки определен, что позволяет добавить дополнительные проверки на ошибки и вывод отладочной информации.

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

Имейте в виду, что если Вы используете не двоичную библиотеку, а подключаете исходный библиотечный код (например, добавляете в проект модули adi_dev.c, adi_dma.c и т. п), то без прямого определения в проекте отладочных макросов ADI_DEV_DEBUG и ADI_SSL_DEBUG проверочный код работать не будет. Макросы в проекте можно определить через общий подключаемый файл, или в настройках проекта, раздел Project -> Compile -> Preprocessor.

VisualDSP ADI debug macro

Стандартная библиотека для управления динамически выделяемой памятью из кучи содержит 4 runtime-функции: malloc(), calloc(), realloc(), free(). Библиотека VisualDSP++ предоставляет дополнительную, нестандартную функцию heap_space_unused (int heapidx), которая возвращает количество (в байтах) оставшегося незанятого места в указанной куче. Для кучи по умолчанию нужно указать 0 в параметре heapidx. Также имеются альтернативные функции для работы с несколькими кучами, подробнее см. [13].

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

u32 freeheap (void)
{
   u32 sizecurr, sizefree;
   
   int i = 1;
   sizefree = 0;
   u8 *pnt;
   while (true)
   {
      sizecurr = 256*i;
      pnt = (u8*)malloc(sizecurr);
      if (pnt==NULL)
         break;
      else
      {
         sizefree = sizecurr;
         free(pnt);
         ++i;
      }
   }
   return sizefree;
}

Принцип работы функции понятен - алгоритм по циклу делает попытки выделения блока постоянно увеличивающегося размера с шагом 256 байт. На определенной итерации цикла, как только блок оказывается слишком большой, функция malloc завершится неудачно и вернет NULL. Размер предыдущего успешно выделенного блока покажет количество свободной памяти в куче.

Компилятор выдаст ошибку, когда stdfix.h будет использоваться в коде C++. Причина в том, что в C++ уже предоставлена поддержка дробных чисел с помощью fract и shortfract типов данных.

По умолчанию массивы помещаются в секцию памяти, которая предсуматривает заполнение переменных нулями при старте программы. Если массивы большие, то это создает проблему - запуск программы задерживается на время инициализации массивов, и при этом сессия отладчика JTAG запускается очень долго.

Если инициализация массивов не нужна, то запуск можно ускорить, если поместить данные массива в неинициализируемую память. Для этого нужно выполнить следующее: создать новую секцию в нужной области памяти с квалификатором NO_INIT, с помощью директивы #pragma section задать размещение массива в нужной секции, перекомпилировать программу.

Ниже приведен пример такого размещения массива в памяти SDRAM. Процесс по шагам:

1. Откройте файл *.LDF проекта, найдите начало списка секций (SECTIONS {...}), поместите туда определение новой секции с квалификатором NO_INIT. Получится наподобие следующего (здесь определена новая секция no_init_sdram):

      /*$VDSG< insert-new-sections-at-the-start >                 */
      no_init_sdram NO_INIT
      {
		 INPUT_SECTION_ALIGN(4)
      	 INPUT_SECTIONS($OBJECTS(no_init_sdram))
      } > MEM_SDRAM0_BANK3
      /*$VDSG< insert-new-sections-at-the-start >                 */

Обратите внимание, что определение новой секции заключено в специальные теги $VDSG, что обеспечит сохранение определения секции при новой автоматической генерации файла LDF. MEM_SDRAM0_BANK3 задает адрес, с которого будет размещяться секция в память (он определен в списке диапазонов адресов MEMORY {...} файла LDF).

2. Добавьте директиву #pragma section ("no_init_sdram", NO_INIT) сразу перед определением массива, у которого Вы не хотите обнулять память. Пример буфера для экрана:

#pragma section ("no_init_sdram", NO_INIT)
unsigned char ScreenData[SCREEN_BUFFER_SIZE];

Перекомпилируйте программу, массив теперь будет размещен в неинициализируемую память.

Такую ошибку выдает компилятор, если указатель на функцию инициализируется глобально, не в коде других функций. Пример:

typedef void (*functype)(void);
 
void dummyfunc (void) { while(1); }
 
functype pFunc;
pFunc = dummyfunc;  //ошибка cc0077: {D} error: this declaration ...
 
void test (void)
{
   //pFunc = dummyfunc;
   pFunc();
}

А вот этот код компилируется без ошибки:

typedef void (*functype)(void);
 
void dummyfunc (void) { while(1); }
 
functype pFunc;
//pFunc = dummyfunc;
 
void test (void)
{
   pFunc = dummyfunc;  //ошибки нет!
   pFunc();
}

Возможно причина в том, что адрес функции dummyfunc при попытке инициализации переменной глобально пока не известен.

При подключениии заголовочного файла common_types.h к коду компилятор выдает ошибку "cc0020: error: identifier ... is undefined":

//[Заголовочный файл common_types.h]
...
struct TScale
{
    u8 scalecode;
    u8 units;
};

struct
TInput { u8 probetype; u8 inptype; bool autoscale; TScale scaleU; //Ошибка cc0020... };

Такое поведение компилятора на первый взгляд кажется странным, потому что тип TScale определен тут же, перед определением структуры TInput. Однако причина ошибки здесь в том, что заголовочный файл с определением TScale подключался к коду С, и для компилятора C такое определение типа не является правильным. Если тип структуры TScale определить через typedef, то ошибка исчезнет:

typedef struct _TScale
{
    u8 scalecode;
    u8 units;
}TScale;

struct
TInput { u8 probetype; u8 inptype; bool autoscale; TScale scaleU; //Теперь в этом месте ошибки нет. };

Если бы код компилировался для C++, то такой проблемы не было бы - для C++ определения типа структуры через struct и typedef struct одинаково допустимы. Если же модуль компилируется как модуль C (имеет расширение *.c), то тогда тип структуры следует определять через typedef struct.

Подробнее про различия определений struct и typedef struct см. в статье [14].

Ошибки и предупреждения компилятора отображаются в окне вывода результатов компиляции Build вместе с кодом ошибки и кратким пояснением. Более подробное пояснение проблемы можно получить в через систему подсказок (Help) VisualDSP++, если выделить код ошибки (например, cc0064), и затем нажать F1.

VisualDSP get help on error code

Появится окошко подсказки с подробным объяснением указанной ошибки.

Представлена декларация типа, в котором определен тип, но не задано его имя. Для классов и перечислений C++ разрешено определять типы только в том случае, если заданы имена полей или значений перечисления. Для C разрешено определять структуры, у полей которых отсутствуют имена полей, а для C++ этого делать нельзя. К примеру, вот такой код для C скомпилируется без ошибки:

typedef struct {
    int;
    float;
} TMyType;

Компиляция этого же самого кода для C++ приведет к ошибке cc0064. Само собой, чтобы устранить ошибку, нужно просто указать имена полей в структуре:

typedef struct {
    int field1;
    float field2;
} TMyType;

Ошибка возникла из-за того, что модуль C компилировался в режиме C++, и у структуры ADI_FAT_BOOT_SECTOR_FAT32 не были явно определены имена полей. Язык C в этом случае позволяет обращаться к полям дочерней структуры без явного указания имени родительского поля. Вот такой код на C, обращающийся к pBPB->bsm.BPB_FSInfo, не даст ошибки:

...
typedef struct
{
   u32 BPB_FATSz32;        // 32-битный счетчик секторов FAT32, занимаемый одной копией FAT.
                            // BPB_FATSz16 должно быть ==0.
   u16 BPB_ExtFlags;       // Определяет зеркалирование FAT.
   u16 BPB_FSVer;          // Старший байт для major-номера ревизии. Младший байт для minor-номера ревизии. 0:0
   u32 BPB_RootClus;       // Номер кластера первого кластера корневой (root) директории.
   u16 BPB_FSInfo;         // Номер сектора структуры FSINFO в зарезевированной области тома FAT32
   u16 BPB_BkBootSec;      // Если не 0, то показывает номер сектора в зарезервированной области тома
                           // копии загрузочной записи (boot record). Обычно ==6. Но рекомендуется другое значение, !=6.
   u8  BPB_Reserved[12];   // Код, который форматирует тома FAT, всегда должен устанавливать эти байты в 0
} ADI_FAT_BOOT_SECTOR_MIDDLE;
...
 
//У структуры ADI_FAT_BOOT_SECTOR_FAT32 не определены имена полей, это разрешено:
typedef struct {
    ADI_FAT_BOOT_SECTOR_START;
    ADI_FAT_BOOT_SECTOR_MIDDLE;
    ADI_FAT_BOOT_SECTOR_END;
} ADI_FAT_BOOT_SECTOR_FAT32;
 
ADI_FAT_BOOT_SECTOR_FAT32 *pBPB;
...
//Прямое обращение к полю BPB_FSInfo дочерней для pBPB структуры ADI_FAT_BOOT_SECTOR_MIDDLE,
// без указания имени самой структуры ADI_FAT_BOOT_SECTOR_MIDDLE. На языке C это допустимо:
if (pBPB->BPB_FSInfo)
{
   ...

Когда код компилируется в режиме C++, то для ADI_FAT_BOOT_SECTOR_FAT32 нужно указать имена полей, и тогда код проверки pBPB->BPB_FSInfo уже будет ошибочным:

...
typedef struct {
    ADI_FAT_BOOT_SECTOR_START bse;
    ADI_FAT_BOOT_SECTOR_MIDDLE bsm;
    ADI_FAT_BOOT_SECTOR_END bse;
} ADI_FAT_BOOT_SECTOR_FAT32;
ADI_FAT_BOOT_SECTOR_FAT32 *pBPB;
...
 
//Ошибка cc0135: error: class "ADI_FAT_BOOT_SECTOR_FAT32" has no member "BPB_FSInfo",
// потому что пропущено имя поля:
if (pBPB->BPB_FSInfo)
{
   ...

Чтобы устранить ошибку, нужно указать имя поля структуры ADI_FAT_BOOT_SECTOR_FAT32:

if (pBPB->bsm.BPB_FSInfo)
{
   ...

Можно использовать стандартные функции ввода/вывода (standard input/output I/O, сокращенно STDIO) - printf() и тому подобные - чтобы отправлять сообщения из отлаживаемого устройства в окружение среды разработки VisualDSP++. Однако такие операции ввода/вывода вовлекают некоторый обмен данными через соединение отладчика с платой, что серьезно замедляет процессор. Это особенно критично, если в момент ввода/вывода процессор должен обработать прерывание. Например, если Вы пытаетесь обработать сигнал с частотой выборок 44 килогерца в реальном времени, то у Вас может быть примерно 500 тактов процессора между отдельными выборками сигнала, за которые Вы должны успеть выполнить обработку. Это означает, что Вы не сможете использовать printf() (или его библиотечный эквивалент).

Замедление кода при выполнении операций STDIO заключается в способе, с которым эти операции выполняются. Типичный пример это функция printf(), которая выполняет 3 операции: сначала она выполняет open, затем read, и затем close. Во время каждого из этих событий DSP зависает на скрытой точке останова (hidden breakpoint по метке __primio), где выполняется запрошенная операция, после чего DSP снова запускается. Когда DSP приостанавливается, эмулятор или агент отладки получает управление и, в случае эмулятора, контекст выполнения перемещается через JTAG, далее через USB к хосту отладки. Затем выполняются операции с памятью, если они необходимы. Когда хост заканчивает свои операции (в случае printf результат выполнения этих операций программист видит в окне консоли VisualDSP - туда выводятся сообщения, генерируемые printf), контекст выполнения кода переключается обратно на DSP, что снова требует прохождения трафика USB, и агент отладки устанавливает запуск процессора. Это делается для каждой из операций printf (open, read, close), что занимает заметное время.

Переключение контекста между DSP и хостом отладки (PC) происходит всякий раз, когда используется любой STDIO, и это занимает некоторое время, необходимое для перемещений данных между хостом и DSP. Хотя отладчики HPUSB-ICE и USB-ICE (или EZ-KIT Debug Agent) выполняют такие же переключения, HPUSB-ICE должен это делать быстрее, чем USB-ICE, поскольку у HPUSB-ICE шире полоса пропускания, так что переключения контекста могут происходить намного быстрее.

Если используете эмулятор и недовольны скоростью работы, то Вам следует просто уменьшить количество данных, которые Вы пытаетесь вывести в лог консоли через printf. К сожалению, из-за того, что операции выполняются через JTAG, они медленные, что может разрушить метрики реального времени выполнения приложения, так как будут происходить несколько команд остановки/запуска.

Тем не менее, самый лучший способ увеличить быстродействие STDIO состоит в том, чтобы полностью устранить переключения контекста эмулятора/агента отладки. Это можно сделать путем перенаправления STDIO в локальное периферийное устройство процессора, например в UART или USB, которые будут передавать отладочные сообщения в приложение терминала (такое как HyperTerminal, putty и т. п.) через последовательный порт Вашего PC. Руководство компилятора (Compiler Manual) предоставляет инструкции для такого перенаправления, см. раздел "File IO Support" (также см. [15]).

Тип памяти можно выбрать при определении переменной с помощью директивы secton("имя_секции_памяти") и директивы #pragma section (подробнее см. документацию и статью [7]). Имена секций можно подсмотреть в файле *.ldf проекта. Примеры выделения памяти разного типа:

//Эта память будет выделена в сверхбыстрой памяти процессора (SRAM):
section("L1_data") u8 myDataL1 [16384];
 
//Эта память будет выделена в SDRAM:
section("sdram0") u8 myDataSDRAM [65536];
 
//Эта память будет выделена в не инициализируемой памяти:
#pragma section ("no_init_sdram", NO_INIT)
u8 ScreenData[SCREEN_BUFFER_SIZE];

ADSP-BF538 во внутренней памяти SRAM (L1) есть 2 банка для данных: банк A и банк B (см. рисунок).

ADSP BF538 memory map proportional

Как раз этим двум банкам и соответствуют секции памяти L1_data_a и L1_data_b. С помощью директивы #pragma section или модификатора определения переменной section пользователь может поместить свои данные (переменные, массивы) в любую из этих секций (подробнее см. Q066).

Секция Диапазон адресов Размер, байт
L1_data_a 0xFF80 0000 .. 0xFF80 7FFF 32768
L1_data_b 0xFF90 0000 .. 0xFF90 7FFF 32768

Секция L1_data относится к обоим секциям, и к L1_data_a, и к L1_data_b. Если пользователь в своем коде будет использовать секцию L1_data, то линковщик может положить данные в банк A или банк B по своему усмотрению - в зависимости от наличия свободного места в банках и размера переменной.

Операторы условной компиляции (#ifdef), срабатывающие по наличию определения __ECC__, встречаются в кроссплатформенных библиотеках ADI, когда необходимо отличать среду компиляции VisualDSP (компиляторы cc21k.exe, ccblkfn.exe, ccts.exe) от других возможных компиляторов (например GCC).

По умолчанию (когда в коде отсутствуют специальные директивы для выбора памяти для переменных) линкер помещает все типы памяти (data, constdata, bsz, strings и autoinit) в самую быструю память L1. Когда память L1 заканчивается, будет использоваться память L3 (SDRAM).

Освободить память L1 можно, если при декларировании переменных, массивов и структур явно задавать нужную секцию памяти с помощью ключевого слова section или директивы #pragma section (см. вопрос Q066).

Также можно в проекте задать тип памяти по умолчанию для размещения переменных. Это делается опцией компилятора -section (подробнее описание этой опции см. в статье [16]). Например, добавление в командную строку следующей опции приведет к тому, что компилятор для размещения данных переменных будет использовать по умолчанию память L3 вместо памяти L1:

-section alldata=sdram0

Опцию -section можно настроить в свойствах проекта, раздел Compile -> General, поле ввода Additional options:

VisualDSP option section

Можно поделить приложение на несколько проектов (переместив некоторые модули в библиотеки), и в нужном проекте, где создается библиотека со сниженными требованиями к быстродействию, задать опцию -section alldata=sdram0, чтобы переменные по умолчанию помещались в память L3. Это избавит от необходимости добавлять к каждой переменной в коде директиву по управлению памятью.

Первое, что нужно сделать, это разрешить генерацию выходных файлов статистики компиляции. Делается это в секции настроек линкера свойств проекта. Откройте диалог редактирования опций проекта (меню Project -> Project Options...), перейдите в раздел настроек Link -> General и в разделе настроек "Additional Output" поставьте галочки "Generate symbol map" и "Generate xref".

Галочка Имя выходного файла Описание
Generate symbol map Debug/имяпроекта.map.xml (или Release/имяпроекта.map.xml) Карта распределения памяти для переменных и кода программы (какие секции кода используются, по каким адресам, и сколько памяти при этом потрачено).
Generate xref Debug/XRef.xml (или Release/XRef.xml) Список перекрестных ссылок на объекты в программе.

linker Additional Output options

Пример MAP-файла bootloader.map.xml, как он выглядит в редакторе:

<?xml version="1.0" standalone="no"?>
<?xml-stylesheet type="text/xsl" href="file:///C:/Program Files (x86)
/Analog Devices/VisualDSP 5.0/System/linker_map_ss1.xsl"?>
<!DOCTYPE DOCUMENT SYSTEM "file:///C:/Program Files (x86)/Analog Devices
/VisualDSP 5.0/System/linker_map.dtd">
<DOCUMENT> <LINKPROJECT guid='8596dd65fa6ec499c' id='036594F8' name='p0' orig_file_name=
'.\Release\bootloader.dxe' architecture='ADSP-BF538' > <FILE_NAME><![CDATA[.\Release\bootloader.dxe]]></FILE_NAME> <MEMORY id='0054CDC8' name='MEM_L1_SCRATCH' start_address='0xffb00000'
end_address='0xffb00fff' type='' qualifier='RAM'
width='0x8' words_used='0x4c' words_unused='0xfb4' > <OUTPUT_SECTIONS> </OUTPUT_SECTIONS> </MEMORY> <MEMORY id='0054CE18' name='MEM_L1_CODE' start_address='0xffa00000'
end_address='0xffa13fff' type='' qualifier='RAM'
width='0x8' words_used='0x9678' words_unused='0xa988' > <OUTPUT_SECTIONS> <OUTPUT_SECTION name='L1_code' id='040B3128' memory_id='0054CE18'
type='SHT_PROGBITS' start_address='0xffa00000' word_size='0x962c'

Вот так выглядит этот же MAP-файл bootloader.map.xml в браузере Internet Explorer:

linker MAP file in IE

[Чем просмотреть?]

Файлы карты памяти (имяпроекта.map.xml) и перекрестных ссылок (XRef.xml) имеют формат XML, и ссылаются соответственно на таблицы стилей linker_map_ss1.xsl и xref.xsl (находятся в каталоге установки системы VisualDSP, см. папку %ProgramFiles% \ Analog Devices \ VisualDSP 5.0 \ System). К сожалению, с просмотром этих XML-файлов есть определенные проблемы. Анализировать большой и сложный XML-файл текстовым редактором, даже если он подсвечивает теги - совсем не вариант.

Изначально компания Analog Device задумывала просматривать MAP-файл в формате XML с помощью браузера Internet Explorer, при этом распределение памяти должно удобно отображаться в виде таблиц (после применения стиля из linker_map_ss1.xsl или linker_map_ss2.xsl). Но к сожалению, не все современные браузеры нормально могут отображать MAP-файл в XML-формате. Firefox отображает безумное нагромождение текста, в котором разобраться невозможно, а браузер Chrome вообще отображает пустую страницу. На моем компьютере корректно просмотреть MAP-файл можно только в браузере Internet Explorer (причем при этом тоже по непонятной причине бывают глюки, как в Firefox).

linker MAP file in chrome linker MAP file in FireFox

Если у Вас не получается просмотреть MAP-файл ни в одном из браузеров, то помочь могут программы MindFusion XML Viewer и Microsoft XML Notepad 2007 (последний вариант мне нравится больше).

MindFusion XML Viewer. Программа бесплатна для некоммерческого использования. Вот так выглядит этот же MAP-файл bootloader.map.xml в программе MindFusion XML Viewer:

linker MAP file in MindFusion XML Viewer1

Чтобы удобно посмотреть этот же файл в виде таблицы, нужно привязать файл стилей linker_map_ss1.xsl с помощью меню Tools -> Apply External XLST..., откроется дополнительное окно просмотра:

linker MAP file in MindFusion XML Viewer2

К сожалению, для анализа кода программа MindFusion XML Viewer подходит не очень хорошо. Хуже всего, что в просмотре в виде таблицы поиск не работает. В этом плане Microsoft XML Notepad 2007 намного удобнее.

Microsoft XML Notepad 2007. Эта программа вообще бесплатна, и работает лучше всего.

linker MAP file in Microsoft XML Notepad1 linker MAP file in Microsoft XML Notepad2

[Код C++]

Компилятор C++ выдает ошибку cc0146 на определение массива, вложенного в структуру. Пример кода, в котором возникает такая ошибка:

struct TSymSize
{
    int x,y;
};
 
struct TFont
{
   TSymSize sym[3];
};
 
TFont MyFont =
{
   //Этот код компилируется с ошибкой cc0146:
   {10,15},{9,15},{11,15}
};

Сообщение об ошибке, которое выдает компилятор:

.\graphics.cpp
".\graphics.cpp", line 56: cc0146:  error: too many initializer values
     {10,15},{9,15},{11,15}
             ^
1 error detected in the compilation of ".\graphics.cpp".
cc3089: fatal error: Compilation failed
Tool failed with exit/exception code: 1.
Build was unsuccessful.

Чтобы исправить ошибку, надо, как это ни странно, либо вообще убрать фигурные скобки, либо добавить еще одни:

TFont MyFont1 =
{
   //Этот код компилируется нормально:
   10,15,9,15,11,15
};
 
TFont MyFont2 =
{
   //Этот код также компилируется нормально:
   {{10,15},{9,15},{11,15}}
};

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

TFont MyFont { { {10,15},{9,15},{11,15} } };
             ^ ^ ^
             | | |
             | | элемент массива
             | массив элементов TSymSize
             класс TFont

[Код C]

В подобной ситуации при отсутствии "лишних" фигурных скобок копилятор C выдаст предупреждение cc1376, которое на самом деле будет ошибкой:

typedef struct _TSymSize
{
    int x,y;
}TSymSize;
 
typedef struct _TFont
{
   TSymSize sym[3];
}TFont;
 
TFont MyFont1 =
{
   //Этот код компилируется нормально:
   {{10,15},{9,15},{11,15}}
};
 
TFont MyFont2 =
{
   //Этот код также компилируется нормально:
   10,15,9,15,11,15
};
 
TFont MyFont2 =
{
   //Этот код даст предупреждение cc1374:
   {10,15},{9,15},{11,15}
};

Предупреждение cc1374, которое выдает компилятор:

.\graphics.c
".\graphics.c", line 69: cc1374: {D} warning: excess initializers are ignored
     {10,15},{9,15},{11,15}
             ^
Creating library...
Build completed successfully.

Такое предупреждение нельзя оставлять без внимания, несмотря на то, что компиляция прошла. Чтобы решить проблему, нужно точно так же, как с кодом C++, либо совсем убрать фигурные скобки, либо добавить еще одну пару фигурных скобок.

Программа в некоторых ситуациях зависает (unknown exception, KernelPanic) при обращении к многобайтовому полю структуры (u16, u32 или u64). Иногда эффект зависания не проявляется.

Причина скорее всего в том, что адрес поля не делится нацело на 4. Это может быть из-за того, что структура упакована до 1 байта (в определении структуры используется директива #pragma pack(1)). В таком случае решить проблему можно несколькими способами:

1. Добавить перед проблемным полем пустые байты, чтобы адрес поля был выровнен на нужное количество байт относительно начала структуры (адрес начала структуры по умолчанию всегда выровнен на 4 байта).

2. Добавить перед проблемным полем директиву #pragma pad (см. вопрос Q027).

3. Применить директиву #pragma pack с нужным коэффициентом выравнивания, например #pragma pack(4) для корректного доступа к 32-битным полям структуры.

Простейшая реализация ASSERT обычно делает простое зацикливание в том месте, где произошла ошибка. Например:

#define MYASSERT(expr) if (!(expr)) while(1)

Если такая ошибка произошла в подпрограмме или функции, которая вызывается во многих местах, то самый простой способ выяснить место ошибочного вызова - пропустить бесконечный цикл и выполнить в отладчике выход из функции с остановкой (Debug -> Step Out Of). Пропустить бесконечный цикл можно, если воспользоваться функцией редактирования исполняемого кода в отладчике. Ниже на скриншотах показано на примере, как это делается.

Предположим, произошло зацикливание на assert из-за ошибочного значения переменной (переменная y1 не может быть больше 0x3F):

skip ASSERT01

Перейдите в окно листинга дизассемблированного кода и правой кнопкой мыши откройте контекстное меню в том месте, где призошло бесконечное зацикливание. Выберите в нем пункт Edit.

skip ASSERT02

Теперь можно ввести в этом месте мнемонику другой команды. Введите NOP и нажмите Enter.

skip ASSERT03 skip ASSERT04

Поставьте в коде функции, где произошло событие assert, точку останова сразу за сработавшим оператором assert:

skip ASSERT05

... и нажмите кнопку F5 (запуск выполнения кода). Выполнение дойдет до точки останова, сработавший код assert будет пропущен благодаря тому, что Вы заменили его на команду NOP:

skip ASSERT06

Теперь в главном меню можно выбрать Debug -> Step Out Of, и можно будет узнать место ошибочного вызова:

skip ASSERT07

Произойдет выход из функции, где произошло срабатывание assert, и можно будет узнать причину ошибки:

skip ASSERT08

Эти макросы были созданы для старых версий VisualDSP++, чтобы обойти проблему Blackfin Silicon Anomaly 05-00-0311. Проблема на некоторых процессорах связана с прямым доступом к флагам (портам GPIO) через прямое управление регистрами MMR.

Предыдущий способ обхода компилятором этой аномалии был сочтен небезопасным, и поэтому он был удален из релиза VisualDSP++ 5.0. Служба Programmable Flag больше не полагается на компилятор для обхода этой аномалии. Таким образом, в этом релизе VisualDSP++ 5.0, служба Programmable Flag, совместно со службой Interrupt Manager вместе обходят эту аномалию безопасным способом. Все версии библиотеки Системных Служб (System Service Libraries) для процессоров Blackfin, на которые потенциально влияет эта аномалия, корректно решают эту проблему. Пользователи системных служб не нуждаются в каких-либо специальных действиях, кроме как простая линковка соответствующей библиотеки System Services. Пользователи System Services не нуждаются ни в подключении заголовка sys/05000311.h, ни в использовании макросов FIO_ANOM_0311_XXX (кроме случаев, когда они пытаются напрямую работать с флагами MMR).

Подробнее описание проблемы см. в разделе "Former Workaround for 05-00-0311 is Not Safe – Blackfin TAR 32344" даташита VisualDSP5_0_Release_Note_2007August28.pdf.

В среду программирования встроена система отладочного вывода, основанная на стандартных вызовах printf. По умолчанию для вывода используется консоль. Это окно Output Windows, которое обычно прикреплено к нижней части главного окна VisualDSP (если оно не отображается, то его можно включить, поставив галку в пункте меню View -> Output Window), закладка Console. Чтобы этот отладочный вывод заработал, достаточно подключить заголовочный файл stdio.h.

VisualDSP stdio printf

Однако в этой стандартной библиотеке есть небольшой глюк, который заключается в том, что если Вы используете printf однократно, либо если при таком однократном использовании не поставили в конце выводимой строки символ перевода строки '\n', то выводимый текст может не отобразиться. Например, если в примере, показанном на скриншоте, убрать в конце выводимой строки символ \n, то Вы не увидите в консоли вывода строку "Hello World!".

Система отладочного вывода построена на файловом вводе-выводе STDIO. Кроме консоли, вывод printf можно отправить на любые периферийные устройства, например в UART или на экран LCD. Подробнее про отладочный вывод и систему STDIO см. [15, 17].

На процессоре ADSP-BF538F столкнулся с ошибкой доступа по невыровненному адресу при попытке заполнения SDRAM (32 мегабайта) нулями. Варианты 1 и 2 заполнения с помощью memset и memcopy чаще всего дает ошибку __unknown_exception_occured (статус секвенсера показывает ошибку EXCAUSE 24, т. е. ошибка доступа по невыровненному адресу, коды ошибок исключений см. в статье [3]), причем иногда по непонятной причине эта ошибка не возникает.

      //Вариант 1. Этот вариант заполнения памяти SDRAM вызывает ошибку:
      memset(0, 0, size);
      //Вариант 2. Этот вариант тоже вызывает ошибку:
      char zeroarr[256];
      memset(zeroarr, 0, sizeof(zeroarr));
      for(int i=0; i < size/sizeof(zeroarr); i++)
         memcpy((u8*)(i*sizeof(zeroarr)), zeroarr, sizeof(zeroarr));

Попробовал заполнять память с помощью цикла, сразу по 4 байта, такой вариант работает стабильно, без ошибки:

      //Вариант 3. А вот этот вариант почему-то работает нормально:
      int *ipnt;
      for (ipnt=0; ipnt < (int*)size; ipnt++)
         *ipnt = 0;

Blackfin memset EXCAUSE 24

Если кто-нибудь знает причину такого поведения библиотечных функций string.h, напишите про это в комментариях.

На самом деле CALL.X это не реальная иструкция ассемблера Blackfin, а расширение, поддерживаемое компилятором и линкером VisualDSP. Оно означает, что действительный, оптимальный тип инструкции CALL будет выбран линкером на момент абсолютной привязки кода к адресам процессора - в зависимости от того, насколько далеко от текущего адреса находится адрес назначения инструкции CALL.X (инструкции CALL разных типов имеют свои ограничения по области действия).

То же самое относится и к инструкции JUMP.X.

Подробнее про инструкции CALL и JUMP см. в статье "Blackfin: система команд (ассемблер) - часть 1".

При портировании проекта примера AN945 (CANopen от Microchip) на Blackfin возникло множество предупреждений о наличии дополнительных скобок в определениях элементов словаря. Пример:

.\CANopen\CO_dict.cpp
".\CANopen\CO_dict.cpp", line 40: cc1204: {D} warning: extra braces are
          nonstandard
  rom DICT_OBJECT_TEMPLATE _db_device[] = {DICTIONARY_DEVICE_INFO};

Предупреждения cc1204 не критические, однако они мешают анализу реально полезной диагностики. Подавить вывод этих сообщений можно с помощью директивы #pragma diag или опциями компилятора. Пример подавления сообщения cc1204 директивой #pragma diag:

#pragma diag(suppress:1204)
rom DICT_OBJECT_TEMPLATE _db_device[] = {DICTIONARY_DEVICE_INFO};

Подробнее по эту директиву см. статью [6], описание опций командной строки см. в статье [16].

Аппаратные точки останова следует применять для установки на память программ FLASH (например в код процессора ADSP-BF504F, расположенный в сегменте FLASH_code), потому что программные точки останова на такой памяти работать не будут. Внешне аппаратная точка останова обозначается в редакторе другим значком - в красном кружке видна иконка корпуса микросхемы.

Вот так выглядит обычная программная точка останова, которая срабатывает на памяти L1 (SRAM) и L3:

VisualDSPBreakpointSoftware

А вот так выглядит аппаратная точка останова, которая работает на любой памяти, в том числе и FLASH:

VisualDSPBreakpointHardware

Программная точка останова ставится кнопкой F9, а аппаратная кнопками Shift+F9. К сожалению, количество аппаратных точек ограничено 6 штуками, количество программных точек останова не ограничено.

Процессор предоставляет выделенный банк 4 килобайта памяти scratchpad data SRAM (так называемая сверхоперативная память данных L1, которая может работать на частоте тактов ядра CCLK). Обычно эта память находится по адресу 0xFFB00000, и её объем равен 4 килобайта.

Область использования и ограничения scrathpad. Scratchpad не зависит от конфигурации других банков памяти L1, и не может быть сконфигурирована как кэш или быть целью для DMA (к scratchpad data SRAM не может получить доступ контроллер DMA). Типичные приложения для использования scratchpad data - места программы, где скорость критична. Например, стеки пользователя и супервизора должны быть привязаны к памяти scratchpad для быстрейшего переключения контекста при обработке прерываний. Загрузка в память scratchpad загрузчиком не поддерживается, поэтому в этой памяти нельзя размещать код и инициализированные при определении переменные и константы.

В памяти scrathpad можно размещать переменные пользователя, которые не требуют инициализации при определении. Для этого при объявлении переменной нужно задать для неё секцию L1_scrathpad. Пример:

section("L1_scratchpad") int MyArray[100];

Для той же цели можно использовать директиву #pragma section (см. статью [6]):

#pragma section("L1_scratchpad")
int MyArray[100];

При использовании для переменных памяти scrathpad будет выводиться предупреждение линкера li2131:

Linking...
 
[Warning li2131] "C:\Program Files (x86)\Analog Devices\VisualDSP 5.0\
Blackfin/ldf/adsp-BF504f.ldf":405 Input section(s) of incompatible
init qualifier detected in the output section 'L1_scratchpad'
For more details, see 'linker_log.xml' in the output directory.
 
Linker finished with 1 warning

На это предупреждение можно не обращать внимания, либо можно подавить его опцией командной строки линкера -W2131:

suppress linker warning li2131

Изменений и нововведений в версии VisualDSP++ 5.0 довольно много. Поэтому хотя проект VisualDSP++ 3.5 можно без проблем открыть в VisualDSP++ 5.0 (с преобразованием в формат новой версии), однако они часто компилируются при этом с ошибками. Это составляет проблему, потому что к многим апноутам Analog Devices (в частности, так у меня получилось с апноутом EE-257) поставляется старый код, рассчитанный именно на версию 3.5.

Многие ошибки связаны с тем, что код полагается на поддержку макросов и переменных, которые отсутствуют в VisualDSP++ 5.0. Поэтому для того, чтобы получить информацию об этих отсутствующих определениях, лучше всего скачать бесплатную 90-версию пакета установки VisualDSP++ с сайта Analog Devices (строка поиска Google VisualDSP++ 3.5 16-bit.exe site:analog.com). Запускать установку пакета не обязательно, достаточно распаковать его содержимое и получить доступ к исходному коду. Распаковать можно любым продвинутым распаковщиком, например содержимое пакета инсталлятора может прекрасно распаковать 7-Zip. Также можно в Total Commander установить плашку курсора на файл VisualDSP++ 3.5 16-bit.exe и нажать комбинацию клавиш Ctrl+PageDown, и Вы получите содержимое архива этого инсталлятора, откуда нужно распаковать файл stage.zip и все что там находится.

[Проблема с MAX_IN_STARTUP и __clk_ctrl]

[Error li1021]  The following symbols referenced in processor 'p0' could not be resolved:
        'MAX_IN_STARTUP' referenced from '.\Debug\basiccrt.doj'
        '__clk_ctrl [___clk_ctrl]' referenced from '.\Debug\basiccrt.doj'
 
Linker finished with 1 error
cc3089: fatal error: Link failed

MAX_IN_STARTUP это просто макрос, который должен быть определен в 1 простым оператором #define. В VisualDSP++ 3.5 это делалось в файле Blackfin\include\sys\pll.h:

#define MAX_IN_STARTUP 1

Добавьте такое определение в свой код, и ошибка исчезнет.

Назначение символа __clk_ctrl описано в файле pll.h VisualDSP++ 3.5 следующим образом:

enum clkctrl_t {
   /* Не будет сделана модификация PLL в CRT startup - задано по умолчанию */
   no_startup_set=NO_STARTUP_SET, 
   /* Код CRT startup настроит PLL на подходящую максимальную частоту процессора */
   max_in_startup=MAX_IN_STARTUP  
};
 
/*
** Определите __clk_ctrl в 1, чтобы код startup настраивал PLL на максимальную
** скорость работы процессора. Библиотеки runtime по умолчанию определяют здесь 0,
** запрещают тем самым настройку максимальной частоты.*/
extern enum clkctrl_t __clk_ctrl;

Экземпляр переменной __clk_ctrl определяется в библиотечном файле Blackfin\lib\src\libc\crt\clkctrl.c VisualDSP++ 3.5:

enum clkctrl_t __clk_ctrl = no_startup_set;

Таким образом, для решения проблемы добавьте у себя в программе переменную enum clkctrl_t __clk_ctrl, и присвойте её значение no_startup_set (эквивалентно 0), если не хотите, чтобы код запуска настраивал PLL (т. е. если Вы сами в своей программе управляете настройкой частоты процессора). Или Вы можете присвоить __clk_ctrl значение max_in_startup, тогда код запуска сам настроит PLL на максимальную скорость процессора.

У меня такие сообщения возникли при компиляции библиотеки zlib 1.2.11, когда я попытался скомпилировать её код как код C++. Пример кода:

int ZEXPORT inflateResetKeep(strm)
z_streamp strm;
{
   struct inflate_state FAR *state;
 
   if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
   state = (struct inflate_state FAR *)strm->state;
   ...

Этот код дает предупреждение:

"..\lib\zlib\inflate.cpp", line 119: cc0267: {D} warning: old-style parameter
          list (anachronism)
  int ZEXPORT inflateResetKeep(strm)
                               ^

Это предупреждение специфично для кода C++. Чтобы его устранить, нужно переделать определение функции следующим образом:

int ZEXPORT inflateResetKeep(z_streamp strm)
//z_streamp strm;
{
   struct inflate_state FAR *state;
 
   if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
   ...

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

Пример такой ошибки, выдаваемой на стадии линковки приложения:

Linking...
[Error el1011] ".\pkrc-m-lite-VDK.ldf":465 Syntax error (missingToken): '-0x2FFFFF' was unexpected
 
[Error el1010] ".\pkrc-m-lite-VDK.ldf":465 Parsing error: Invalid linker description file $MACRO syntax
 
[Error el1011] ".\pkrc-m-lite-VDK.ldf":465 Syntax error (missingToken): ',' was unexpected
 
[Error el1010] ".\pkrc-m-lite-VDK.ldf":465 Parsing error: Invalid expression syntax
 
[Error el1011] ".\pkrc-m-lite-VDK.ldf":465 Syntax error (missingToken): ')' was unexpected
 
Linker finished with 5 errors
cc3089: fatal error: Link failed
Tool failed with exit/exception code: 1.
Build was unsuccessful.

Причина ошибки в том, что отсутствовали пробелы в арифметическом операторе (это место выделено жирным красным шрифтом):

         /*$VDSG< insert-input-sections-at-the-end-of-sdram0_bank1 >  */
         /* Text inserted between these $VDSG comments will be preserved */
         RESERVE(deflate_ldr_start=MEMORY_END(MEM_SDRAM0_BANK1)-0x2FFFFF,
                 deflate_ldr_size = 0x300000)
         /*$VDSG< insert-input-sections-at-the-end-of-sdram0_bank1 >  */

Добавьте пробел перед знаком минуса и после него, и ошибка исчезнет.

По умолчанию никакой разницы нет, оба этих определения соответствуют 4-байтному (32-битному) представлению чисел с плавающей запятой, с точностью до 9 значащих десятичных разрядов, в соответствии со стандартом IEEE single precision (см. Википедию, статья "Single-precision floating-point format"). Т. е. следующие определения переменных var1 и var2 совершенно эквивалентны, и код для обработки этих переменных будет генерироваться абсолютно одинаковый. Этот вариант представления чисел double соответствует опции командной строки компилятора -double-size-32.

// Для IEEE single precision определения float и double являются синонимами:
float var1;          //32-битное число с плавающей запятой
double var2:         //32-битное число с плавающей запятой

При этом для double есть возможность изменить формат числа на двойную точность, если применить модификатор long:

float var1;          //32-битное число с плавающей запятой
long double var2:    //64-битное число с плавающей запятой

Также можно в настройках проекта поменять формат по умолчанию для double на двойную точность IEEE single precision, тогда double будет представлен с точностью до 17 значащих десятичных разрядов, 8 байтами (64 бита, см. Википедию, статья "Double-precision floating-point format"). Эта настройка меняется в разделе Project Options -> Project -> Compile -> Processor (1) -> радиокнопки Double Size (см. скриншот).

VisualDSP float double

Примечание: галочка "Allow mixing of sizes" говорит компилятору, что компилируемый исходный код не будет использовать числа double, так что полученный после компиляции объектный код может линковаться с кодом, который выбрал любую из опций представления чисел double. Эта галочка соответствует использованию опции командной строки компилятора -double-size-any.

После переключения на "64-bit (IEEE double precision)" double по умолчанию уже будет представлен 64 битами, а float так и останется 32-битным (этот вариант соответствует опции командной строки компилятора -double-size-64).

// Для IEEE double precision определения float и double различаются:
float var1;          //32-битное число с плавающей запятой
double var2:         //64-битное число с плавающей запятой

Скорее всего Ваш код скомпилирован для поддержки чисел double одинарной точности (IEEE single precision), что соответствует опции командной строки компилятора -double-size-32. Эти настройки задаются для проекта по умолчанию. Для такой ситуации на печать могут выводиться только числа float и double (но не long double!), которые представлены 4 байтами. Числа long double стандартными функциями вывода (printf, sprintf, fprintf) обрабатываться не будут.

Чтобы можно было выводить на печать числа double двойной точности, нужно перекомпилировать код для 64-битного double, настройка "64-bit (IEEE double precision)" в свойствах проекта Project Options -> Project -> Compile -> Processor (1) -> радиокнопки Double Size (см. предыдущий вопрос Q084). Этот вариант соответствует командной строке компилятора -double-size-64. В спецификаторах преобразования a, A, e, E, f, F, g или G для вывода чисел с плавающей запятой двойной точности следует в этом случае применять модификатор размера L (см. [18]).

[Заплатка для вывода long double в случае компиляции с ключом -double-size-64]

В качестве временного решения (если нельзя перекомпилировать код для -double-size-64) можно для вывода чисел long double применить простую функцию наподобие следующей:

char* dtoa (long double d)
{
   static char strresult [128];
   char strtmp [16];
   float intpart, fracpart;
   
   fracpart = modf(d, &intpart);   
   long double dfrac = d - intpart;
   sprintf(strresult, "%i", (int)intpart);
   sprintf(strtmp, "%.6f", (float)dfrac);
   strcat(strresult, strtmp+1);
   return strresult;
}

Симптомы: ldr-файл нормально записывается во FLASH с помощью программатора Tools -> Flash Programmer среды VisualDSP, но при сбросе или включении питания программа из FLASH не загружается и не запускается. Возможные причины:

1. Неправильно установлены перемычки BMODE1 и BMODE0. Чтобы программа загружалась из FLASH, состояние уровней на этих входах должно быть BMODE1 = 0, BMODE0 = 1.

2. Неправильно сформирован ldr-файл (образ загрузки). Для FLASH-памяти ADSP-BF538F образ загрузки должен быть 16-битный. Проверьте настройку опций проекта, в разделе Load -> Options радиокнопки Output Width должны стоять в положении 16-bit:

ADSP BF538F ldr file 16bit

Причина может быть в неправильно установленных опциях линковки библиотек (см. ниже "Опции Link -> Processor (1)"), неправильной строке форматирования, ошибочно переданных по значению параметов, неправильном количестве параметров. Например:

1. Вызов sscanf возвращает правильное количество просканированных аргументов, но эти аргументы не установлены, или установлены в 0. Скорее всего по ошибке в функцию переданы устанавливаемые аргументы не по указателю, а по значению, и в опциях линковки проекта выбран вариант High performance для библиотек ввода/вывода.

2. Вызов sscanf возвращает -1, сигнализируя об ошибке. Возможно, что неправильно выбран формат для обработки, или вместо указателей в функцию переданы значения.

[Link -> Processor (1)]

Проверьте, правильно ли Вы выбрали вариант библиотек ввода/вывода, он должен соответствовать типу аргументов, которые Вы обрабатываете с помощью sscanf. По умолчанию выбрано High performance:

VisualDSP Link IO libraries

• High performance: самый простой вариант обработки, он не определяет ошибку, что неправильно переданы устанавливаемые аргументы.

• High performance I/O with support for fixed-point types: поддержка целочисленных устанавливаемых аргументов и аргументов с фиксированной запятой.

• Full ANSI C compliance: полная поддержка обрабатываемых форматов и типов устанавливаемых аргументов.

[Форматирование даты или времени]

Хороший пример неправильно подобранного формата sscanf для получения времени из строки типа "hh:mm:ss":

int hh, mm, ss;
hh = mm = ss = 0;
char *strDT = "11:08:00";
 
if (3==sscanf(strDT, "%02i:%02i:%02i", &hh, &mm, &ss))
{
   umsg("OK\n");
}
else
{
   umsg("Ошибка!");
}

В этом примере sscanf правильно преобразует только часы, и вместо числа 3 вернет 2. Текст преобразуемых чисел, начинающийся на 0, будет преобразован неправильно. Если передать строку "11:23:45", то ssacnf отработает правильно.

Чтобы исправить ошибку, нужно формат %02i заменить на формат %02d:

if (3==sscanf(strDT, "%02d:%02d:%02d", &hh, &mm, &ss))
{
   ...

[Пример сбоя sscanf из-за несоответствия типа формату]

Следующий код в некоторых случаях может привести к падению программы в исключение unknown_exception:

char *strparam = "1000";
// Считывание скорости CAN из строки:
u16 speedval;
// Следующая строка приведет к сбою из-за неправильного формата "%u":
if (1==sscanf(strparam, "%u", &speedval))
{
   // Тут установка скорости:
   ...
}

Причина ошибки в том, что тип u16 (это тип unsigned short, ширина 16 бит) не соответствует формату "%u", который обозначает 32-битное число без знака. Правильным форматом должен быть "%hu", будьте внимательны:

u16 speedval;
if (1==sscanf(strparam, "%hu", &speedval))
{
   ...

Это окно выводит иерархию вложенных вызовов подпрограмм и функций, что помогает отследить место возникновения ошибки в программе. Ниже показан пример ситуации, когда происходило зацикливание на проверке ASSERT из-за ошибок в параметрах вызова функции CleanWindow. Окрытие окна Call Stack позволяет узнать имя функции, откуда был произведен вызов функции CleanWindow, и благодаря этому узнать, по какой причине были переданы ошибочные параметры:

VisualDSP Call Stack Info

Отображение расширения файлов (*.h, *.c, *.cpp) в имени файлов на закладках редактора зависит, как ни странно, от настроек Проводника Windows. Если в Проводнике отключено отображение расширений зарегистрированных типов файлов, то тогда на закладках имена модулей и их заголовков будут отображаться одинаково, без расширений файла. Это очень неудобно, потому что затрудняет навигацию по коду с помощью закладок открытых в VisualDSP файлов.

Если Вы хотите, чтобы на закладках редактора VisualDSP отображалось полное имя модуля вместе с расширением, то в настройках Проводника "Вид" снимите галочку "Скрывать расширения для зарегистрированных типов файлов":

Explorer hide registered file types disable

Из-за того, что архитектура внутреннего устройства процессоров разных моделей различается, бывает необходимо в программе проверять, какой используется процессор.

Среда разработки VisualDSP автоматически определяет макросы в зависимости от выбранного в проекте процессора (например __ADSPBFnnn__, где nnn обозначает модель процессора). Список этих макросов для Blackfin можно посмотреть в таблице 1-1 статьи [19]. Пример проверки типа процессора для настройки прерываний приема CAN:

   //Инициализация прерывания, привязка CAN к приоритету IVG:
#if defined(__ADSPBF504__) ||  defined(__ADSPBF504F__)
   // Код для процессора ADSP-BF504
   *pSIC_IAR3 |= PX_IVG(26, ik_ivg8);
   register_handler(ik_ivg8, CAN_RX_HANDLER);
   *pSIC_IMASK0 |= CAN_RX_IRQ;
#endif
#if defined(__ADSPBF538__)
   // Код для процессора ADSP-BF538F
   *pSIC_IAR5 |= PX_IVG(47, ik_ivg8);
   register_handler(ik_ivg8, CAN_RX_HANDLER);
   *pSIC_IMASK1 |= CAN_RX_IRQ;
#endif

Совершенно неожиданно при компиляции проекта появилась ошибка отсутствия скобки в определении типа структуры:

"C:\Program Files ...\...\include\drivers/adi_dev.h", line 636: cc0018:  error:
          expected a ")"
          u32                     DevNumber,          /* device number
                                  ^

Долго ломал голову. Оказалось, что проблема в совпадении имени поля DevNumber в структуре и определением #define, где случайно было объявлено такое же имя:

// Номер контроллера TWI, к которому подключена микросхема
// EEPROM и индикатор EA DOGS104W-A:
#define DevNumber 1

После переименования константы в #define ошибка пропала.

[Словарик]

1-D, 2-D варианты работы DMA - прямой доступ к памяти может оперировать как с одномерным, так и с двумерным массивом данных.

ADC Analog-Digital Converter, аналого-цифровой преобразователь (АЦП).

ALU Arithmetic Logic Unit

~AMSx Asynchronous memory selects, выходы сигналов асинхронной выборки памяти (~AMS3, ~AMS2, ~AMS1 и ~AMS0).

Boot Block блок загрузки. Из нескольких таких блоков состоит поток загрузки. Эти блоки содержат данные загрузки, которым предшествует заголовок блока Заголовок инструктирует ядро загрузки о том, как интерпретировать полезную нагрузку/данные блока. В некоторых случаях блок может состоять только из заголовка, в котором содержатся специальные инструкции. В таком блоке скорее всего не будет полезной нагрузки данных.

Boot Host хост загрузки. Некое интеллектуальное устройство (отдельный микроконтроллер, процессор или микросхема FPGA), которое предоставляет процессору образ загрузки или поток загрузки.

Boot Image образ загрузки. Это можно рассматривать как двоичная версия Loader File (файл загрузки). Обычно файл загрузки сохраняется на устройстве физической памяти, доступной целевому процессору или его хосту. Часто он прошивается в EPROM или выгружается в память FLASH с помощью плагина VisualDSP++ Flash Programmer.

Образ загрузки организован специальным образом, как это требует ядро загрузки (boot kernel). Этот формат называется поток загрузки (boot stream). Поток загрузки может содержать один или несколько потоков загрузки. Иногда само ядро загрузки является частью образа загрузки.

Boot Kernel ядро загрузки. Это специальное программное обеспечение, которое запускается на целевом процессоре. Оно читает данные источника загрузки и интерпретирует их как формат потока загрузки (boot stream). Ядро загрузки может находиться либо на кристалле процессора (это так называемое Boot ROM), либо на внешнем устройстве памяти ROM. Часто ядро предварительно загружается из источника загрузки перед тем, как оно может быть выполнено. В этом случае утилита загрузки помещает ядро по умолчанию в начало образа загрузки, или позволяет пользователю выбрать свое, специальным образом скомпилированное ядро загрузки.

Boot Manager менеджер загрузки. Это firmware, которое принимает решение, какое из приложений следует загрузить. Приложение обычно предоставляется проектом VisualDSP++, и сохраняется в виде исполняемого файла *.dxe. Сам менеджер загрузки может находиться либо в файле *.dxe приложения, либо находиться в отдельном файле *.dxe. Часто менеджер загрузки выполняется из так называемого кода инициализации (initcode).

В сценариях Slave Boot управление процессом загрузки берет на себя устройство хоста, и это не требует специальной поддержки со стороны VisualDSP++.

Boot Mode режим загрузки. Большинство процессоров поддерживают несколько режимов загрузки. Режим загрузки определяется в момент включения питания/сброса/выхода из состояния power-down по логическим уровням на специальных выводах процессора.

Boot ROM это встроенная в кристалл процессора память "только для чтения", где находится boot kernel и, в некоторых случаях дополнительные продвинутые подпрограммы загрузки.

Boot Source относится к интерфейсу, через который загружается образ загрузки (boot image), а также к устройству хранения этого образа (микросхема памяти или хост, который предоставляет данные загрузки).

Boot Strapping если процесс загрузки состоит из нескольких шагов, таких как предварительная загрузка Boot Kernel или управление вторичными загрузчиками, то этот процесс называется boot strapping или Boot ROM.

Boot Stream поток загрузки. В основном это список загружаемых блоков. Это структура данных, которая обрабатывается и интерпретируется ядром загрузки (часто это код BootROM). Утилита загрузки (loader utility) VisualDSP++ генерирует файлы загрузки, в которых содержится один (чаще один) или несколько потоков загрузки. Один поток загрузки часто представляет одно приложение. Однако связанный список нескольких потоков загрузки также называют потоком загрузки.

~BR Bus Request, сигнал выборки шины.

Callback RoutineCallback функция обратного вызова. Некоторые процессоры могут опционально вызывать определенную пользователем (программистом) функцию, которая называется callback. В контексте ядра загрузки эта технология позволяет перехватывать обработку специальных событий наподобие реализации вычисления контрольной суммы или декомпрессии сжатого потока данных.

CCLK тактовая частота ядра. Основной фактор быстродействия процессора.

CEC Core Event Controller, контроллер управления событиями ядра. Работает совместно с SIC, чтобы обеспечить приоритет обработки всех системных событий (прерываний).

CLI инструкция глобального запрета прерываний.

CLKIN вход для подачи внешних тактов ядра, либо вход генератора, работающего с кварцевым резонатором.

CPLB Cacheability Protection Lookaside Buffer, специальные пары регистров для кэширования памяти данных и инструкций (16 пар для данных и 16 пар для инструкций). Позволяет организовать защиту областей памяти от несанкционированного доступа (обычно используется в многозадачной среде). По умолчанию (после сброса) эта функция отключена.

CRT C Runtime Library, библиотека кода языка C, которая используется во время выполнения приложения.

DAC Digital-Analog Converter, цифро-аналоговый преобразователь (ЦАП).

DAG1, DAG0 Data Address Generator.

DMA Direct Memory Access, прямой доступ к памяти.

EBIU External Bus Interface Unit, блок интерфейса внешней шины.

EVT Event Vector Table, таблица векторов событий (адреса обработчиков прерываний).

~FCE вход, сигнал Flash Enable (в чипе ADSP-BF538 этот сигнал подключен к земле внутри чипа).

Global Header глобальный заголовок. Некоторые ядра загрузки ожидают поступления потока загрузки, чтобы он был в начале снабжен специальным информационным блоком. Этот блок называется глобальным заголовком.

GPIO General Purpose Input/Output, порты ввода/вывода общего назначения.

I2C популярный последовательный двухпроводный интерфейс для обмена данными с внешними периферийными устройствами (применяется в телевизорах, бытовой и промышленной технике).

I2S последовательный интерфейс для передачи цифрового звука.

IAR Interrupt Assignment Register, регистр назначения прерывания.

IDE Integrated Development Environment, интегрированная среда разработки.

IDDE Integrated Development and Debugging Environment, интегрированная среда разработки и отладки.

ILAT Interrupt Latch Register, регистр захвата прерываний.

Initialization Code или initcode часть потока загрузки для процессоров Blackfin, в котором он может быть виден как специальный блок загрузки (boot block). В то время как обычно все блоки загрузки приложения загружаются первыми, и управление после этого передается приложению, код инициализации запускается во время загрузки. Обычно код инициализации загружается и запускается перед любым другим блоком загрузки. Этот код может настроить целевую систему для оптимизации обработки загрузки, либо делает такую загрузку вообще возможной в принципе (например, инициализирует необходимую для работы память SDRAM).

IPEND Interrupts Pending, регистр отложенных (ожидающих) прерываний.

ISR Interrupt Service Routine, подпрограмма обработки прерывания.

IVG Interrupt Vector Group, группа векторов прерывания. Группа имеет номер (от 0 до 15). Чем меньше номер группы, тем выше приоритет. Номера групп определены в перечислении enum файла adi_int.h, а также в перечислении typedef enum interrupt_kind файла exception.h.

LDF Linker Description File, специальный файл команд линкера, предназначенный для управления работой линкера. Этот файл полностью определяет привязку объектов программы к различным областям и типам памяти процессора.

LDR имеется в виду блок данных специального формата (файл, генерируемый средой разработки VisualDSP++), в котором содержится рабочий код программы для Blackfin. Этот файл предназначен для загрузки под управлением boot ROM.

Loader, Loader Utility имеется в виду загрузчик и утилита загрузки. Загрузчик это специальный код, предназначенный для обработки и загрузки boot stream. Утилита загрузки это специальная программа, входящая в комплект поставки VisualDSP++, предназначенная для генерации файла boot stream (*.ldr) из исполняемого файла (*.dxe). В процессе обработки исполняемого файла утилита загрузки извлекает оттуда отдельные сегменты, определенные командой TYPE(RAM) файла LDF, и генерирует из этих сегментов блоки, которые помещает в выходной файл потока загрузки (*.ldr). Из-за того, что формат исполняемого файла это стандартный Executable and Linkable Format (ELF), утилита загрузки часто называется elfloader [11].

Loader File (файл загрузки) это файл, который сгенерировала утилита загрузки (Loader Utility) [11]. Обычно этот файл имеет расширение *.ldr, и называется файлом LDR. Loader File может получать разные форматы. Чаще всего применяется формат Intel HEX-32, двоичный формат, иногда применяется формат ASCII и другие форматы. Независимо от формата Loader File описывает образ загрузки (boot image), который можно рассматривать как двоичный образ Loader File.

MAC Multiply/Accumulate, умножение с накоплением - специальные оптимизированные инструкции для цифровой обработки сигналов.

Master Boot главное устройство загрузки. Этот термин относится ко всем режимам, где целевой процессор функционирует как главное устройство. Случай, когда целевой процессор сам загружает данные загрузки из внешнего параллельного или последовательного устройства энергонезависимой памяти.

MMU Memory Management Unit, блок управления памятью. Предоставляет возможность защиты областей памяти для отдельных задач ядра, и может защитить системные регистры от нежелательного доступа.

MMR Memory Mapped Register, встроенный в общее адресное пространство регистр. MMR системы и ядра начинаются с адреса 0xFFC0 0000. Этот регион памяти защищен от доступа в режиме пользователя (User mode). Любая попытка доступа к области MMR в режиме пользователя приведет к исключению (exception).

Multi-.dxe Boot файл загрузки (loader file) может содержать данные нескольких приложений (*.dxe), если утилита загрузки запускалась с указанием нескольких входных файлов *.dxe. Какое из приложений загрузить и запустить решает либо специальный отдельный менеджер загрузки, либо эти функции берет на себя одно из приложений. В некоторых случаях одно приложение может состоять из нескольких файлов *.dxe (multicore-приложение).

Next .dxe File Pointer, NDP указатель на следующий исполняемый файл. Если файл загрузки содержит несколько приложений, то некоторые форматы загрузки разрешают это организовать в виде связанного списка. NDP просто указывает на местонахождение следующего приложения в потоке загрузки.

NMI Non-Maskable Interrupt, немаскируемое прерывание (прерывание, которое нельзя запретить). Обработчик прерывания NMI может быть вызван изменением уровня на внешнем выводе NMI процессора, а также с помощью срабатывания сторожевого таймера. Полярность уровня на выводе NMI зависит от модели процессора. Для процессора ADSP-BF538 активным уровнем для NMI будет лог. 0.

OTP One Time Programmable, однократно программируемая память.

PAB Peripheral Access Bus, шина доступа к периферийным устройствам.

PC Program Counter, программный счетчик - адрес выполняемой команды программы.

Peripheral Interrupt ID числовой идентификатор прерывания для аппаратуры, который равен номеру бита IMASK/ISR/IWR. Идентификаторы аппаратуры определены в typedef enum ADI_INT_PERIPHERAL_ID файла adi_int.h.

PPI Parallel peripheral interface.

Preboot Routine представляет в Boot ROM части функционала памяти OTP процессора. Preboot читает память OTP и настраивает некоторые регистры MMR на основе заводских или пользовательских инструкций, запрограммированных в OTP. Код Preboot Routine запускается перед ядром загрузки (Boot Kernel).

PREG Pointer Register, это FP или SP.

pulldown, pull-down нижний нагрузочный резистор (притягивает лог. уровень к 0).

pullup, pull-up верхний нагрузочный резистор (притягивает лог. уровень к 1).

RAB Register Access Bus.

RTC Real-Time Clock, часы реального времени.

SAA subtract/absolute value/accumulate, вычитание/взятие абсолютного значения/накопление - специальная инструкция для ускорения математической обработки.

SAFP Stand-Alone Flash Programmer - специальная технология VisualDSP++, которая позволяет применять загружаемый в RAM исполняемый файл (файл *.dxe) в качестве программатора энергонезависимой памяти программ (обычно это FLASH). Также существует API, которое позволяет на различных языках программирования (в том числе и на VBasic script) писать скрипты, упрощающие программирование чипов в процессе производства (не требуется запускать среду VisualDSP++).

SCLK system clock, системная тактовая частота.

Second-Stage Loader вторичный загрузчик, специальное ядро загрузки, которое расширяет механизмы загрузки по умолчанию процессора дополнительным функционалом. Обычно вторичный загрузчик загружается первичным ядром загрузки (часто это Boot ROM) в стандартном режиме загрузки. После этого вторичный загрузчик запускается и загружает конечное рабочее приложение.

SIC System Interrupt Controller, системный контроллер прерываний. Служит для привязки и перенаправления событий прерываний от множества периферийных устройств на входы группы приоритетов прерываний CEC.

Slave Boot подчиненное устройство загрузки. Этот термин используется в контексте всех режимов загрузки, где целевой процессор (в который загружается код) функционирует как подчиненное устройство на канале передачи данных загрузки. Это типовой случай, когда устройство хоста (главное устройство) загружает данные в области памяти целевого процессора. Целевой процессор может пассивно ожидать в режиме приостановки (idle mode), или активно поддерживать передачи данных под управлением хоста.

Splitter Utility это часть пакета разработки VisualDSP++. Это специальная программа для пост-обработки одного (иногда нескольких, в случае много процессорной системы) исполняемых файлов *.dxe. В процессе этой обработки из файла *.dxe распаковываются сегменты, декларируемые командой TYPE(ROM) файла LDF, и на основе этих данных генерируется файл, содержащий непосредственно исполняемый код инструкций процессора. Этот файл прошивается в энергонезависимую память EEPROM или FLASH, подключенную к параллельной системной шине процессора, и процессор может напрямую считывать и выполнять оттуда код инструкций.

Работа сплиттера и утилиты загрузки могут (в зависимости от типа процессора) выполняться отдельными программами или одной и той же программой (в зависимости от указанных опций командной строки). В последнем случае сгенерированный выход может содержать коды инструкций и потоки загрузки.

SPORT последовательный порт, которых в ADSP-BF538 всего 4 штуки (SPORT0, SPORT1, SPORT2 и SPORT3).

STI инструкция глобального разрешения прерываний.

TWI Two Wire Interface, двухпроводный интерфейс, то же самое что и I2C.

VCO voltage-controlled oscillator, генератор, управляемый напряжением (входит в систему PLL).

W1C Write-1-Clear, принцип очистки бита, когда бит (или биты) сбрасывается записью в него единицы.

Гарвардская архитектура принцип построения процессорной системы, когда данных и инструкций предоставляются разные физические области памяти, и когда шина данных и шина инструкций также физически разделены. Архитектура Blackfin считается модифицированной Гарвардской, потому что и данные, и инструкции, и порты ввода/вывода находятся в едином адресном пространстве, но внутри процессора шина данных и шина инструкций разделены.

[Ссылки]

1. Blackfin ADSP-BF538.
2. Память Blackfin.
3. ADSP-BF538: обработка событий (прерывания, исключения).
4. ADSP-BF538: управление портами GPIO F.
5. ADI Home -> Processors and DSP -> Software Tools Upgrades site:analog.com.
6. VisualDSP: полезные директивы #pragma.
7. VisualDSP: использование секций памяти.
8. Как происходит загрузка ADSP-BF533 Blackfin.
9. ADSP-BF538: блок интерфейса внешней шины.
10. BfSdcCalculation_Release.xlsx, см. site:ez.analog.com.
11. Blackfin: утилита elfloader.exe.
12. Использование кэша в процессорах Blackfin.
13. VisualDSP: работа с динамически выделяемой памятью.
14В чем разница между struct и typedef struct?
15. Blackfin: форматированный вывод в окно терминала через UART.
16. Опции командной строки компилятора Blackfin.
17. VisualDSP: ввод/вывод с использованием файлов (stdio).
18VisualDSP библиотека C runtime, fprintf.
19VDK: драйверы устройств и системные службы процессоров Blackfin.

 

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


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

Top of Page