В этой части книги описывается создание первой рабочей программы для сигнального процессора и ее трансляции для получения исполняемого кода. Дается описание процедуры загрузки и портов, через которые возможна данная операция. Приводится описание разработанного автором устройства для загрузки программ, просмотра и редактирования памяти процессора с помощью компьютера. Описываются назначение и работа специальной программы менеджера памяти. Дается описание директив ассемблера, формата данных и система команд для сигнального процессора.
В этой главе говорится о создании программ для сигнального процессора, инструментальном программном обеспечении, отладке и способах загрузки программ в сигнальный процессор.
Начнем изучение программирования сигнального процессора с конкретного примера программы, приведенной ниже. Эта программа поможет нам в изучении структуры программы, системы команд процессора и средств разработки, а также позволит на практике освоить процедуру отладки и загрузки программы в память процессора.
Введите текст этой программы в компьютер с помощью любого текстового редактора и сохраните в файле с именем mem_clr.dsp или скопируйте с компакт-диска, прилагаемого к данной книге.
/***********************************************************/
/* Программа mem_clr загружается в память программ и после */
/* запуска заполняет память данных сигнального процессора */
/* с адреса 0x0000 по адрес 0x3fdf числовыми значениями */
/* */
/* Версия: 1.0 */
/* Автор: О.Д.Вальпа */
/***********************************************************/
.module/RAM/ABS=0 mem_clr; { Модуль памяти mem_clr с адреса 0 }
.include ; { Включить файл определений }
.var/dm/ram/circ buf_dm[0x3fdf]; { Циклический буфер в памяти данных }
jump BEGIN; nop; nop; nop; { Вектор прерывания сброса процессора }
rti; nop; nop; nop; { Вектор прерывания IRQ2 }
rti; nop; nop; nop; { Вектор прерывания IRQL1 }
rti; nop; nop; nop; { Вектор прерывания IRQL0 }
rti; nop; nop; nop; { Вектор прерывания SPORT0 TX }
rti; nop; nop; nop; { Вектор прерывания SPORT0 RX }
rti; nop; nop; nop; { Вектор прерывания IRQE }
rti; nop; nop; nop; { Вектор прерывания BDMA }
rti; nop; nop; nop; { Вектор прерывания SPORT1 TX (IRQ1) }
rti; nop; nop; nop; { Вектор прерывания SPORT1 RX (IRQ0) }
rti; nop; nop; nop; { Вектор прерывания TIMER }
rti; nop; nop, nop; { Вектор прерывания POWER DOWN }
/************** начало программы ************************************/
BEGIN: { Метка начала программы }
ax0 = b#0111111110000000; dm(pftype) = ax0; {Инициализация флагов PF}
{ │└┬┘││││76543210 }
{ │ │ ││││└+++++++ - PF0-PF7: 0-вход 1-выход }
{ │ │ │││└──────── - PM - выход -CMS }
{ │ │ ││└───────── - DM | 0-запрещен }
{ │ │ │└────────── - BM | 1-разрешен }
{ │ │ └─────────── - IOM-- }
{ │ └───────────── - От 0 до 7 циклов задержки BDMA }
{ └ He используется, всегда=0 }
ax0 = b#0000000010000000; dm(PFDATA) = ax0;{Управление светодиодом }
{ 76543210 }
{ │ └──┴ - Вход:Код клавиш }
{ └─────── - Выход:Светодиод }
i0 = ^buf_dm; { Индексный регистр i0=адресу начала буфера }
l0 = %buf_dm; { Регистр длины l0=длине буфера }
m0 = 1; { Регистр модификатора m0=1 }
ar = 0x1234; { Записать данные в рабочий регистр ar }
cntr=10; { Загрузить счетчик циклов }
do CLR_DM until се; { Выполнять до CLR_DM пока счетчик не обнулится }
dm(i0, m0) = ar; { Заполнение очередной ячейки памяти данных }
CLR_DM:nop; { Пустая команда }
toggle fl2; { Инвертировать вывод процессора FL2 }
dm(0) = ar; { Заполнение ячейки памяти данных с адресом 0 }
ar = ar + 1; { увеличивающимися значениями }
ax0 = dm(PFDATA); { Читать код клавиш }
dm(1) = ax0; { и записать в ячейку памяти данных по адресу 1}
jump CLR_DM; { Зациклить программу }
.endmod; { Конец программы }
Имя данного файла можно изменить на любое другое, длиной от одного до восьми символов, разрешенных для имен файлов. Расширение «dsp» выбрано не случайно. При трансляции программы, компилятор будет искать файл программы именно с таким расширением. Если он не найдет такой файл, то выведет сообщение Preprocessor failed to open mem_clr.dsp.
Рассмотрим структуру и состав приведенного выше файла. Как видно из примера, в начале файла программы находится описание назначения программы, ее версии и пр. атрибуты, заключенные между символами /* и */ в качестве комментариев. Далее следуют три строки директив с комментариями, описывающими назначение этих директив и заключенными между фигурными скобками. Фигурные скобки также предназначены для вставки комментариев в программу. Файл def2181.h, включаемый в файл программы с помощью директивы #include, состоит из строк, присваивающих символьным именам регистров значение их адресов в области памяти данных процессора.
Использование символьных имен позволяет записывать в программе не конкретные цифровые значения адресов или данных, а их словесные описания, более понятные и легче запоминающиеся. Это делается для придания программе наглядности, предотвращения машинальных ошибок во время ее создания и облегчения ее понимания. Содержимое файла def2181.h приведено ниже.
.const IDMA= 0x3fe0;
.const BDMA_BIAD= 0x3fe1;
.const BDMA_BEAD= 0x3fe2;
.const BDMA_BDMA_Ctrl= 0x3fe3;
.const BDMA_BWCOUNT= 0x3fe4;
.const PFDATA= 0x3fe5;
.const PFTYPE= 0x3fе6;
.const SPORT1_Autobuf= 0x3fef;
.const SPORT1_RFSDIV= 0x3ff0;
.const SPORT1_SCLKDIV= 0x3ff1;
.const SPORT1_Control_Reg= 0x3ff2;
.const SPORT0_Autobuf= 0x3ff3;
.const SPORT0_RFSDIV= 0x3ff4;
.const SPORT0_SCLKDIV= 0x3ff5;
.const SPORT0_Control_Reg= 0x3ff6;
.const SPORT0_TX_Channels0= 0x3ff7;
.const SPORT0_TX_Channels1= 0x3ff8;
.const SPORT0_RX_Channels0= 0x3ff9;
.const SPORT0_RX_Channels1= 0x3ffa;
.const TSCALE= 0x3ffb;
.const TCOUNT= 0x3ffс;
.const TPERIOD= 0x3ffd;
.const DM_Wait_Reg= 0x3ffe;
.const System_Control_Reg= 0x3fff;
Описание этих символьных имен и их соответствие адресам памяти данных процессора приведено в табл. 6.1.
Таблица 6.1 Описание символьных имен регистров управления и состояния процессора ADSP-2181
Имя | Описание | Адрес |
---|---|---|
IDMA | Регистр управления IDMA | 0x3FE0 |
BDMA_BIAD | Регистр внутреннего адреса BDMA | 0x3FE1 |
BDMA_BEAD | Регистр внешнего адреса BDMA | 0x3FE2 |
BDMA_BDMA_Ctrl | Регистр управления BDMA | 0x3FE3 |
BDMA_BWCOUNT | Регистр счетчика слов BDMA | 0x3FE4 |
PFDATA | Регистр данных программируемых флагов | 0x3FE5 |
PFTYPE | Регистр управления программируемыми флагами | 0x3FE6 |
SPORT1_Autobuf | Регистр управления автобуферизацией SPORT1 | 0x3FEF |
SPORT1_RFSDIV | Регистр делителя кадровых импульсов SPORT1 | 0x3FF0 |
SPORT1_SCLKDIV | Регистр делителя тактовых импульсов SPORT1 | 0x3FF1 |
SPORT1_Control_Reg | Регистр управления SPORT1 | 0x3FF2 |
SPORT0_Autobuf | Регистр управления автобуферизацией SPORT0 | 0x3FF3 |
SPORT0_RFSDIV | Регистр делителя кадровых импульсов SPORT0 | 0x3FF4 |
SPORT0_SCLKDIV | Регистр делителя тактовых импульсов SPORT0 | 0x3FF5 |
SPORT0_Control_Reg | Регистр управления SPORT0 | 0x3FF6 |
SPORT0_TX_Channels0 | Регистр младшего слова передатчика SPORT0 | 0x3FF7 |
SPORT0_TX_Channels1 | Регистр старшего слова передатчика SPORT0 | 0x3FF8 |
SPORT0_RX_Channels0 | Регистр младшего слова приемника SPORT0 | 0x3FF9 |
SPORT0_RX_Channels1 | Регистр старшего слова приемника SPORT0 | 0x3FFA |
TSCALE | Регистр масштабирования таймера | 0x3FFB |
TCOUNT | Регистр счетчика таймера | 0x3FFC |
TPERIOD | Регистр периода таймера | 0x3FFD |
DM_Wait_Reg | Регистр тактов ожидания памяти данных | 0x3FFE |
System_Control_Reg | Регистр управления системой | 0x3FFF |
Как видно из содержимого данного файла, в каждой его строке производится директивное назначение определенному символьному имени конкретного числового значения. Символьные имена могут быть произвольными, важно только, чтобы они одинаково записывались в данном файле и в самой программе. В дальнейшем мы часто будем использовать символьное описание регистров, для обращения к ним по записи или чтению из программы. В частности, в нашей программе mem_clr.dsp содержатся строки команд для обращения к регистру конфигурирования PFTYPE и регистру данных PFDATA программируемых флагов PF процессора. Файл def2181.h должен находиться в том же каталоге, в котором будет храниться файл программы во время ее трансляции. Иначе компилятор выдаст сообщение об ошибке во время компиляции программы.
Далее, в файле программы начинается запись команд. Поскольку с нулевого адреса памяти программ должны быть записаны векторы прерываний или их обработчики, первой располагается команда перехода jump BEGIN на начало программы, а затем команды возврата из прерывания rti для каждого из прерываний процессора. Все команды должны заканчиваться точкой с запятой, в соответствии с требованиями синтаксиса ассемблера. Заметьте, что в одной строке присутствует сразу несколько команд. Такая запись допускается компилятором ассемблера. Так как для каждого вектора прерывания в памяти программ отведено по четыре 24-разрядных ячейки памяти, а каждая команда процессора занимает одну 24-разрядную ячейку памяти, то оставшиеся неиспользованные ячейки памяти заполняются пустыми командами пор. Сразу же после метки BEGIN записаны команды инициализации конфигурационного регистра флагов PFTYPE с помощью рабочего регистра ax0 блока ALU процессора. Непосредственная запись константы в память данных не поддерживается процессором. Ниже располагаются строки комментариев с пояснением назначения каждого бита регистра флагов PFTYPE. Подобные записи позволяют хорошо документировать программу и облегчают тем самым ее отладку и последующее сопровождение. Аналогично описанным выше записям, в программе присутствуют строки команд записи в регистр данных PFDATA программируемых флагов слова управления светодиодом. Далее следуют команды инициализации некоторых регистров сигнального процессора. В частности, команда i0 = ^buf_dm; производит инициализацию индексного регистра, входящего в блок DAG сигнального процессора, т.е. присваивает индексному регистру i0 значение адреса начала буфера buf_dm, располагающегося в памяти данных процессора. Аналогично происходит инициализация других регистров этого блока и инициализация регистра ar блока ALU. Я намеренно указал на принадлежность регистров блокам процессора, для того чтобы показать связь между архитектурой процессора и выполняемой программой и тем самым облегчить понимание того, что происходит в самом процессоре при выполнении перечисленных команд программы. Далее в программе организуется циклическое заполнение памяти данных процессора значением регистра ar. После чего организуется цикл, состоящий из команд инвертирования выходного флага FL2 процессора и заполнения нулевой ячейки памяти регулярно увеличивающимся значением из регистра ar. Кроме того, в данном цикле организовано чтение состояния входов PF0-PF3 процессора и запись этих значений в ячейку памяти данных по адресу 1. Это сделано для того, чтобы при работе программы можно было увидеть с помощью осциллографа генерацию сигнала на выводе FL2, а также следить за изменением значений ячеек памяти с помощью симулятора или других аппаратно-программных средств, убеждаясь тем самым в правильной работе программы и нормальном функционировании процессора. Завершает программу директива окончания модуля программы endmod. В общем случае структура файла должна быть следующей:
/* Строки комментариев, описывающие */
/* название и назначение программы */
.Директива начала и названия модуля программы
.Директива 1
.Директива 2
...
.Директива N
Метка1: Команда1 и операторы; /*Комментарии*/
Метка2: Команда2 и операторы; /*Комментарии*/
...
МеткаN: КомандаN и операторы; /*Комментарии*/
.Директива окончания программы
Естественно, что комментарии, некоторые директивы, метки и команды с операторами могут отсутствовать в программе. Здесь приведен лишь общий пример записи программ.
Директивы и команды заранее определены языком программирования и не допускают произвольных записей. Метка может состоять из произвольного набора букв, символа подчеркивания и цифр без пробелов, начинающихся с буквы, длиной не более 32 символов, и должна заканчиваться двоеточием. Команды могут записываться в верхнем или нижнем регистре. Компилятор по умолчанию не распознает регистр записей программы, т.е. является контекстно-независимым, и допускает запись комментариев, директив, меток и команд с операторами в любом регистре. Это необходимо учитывать при задании имен меткам. Так, например, метка MET1: и met1: будут восприниматься компилятором как одна и та же, что приведет к сообщению об ошибке. Для того чтобы компилятор различал регистр букв, необходимо включить в строку его запуска ключ -с. Комментарии могут содержать любые наборы произвольных символов в одной или нескольких строчках, заключенных между открывающейся { и закрывающейся фигурной скобкой } или между символами /* и */.
Позже мы рассмотрим более подробно все разрешенные директивы и систему команд процессора. Но вначале доведем процедуру программирования процессора на примере нашей программы до конца. Надеюсь, это поможет пробудить живой интерес к изучению и позволит быстрее освоить на практике программирование сигнального процессора. Итак, после создания файла с программой нам необходимо выполнить его трансляцию, для получения машинных кодов и загрузить в симулятор для отладки. После отладки программы ее можно будет загрузить в память процессора для выполнения.
Для трансляции программы с целью получения машинного загрузочного кода для процессора нам потребуются программные средства разработки. Семейство процессоров ADSP сопровождается полным набором программного обеспечения (трансляторы и эмуляторы) и аппаратных отладочных средств (отладочные комплекты и внутрисхемные эмуляторы), предоставляемые фирмой Analog Device для разработки программ и облегчения процесса освоения программирования на практике.
На сегодняшний день существует два программных пакета кросс средств для разработки и отладки программ для сигнального процессора ADSP-2181. С появлением новых процессоров продолжают добавляться дополнительные инструментарии разработки программ. Первый из этих пакетов входит в поставку отладочного комплекта EZ-KIT Lite и работает под управлением операционной системы DOS, или в режиме эмуляции DOS под Windows. Второй пакет Visual DSP, более позднего происхождения, распространяется самостоятельно и работает под управлением операционной системы Windows. Все кросс средства доступны на сайте компании Analog Device по адресу www.analog.com. Каким из этих пакетов пользоваться, обычно решает сам пользователь. Каждый из пакетов имеет свои преимущества и недостатки. Поэтому познакомим читателей с обоими пакетами поочередно, а они сами сделают свой выбор. Вначале рассмотрим пакет кросс средств для работы под DOS. Данные средства разработки для сигнальных процессоров ADSP-2181 включают в себя:
• System Builder (системный конфигуратор (построитель) программы). Это программный инструмент для описания особенностей оборудования. Он определяет структуру аппаратной системы, позволяя разработчику указать количество доступной памяти, местоположение программной памяти и памяти данных и любого отображенного в память порта ввода-вывода для аппаратного окружения. Данный построитель использует высокоуровневые конструкции, записываемые программистом в файле с расширением sys. После обработки данного файла построитель формирует файл архитектуры с расширением ach. Файл архитектуры используется в дальнейшем компоновщиком, симулятором и эмулятором. Допускается не выполнять процедуру системного конфигурирования при использовании готового файла архитектуры для конкретного процессора.
• C compiler (компилятор языка Си). Осуществляет проверку и преобразование текстов программы, написанных на языке Си формата ANSI, в тексты программ на языке ассемблер семейства ADSP-21XX. Кроме того, он поддерживает встроенный ассемблерный код. Данный компилятор необходим только для трансляции программ, написанных на языке Си. При создании программ на языке ассемблера компилятор Си не используется.
• Assembler (ассемблер-компилятор). Преобразует тексты программ, написанных на языке ассемблера, в объектный (машинный) код процессора. Он поддерживает высокоуровневый синтаксис набора команд и осуществляет полную проверку синтаксиса программы. Кроме того, компилятор поддерживает гибкую структуру макросов и включение дополнительных файлов с помощью директивы include с помощью программы препроцессора. Как правило, компилятор состоит из нескольких программных утилит.
• Linker (компоновщик). Объединяет отдельно ассемблированные модули программы (отдельные файлы с текстами программ) в единый связанный машинный код. При необходимости, на этапе компоновки программы используется программа библиотекаря, для создания законченных программных библиотек, включаемых впоследствии в основную программу.
• Simulators (симулятор). Позволяет отлаживать программы путем имитации работы сигнального процессора в памяти персонального компьютера. Интерактивный пользовательский интерфейс симулятора поддерживает полное символическое ассемблирование и дизассемблирование эмулируемых команд. Симулятор полностью эмулирует конфигурацию аппаратного обеспечения, описанную с помощью файла архитектуры. Он обеспечивает покомандное выполнение ассемблерного кода ADSP-2181 и позволяет отображать на экране монитора внутренние регистры и память процессора.
• Splitter («сплиттер» — распределитель). Эта программа-утилита по выходным данным компоновщика формирует файл для прошивки ПЗУ, из которой автоматически может производиться загрузка программы в память процессора через порт BDMA.
Из описания кросс средств, изложенного выше, становится понятно, что при трансляции программ, написанных на языке ассемблера, можно исключить операции системного конфигуратора программы и компилятора языка Си.
Полный пакет кросс средств можно приобрести вместе с отладочным комплектом EZ-Kit Lite у представителей фирмы Analog Device. Облегченную версию кросс средств, для сигнального процессора ADSP-2181 под управлением операционной системы DOS, можно скачать с сайта www.xxivek.narod.ru. В качестве отладочного средства при этом можно использовать тестовую плату, описанную в гл. 3.
Установка полного пакета на компьютер не вызывает сложностей. Его необходимо инсталлировать на диск С в корневой каталог ADI_DSP. Облегченная версия кросс средств с сайта www.xxivek.narod.ru просто распаковывается и копируется в каталог C:\ADI_DSP. После этого необходимо создать рабочий каталог для ваших будущих рабочих программ на любом диске. Для каждого нового проекта лучше всего создавать отдельный каталог или подкаталог, это позволит упорядочить все проекты.
После установки средств разработки, в каталоге ADI_DSP должны находиться подкаталоги с файлами транслятора, библиотеками и другими служебными файлами. Основные исполняемые файлы располагаются после инсталляции пакета в каталоге C:\ADI_DSP\21XX\BIN\. Перечень исполняемых файлов и их назначение приведены в табл. 6.2.
Таблица 6.2 Назначение исполняемых файлов
Имя файла | Назначение |
---|---|
bld21.exe | Системный конфигуратор (построитель) программы |
cc1.exe, g21.exe | Компиляторы языка Си |
asm21.exe | Компилятор ассемблера |
asmpp.exe | Ассемблер препроцессор |
asm2.exe | Ассемблер |
ld21.exe | Компоновщик (редактор связей) |
Iib21.exe | Библиотекарь (программа для работы с библиотеками программ) |
spl21.exe | Разделитель программ для программирования ПЗУ (сплиттер) |
sim2181.exe | Симулятор программ для процессора ADSP-2181 |
hexbin2.exe | Преобразователь HEX кода в двоичный формат |
При инсталляции пакета кросс средств, в файле автозагрузки автоматически создаются служебные строки, определяющие пути к каталогам пакета и некоторые переменные среды окружения операционной системы. Подобные записи создают многие инсталляционные пакеты. В результате чего может произойти переполнение служебной области памяти компьютера, и некоторые программы не будут работать.
Для того чтобы избежать подобных неприятностей, можно воспользоваться другим способом. Независимо от того, были ли произведены записи в файл автозагрузки или нет, можно включить все необходимые служебные строки в собственный командный файл, с помощью которого в дальнейшем выполнять автоматическую трансляцию программ. Дело в том, что при выполнении командного файла операционная система автоматически запускает новый сеанс программы command.com, в котором можно определить новые пути поиска файлов и переменные среды с помощью соответствующих команд. На практике это делается следующим образом.
Создается командный файл, в начале которого записываются строки определения пути поиска файлов и установки переменных среды, а далее записываются строки команд для автоматизации необходимого процесса трансляции. После запуска такого командного файла, с именем файла транслируемой программы в качестве параметра, произойдет полная трансляция файла программы, с получением всех необходимых файлов для загрузки в процессор. Ниже приведен текст такого командного файла, специально созданного мною для облегчения и ускорения трансляции программ для сигнального процессора ADSP-2181. Данный командный файл позволяет выполнять трансляцию файлов программ, начиная с компилятора, минуя операции построителя системы System Builder и трансляции программы с языка Си.
@echo off
set path=c:\adi_dsp\21XX\bin
set adi_dsp=c:\adi_dsp
if %1 == goto no_file
echo Компиляция...
asm21 %1 -2181 -l
if not exist %1.obj goto error
echo ...завершена успешно pause
echo Компоновка...
ld21 %1 -a adsp2181 -e %1 -x -g
if not exist %1.lda goto next1
del %1.lda
:next1
ren %1.exe %1.lda
if not exist %1.lda goto error2
echo ...завершена успешно pause
echo Сплиттинг...
rem для загрузки через BDMA
spl21 %1.lda %1 -loader -2181 > %1.msg
echo ...завершен успешно
pause
echo Преобразование в бинарный файл...
hexbin2 %1.bnm %1.bin m 0 > %1.msg
echo ...завершено успешно
pause
goto end
:error
echo Ошибка компиляции!
pause
goto end
:error2
echo He найден файл %1.lda !
pause
goto end
:no_file
echo Используйте: asmdsp.bat имя файла без расширения
pause
:end
Введите текст этого файла с помощью любого текстового редактора с кодировкой DOS в компьютер и сохраните в файле с именем asmdsp.bat или скопируйте с компакт-диска, прилагаемого к книге. Он позволит автоматизировать процесс трансляции файлов с программами для сигнального процессора. Если просмотреть строки этого командного файла, то нетрудно понять что он делает. В первой строке командного файла находится команда запрета отображения командных строк на экран. Далее следуют две строки для установки пути к рабочему каталогу с файлами трансляции и переменной adi_dsp. Затем проверяется, было ли введено имя файла в командной строке при запуске данного файла. Если имени задано не было, производится переход на метку с названием no file, после которой на экран отобразится подсказка о правильном использовании командного файла. Команда pause выводит строку «Нажмите любую клавишу…» и ожидает соответствующее действие от пользователя. Если имя файла для трансляции было задано в строке при запуске командного файла, то переход на метку не производится и начинается процесс компиляции файла с программой. Все процессы трансляции предваряются соответствующим сообщением. Компиляция выполняется командой asm21 %1 -2181 -l. Здесь после вызова программы компилятора asm21.exe с именем файла из командной строки %1 используется ключ -2181 для задания типа процессора и ключ -I для генерации листинга программы. Список всех ключей для конфигуратора bld21.exe, компилятора asm21.exe, компоновщика ld21.exe, распределителя spl2.exe и симулятора sim2181.exe можно получить, запустив отдельно соответствующую программу без параметров или с ключом -help. Переведенный текст этих сообщений приведен в табл. 6.3.
Таблица 6.3 Формат записи программ и назначение ключей
bld21.exe [-ключ] имя_файла | |
---|---|
-c | Установить чувствительность к регистру символов |
-help | Вывести на экран справку о программе |
asm21.exe [-ключ_1] [-ключ_2] … [-ключ_N] имя_файла | |
-Dvar[=exp] | Определить идентификатор для Си препроцессора, например -dten=10 |
-c | Установить чувствительность к регистру символов |
-cp | Включить Си препроцессор |
-I | Создать файл листинга программы |
-i [глубина] | Раскрыть содержимое включенных файлов в листинге с указанной глубиной |
-m [глубина] | Раскрыть макросы в файле листинга с указанной глубиной |
-p | Использовать только препроцессор |
-s | Отменить семантическую проверку много функциональных инструкций |
-o имя_файла | Переименовать выходной файл |
-2181 | Поддержать специальные ассемблерные инструкции для процессора ADSP-2181 |
-help | Вывести на экран справку о программе |
ld21.exe имя_файла1 [имя_файла2] … [имя_файлаN] [-ключ_1] [-ключ_2] … [-ключ_N] | |
-i имя_файла | Указать командный списочный файл |
-e имя | Присвоить выходным файлам новое имя (по умолчанию 210x.exe) |
-a имя_файла | Указать файл описания архитектуры |
-user имя_файла | Поиск библиотечного файла, созданного утилитой построителя библиотеки Iib21.exe |
-s размер | Задать размер динамической памяти |
-dir путь | Указать пути каталога для поиска файлов библиотек |
-lib | Компоновать программу с библиотекой Си |
-g | Создать файл таблицы символов *.sym |
-pmstack | Переместить стек в память программ (PM) |
-rom | Использовать ПЗУ версии библиотечных программ Си |
-c | Создать стек для компилированных Си программ (DM) |
-dryrun | Быстрый запуск для теста на ошибки без создания файла загрузки |
-x | Создать файл распределения памяти *.map |
-p | Поместить копию библиотечной подпрограммы на загрузочных страницах |
-group file | Использовать файл групповых модулей |
-help | Вывести на экран справку о программе |
spl21.exe входной_файл выходной_файл [-ключ_1] [-ключ_2] … [-ключ_N] | |
-dm | Распределить только память данных DM |
-pm | Распределить только память программ РМ |
-bm | Распределить только память загрузки BM |
-bs # | Установить размер загрузочной памяти (2048, 1024, 512 или 256 байт) |
-bb # | Установить размер границы загрузки (2048 или 1024 байт) |
-u | Формат выходного файла Motorola S (по умолчанию) |
-i | Формат выходного файла Intel Hex |
-us | Формат Motorola S1, байтовый (только с ключами -pm или -dm) |
-us2 | Формат Motorola S2, байтовый (только с ключами -pm или -dm) |
-ui | Формат Intel Hex, байтовый (только с ключами -pm или -dm) |
-loader | Создать загрузочный файл |
-bp # | Большие загрузочные страницы, где # как HEX адрес |
-bi # | Большие загрузочные страницы, где # как HEX величина |
-flag | Большие загрузочные страницы с использованием выходных флагов |
-Id | Использовать специальный загрузчик |
-2181 | Создать код для процессора ADSP-2181 |
sim2181 [-a имя_файла] [-c] [-e имя_файла] [-h] [-k имя_файла] [-v] [-w имя_файла] | |
-a имя_файла | Считывать при запуске файл архитектуры |
-c | Установить чувствительность к регистру символов |
-e имя_файла | Загрузить при запуске входной файл с расширением exe (lda) |
-h | Вывести на экран справку о программе |
-k имя_файла | Загрузить и выполнить файл командных строк |
-o имя_файла | Генерировать файл с сообщениями об ошибках |
-v | Избыточность |
-w имя_файла | Запустить симулятор с загрузкой ранее созданного файла конфигурации окон с расширением win |
После компиляции программы будет создан файл с именем программы и расширением obj. Однако если на этапе компиляции будут обнаружены ошибки, этого не произойдет и с помощью команды if not exist %1.obj goto error будет осуществлен переход на метку error, для выдачи соответствующего сообщения. При успешной компиляции трансляция программы продолжится.
Компоновка программы выполняется с помощью командной строки: ld21 %1 -a adsp2181 -e %1 -x -g. Здесь после команды вызова компоновщика ld21.exe с именем файла из строки запуска командного файла %1, следует несколько ключей с параметрами. Ключ -a adsp2181 указывает имя файла архитектуры процессора adsp2181.ach. Данный файл создан с помощью построителя программ System Builder и входит в поставку пакета кросс средств. Он состоит из пяти следующих строк:
$2181
$ADSP2181
$0000 3FFF paxINT_PM_USER t
$0000 3FDF dadINT_DM_USER t
$
Этот файл содержит данные, определяющие тип процессора и структуру его памяти, и всегда должен находиться в том же каталоге, где находится файл транслируемой программы. Ключ -е %1 назначает имя выходного файла. Ключи -x и -g предназначены для генерации файла карты памяти *.map и файла с таблицей символьных имен *.sym соответственно. Эти файлы можно использовать в дальнейшем для анализа распределения памяти процессора. Файл с расширением sym необходим, кроме того, для отладки программы с помощью симулятора sim2181.exe. В результате компоновки будет получен файл с именем программы и расширением exe. Этот файл уже можно использовать для загрузки в симулятор или непосредственно в процессор, например через интерфейс IDMA. Формат данного файла приведен в табл. 6.4.
Таблица 6.4 Формат загрузочного файла
Символ | Описание назначения |
---|---|
←←i | Признак начала загрузочного файла |
@PA | Признак блока памяти программ РМ |
0000 | Адрес загрузки кодов в память РМ |
Символ | Описание назначения |
123456 | Первый код команды |
789abc | Второй код команды |
def012 | Третий код команды |
… | и т.д. |
#123XXXXXXXX | Признак конца блока команд с контрольной суммой |
@DA | Признак блока памяти данных DM |
0000 | Адрес загрузки данных в память DM |
1234 | Первое слово данных |
5679 | Второе слово данных |
… | и т.д. |
#123XXXXXXXX | Признак конца блока данных с контрольной суммой |
←←o | Признак конца загрузочного файла |
X — контрольная сумма адреса и данных.
Позже мы подробно рассмотрим способ его загрузки в процессор через интерфейс IDMA. Несмотря на то, что этот файл имеет расширение exe, он не имеет ничего общего с исполняемыми файлами для персональных компьютеров. Тем не менее, случайный его запуск на компьютере может привести к непредсказуемым результатам. Непонятно, чем руководствовалась фирма Analog Device, давая такое расширение этому файлу при разработке кросс средств. Для устранения таких неприятных моментов в командный файл включены следующие четыре строчки:
if not exist %1.lda goto next1
del %1.lda
:next1
en %1.exe %1.lda
Эти строки предназначены для переименования нежелательного расширения загрузочного файла с расширением exe в файл с нейтральным расширением lda. Это делается в последней командной строке. Первые три строки предназначены для проверки наличия файла с расширением Ida от предыдущей трансляции и его удаления, в случае обнаружения, для генерации нового файла.
При успешной генерации загрузочного файла будет выведено соответствующее сообщение и осуществлен переход к группе команд для формирования файла прошивки для ПЗУ, из которого может производиться автоматическая загрузка процессора через интерфейс BDMA. Данный процесс называется «сплиттингом» (распределением) и осуществляется командой: spl21 %1.lda %1 - loader-2181 > %1.msg.
Здесь после команды вызова сплиттера sp21.exe с именем файла из командной строки с расширением Ida, следует несколько ключей и команда перенаправления вывода сообщений, с помощью символа >, в файл с расширением msg. Ключ -loader предназначен для добавления в выходной файл кода загрузчика, а ключ -2181 позволяет формировать код загрузки для процессора ADSP-2181. О коде загрузки я расскажу чуть позже.
В результате работы сплиттера будет создан файл прошивки для ПЗУ, с расширением bnm HEX формата Motorola. На практике, для программирования микросхем памяти чаще пользуются бинарным форматом файлов. Поэтому в командном файле добавлено еще несколько строк, осуществляющих преобразование файла bnm в бинарный файл с расширением bin. Для этого используется распространенная программа hexbin2.exe. Эту программу также можно найти на компакт-диске.
Теперь, когда мы познакомились со всеми процедурами трансляции, выполняемыми с помощью командного файла asmdsp.bat, можно начать транслировать нашу программу. Не забудьте, что вместе с программой в каталоге проекта должен находиться файл описания def2181.h, файл архитектуры adsp2181.ach и созданный и описанный выше командный файл asmdsp.bat.
Выполните трансляцию программы mem_clr.dsp с помощью командной строки asmdsp.bat mem_clr. Обратите внимание на то, что в качестве параметра для командного файла используется имя нашей программы без расширения. Оно будет автоматически подставляться вместо символов %1 командного файла. В результате работы командного файла на экране должны последовательно появиться сообщения:
Компиляция...
...завершена успешно
Нажмите любую клавишу...
Компоновка...
...завершена успешно
Нажмите любую клавишу...
Сплиттинг...
...завершен успешно
Нажмите любую клавишу...
Преобразование в бинарный файл...
...завершено успешно
Нажмите любую клавишу...
По завершении работы командного файла в каталоге с программой должны появиться файлы с именем программы и расширениями int, lst, obj, lda, map, sym, msg, cde, bnl, bnm, bnu и bin. Описание назначения данных файлов проекта в краткой форме приведено в табл. 6.5.
Таблица 6.5 Описание расширений файлов проекта
Расширение | Описание |
---|---|
bat | Командный файл |
sys | Исходный файл системной архитектуры |
ach | Файл описания архитектуры |
dsp | Файл ассемблерного модуля (текст программы) |
lst | Файл листинга |
int | Файл инициализации |
obj | Объектный файл |
cde | Файл кода |
dat | Файл инициализации данных кода |
exe (lda) | Файл загрузки образа памяти |
map | Файл распределения памяти |
sym | Файл символов |
bnu | Выходной файл разделителя программ. Содержит старшие байты |
bnm | Выходной файл разделителя программ. Содержит средние байты |
bnl | Выходной файл разделителя программ. Содержит младшие байты |
bin | Файл для программирования ПЗУ в двоичном формате |
msg | Файл сообщений |
Итак, с помощью приведенного выше командного файла мы получили необходимые нам файлы с расширениями lda и bin для непосредственной загрузки в память процессора и для прошивки ПЗУ соответственно. Файл с расширением sym понадобится нам для отладки программы с помощью программы симулятора sim2181.exe.
Остальные перечисленные выше файлы в каталоге с программой являются вспомогательными, которые после их просмотра и изучения можно смело удалить для очистки каталога. Например, файл с расширением lst содержит листинг программы с нумерацией всех строк программы и машинными кодами процессора, соответствующими ассемблерным командам программы. С его помощью можно увидеть соответствие ассемблерных команд машинным кодам сигнального процессора.
Для автоматического удаления этих файлов я рекомендую создать и использовать еще один командный файл asmclr.bat, состоящий из строк:
del *.map
del *.lst
del *.hex
del *.msg
del *.cde
del *.int
del *.obj
del *.bnl
del *.bnm
del *.bnu
Создав и запустив такой файл в каталоге с программой, все ненужные файлы будут удалены автоматически, и в каталоге останутся только необходимые для дальнейшей работы файлы.
Теперь можно приступить к отладке программы. Проследить за ходом выполнения программы и изменением состояния внутренних регистров и ячеек памяти процессора нам поможет программа симулятора sim2181.exe, входящая в состав средств разработки программ.
Данная программа работает только в операционной системе DOS и даже не допускает работу в режиме эмуляции DOS из Windows. Поэтому для запуска данного симулятора необходимо перегрузить компьютер в режим работы DOS, после чего необходимо запустить программу симулятора в командной строке sim2181.exe. После загрузки программы на экране монитора должно появиться окно с изображением названия и версии программы (рис. 6.1).
Рис. 6.1. Окно с изображением названия и версии программы
Несколько секунд спустя должно появиться окно с главным меню и приглашением к вводу команд в центре экрана (рис. 6.2).
Рис. 6.2. Окно с главным меню
Главное меню программы состоит из десяти раскрывающихся закладок команд. Симулятор имеет встроенную справочную систему по всем командам меню, вызываемую с помощью клавиши F1 клавиатуры. Назначение клавиш управления симулятора приведено в табл. 6.6.
Таблица 6.6. Назначение клавиш управления симулятора
Клавиша или комбинация | Описание выполняемой функции |
---|---|
Функции выполнения программы | |
F4 | Запустить программу на выполнение |
F10 | Выполнять программу по шагам |
Shift+F10 | Выполнять N шагов программы |
F9 | Установить/сбросить точку останова программы |
Shift+F9 | Установить/сбросить специальную точку останова программы |
Функции памяти | |
Ctrl+D | Дамп памяти |
Ctrl+F | Заполнить память |
Ctrl+G | Перейти по адресу памяти |
Ctrl+M | Загрузить память |
Функции размера окна | |
Shift+F5 | Увеличить окно по вертикали |
Shift+F6 | Уменьшить окно по вертикали |
Shift+F7 | Увеличить окно по горизонтали |
Shift+F8 | Уменьшить окно по горизонтали |
Функции перемещения окна | |
F5 | Переместить окно вверх |
F6 | Переместить окно вниз |
F7 | Переместить окно влево |
F8 | Переместить окно вправо |
Функции выбора окна | |
ESC | Закрыть текущее окно |
F2 | Выбрать следующее окно |
F3 | Выбрать окно главного меню |
Ctrl+L | Показать список активных окон |
Функции отображения окна | |
F1 | Вызов окна помощи |
Ctrl+T | Переключение формата отображения окна |
Для перехода в главное меню и загрузки программы нажмите последовательно клавиши Esc и F3 на клавиатуре. С помощью стрелок курсора выделите закладку File главного меню и раскройте ее, нажав клавишу Enter. В раскрывшемся списке команд выберите команду Load Executable File (загрузить исполняемый файл), а в открывшемся окне ввода введите имя файла mem_clr.lda и завершите ввод, нажав клавишу Enter.
Откройте в пунктах главного меню Memory и Register окна памяти программ Program Memory, памяти данных Data Memory, флагов Flags, регистров генератора адреса DAG Registers и регистров арифметико-логического устройства Computational Alternate Registers. После чего разместите эти окна на экране с помощью клавиш управления окнами симулятора в соответствии с рис. 6.3, используя клавиши управления, описанные в табл. 6.6.
Рис. 6.3. Экран управления окнами симулятора
Данные окна позволят наблюдать за ходом выполнения программы и состоянием памяти и регистров процессора. Теперь можно выполнять программу по шагам, нажимая клавишу F10 на клавиатуре, или запустить автоматическое выполнение программы с помощью клавиши F4.
Останов программы производится повторным нажатием клавиши F4. Во время выполнения программы в окне памяти программ Program Memory можно наблюдать процесс выполнения ассемблерных команд, а в окнах регистров и памяти данных изменение значений регистров и ячеек памяти.
Для сохранения конфигурации окон и использования этой конфигурации при повторных запусках симулятора можно сохранить ее в файле с помощью команды Save Window File из закладки File главного меню. При сохранении конфигурации необходимо будет задать имя файла, например mem_clr. После сохранения конфигурации и выхода из симулятора в каталоге будет находиться файл с именем mem_clr.win. Для выхода из симулятора необходимо выбрать команду Exit или Quit из закладки Quit главного меню и в открывшемся окне подтверждения выхода нажать поочередно клавишу пробела Space, а затем клавишу Enter. При новом запуске симулятора, вместо повторного открытия окон, достаточно будет загрузить файл с помощью команды Load Window File из закладки File главного меню симулятора.
Рекомендую читателям самостоятельно познакомиться поближе с симулятором на практике и поэкспериментировать с открытием различных окон симулятора для изучения внутренних регистров процессора, а также опробовать множество команд, предоставляемым симулятором для отладки программ.
В этой главе описывается порт байтового прямого доступа к памяти процессора, под названием BDMA (Byte Data Memory Access).
Теперь можно приступить к загрузке транслированной и отлаженной программы непосредственно в сигнальный процессор. Для выполнения данной процедуры и практического освоения сигнального процессора с отладкой программ аппаратно можно воспользоваться тестовой платой, описанной в гл. 3.
Запрограммируйте флэш ПЗУ типа Am29F040 содержимым файла mem_clr.bin с помощью любого подходящего для этих целей программатора и вставьте запрограммированную микросхему в панельку на место микросхемы D7 тестовой платы. После подачи напряжения питания на тестовую плату произойдет автоматическая загрузка программы из ПЗУ через порт BDMA в память сигнального процессора, и ее выполнение. Убедиться в том, что программа работает, можно будет с помощью осциллографа, подключенного к выводу FL2 процессора. На этом выводе должен формироваться сигнал в виде меандра с частотой около 2МГц, а светодиод HL2 должен при этом светиться.
Рассмотрим подробнее механизм автоматической загрузки сигнального процессора из ПЗУ, для лучшего понимания происходящих при этом процессов. При подаче питания на тестовую плату, описанную в гл. 3, цепь сброса R1, CP1 формирует сигнал -RKEY, который устанавливает триггер, построенный на элементах D6.1 и D6.2, в состояние, при котором сигнал BMODE будет находиться в состоянии логического нуля. Сигнальный процессор анализирует состояние данного сигнала и сигнала на входе вывода MMAP после сброса и распознает, что загрузка программы в программную память процессора будет производиться из ПЗУ через интерфейс BDMA. При этом процессор автоматически заносит в регистры порта BDMA определенные значения, описанные в предыдущей главе, для того чтобы произвелась автоматическая загрузка 32 слов программы-загрузчика разрядностью 24 бита побайтно в программную память процессора и затем ей было передано управление для загрузки остальной части программы.
Как было сказано выше, программа-загрузчик автоматически записывается в начало файла прошивки ПЗУ с помощью сплиттера во время трансляции программы. После автоматической загрузки из ПЗУ в память программ процессора 32 слов программы- загрузчика через интерфейс BDMA происходит перезапуск процессора и начинает выполняться программа загрузчика, которая осуществляет полную загрузку основной рабочей программы из ПЗУ в процессор.
После чего вновь происходит перезапуск процессора, и начинает выполняться основная загруженная программа. При загрузке основной программы загрузчик полностью замещается в программной памяти процессора самой программой. Такой непростой механизм загрузки реализован разработчиками процессора с целью обеспечения гибкости при загрузке процессора, и для обеспечения возможности разработчикам программного обеспечения создавать собственные загрузчики программ. Машинные коды загрузчика можно увидеть в первых байтах файла mem_clr.bin с помощью любого редактора шестнадцатеричных файлов или простой программы просмотра файлов.
В файле mem_clr.bin и в ПЗУ все коды 24-разрядных слов команд процессора располагаются в строгом порядке. Первым располагается в ПЗУ старший байт команды, затем следует средний байт и последним записывается младший байт команды. Формат хранения слов программы в ПЗУ приведен в табл. 7.1.
Таблица 7.1 Формат хранения слов программы в ПЗУ
Адрес памяти программ | Слово программы | Адрес памяти ПЗУ | Байты программы |
---|---|---|---|
0x0000 | 0xABCDEF | 0x00 | 0xAB |
0x01 | 0xCD | ||
0x02 | 0xEF | ||
0x0001 | 0x123456 | 0x03 | 0x12 |
0x04 | 0x34 | ||
0x05 | 0x56 | ||
и т.д. |
Способ загрузки процессора из ПЗУ хорошо применим для автономных процессорных устройств, работающих под управлением одной программы. Однако на практике довольно часто встречаются задачи, когда необходимо оперативно менять управляющую программу для процессора какого-либо электронного устройства. Это также необходимо при отладке программ с многочисленными итерациями трансляции и загрузки отлаживаемой программы в процессор. Удачным решением для такого рода задач является загрузка программ в сигнальный процессор через интерфейсный порт процессора IDMA. Но прежде чем начать освоение данной процедуры, необходимо подробнее познакомиться с этим интерфейсом. С этого мы продолжим изучение сигнального процессора в следующей главе.
В этой главе описывается интерфейсный порт IDMA и способы загрузки и отладки программ для сигнального процессора через этот порт.
Помимо порта прямого доступа к байтовой памяти BDMA (Byte Data Memory Access), сигнальный процессор ADSP-2181 имеет порт прямого доступа к внутренней памяти процессора IDMA (Internal Data Memory Access). Он представляет собой 16-разрядный параллельный порт, через который можно читать и записывать данные памяти процессора. Порт IDMA имеет 16-разрядную мультиплексированную шину адреса/данных IAD0–IAD15, 4 сигнала управления и 1 сигнал контроля. К сигналам управления относятся: сигнал выбора порта -IS, сигнал записи адреса ячейки памяти IAL, сигнал чтения -IRD и записи -IWR данных через порт. Сигнал контроля -IACK позволяет определить готовность порта IDMA. При чтении и записи памяти через IDMA данные транслируются через буферный регистр порта IDMA. В памяти данных процессора по адресу 0x3FE0 находится регистр управления IDMA, который предназначен для хранения адреса ячейки памяти, к которой будет производиться обращение через порт IDMA. Формат этого регистра представлен в табл. 8.1.
Таблица 8.1 Формат данных регистра управления IDMA
Разряд | Назначение |
---|---|
15 | Не используется |
14 | Указатель типа памяти: 0=память программ (РМ) 1=память данных (DM) |
13–0 | Значение начального адреса памяти процессора при обмене через IDMA (от 0 до 0x3FFF) |
Порт IDMA имеет встроенную функцию автоинкремента (автоматического увеличения на единицу адреса памяти), что позволяет ускорить выполнение блочных операций. Кроме того, порт IDMA позволяет выполнять загрузку памяти программ процессора с автоматическим запуском программы после заполнения нулевой ячейки памяти. Протокол обращения к памяти процессора через порт IDMA достаточно прост и показан на рис. 8.1.
Рис. 8.1. Алгоритм обращения к памяти процессора
Вначале все сигналы управления портом переводятся в пассивное состояние и проверятся готовность IDMA, путем ожидания установки сигнала -IACK в логический ноль. Затем осуществляется вывод на шину IAD0–15 сигналов адреса памяти процессора, к которой будет производиться обращение. После чего формируется строб сигнала записи адреса IAL в регистр управления порта IDMA. Временная диаграмма данной операции показана на рис. 8.2, а в табл. 8.2 приведены временные параметры для этой диаграммы.
Рис. 8.2. Временная диаграмма операции защелкивания адреса IDMA
Таблица 8.2 Временные параметры диаграммы защелкивания адреса IDMA
Параметр | Минимум |
---|---|
Защелкивание адреса IDMA | |
Требуемые длительности: | |
tIALP длительность защелкивания адреса, нс1,2 | 10 |
tIASU установка адреса перед окончанием защелкивания, нс2 | 5 |
tIAH удержание адреса после защелкивания, нс2 | 2 |
tIKA-IACK=0 перед защелкиванием, нс1 | 0 |
tIALS начало записи или чтения после защелкивания, нс2,3 | 3 |
Примечания:
1 Начало защелкивания — IS=0 и IAL=1.
2 Конец защелкивания — IS=1 или IAL=0.
3 Начало записи или чтения — IS=0 и (IWR=0 или IRD=0).
Как видно из таблицы, минимальные временные значения сигналов не превышают 10 нс, что говорит о поддержке высокой скорости операций портом IDMA. Далее производится операция чтения или записи памяти. При операции чтения данные считываются с шины IAD0–IAD15 во время активизации управляющего сигнала -IRD.
При операции записи данные выставляются на шину IAD0–IAD15, и записываются в память процессора с помощью активизации сигнала -IWR.
Порт IDMA поддерживает короткий (быстрый) и длинный (долгий) циклы обращения к памяти. Во время короткого цикла данные читаются и записываются без ожидания готовности порта по сигналу -IACK, используя при этом буфер порта IDMA с данными. Временные диаграммы для данных циклов обращения показаны на рис. 8.3 и 8.4 соответственно. В табл. 8.3 и 8.4 приведены характеристики сигналов для этих диаграмм.
Рис. 8.3. Временные диаграммы короткого цикла чтения через порт IDMA
Рис. 8.4. Временные диаграммы короткого цикла записи через порт IDMA
Таблица 8.3 Временные параметры диаграммы короткого цикла чтения через порт IDMA
Параметр | Минимум | Максимум |
---|---|---|
Короткий цикл чтения через IDMA | ||
Требуемые длительности: | ||
tIKR-IACK=0 до начала чтения1, нс | 0 | |
tIRP Продолжительность сигнала чтения, нс | 15 | |
Характеристики переключения: | ||
tIKHR-IACK=0 после начала чтения1, нс | 15 | |
tIKDH Удержание данных после окончания чтения2, нс | 0 | |
tIKDD Сброс данных после окончания чтения2, нс | 10 | |
tIRDE Активизация предыдущих данных, нс | 0 | |
tIRDV Достоверность предыдущих данных | 15 |
Примечания:
1 Начало чтения: -IS=0 и -IRD=0.
2 Конец чтения: -IS=1 или -IRD=1.
Таблица 8.4 Временные параметры диаграммы короткого цикла записи через порт IDMA
Параметр | Минимум | Максимум |
---|---|---|
Короткий цикл записи через IDMA | ||
Требуемые длительности: | ||
tIKW-IACK=0 до начала записи1, нс | 0 | |
tIWP Продолжительность записи1,2, нс | 15 | |
tIDSU Установка данных до окончания записи2, нс | 5 | |
tIDH Удержание данных после окончания записи2, нс | 2 | |
Характеристики переключения: | ||
tIKHW от начала записи до -IACK=1, нс | 15 |
Примечания:
1 Начало записи: -IS=0 и -IWR=0.
2 Конец записи: -IS=1 или -IWR=1.
Во время длинного цикла данные читаются и записываются с ожиданием готовности порта по сигналу -IACK, который активизируется только после непосредственной записи или чтения данных в памяти процессора.
Временные диаграммы для данных циклов обращения показаны на рис. 8.5 и 8.6 соответственно. В табл. 8.5 и 8.6 приведены характеристики сигналов для этих диаграмм.
Рис. 8.5. Временные диаграммы длинного цикла чтения через порт IDMA
Рис. 8.6. Временные диаграммы длинного цикла записи через порт IDMA
Таблица 8.5 Временные параметры диаграммы длинного цикла чтения через порт IDMA
Параметр | Минимум | Максимум |
---|---|---|
Длинный цикл чтения через IDMA | ||
Требуемые длительности: | ||
tIKR-IACK=0 до начала чтения1, нс | 0 | |
tIRP Продолжительность сигнала чтения, нс | 15 | |
Характеристики переключения: | ||
tIKHR-IACK=0 после начала чтения1, нс | 15 | |
tIKDS Установка данных перед -IACK=0, нс | 0,5 tCK5-10 | |
tIKDH Удержание данных после окончания чтения2, нс | 0 | |
tIKDD Сброс данных после окончания чтения2, нс | 10 | |
tIRDE Активизация предыдущих данных, нс | 0 | |
tIRDV Достоверность предыдущих данных, нс | 15 | |
tIRDH1 Удержание предыдущих данных (DM/PM1), нс | 2 tCK-5 | |
tIRDH2 Удержание предыдущих данных (PM2), нс | tCK-5 |
Примечания:
1 Начало чтения: -IS=0 и -IRD=0.
2 Конец чтения:-IS=1 или -IRD=1.
3 Чтение памяти DM или первой половины РМ.
4 Чтение второй половины РМ.
5 tCK — период тактовой частоты процессора.
Таблица 8.6 Временные параметры диаграммы длинного цикла записи через порт IDMA
Параметр | Минимум | Максимум |
---|---|---|
Длинный цикл записи через IDMA | ||
Требуемые длительности: | ||
tIKW-IACK=0 до начала записи1, нс | 0 | |
tIKSU Установка данных перед -IACK=0, нс | 0,5 tCK2+1 | |
tIKH Удержание данных после -IACK=0, нс | 2 | |
Характеристики переключения: | ||
tIKLW От начала записи до -IACK=0, нс | 1,5 tCK | |
tIKHW От начала записи до -IACK=1, нс | 15 |
Примечания:
1 Начало записи: -IS=0 и -IWR=0.
2 tСK — период тактовой частоты процессора.
Сигнал подтверждения данных -IACK допускается не использовать, если скорость обращения к порту со стороны адаптера невысокая и достоверность данных обеспечивается задержкой самих сигналов управления.
При обращении через порт IDMA к памяти данных операции чтения и записи выполняются за один цикл, поскольку разрядность порта составляет 16 бит. Однако при обращении к 24-разрядным данным памяти программ требуется два цикла обращения. В первом цикле происходит чтение или запись старших 16 бит слова памяти программ. Во втором цикле по линиям IAD0–IAD7 передаются младшие 8 бит слова. Линии IAD8–IAD15 при этом игнорируются. Автоинкремента адреса между этими циклами не происходит.
Через порт IDMA возможен доступ ко всей памяти процессора, за исключением регистров управления и состояния, отображенных на область памяти данных. При обращении через IDMA к области памяти данных, отведенной для регистров управления и состояния с адреса 0x3FE0 по адрес 0x3FFF, запись и чтение данных все же будет производиться. Но эта область памяти фактически будет дополнительной для пользователя, поскольку она дублирует системную область памяти данных, отведенную для регистров управления и состояния процессора.
Данную область допускается использовать для хранения произвольной служебной информации, но только при обращении к этой памяти через порт IDMA, поскольку другой возможности обращения к данной памяти у процессора не существует. Для обеспечения доступа через порт IDMA к регистрам управления и состояния процессора необходимо обеспечить пересылку содержимого этих регистров в другую область памяти из самой программы процессора. Такая аппаратная конфигурация процессора обеспечивает защиту информации в регистрах управления и состояния от случайной записи и от программных ошибок.
Процессор ADSP-2181 поддерживает также начальную загрузку программ в память программ через порт IDMA. Для этого необходимо установить сигналы на выводах процессора BMODE=1 и MMAP=0. Затем необходимо произвести сброс процессора и загрузить программу в память программ процессора, начиная с адреса 1 через порт IDMA. Кроме того, можно записать произвольные данные в память данных процессора. После этого следует записать слово программы по адресу 0, и процессор запустит загруженную программу.
Теперь, после знакомства с портом IDMA, можно приступить к рассмотрению устройства, позволяющего загружать программы и редактировать данные памяти сигнального процессора через интерфейсный порт IDMA.
Для этих целей был разработан небольшой адаптер LPT-IDMA, который позволяет подключить порт IDMA сигнального процессора к LPT порту персонального компьютера. Схема этого адаптера приведена на рис. 9.1.
Рис. 9.1. Схема адаптера (начало)
Рис. 9.1. Схема адаптера (продолжение)
Рис. 9.1. Схема адаптера (окончание)
Адаптер выполнен на базе одной программируемой логической микросхемы D1 типа XC9536-15VQ44C фирмы XILINX. Схема внутреннего содержимого этой микросхемы представлена на рис. 9.2.
Рис. 9.2. Функциональная схема микросхемы (начало)
Рис. 9.2. Функциональная схема микросхемы (окончание)
Данная схема обеспечивает формирование 16-разрядной шины адреса и управляющих сигналов для порта IDMA с помощью 8-разрядной шины данных LPT порта. Чтение 16-разрядных данных из порта IDMA осуществляется через сигналы состояния LPT порта с использованием мультиплексоров M2_1, запрограммированных в схеме ПЛИС.
Прошивка для программирования данной ПЛИС в виде файла lpt_idma.jed находится на диске, прилагаемом к книге. Программирование ПЛИС осуществляется через соединитель J1 с помощью программатора, описываемого в приложении.
Все элементы адаптера можно распаять на небольшой макетной плате или изготовить для этих целей специальную печатную плату. Адаптер подключается к LPT порту компьютера с помощью ленточного 25-жильного кабеля, через разъем X1 «LPT». Чертеж такого кабеля изображен на рис. 9.3, а в табл. 9.1 приведено назначение цепей и соответствие контактов разъемов.
Рис. 9.3. Ленточный 25-жильный кабель
Таблица 9.1 Назначение цепей кабеля LPT
Цепь | Вилка DB-25M | Розетка DB-25F | Цепь | Вилка DB-25M | Розетка DB-25F |
---|---|---|---|---|---|
-STB | 1 | 1 | -AUTOLF | 14 | 14 |
D0 | 2 | 2 | -ERR | 15 | 15 |
D1 | 3 | 3 | -INIT | 16 | 16 |
D2 | 4 | 4 | -SELIN | 17 | 17 |
D3 | 5 | 5 | GND | 18 | 18 |
D4 | 6 | 6 | GND | 19 | 19 |
D5 | 7 | 7 | GND | 20 | 20 |
D6 | 8 | 8 | GND | 21 | 21 |
D7 | 9 | 9 | GND | 22 | 22 |
-ACK | 10 | 10 | GND | 23 | 23 |
BUSY | 11 | 11 | GND | 24 | 24 |
РЕ | 12 | 12 | GND | 25 | 25 |
SEL | 13 | 13 | - | - | - |
С помощью разъемов XU и XP адаптер подключается непосредственно к устройству с сигнальным процессором через интерфейсный порт IDMA.
Питание адаптера осуществляется от источника питания напряжением +5 В самого устройства. В качестве такого устройства с сигнальным процессором можно использовать тестовую плату, описанную ранее. Эта плата имеет все необходимое для подключения адаптера LPT-IDMA без всяких переделок.
Все схемы и проект прошивки для ПЛИС прилагаются в виде соответствующих файлов на компакт-диске этой книги.
В этой главе описывается программа под названием «Менеджер памяти», разработанная автором книги для загрузки в сигнальный процессор программ и их отладки.
Для того чтобы можно было использовать адаптер LPT-IDMA для загрузки программ и данных в сигнальный процессор, а также иметь возможность редактировать содержимое памяти сигнального процессора во время его работы, мною была написана программа менеджера памяти цифрового сигнального процессора «mngdsp». Эта программа является приложением DOS и может работать в режиме DOS под Windows95/98/Me. Данная программа в составе проекта для Borland С++ 3.1, с исходными текстами программы находится на диске, прилагаемом к книге.
После запуска программы mngdsp.exe на экране монитора должно появиться окно, изображенное на рис. 10.1.
Рис. 10.1. Экран монитора после запуска программы mngdsp.exe
Данное окно отображает содержимое памяти сигнального процессора в шестнадцатеричном формате. Внизу этого окна приведены команды управления программой. При перемещении маркера по окну с помощью клавиш управления, можно выбрать любую доступную ячейку памяти процессора, для записи в нее произвольных данных. Адрес выбранной ячейки памяти отображается слева вверху. Переключение между памятью данных и памятью программ процессора производится с помощью клавиши TAB клавиатуры. Адаптер позволяет подключаться параллельно к двум портам IDMA разных процессоров. Например, к мультипроцессорному устройству. Для этого он использует сигнал -SEL, подключаемый к выводу - IS порта IDMA. Поскольку при пассивном состоянии данного сигнала порт IDMA не активизируется, допускается запараллеливание всех сигналов IDMA двух процессоров, за исключением сигнала -IS, который позволит выбирать один из двух процессоров. При такой схеме включения необходимо к выводу -IS одного из двух процессоров подключать сигнал -SEL непосредственно, а к выводу -IS второго процессора через инвертор. Выбор процессора из программы производится клавишами F1 и F2. С помощью клавиши F3 можно заполнить всю память данных процессора произвольным числовым значением. Клавиша F4 позволяет очистить память данных процессора путем записи во всю область памяти нулевого значения. Клавиша F8 производит принудительный сброс процессора путем формирования сигнала сброса -INIT для процессора. Кроме того, программа менеджер позволяет загрузить программу в память процессора и запустить ее. Для этого используется клавиша F9. После нажатия этой клавиши, программа запросит имя загружаемого файла, а также предложит загрузить файл с именем dsp.lda, используемым в программе по умолчанию. Поэтому перед запуском программы можно переименовать файл, который должен загружаться в процессор, в файл с именем dsp.lda либо ввести имя загружаемого файла по запросу программы. О формате данного файла и о том, как получить такой файл, было рассказано ранее.
Таким образом, используя программу менеджера памяти DSP, можно загружать и отлаживать программы для сигнального процессора, пользуясь при этом многократными итерациями, высокой скоростью и удобством интерфейса программы.
Приведу еще один пример использования порта IDMA, который можно использовать в мультипроцессорных системах. При разработке таких систем часто возникает необходимость загрузки программы или данных в один из ведомых процессоров с помощью ведущего процессора. Для этих целей также хорошо подходит порт IDMA. Структурная схема подключения двух процессоров между собой через порт IDMA показана на рис. 10.2.
Рис. 10.2. Структурная схема подключения двух процессоров
Как видно из схемы, ведущий процессор подключен к порту IDMA ведомого процессора с использованием сигнала обращения к портам ввода-вывода -IOMS, сигнала записи -IWR, двух сигналов программируемых флагов PF0 и PF2, адресного сигнала A0, шины данных D8–D23 и сигнала синхронизации тактовой частоты CLKOUT. Такая схема включения позволяет выполнять операцию записи данных через порт IDMA ведомого процессора с помощью одной команды записи данных в порт ввода-вывода io(address)=data. Сигнал CLKOUT обеспечивает маскирование адресной линии A0 при формировании сигнала защелки адреса IAL. Такое маскирование необходимо для блокировки переходных процессов на линии А0 при установке адреса. Это хорошо видно из временной диаграммы цикла записи данных процессором в порт ввода-вывода, приведенной на рис. 10.3.
Рис. 10.3. Временная диаграмма цикла записи
Сигнальные цепи PF2 и PF0 используются ведущим процессором для активизации схемы защелки адреса в порт IDMA и для анализа состояния готовности порта IDMA ведомого процессора.
Ниже приведен исходный текст программы idmaidma.dsp для ведущего процессора данной схемы, позволяющей ему загрузить в память программ ведомого процессора массив данных, размещенных в файле idmaidma.dat, являющихся программой для ведомого процессора.
/*********************************************************************/
/* Эта программа транслирует данные от ведущего процессора ADSP-2181 */
/* к ведомому процессору ADSP-2181 через IDMA порт */
/*********************************************************************/
.module/abs=0x0000 idma_transfer;
.include ; { Включить файл определений }
.const length=68; { Десятичное значение длины вектора }
.var/pm/ram x_input[length]; { Описание массива данных }
.init x_input: ; { Файл передаваемых данных }
jump start; nop; nop; nop; { Таблица векторов прерываний }
RTI; NOP; NOP; NOP; { Прерывания не используются }
RTI; NOP; NOP; NOP;
RTI; NOP; NOP; NOP;
RTI; NOP; NOP; NOP;
RTI; NOP; NOP; NOP;
RTI; NOP; NOP; NOP;
RTI; NOP; NOP; NOP;
RTI; NOP; NOP; NOP;
RTI; NOP; NOP; NOP;
RTI; NOP; NOP; NOP;
/*** Начало программы ***/
start:
ax0=0x0000;
dm(System_Control_Reg)=ax0; { Такты ожидания PM отключить }
dm(PFTYPE)=ax0; { Все флаги PF назначить выходами }
dm(DM_Wait_Reg)=ax0; {Такты ожидания DM и портов ввода-вывода отключить}
l6=0; { Длина буфера данных }
i6=^x_input; { Адрес начала буфера транслируемых данных }
m6=1; { Приращение = 1 }
modify(i6,m6); { Модификация адреса }
/*** Защелкнуть адрес 0x0001 ***/
call check_ack; { Проверить готовность к приему }
ax0=0x0001; { Стартовый адрес для записи в ведомый процессор }
io(0x0001)=ах0; { Запись этого адреса с переключением линий -IS и IAL }
/*** Запись всех данных начиная с адреса 0x0001 ***/
cntr=length-1; { Установить счетчик транслируемых слов }
do loop1 until се; { Организовать цикл записи }
call check_ack; { Проверить готовность к приему }
call format; { Форматировать данные }
io(0x0100)=ay0; { -IWR и IS записывают старшие 16 бит IAD 0-15 }
call check_ack; { Проверить готовность к приему }
io(0x0100)=ay1; { -IWR и IS записывают младшие 8 бит IAD 0-15 }
loop1: nop;
/*** Защелкнуть адрес 0x0000 ***/
call check_ack; { Проверить готовность к приему }
ax0=0x0000; { Стартовый адрес для записи в ведомый процессор }
io(0x0001)=ax0; { Запись этого адреса с переключением линий -IS и IAL }
/*** Запись по адресу 0x0000 ***/
i6=^x_input; { Указатель на начало буфера данных }
call check_ack; { Проверить готовность к приему }
call format; { Форматировать данные }
io(0x0100)=ay0; { -IWR и IS записывают старшие 16 бит IAD 0-15 }
call check_ack; { Проверить готовность к приему }
io(0x0100)=ay1; { -IWR и IS записывают младшие 8 бит IAD 0-15 }
idle; { Режим ожидания }
/****************************************************************/
/* Подпрограмма переформатирования 24-бит слова памяти программ */
/****************************************************************/
format: ay0=pm(i6,m6); { 16 старших бит запомнить в регистре ay0 }
ay1=px; { 8 младших бит запомнить в регистре ay1 }
rts;
/***************************************************************************/
/* Подпрограмма ожидания перехода состояния линии -IACK в низкое состояние */
/***************************************************************************/
check_ack:
ax0=dm(0x3fе5); { Читать PF1, который подключен к -IACK }
ar=tstbit 1 of ax0; { Анализировать PF1 }
if ne jump check_ack; { Если процессор не готов вновь контролировать -IACK}
rts;
.endmod;
Многие директивы и команды данной программы уже знакомы читателям из описания предыдущей программы. Текст программы прокомментирован, что позволяет без труда понять алгоритм ее работы. Как видно из текста, в программе использован ряд подпрограмм, в которых сосредоточены часто используемые операции. Вызов данных подпрограмм осуществляется командой call, после которой записывается имя подпрограммы. Команда io(0x0001)=ax0 осуществляет запись содержимого регистра ax0 в порт ввода- вывода по адресу 0x0001. Команда ar=tstbit 1 of ax0 заносит в регистр ar результат тестирования первого разряда регистра ax0. Следующая за этой, команда if ne jump check_ack проверяет состояние флагов ALU и осуществляет переход на метку check_ack, если результат проверки предыдущей операции был отрицательным. Подробнее со всеми командами сигнального процессора ADSP-2181 мы познакомимся позже.
При компиляции этой программы, все данные из файла idmaidma.dat будут автоматически помещены в файл загрузки idmaidma.lda. В качестве файла данных idmaidma.dat можно использовать загрузочный файл рассмотренной нами ранее программы mem_clr.lda, из которого предварительно следует удалить первые три и последние две служебные строки. Данную операцию можно выполнить с помощью любого текстового редактора. После чего файл необходимо переименовать, задав ему имя idmaidma.dat. Кроме того, можно изменить имя загружаемого файла в самой программе.
В этой главе говорится о директивах, которые используются для управления процессом ассемблирования программ.
На примере программ, рассмотренных ранее, было показано, что программа может включать в себя строки различных директив. Директивы управляют процессом ассемблирования. Они обрабатываются препроцессором, но в отличие от инструкций не генерируют при ассемблировании код. Любая директива ассемблера начинается с точки и заканчивается точкой с запятой. Некоторые директивы имеют параметры и аргументы. Параметры следуют сразу за директивой и разделяются косой чертой, аргументы следуют после параметров. Директивы ассемблера могут содержать однострочный комментарий, который не может быть продолжен на следующей строке. Если требуется многострочный комментарий, необходимо начать новый комментарий на следующей строке. В общем случае запись директив выглядит следующим образом:
.ДИРЕКТИВА [/ПАРАМЕТР1][/ПАРАМЕТР2] ... [АРГУМЕНТ]; {Комментарий}
В данной записи квадратные скобки означают, что оператор, заключенный между ними, не является обязательным и может отсутствовать в некоторых директивах. Комментарий не является обязательным элементом директивы и выполняет вспомогательную функцию пояснения.
Помимо директив ассемблера, в среде разработки программ для ADSP-2181 существует несколько директив для системного конфигуратора. Ранее говорилось о том, что системный конфигуратор можно не использовать, если имеется готовый файл описания архитектуры с расширением ach для конкретного устройства, построенного на определенном типе сигнального процессора. Этот файл используется компоновщиком и программой эмулятора для того, чтобы определить размер памяти системы, тип памяти процессора (ОЗУ или ПЗУ), какая часть памяти является внутренней и внешней и какая периферия отображена в памяти. При необходимости, можно воспользоваться системным конфигуратором с целью получения нового файла описания архитектуры для нового устройства или другого типа процессора.
Перед запуском системного конфигуратора необходимо создать файл с расширением sys, содержащим директивы описания устройства (системы). Запуск системного конфигуратора осуществляется командой:
bld21 имя_файла[sys] [-c]
Здесь имя_файла — это имя исходного файла системной конфигурации. Имя файла может быть указано с расширением. Если расширение отсутствует, системный конфигуратор добавляет по умолчанию расширение sys. Системный конфигуратор создает выходной файл описания архитектуры с именем входного файла и расширением ach. Ключ -с делает системный конфигуратор чувствительным к регистру символов, различающим использование верхнего или нижнего регистра (прописных или строчных) символов. Если ключ -с не используется, то на выходе системного конфигуратора будет создан файл с символами верхнего регистра. Для вызова справки системного конфигуратора, наберите команду:
bld21 -help
Этот ключ позволяет получить справку о версии программы и посмотреть список возможных команд. Ключ -help работает в большинстве программ средств разработки.
Рассмотрим более подробно основные директивы системного конфигуратора и ассемблера для сигнальных процессоров семейства ADSP.
Директива SYSTEM определяет имя (название) системы и должна быть первой командой в исходном файле системной конфигурации. Директива имеет формат:
.SYSTEM имя_системы;
В языке ассемблера допускается использовать в качестве имен набор символов, содержащий:
- прописные латинские буквы от «A» до «Z»;
- строчные латинские буквы от «a» до «z»;
- знак подчеркивания «_».
Имена не должны начинаться с цифры и могут иметь длину до 32 знаков. Ниже приведено несколько примеров имен с описанием их назначения:
Main_prog
{Программный модуль на языке ассемблер}
var1
{Переменная};
buff
{Буфер данных};
met173
{Программная метка};
DAC
{Порт, отображенный в памяти}.
Эта директива определяет, какой тип процессора семейства ADSP2100 используется в системе. Данная информация передается компоновщику (редактору связей) и симулятору через выходной файл с расширением ach. После этого редактор связей в состоянии определить место расположения программы и данных в соответствии с организацией памяти для конкретного типа процессора. Эта директива принимает одну из следующих форм:
.ADSP2100; {Для ADSP-2100 и ADSP-2100A}
.ADSP2101;
.ADSP2105;
.ADSP2111;
.ADSP2150; {Для ADSP-21msp50 и ADSP-21msp55}
.ADSP2151; {Для ADSP-21msp51 и ADSP-21msp56}
.ADSP2101MV; {Для вариантной системы ADSP-2101 (ADSP-2115)}
.ADSP2101P; {Для системы со страничной памятью ADSP-2101}
.ADSP2181; {Для ADSP-2181}
и т.п.
Эта директива определяет логическое состояние вывода MMAP процессора в системе. Она принимает одну из двух форм:
.MMAP0 {Вывод MMAP=0}
.MMAP1 {Вывод MMAP=1}
Если используется MMAP0, загрузка программы из памяти начальной загрузки происходит после перезагрузки и начинается с нулевого адреса внутренней памяти программ. Если используется MMAP1, загрузка программы из памяти начальной загрузки недоступна, и внутренняя память программ отображается на верхние адреса пространства памяти программ. Когда эта директива пропускается, программа моделирования принимает по умолчанию значение MMAP1.
Директива SEG определяет специальную секцию системной памяти и описывает ее атрибуты. Не существует разделения памяти по умолчанию, поэтому необходимо определить системную память с помощью директив SEG самостоятельно. Эта информация передается редактору связей, симулятору и эмулятору через выходной файл с расширением ach. Директива SEG имеет форму:
.SEG/параметр1/параметр2/... имя_сегмента[размер];
Сегменту присваивается символьное имя. Присвоенное имя позволяет разместить фрагменты программы и данных в памяти. На языке ассемблер это достигается параметром SEG. Размер сегмента указывается внутри скобок. Эта величина интерпретируется как количество 16-разрядных слов данных или 24-разрядных слов инструкций в сегменте.
Для директивы SEG необходимо указать два обязательных параметра:
PM, или DM, или BOOT=0-7 — тип сегмента памяти;
RAM или ROM — тип памяти процессора.
Четыре других параметра являются необязательными:
ABS=адрес — абсолютный стартовый адрес;
DATA, или CODE, или DATA/CODE — тип данных, хранимых в сегменте;
EMULATOR или TARGET — распределение памяти для эмулятора;
INTERNAL — расположение в памяти процессора ADSP-2101 с вариантной памятью.
Параметры PM, DM и BOOT определяют тип сегмента памяти, который может быть отведен под память программ, память данных или одну из восьми страниц памяти начальной загрузки. Остальные параметры определяют тип памяти процессора, начальный адрес сегмента, тип данных сегмента (данные и/или программы) и распределение памяти эмулятора.
Каждый сегмент памяти должен адресоваться раздельно, поскольку, например, адрес 0x10 в памяти программ отличается от адреса 0x10 в памяти данных из-за разной длины слов этих данных. Сегмент памяти программ PM может хранить только программы (CODE), данные (DATA) или одновременно программы и данные. Если не указать ни одну из опций, по умолчанию принимается хранение программ. Для сегмента PM, содержащего программу и данные, должны быть указаны оба параметра. Параметр памяти начальной загрузки должен определять только один номер страницы, например BOOT=0. Необходимо разделять объявление сегмента для каждой страницы памяти начальной загрузки системы. Система может иметь до 8 страниц памяти начальной загрузки с номерами от 0 до 7. Каждая страница памяти начальной загрузки для ADSP-2101, ADSP-2111, ADSP21ms50 может сохранять до 2К программ и данных. Каждая страница памяти начальной загрузки для ADSP-2105, ADSP-2115 может сохранять до 1К слов. Параметр ABS для страниц памяти начальной загрузки не используется, поскольку системный конфигуратор самостоятельно присваивает соответствующие адреса.
Примеры директивы SEG:
.SEG/PM/RAM/ABS=0/CODE/DATA start [2048];
Здесь объявлен сегмент памяти программ ОЗУ, называемый start, который расположен по адресу 0. Сегмент может содержать до 2048 слов программ и данных.
.SEG/ROM/BOOT=0 boot0[1536];
Здесь объявлен сегмент памяти начальной загрузки boot0, который расположен на странице номер 0 памяти начальной загрузки (автоматически соответствует адресу 0 в памяти начальной загрузки). Длина сегмента составляет 1536 слов. Сегменты памяти начальной загрузки должны всегда быть объявлены как ROM (ПЗУ).
Директива PORT является директивой системного построителя и ассемблера. Она объявляет порт ввода/вывода отображенный в памяти процессора. Каждый порт должен иметь уникальное имя и адрес в системе. Порты могут быть определены в памяти данных или памяти программ. Для процессоров с внутренней памятью портам могут быть присвоены адреса только во внешней памяти.
Директива PORT принимает одну из двух форм:
.PORT/DM/ABS=адрес имя_порта;
или
.PORT/PM/ABS= адрес имя_порта;
Параметр DM показывает, что порт отображен в памяти данных. Параметр PM показывает, что порт отображен в памяти программ. Если параметр не задан, по умолчанию принимается DM. Порт располагается по абсолютному адресу, который определен параметром ABS, и присваивается символическому имени порта. Это имя используется в инструкциях ассемблера для доступа к порту, например:
.PORT/DM/ABS=0x0020 p1;
Объявляет имя порта p1, который отображен в памяти данных по адресу 0x0020 (шестнадцатеричный) или 32 (десятичный). Ассемблерная программа обращается к этому имени, чтобы представить его редактору связей, базирующемуся на содержании файла описания системной конфигурации с расширением ach. Отображение порта в памяти данных позволяет 16-и разрядное чтение/запись, в то время как отображение порта в памяти программ допускает 16- или 24-разрядный обмен данными.
В программе директива PORT повторяет директиву системного построителя без указания адреса порта. Например, в файле конфигурации системы для системного построителя директива PORT объявляет порты CODEC и DAC по адресам 0x3000 и 0x3800 соответственно:
.PORT/ABS=0x3000 CODEC;
.PORT/ABS=0x3800 DAC;
В программе директива PORT объявляет эти порты следующими строками:
.PORT CODEC;
.PORT DAC;
После чего в программе можно делать обращения к описанным портам с помощью команд, подобным следующим:
AR=DM(DAC);
DM(DAC)=AR;
AR=DM(CODEC);
DM(CODEC)=AR;
Директива ENDSYS должна быть последней командой в файле. Системный конфигуратор останавливает свою работу на директиве .ENDSYS. Директива имеет форму записи:
.ENDSYS;
Ниже приведен пример законченного файла example.sys для системного конфигуратора.
.SYSTEM example;
.ADSP2181;
.MMAP0;
.SEG/PM/RAM/ABS=0x0000/CODE/DATA mem_pm[0x4000];
.SEG/DM/RAM/ABS=0x0000/DATA mem_dm[0x3FE0];
.ENDSYS;
После обработки этого файла системным конфигуратором должен получиться файл example.ach с таким содержимым:
$EXAMPLE
$ADSP2181
$MMAP0
$0000 3FFF pax_PM t
$0000 3FDF dadMEM_DM t
$
Теперь рассмотрим основные директивы ассемблера, которые применяются в текстах программ для сигнальных процессоров семейства ADSP.
Директива MODULE обозначает начало программного модуля и определяет название модуля. Как было сказано ранее, программа для ADSP-2181 может состоять из одного или нескольких модулей, которые отдельно ассемблируются и затем связываются вместе. Каждый файл с исходным текстом программы может содержать только один программный модуль. Директива имеет следующую форму записи:
.МОDULЕ/ПАРАМЕТР1/ПАРАМЕТР2 ... ИМЯ_МОДУЛЯ;
Параметры могут быть следующими:
RAM или ROM — тип памяти для размещения программы;
ABS=адрес — абсолютный стартовый адрес (не используется вместе с директивой STATIC);
SEG=сегмент — размещение программы в указанном сегменте;
STATIC — статичное размещение модуля в памяти (не используется вместе с директивой ABS).
Параметр RAM (ОЗУ) или ROM (ПЗУ) определяет тип памяти процессора для размещения программы. Если тип памяти не определен, то по умолчанию принимается тип RAM.
Параметр ABS размещает коды модулей программ по определенным адресам памяти программ, что делает их не перемещаемыми компоновщиком. Это означает, что компоновщик резервирует память для модулей по указанным адресам. Модули, которые не имеют параметра ABS, распределяются компоновщиком в памяти процессора автоматически.
Параметр SEG размещает модуль в указанный сегмент памяти, который объявлен в файле системной конфигурации. Если определить оба параметра ABS и SEG и указать абсолютные адреса, которых нет в данном сегменте, компоновщик выдаст сообщение об ошибке.
Параметр STATIC сохраняет модуль в памяти программ, когда загружаются страницы начальной загрузки. Пример записи директивы:
.MODULE/RAM/ABS=0 demo;
{Определение имени demo программного модуля}
{с размещением в ОЗУ по абсолютному адресу 0}
Директива CONST определяет соответствие между именем константы и ее значением. Данная директива имеет следующую форму записи:
.CONST имя_константы1 = значение1, имя_константы2 = значение2...;
Значения констант могут быть числовыми или символьными константами. Кроме того, допускается присваивать константам выражения. В выражениях разрешаются только арифметические или логические операции над двумя или более целыми константами. В качестве знаков выражений в ассемблере допускается использовать следующие символы:
+ — сложение;
- — вычитание;
* — умножение;
/ — деление;
% — модуль (целая часть от деления);
( — левая скобка;
) — правая скобка;
& — логическое «И» (AND);
| — логическое «ИЛИ» (OR);
~ — исключающее «ИЛИ» (XOR);
<< — логический сдвиг влево;
>> — логический сдвиг вправо.
Запись чисел в программах может осуществляться в нескольких форматах. Для записи шестнадцатеричных чисел используется префикс 0x (ноль и икс) или H#. Например:
0x24FF, H#CF8A
Для восьмеричных чисел применяется префикс 0 (ноль):
0777, 0123, 07777
Двоичные числа записываются с префиксом B#:
В#01110100
Десятичные числа записываются в программе без префиксов. Т.е. по умолчанию формат числа считается десятичным:
1024, -55, 0
Пример записи арифметического выражения:
X = (29+129)-(128-48)/3
Пример логического выражения:
Y = 0x55&0x0F
Пример записи директивы:
.CONST N=1024, LEN_BUF=N/2; {Число точек отсчета = 1024, длина буфера = 512}
Директива VAR объявляет переменные и буферы переменных. Буфер переменных представляет собой набор ячеек памяти, расположенных упорядоченно друг за другом по смежным (соседним) адресам. Буфер может быть многомерным и одномерным, т.е. состоящим из нескольких или из одной переменной. Буфер должен быть объявлен до использования в программе. Буферы удобны для организации приема и передачи блочных данных и часто используются в программах.
Директива VAR имеет следующую форму записи:
.VAR/параметр1/параметр2 ... имя_буфера[длина],...;
С директивой могут использоваться следующие параметры:
PM или DM — тип памяти для размещения буфера;
RAM или ROM — тип памяти процессора;
ABS=адрес — абсолютный адрес (нельзя использовать вместе с директивой STATIC);
SEG=сегмент — размещение буфера в сегменте, объявленном системным конфигуратором;
CIRC — кольцевой буфер;
STATIC — предотвращает перезапись буфера во время загрузки начальной страницы.
Одна директива VAR позволяет объявить в одной строке длиной до 200 символов несколько буферов, разделенных запятыми.
Буферы могут быть линейными и кольцевыми. Буферы могут быть произвольной длины от единицы до размера, не превышающего объем памяти процессора. Линейный буфер размещается в памяти с любого адреса. Кольцевой буфер может быть размещен в памяти с некоторыми ограничениями, связанными с аппаратными особенностями памяти процессоров. Так, кольцевой буфер должен стартовать с базового адреса, который кратен 2 в степени n, где n — количество бит требуемых для представления длины буфера в двоичном виде. Это необходимо учитывать при создании кольцевых буферов с параметром ABS.
При многочисленном объявлении линейных буферов в одной строке редактор связей размещает их в смежных областях памяти. Если при таком объявлении используется параметр CIRC, то создается единственный кольцевой буфер, а остальные объявленные в этой строке буферы будут линейными.
Буферы могут быть размещены как в памяти программ PM, так и в памяти данных DM, по умолчанию. Тип памяти по умолчанию устанавливается в RAM для памяти DM и PM.
Параметр ABS размещает буфер с указанного стартового адреса, делая его неперемещаемым.
Параметр SEG размещает буфер в указанном сегменте памяти, который был объявлен в файле системного конфигуратора.
Параметр CIRC определяет кольцевой буфер. Буфер будет организован как линейный, если не применен атрибут CIRC.
Параметр STATIC предотвращает перезапись буфера, когда загружается страница начальной загрузки.
Пример объявления буфера:
.VAR/PM/RAM/SEG=segdata buffer[10];
Здесь линейный буфер объявлен в памяти программ RAM, который перемещаем в пределах сегмента с именем segdata. Буфер с именем buffer состоит из 10 ячеек в памяти программ. Длина буфера указывается в квадратных скобках.
Для объявления одномерных буферов, т.е. простых переменных, применяется директива VAR без указания длины буфера. Например:
.VAR/DM/RAM/ABS=0x000C xdata;
Данная директива объявляет однословную переменную с именем xdata в памяти данных RAM по шестнадцатеричному адресу 0x000C.
Ниже приведен пример объявления автоматически перемещаемого кольцевого буфера с именем buffer_c, длина которого определяется величиной константы size, объявленной директивой CONST.
.CONST size = 15;
.VAR/DM/CIRC buffer_c[size];
Ниже приведены примеры, позволяющие понять, как можно размещать циклические буферы в памяти. Следующий оператор объявляет кольцевой буфер с именем abuf из шести ячеек памяти:
.VAR/CIRC abuf[6];
Поскольку ближайшим числом больше шести и кратным двум является число 8, то базовый (стартовый) адрес буфера должен быть кратен 8. Три младших значимых разряда (МЗР) этого адреса будут равны нулю. В табл. 11.1 показано размещение этого буфера в памяти.
Таблица 11.1 Размещение одного циклического буфера в памяти сигнального процессора
Имя буфера | Элемент буфера | Двоичный адрес |
---|---|---|
… | ||
abuf | abuf[0] | XXXXXXXXXX0000 |
abuf[1] | XXXXXXXXXX0001 | |
abuf[2] | XXXXXXXXXX0010 | |
abuf[3] | XXXXXXXXXX0011 | |
abuf[4] | XXXXXXXXXX0100 | |
abuf[5] | XXXXXXXXXX0101 | |
XXXXXXXXXX0110 | ||
… |
Если в одной строке объявлены несколько буферов с параметром CIRC, то будет создан один кольцевой буфер, вмещающий в себя все буферы. Первый из этих буферов будет кольцевым, а отдельные буферы будут простыми линейными буферами. Например, следующая директива создает один кольцевой буфер abuf и два линейных буфера с именами bbuf и cbuf.
.VAR/CIRC abuf[6], bbuf[3], cbuf[5];
Для размещения всех этих буферов в памяти процессора потребуется 6+3+5=14 ячеек памяти. Поскольку первым в директиве объявлен буфер abuf, то он будет кольцевым. Ближайшим числом больше 14 и кратным двум является число 16. Поэтому базовый адрес буфера abuf должен быть кратен 16 (четыре младших значащих разряда будут равны нулю). Базовый адрес буфера bbuf будет следовать сразу за адресом последнего элемента буфера abuf. Аналогично будет размещен и буфер cbuf. Размещение всех этих буферов в памяти процессора показано в табл. 11.2.
Таблица 11.2 Размещение трех буферов в памяти процессора
Имя буфера | Элемент буфера | Двоичный адрес |
---|---|---|
… | ||
abuf | abuf[0] | XXXXXXXXXX0000 |
abuf[1] | XXXXXXXXXX0001 | |
abuf[2] | XXXXXXXXXX0010 | |
abuf[3] | XXXXXXXXXX0011 | |
abuf[4] | XXXXXXXXXX0100 | |
abuf[5] | XXXXXXXXXX0101 | |
bbuf | bbuf[0] | XXXXXXXXXX0110 |
bbuf[1] | XXXXXXXXXX0111 | |
bbuf[2] | XXXXXXXXXX1000 | |
cbuf | cbuf[0] | XXXXXXXXXX1001 |
cbuf[1] | XXXXXXXXXX1010 | |
cbuf[2] | XXXXXXXXXX1011 | |
cbuf[3] | XXXXXXXXXX1100 | |
XXXXXXXXXX1101 | ||
… |
Следующий пример показывает использование трех директив для объявления трех различных кольцевых буферов:
.VAR/CIRC abuf[6];
.VAR/CIRC bbuf[3];
.VAR/CIRC cbuf[5];
Поскольку буферы объявлены отдельно, все они будут кольцевыми и не будут объединены. Правила размещения для каждого из этих буферов такие же, как в рассмотренном выше примере для одиночного буфера abuf. Размещение этих буферов в памяти процессора показано в табл. 11.3.
Таблица 11.3 Размещение трех кольцевых буферов в памяти процессора
Имя буфера | Элемент буфера | Двоичный адрес |
---|---|---|
… | ||
abuf | abuf[0] | XXXXXXXXX00000 |
abuf[1] | XXXXXXXXX00001 | |
abuf[2] | XXXXXXXXX00010 | |
abuf[3] | XXXXXXXXX00011 | |
abuf[4] | XXXXXXXXX00100 | |
abuf[5] | XXXXXXXXX00101 | |
… | ||
bbuf | bbuf[0] | XXXXXXXXX01000 |
bbuf[1] | XXXXXXXXX01001 | |
bbuf[2] | XXXXXXXXX01010 | |
… | ||
cbuf | cbuf[0] | XXXXXXXXX10000 |
cbuf[1] | XXXXXXXXX10001 | |
cbuf[2] | XXXXXXXXX10010 | |
cbuf[3] | XXXXXXXXX10011 | |
… |
Чтобы получить доступ к буферу abuf из программы, необходимо инициализировать индексные регистры DAG и регистры длины буфера следующими инструкциями:
I0 = ^abuf; {Присвоить индексному регистру I0 базовый адрес буфера abuf}
L0 = %abuf; {Присвоить L0 длину буфера abuf}
M0 = 1; {Присвоить модификатору M0 значение наращивания
указателя адреса буфера abuf}
Далее в программе можно читать данные из буфера с помощью инструкции AR=DM(I0, L0) или записывать данные в буфер инструкцией DM(I0, L0)=AR. Естественно, вместо регистра AR могут применяться и некоторые другие регистры процессора. При размещении этих инструкций в цикле каждое обращение к буферу будет автоматически увеличивать индексный регистр адреса I0 на величину M0. При достижении регистром I0 величины, равной L0, индексный регистр I0 вновь загрузится базовым адресом буфера. Таким образом, обращения к буферу будут кольцевыми. Для линейных буферов регистр длины буфера должен быть установлен в ноль.
Данные директивы предназначены для размещения программ и данных в сегментах памяти. Директива PMSEG указывает редактору связей на необходимость разместить все программы и данные модуля в определенном сегменте памяти программ. Директива DMSEG указывает редактору связей на необходимость разместить все структуры данных модуля в определенном сегменте памяти данных. Сегменты должны быть предварительно определены в файле описания архитектуры системного конфигуратора. Директивы PMSEG и DMSEG подобно параметру SEG директив MODULE и VAR имеют следующий формат:
.PMSEG имя_сегмента_pm;
.DMSEG имя_сегмента_dm;
Обычно, чтобы расположить все программы и данные исходного модуля в определенном системным конфигуратором сегменте памяти, необходимо повторить параметр SEG в директиве MODULE и всех директивах VAR внутри модуля. Директивы PMSEG и DMSEG используются для исключения многократного повторения параметров SEG. Директивы PMSEG и DMSEG должны быть размещены в исходном файле программного модуля перед директивой MODULE. Ниже приводится пример, в котором модуль prog1, некоторые буферы и переменную var1 располагают в памяти данных в сегменте с именем seg1:
.DMSEG seg1;
.MODULE/RAM prog1;
.VAR/DM/RAМ/СIRС buf1[15];
.VAR/DM/RAM buf2[5];
.VAR/DM/RAM buf3[5];
.VAR/DM/RAM var1;
...
.ENDMOD;
Директива INIT используется для инициализации переменных и буферов в ПЗУ. Редактор связей помещает данные инициализации в файл образа памяти, который затем используется разделителем программ (splitter) при подготовке данных для записи в ПЗУ. Разделитель трансформирует части этого файла в формат, совместимый с промышленным стандартом программатора ПЗУ.
Инициализирующие значения могут быть перечислены в директиве или указаны во внешнем файле. Директива INIT может иметь одну из следующих форм:
.INIT имя_буфера: значение1, значение2,...;
.INIT имя_буфера: ^другой_буфер или %другой_буфер,...;
.INIT имя_буфера: <имя_файла>;
Операторы ^ и % используются для инициализации буфера или переменной базовым адресом, или длиной, или даже другими буферами. Любые комбинации констант, указателей адресов буфера и величин длины буфера могут быть заданы через запятую. Примеры:
.INIT x: 0x3FFF;
Данный пример инициализирует переменную x шестнадцатеричной константой 0x3FFF.
.INIT buf: 9,0,3,5,7;
Эта директива инициализирует буфер buf списком констант.
.INIT ab: ^buf;
Здесь переменная ab инициализируется указателем стартового адреса буфера buf.
Допускается инициализировать только часть данных буфера, задавая смещение его базового адреса (индекса):
.INIT buf[2]: 3,5,7;
Так, инициализирующие величины будут размещены, начиная с элемента buf[2]. Здесь инициализируются второй, третий и четвертый элементы буфера buf величинами 3, 5 и 7 соответственно.
Третья форма директивы INIT указывает имя файла, который содержит инициализирующие величины. Ассемблер устанавливает указатель на этот файл, и данные присоединяются при запуске редактора связей. Следующий пример заставляет редактор связей инициализировать буфер sin содержимым файла sinus.dat:
.INIT sin: ;
Если файл с данными находится в директории с программой, то необходимо указать в скобках только имя этого файла. Если файл находится в другом каталоге, необходимо указать путь к этому каталогу и имя файла. Например, если файл init.dat для буфера с именем buff размещен в директории C:\ADSP2181\PROG1\, тогда директива INIT должна быть применена следующим образом:
.INIT buff:
Это позволит редактору связей найти файл. Данный способ широко используется для загрузки буферов данными, выработанными другими программами, такими, например, как нахождение коэффициентов фильтра.
После того как редактор связей считает и присоединит содержимое файла, изменение данных потребует лишь выполнить повторную компоновку программы.
Переменные данных и буферов могут быть инициализированы с помощью семиразрядного ASCII кода. Следующий пример инициализирует один четырехразмерный буфер input кодами ASCII для букв A, E, F, Z. ASCII коды размещаются в семи младших разрядах 16-разрядной памяти данных или 24-разрядной памяти программ. Символы необходимо заключать в апострофы.
.INIT input: 'AEFZ'; {Инициализировать буфер символами ASCII}
Специальный синтаксис директивы INIT24, позволяет сохранять 24-х разрядные данные в памяти программ. Это дает возможность получить доступ к младшим 8-и разрядам каждого 24-х разрядного слова памяти программ при инициализации буферов данных или переменных в исходной программе.
Например, эта директива позволяет вычислить 16-разрядный адрес для переменной var:
.INIT var: ^buff + 17;
А следующая директива вычисляет 24-разрядный адрес для этой же переменной:
.INIT24 var: ^buff + 17;
Директива GLOBAL обеспечивает доступ к переменным, буферам и портам из других модулей программы. Для доступа к одной из этих структур из других модулей необходимо объявить ее директивой GLOBAL. Директива GLOBAL имеет формат:
.GLOBAL внутренний_символ 1, внутренний_символ 2,...;
Пример:
.VAR/PM/RAM buff[10]; {Создать буфер buff размерностью
10 элементов}
.GLOBAL buff; {Объявить буфер видимым из других модулей}
После того как структура объявлена глобальной, другие модули могут обращаться к ней в программе. Предварительно эта структура в них должна быть объявлена как внешняя с помощью директивы EXTERNAL.
Директива ENTRY позволяет обращаться к программным меткам в других модулях. Это позволяет использовать метку для вызова подпрограммы или межмодульных переходов. Директива ENTRY имеет формат:
.ENTRY метка 1, метка 2, ...;
Пример:
.ENTRY met; {Делает метку met доступной из других модулей}
С тех пор как метка объявлена директивой ENTRY, другие модули могут обращаться к ней, идентифицируя ее как внешнюю с помощью директивы EXTERNAL.
Директива EXTERNAL позволяет программному модулю обращаться к глобальным структурам данных (переменным, буферам и портам) и программным меткам, объявленным в других модулях.
Структура должна быть определена до этого с помощью директивы GLOBAL или ENTRY в тех модулях, где она впервые объявлена. Другие модули должны использовать директиву EXTERNAL для открытия доступа к внешним структурам. Директива имеет формат:
.EXTERNAL структура 1, структура 2, ...;
Пример:
.EXTERNAL met; {Метка находится в другом модуле}
Директива INCLUDE используется для включения других исходных файлов в файл, предназначенный для ассемблирования. Ассемблер открывает, читает и ассемблирует указанный файл, когда он встречает строку оператора INCLUDE. Ассемблированный код объединяется в выходном файле с расширением obj. Когда ассемблер достигает конца включенного файла, он возвращается в первичный исходный файл и продолжает обработку. Директива INCLUDE имеет следующий формат:
.INCLUDE <имя_файла>;
Если файл, который должен быть включен директивой INCLUDE, находится в текущей директории вместе с программой, в угловых скобках требуется указать только имя файла. Если файл находится в другом каталоге, необходимо указать полный путь к этому каталогу и имя файла (или использовать переменную среды окружения ADII). Например, если файл, который должен быть включен, называется filter.dsp и расположен в директории C:\ADSP2181\PROG2\, тогда директива INCLUDE должна быть задана следующим образом:
.INCLUDE ;
Это позволит ассемблеру найти файл. Существует и другой способ указания пути к файлу. Так можно указать путь, используя переменную среды окружения ADII с помощью команды:
Set ADII=C:\ADSP2181\PROG2
Данная команда должна записываться в файле autoexec.bat операционной системы или в командном файле для трансляции программы. Установка ADII, равной пути к каталогу, позволит ассемблеру обнаружить файл. В этом случае можно задавать имя файла без указания полного пути. Файл, включенный директивой INCLUDE, может также содержать внутри себя директиву INCLUDE. Вложение файлов директивами INCLUDE ограничивается только размером свободной оперативной памяти.
Директива INCLUDE допускает использовать принцип модульного программирования. Например, во многих случаях она используется, чтобы развить библиотеку подпрограмм или макросов, которые применяются в различных программах. Вместо того чтобы каждый раз переписывать подпрограммы, можно присоединить макробиблиотеку в ассемблерный модуль, воспользовавшись директивой INCLUDE.
Пример:
.INCLUDE <библиотека_макросов>;
Директива MACRO создает в программе макрос (набор инструкций). Макрос используется для повторения часто используемых последовательностей инструкций в исходном тексте программы. Передачей аргументов макросу реализуется подобие подпрограммы, которая может быть использована в различных программах. Допускается делать вложение макросов. Макровложения ограничены только размером свободной оперативной памяти. Вложенные макросы должны быть объявлены в определенной последовательности. Первым объявляется внутренний макрос. Внешний макрос объявляется последним. Все константы, используемые в макросах, должны быть объявлены перед объявлением макросов. Макрос определяется двумя директивами. Первая директива имеет формат:
.MACRO имя_макроса (аргумент1, аргумент2, ...);
Завершает макрос директива окончания макроса:
.ENDMACRO;
Каждый оператор внутри макроса может быть инструкцией, директивой или макровключением. Макрос вызывается по своему имени. Чтобы выполнить макрос с именем mac1, необходимо вставить в текст программы команду mac1;. Макровызов не должен содержать дополнительных операторов в строке вызова (т.е. инструкций, директив препроцессора или других макровключений).
Аргументы макроса имеют форму записи:
%n
, где n = 0, 1, 2, …, 9;
Следующий пример определяет макрос sum с тремя аргументами:
.MACRO sum(%0, %1, %2);
В коде макроса аргументы маркируются служебными символами %1, %2, %3, и т.д. При вызове макроса служебные символы замещаются величинами аргументов, переданных в макрос. При вызове макроса ему должно быть передано правильное число аргументов. Допустимые передаваемые аргументы и исключения для них приведены в табл. 11.4.
Таблица 11.4 Допустимые передаваемые в макрос аргументы и исключения для них
Аргумент | Исключение |
---|---|
Константа или арифметическое выражение | Нет |
Символы | MACRO, ENDMACRO, CONST, INCLUDE |
^буфер | ^%n |
%буфер | %%n |
Операторы ^ и % не могут быть использованы с аргументами, замещающими служебные символы в макроопределении. Тем не менее, аргументы, переданные в макрос, могут использовать эти операторы. Например: read_data(^input);
Директива LOCAL задает программные метки, используемые в макросе. Данная директива указывает ассемблеру создавать уникальную версию метки при каждом включении макроса. Это предотвращает ошибку дублирования меток в случае, когда макрос вызывается несколько раз в одном программном модуле. Директива имеет формат записи:
.LOCAL метка1, метка2, ...;
Ассемблер создает уникальные версии меток макроса, добавляя к ним номер. Это можно увидеть в файле листинга с расширением lst. Помните, что для раскрытия макросов в листинге необходимо при запуске программы компилятора asm21 добавить ключ -m.
Ниже приведен простейший пример макроса, позволяющий вставить в любое место программы несколько пустых команд пор.
.MACRO nops;
nop; nop; nop; nop; nop; nop;
.ENDMACRO;
Вызов данного макроса в программе осуществляется командой nops;. Следующий пример показывает, как создать макрос wait с одним параметром и одной локальной меткой.
MACRO wait(%0);
local loop;
cntr=%0;
do loop until ce;
loop: nop;
ENDMACRO;
Этот макрос позволяет организовать в программе функцию задержки на 100 циклов лаконичной записью:
wait(100);
Естественно, что вместо числа 100 можно вписывать любое число в разрешенном диапазоне для счетчика циклов.
Ниже приведен пример макроса, реализующий подпрограмму, которая переносит содержимое буфера данных из одной области памяти в другую.
.MACRO mem_trans (%0, %1, %2, %3, %4) {Использует 5 аргументов}
.LOCAL trans;
I4=%0; {Установить I4 как адрес источника}
I5=%1; {Установить I5 как адрес приемника}
M4=1 {Установить указатель на инкремент 1}
CNTR=%2 {Установить длину буфера}
DO trans UNTIL СЕ; {Перенос данных}
si=%3(I4, M4) {Перенос данных из типа %3 памяти}
trans: %4(I5, M4)=si; {В тип %4 памяти}
.ENDMACRO
Пример вызова этого макроса в программе может быть таким:
mem_trans (^tabl_coef, ^buf, %buf, PM, DM);
Здесь в качестве аргументов в макрос передаются адреса буферов tabl_coef и but, длина буфера but и тип памяти. Заметьте, что зарезервированные ключевые слова PM и DM переданы как аргументы.
Несколько следующих директив определяют формат файла листинга. Файл листинга с расширением lst позволяет представить результаты процесса ассемблирования. Ниже показан пример файла листинга простейшей программы. В этом файле используются следующие информационные поля:
addr — смещение от базового адреса модуля;
inst — код операции;
source line — номер строки исходного файла и код.
Release 5.11 Assembler Version 1.34s 9/19/95
D:\TEMA\IKS\PROG\TEST.DSP\DOS.dsp Mon Jun 21 17:07:36 2004 Page 1
addr inst source line
1 .module/RAM/ABS=0 example;
2 .PAGEWIDTH 70;
3 {Описание переменных}
4 .var/dm flag;
5
6 {Инициализация переменных}
7 .init flag: 0x000f;
8
9 again:
0000 02010F 10 toggle FL1; /* Инвертировать флаг */
0001 18000Fu 11 jump again; /* Зациклить */
12 .endmod;
Каждая строка в листинге этой программы имеет свой номер. При обнаружении ошибки в какой-либо строке программы компилятор выдает именно этот номер строки. Символ «u» в поле inst показывает, что код операции содержит адрес перехода.
Существует пять директив ассемблера для формирования выходного файла листинга.
Директива NEWPAGE вставляет разделители страниц. После такой директивы листинг программы продолжится с новой страницы. Данная директива, как и последующие, применяется для выделения программных фрагментов и оформления листинга программы. Пример ее использования:
.NEWPAGE;
Директива PAGELENTH регулярно вставляет разделитель страниц после указанного количества строк. Формат ее записи:
.PAGELENGTH число_строк;
Директива LEFTMARGIN оставляет левое поле с указанным числом столбцов (знакомест символов). Формат ее записи:
.LEFTMARGIN число_столбцов;
Директива INDENT осуществляет отступ строк программы в исходном коде с указанным числом столбцов. Формат ее записи:
.INDENT число_столбцов;
Директива PAGEWIDTH определяет ширину строк листинга с указанным числом столбцов. Формат ее записи:
.PAGEWIDTH число_столбцов;
Директивы NEWPAGE и PAGELENGTH могут быть использованы для нумерации страниц, в то время как директивы LEFTMARGIN, INDENT и PAGEWIDTH используются для того, чтобы сделать каждую страницу удобной для чтения. Эти директивы могут быть помещены в любом месте исходного файла.
Директива ENDMOD указывает на завершение программного модуля. Программа ассемблера останавливается, когда достигает директивы ENDMOD. Формат ее записи:
.ENDMOD;
В этой главе говорится о форматах представления данных в сигнальном процессоре при выполнении вычислительных операций.
Сигнальные процессоры используются в основном для цифровой обработки сигналов. Поэтому в программах для них часто используются арифметические команды для математической обработки чисел. Поскольку все данные в процессоре представлены в двоичном формате, в нем используется двоичная арифметика. Для лучшего понимания операций, выполняемых над числами в двоичной арифметике, необходимо хорошо представлять форматы данных этих чисел и приемы, используемые в двоичной арифметике.
С форматом представления данных в процессоре тесно связаны такие понятия, как целочисленная и плавающая арифметика. Другими словами, арифметические операции, выполняемые процессором, могут производиться с целыми или с вещественными числами. Известно, что вещественные числа имеют целую и дробную части, отделяемые друг от друга запятой. Если запятая не меняет своего положения в формате представления чисел при выполнении арифметических операций, говорят, что данные представлены в формате с фиксированной запятой. Например: 7,4×0,5=3,7. Здесь положение запятой фиксировано как для операндов, так и для результата. Недостатком такого представления является небольшой диапазон обрабатываемых чисел, определяемый количеством разрядов шины данных. Другим форматом представления данных может служить формат с плавающей запятой, где используется показатель степени. Например: 1,8×103:2,0×102=9,0×100. Здесь существенно расширяется диапазон обрабатываемых чисел за счет введения показателя степени, но снижается точность результата.
Аппаратное обеспечение процессоров семейства ADSP-21XX поддерживает 16-разрядные данные с фиксированной точкой. Тем не менее, вычислительные устройства этих процессоров обладают особыми характеристиками, которые позволяют работать с другими форматами за счет соответствующего использования программных средств. Позже будет показано, как с помощью программных средств можно реализовать операции с блоками чисел в формате с плавающей точкой.
Вспомним форматы представления данных в двоичной арифметике. Формат представления двоичного числа обычно включает в себя знак, запятую и величину. Знак показывает, является число положительным или отрицательным. Запятая отделяет целую и дробную части числа.
Знак двоичного числа может быть представлен одним битом. Как правило, ноль указывает на положительное значение числа, а единица на отрицательное его значение. Знаковый бит обычно крайний левый бит (старший бит).
Как известно, в двоичной арифметике операция вычитания заменяется сложением чисел, представленных в дополнительном коде. Положительные числа не требуется переводить в дополнительный код. Отрицательные числа в дополнительном коде изменяют свою запись.
Существует несколько форматов представления отрицательных чисел в дополнительном коде. Наиболее распространенным методом является дополнение до двух, который используется в семействе процессоров ADSP-21XX.
Преимущество формата дополнения до двух состоит в том, что он обеспечивает уникальное представление нулевого значения числа, в то время как другие форматы имеют разные представления отрицательного и положительного нуля. В формате дополнения до двух ноль считается положительным, поэтому абсолютная величина максимального отрицательного числа, которое может быть представлено определенным количеством двоичных разрядов, больше на единицу, чем величина наибольшего положительного числа.
С помощью n бит в дополнительном коде можно представлять диапазон чисел от -2n-1 до 2n-1-1, где n-1 разрядов отводится под величину, а 1 разряд под знак числа. Например, с помощью 16-разрядной шины данных в дополнительном коде можно представить числа в диапазоне от -32768 до 32767.
Существует два способа перевода отрицательного числа в формат дополнения до двух. Первый способ состоит из инвертирования всех разрядов числа и прибавления к нему единицы. Второй способ заключается в инвертировании всех разрядов числа левее младшего единичного разряда. Положительные числа не требуется переводить в дополнительный код. Ниже приведен пример перевода отрицательного 16-разрядного числа -5 по первому способу:
Двоичная запись числа 5:0000 0000 0000 0101
Инверсия бит: 1111 1111 1111 1010
Добавление 1: 0000 0000 0000 0001
---------------------------------
Результат=-5: 1111 1111 1111 1011
Пример перевода отрицательного числа -5 по второму способу:
Двоичная запись числа 5:0000 0000 0000 0101
Инверсия бит: 1111 1111 1111 1011
---------------------------------
Результат=-5: 1111 1111 1111 1011
Если сложить число -5 в дополнительном коде с числом 6, получим:
Число -5: 1111 1111 1111 1011
Число 6: 0000 0000 0000 0110
----------------------------
Число 1: 0000 0000 0000 0001
Как видим, результат получился верным.
При записи числа в дробном формате предполагается, что запятая размещается между какими-то определенными двоичными разрядами в числе. Биты слева от запятой представляют целую часть числа, а биты справа от запятой дробную часть числа. Существует два способа указания местоположения запятой, в формате с фиксированной точкой и в формате с плавающей точкой.
Формат с фиксированной точкой располагает запятую в одном, предопределенном месте. Часто это местоположение слева от всех разрядов числа, когда все разряды отводятся под дробную часть, или справа от всех разрядов, когда все разряды отводятся под целую часть. Местоположение запятой устанавливается программным способом. Некоторые арифметические операции, например умножение, могут изменять позицию запятой, поэтому может понадобиться сдвиг, чтобы сохранить число в том же формате с фиксированной точкой.
Формат с плавающей точкой состоит из двух частей, которые представляют значение мантиссы и экспоненты числа. Экспонента указывает местоположение запятой. Она может храниться вместе с мантиссой или в отдельном регистре.
Семейство процессоров ADSP-21XX поддерживают операции с числами в формате с фиксированной точкой. В настоящем изложении при указании формата числа используется запись L.R, где L — число двоичных разрядов слева от запятой, a R — число разрядов справа от запятой. Например, формат записи 1.15 указывает на то, что дробное число имеет один знаковый разряд и 15 разрядов для представления величины дробной части. Целое число имеет для такого представления формат записи 16.0. Для большинства приложений обработки сигналов предполагается использование дробных чисел в формате 1.15, поскольку умножитель и делитель процессора ADSP-2181 оптимизированы для операций с числами именно в таком формате.
Ассемблер ADSP-21XX поддерживает четыре формата представления данных при вычислениях. Формат представления данных имеет следующие условные названия: двоичная строка, двоичные числа без знака, знаковые числа в дополнительном коде и дробное представление.
Двоичная строка является самым простым форматом представления чисел в процессорах семейства ADSP-21XX. Этот формат используется при выполнении элементарных логических операций типа NOT, OR, AND и XOR. Ниже приведены весовые значения битов для этого формата записи.
21521421321221121029282726252423222120
Под двоичными числами без знака понимаются положительные числа. Диапазон их представления в процессоре в два раза больше чем у чисел со знаком, за счет самого знакового разряда. Фактически они выглядят также, как двоичная строка.
Знаковые числа можно отнести к числам в дополнительном коде. Большинство операций процессора поддерживают работу с числами в дополнительном коде. Процессоры семейства ADSP-21XX не поддерживают работу с остатками и числами в двоично-десятичном формате BCD (Binary Coded Decimal), когда каждая десятичная цифра представляется 4-битным двоичным позиционным кодом. Ниже приведены весовые значения битов для этого формата знаковых чисел.
-2021421321221121029282726252423222120
Дробное представление чисел имеет формат записи 1.15. Это означает, что 16-разрядное число имеет один знаковый разряд и пятнадцать дробных битов. Ниже представлены весовые значения битов в формате 1.15.
-202-12-22-32-42-52-62-72-82-92-102-112-122-132-142-15
Для вычисления числа, записанного в этом формате, необходимо подставить вместо цифры 2 значение соответствующего разряда (0 или 1) и сложить все полученные значения. В табл. 12.1 приведены примеры соответствия чисел записанных в шестнадцатеричном виде формата 1.15 их десятичным значениям.
Таблица 12.1 Примеры соответствия чисел, записанных в шестнадцатеричном виде формата 1.15, их десятичным значениям
Число в формате 1.15 | Число в HEX формате | Десятичное значение |
---|---|---|
0000 0000 0000 0000 | 0x0000 | +0 |
0000 0000 0000 0001 | 0x0001 | +0,000030517578125 |
0000 0000 0000 0010 | 0x0002 | +0,00006103515625 |
1000 0000 0000 0000 | 0x8000 | -1,000000 |
0111 1111 1111 1111 | 0x7FFF | +0,999969482421875 |
1111 1111 1111 1111 | 0xFFFF | -0,000030517578125 |
Как видно из таблицы, самый младший разряд эквивалентен десятичному значению 0,000030517578125. Это значение и определяет точность представления всех чисел в данном формате. Для достижения большей точности необходимо расширять разрядность числа, например программным образом размещая число в два или три 16-разрядных регистра. Аналогично можно увеличить и диапазон обрабатываемых чисел.
В табл. 12.2 приведены все возможные варианты форматов для 16-разрядного числа. Эта таблица дает наглядное представление о том, как изменяется, в зависимости от формата, диапазон представляемых чисел и их точность.
Таблица 12.2 Возможные варианты форматов для 16-разрядного числа
Формат | Количество целых разрядов | Количество дробных разрядов | Максимальное положительное значение | Максимальное отрицательное значение | Вес младшего разряда |
---|---|---|---|---|---|
1.15 | 1 | 15 | 0,999969482421875 | -1,0 | 0.000030517578125 |
2.14 | 2 | 14 | 1,999938964843750 | -2,0 | 0.000061035156250 |
3.13 | 3 | 13 | 3,999877929687500 | -4,0 | 0.000122070312500 |
4.12 | 4 | 12 | 7,999755859375000 | -8,0 | 0.000244140625000 |
5.11 | 5 | 11 | 15,999511718750000 | -16,0 | 0.000488281250000 |
6.10 | 6 | 10 | 31,999023437500000 | -32,0 | 0.000976562500000 |
7.9 | 7 | 9 | 63,998046875000000 | -64,0 | 0.001953125000000 |
8.8 | 8 | 8 | 127,996093750000000 | -128,0 | 0.003906250000000 |
9.7 | 9 | 7 | 255,992187500000000 | -256,0 | 0.007812500000000 |
10.6 | 10 | 6 | 511,984375000000000 | -512,0 | 0.015625000000000 |
11.5 | 11 | 5 | 1023,968750000000000 | -1024,0 | 0.031250000000000 |
12.4 | 12 | 4 | 2047,937500000000000 | -2048 | 0.062500000000000 |
13.3 | 13 | 3 | 4095,875000000000000 | -4096,0 | 0.125000000000000 |
14.2 | 14 | 2 | 8191,750000000000000 | -8192,0 | 0.250000000000000 |
15.1 | 15 | 1 | 16383,500000000000000 | -16384,0 | 0.500000000000000 |
16.0 | 16 | 0 | 32767,000000000000000 | -32768,0 | 1.000000000000000 |
Из таблицы видно, что чем больше двоичных разрядов отводится под целую часть числа, тем больший диапазон чисел можно охватить с помощью такого формата. Однако при этом снижается точность представления числа, т.е. его дробная часть сводится к нулю. И наоборот, чем больше двоичных разрядов отводится под дробную часть числа, тем меньший диапазон чисел можно охватить с помощью такого формата. Но при этом увеличивается точность представления числа, т.е. его дробная часть может быть очень малой величиной, составляющей менее одной миллиардной доли единицы.
В зависимости от типа решаемой задачи программист сам определяет, какой именно формат лучше использовать.
В этой главе начинается изложение системы команд сигнального процессора.
Сигнальные процессоры семейства ADSP-21XX используют единую систему команд для совместимости с устройствами с более высокой интеграцией. Процессоры ADSP-2181 имеют ряд дополнительных команд. Каждая команда может быть выполнена в процессоре за один такт. Кроме того, система команд процессора позволяет выполнять за один такт многофункциональные команды. Как было сказано ранее, язык ассемблер семейства ADSP-21XX использует высокоуровневый алгебраический синтаксис записи команд, что облегчает их понимание и запоминание. Операнды источника и приемника команды явно присутствуют в ее записи в виде мнемоники. Мнемоника основных регистров процессора с описанием их назначения приводится в табл. 13.1.
Таблица 13.1 Мнемоника основных регистров процессора с описанием их назначения
Мнемоника | Определение |
---|---|
AX0, AX1, AY0, AY1 | Входные регистры АЛУ |
AR | Регистр результата АЛУ |
AF | Регистр обратной связи АЛУ |
MX0, MX1, MY0, MY1 | Входные регистры умножителя MAC |
MR0, MR1, MR2 | Регистры результата умножителя |
MF | Регистр обратной связи умножителя |
SI | Входной регистр устройства сдвига Shifter |
SE | Регистр экспоненты устройства сдвига |
SR0, SR1 | Регистры результата устройства сдвига |
SB | Регистр блочного сдвига (используется в блочных операциях с плавающей точкой) |
PX | Регистр шинного обмена памяти программ PM с памятью данных DM |
I0–I7 | Индексные регистры генераторов адреса DAG |
M0–M7 | Регистры модификации DAG |
L0–L7 | Регистры длины DAG (для циклических буферов) |
PC | Программный счетчик |
CNTR | Счетчик циклов |
ASTAT | Регистр арифметического статуса |
MSTAT | Регистр статуса режима |
SSTAT | Регистр статуса стека |
IMASK | Регистр маски прерываний |
ICNTL | Регистр режимов управления прерываниями |
RX0, RX1 | Регистры приема данных последовательных портов SPORT |
TX0, TX1 | Регистры передачи данных последовательных портов SPORT |
Набор команд ADSP-2181 совместим вверх с набором команд ADSP-21XX, поэтому, программы, написанные для ADSP-21XX, могут выполняться на ADSP-2181 с минимальными изменениями. Процессор ADSP-2181 имеет ряд дополнительных команд. Каждая команда может быть выполнена отдельно в своем такте. Кроме того, система команд позволяет выполнять за один такт процессора многофункциональные команды.
Язык ассемблера позволяет использовать в командах арифметические или логические выражения. Список этих выражений и выполняемых с их помощью действий приведен в табл. 13.2.
Таблица 13.2 Список арифметических или логических выражений и выполняемых с их помощью действий
Выражение | Действие |
---|---|
= | Присвоение |
+ | Сложение |
- | Вычитание |
* | Умножение |
AND | Логическое «И» |
OR | Логическое «ИЛИ» |
NOT | Логическое отрицание |
XOR | Исключающее «ИЛИ» |
PASS | Пересылка |
ABS | Абсолютное значение |
Ниже приведены примеры записи некоторых команд языка ассемблера ADSP-21XX с объяснением их назначения.
DM(var1) = AX0;
Эта команда, знакомая читателям из предыдущих примеров программ, присваивает (записывает) значение регистра AX0 переменной var1, хранящейся в памяти данных.
MR = MR + MX1*MY1;
Данная команда выполняет операцию умножения и сложения. Он умножает входные значения из регистров MX1 и MY1, складывает результат умножения с текущим содержимым регистра MR (результат предыдущего умножения), а затем записывает результат в регистр MR.
При рассмотрении системы команд мы будем пользоваться условными обозначениями, приведенными в табл. 13.3. Эти обозначения позволяют сделать описание команд компактным.
Таблица 13.3 Условные обозначения системы команд
Обозначение | Описание |
---|---|
I0–I7 | Индексные регистры DAG для косвенной адресации |
M0–M7 | Регистры модификации DAG для косвенной адресации |
L0–L7 | Регистры длины DAG для циклических буферов |
Непосредственное значение данных | |
Непосредственное значение адреса (абсолютный адрес или программная метка) | |
Порядок (значение сдвига) в командах непосредственного сдвига (8-разрядное знаковое число) | |
cond | Код условия в условных командах |
term | Код условия выхода из цикла DO UNTIL |
dreg | Регистры данных (АЛУ, умножителя-накопителя или устройства сдвига) |
reg | Любой регистр (включая регистры данных «dreg») |
; | Точка с запятой, разделитель команд |
, | Запятая, разделитель нескольких операций в одной команде |
[] | Квадратные скобки, содержащие дополнительные, необязательные части команд |
[, …] | Запись нескольких операций или команд в любом порядке, разделенных запятыми |
|опция1| |опция2| |опция3| | Список опций (в команде выбирается одна из опций) |
xop | Операнд «x» |
yop | Операнд «y» |
constant | Операнд-константа |
В табл. 13.4 приведен список регистров, допустимых к использованию в командах пересылки и многофункциональных командах.
Таблица 13.4 Список регистров допустимых к использованию в командах пересылки и многофункциональных командах
Регистр | Примечание |
---|---|
Регистры данных |
|
AX0, АХ1, AY0, AY1 | – |
AR | – |
MX0, МХ1, MY0, MY1 | – |
MR0, MR1, MR2 | – |
SI, SE, SR0, SR1 | – |
Остальные регистры |
|
I0, I1, I2, I3, I4, I5, I6, I7 | – |
M0, M1, M2, M3, M4, M5, M6, M7 | – |
L0, L1, L2, L3, L4, L5, L6, L7 | – |
TX0, TX1, RX0, RX1 | – |
SB, PX | – |
ASTAT, MSTAT | – |
SSTAT | Только чтение |
IMASK, ICNTL | – |
IFC | Только запись |
CNTR | – |
OWRCNTR | Только запись |
Следует обратить внимание на то, что регистры
Команда сложить или сложить с переносом, записанная с применением приведенных выше условных сокращений, будет выглядеть следующим образом:
[IF cond] |AR| = xop + | yop |;
|AF| | C |
| yop + C |
| constant |
Она означает, что если условие cond предыдущей команды выполняется, то в регистр AR или AF будет занесен результат сложения операнда xop с операндом yop или с переносом или с константой. Разрешенные операнды xop, yop и константы constant указываются для группы команд индивидуально, поскольку они не всегда могут быть произвольными. Например, для команд АЛУ разрешено использовать в качестве операнда xop регистры; AX0, АХ1, AR, MR0, MR1, MR2, SR0, SR1. В качестве операнда yop допускается использование регистров: AY0, AY1 и AF. Причем команда DIVS не может использовать регистр AY0.
Коды условий cond с описанием их назначения приведены в табл. 13.5.
Таблица 13.5 Коды условий cond с описанием их назначения
Код условия cond оператора IF | Описание |
---|---|
EQ | Равно нулю |
NE | He равно нулю |
LT | Меньше нуля |
GE | Больше или равно нулю |
LE | Меньше или равно нулю |
GT | Больше нуля |
AC | Перенос АЛУ |
NOT AC | Нет переноса АЛУ |
AV | Переполнение АЛУ |
NOT AV | Нет переполнения АЛУ |
MV | Переполнение умножителя |
NOT MV | Нет переполнения умножителя |
NEG | Входной операнд xop отрицателен |
POS | Входной операнд xop положителен |
NOT CE | Счетчик циклов не пуст |
FLAG_IN* | Вывод FI = 1 |
NOT FLAG_IN* | Вывод FI = 0 |
* Только для команд JUMP и CALL.
Таким образом, предыдущая запись означает, что команда сложения может быть записана в одном из следующих вариантов:
IF EQ AR=AX0+AY1;
IF EQ AR=AR+AY1+C;
IF EQ AF=AX0+AY1;
IF NE AF=AX0+AY1;
AR=AX0+AF;
и т.п.
Обратите внимание на то, что оператор IF с кодом условия заключен в квадратные скобки, т.е. является не обязательным в команде и может быть исключен из нее.
При изучении системы команд мы подробнее познакомимся с вычислительными блоками процессора и форматом представления данных при выполнении различных арифметических и логических операций над ними. Это позволит лучше понять, каким образом выполняются те или иные действия в процессоре, и избежать тем самым неправильного применения команд при написании программы.
Для завершения обзора команд сигнального процессора нам остается рассмотреть многофункциональные команды и команды, которые нельзя отнести к какой-либо конкретной группе в силу того, что они выполняют различные функции. Здесь они будут называться дополнительными командами.
В многофункциональных командах реализуются свойства параллелизма архитектуры процессоров ADSP2181. Благодаря чему становится возможным одновременное выполнение в одном цикле процессора пересылки данных, операций чтения/записи в память и вычислений.
Рассмотрим многофункциональную команду выполнения операций АЛУ и MAC с одновременным чтением операндов из памяти данных и памяти программ. Пример такой команды приведен ниже:
MR=MR+MX0*MY0(SS), MX0=DM(I3,M0), MY0=PM(I7,M4);
В первой части этой команды в регистр MR (регистр результата MAC), записывается сумма его предыдущего значения и произведения текущих значений входных регистров умножителя MX0 и MY0. Здесь оба слагаемых рассматриваются в знаковом формате.
Вторая и третья части данной многофункциональной команды осуществляют выбор двух новых операндов. Один операнд выбирается из памяти данных DM по адресу, указанному в индексном регистре I3, содержимое которого затем модифицируется с помощью значения регистра модификации M0. Второй операнд выбирается из памяти программы PM по адресу, указанному в регистре I7, содержимое которого затем модифицируется величиной, содержащейся в регистре модификации M4. Значения индексов берутся из регистров генераторов адреса данных. Любой индексный регистр I одного генератора адреса данных может использоваться в паре с любым регистром модификации M того же генератора адреса данных.
Поскольку считывание данных из регистров процессора производится в начале цикла, а запись в регистры - в конце цикла, операнды, находящиеся в регистрах MX0 и MY0 в начале командного цикла, перемножаются, а затем произведение добавляется к содержимому регистра результата умножителя MR. После окончания операции умножения операнды обновляются новыми значениями, которые были выбраны в конце того же командного цикла и становятся доступными для вычислений с ними в следующем цикле. Естественно, что одновременно с вычислением можно загрузить любой регистр данных, а не только регистры умножителя/накопителя, как в приведенном выше примере.
Рассмотренная команда является, пожалуй, самой распространенной в алгоритмах цифровой обработки сигналов операцией. Выполняется она следующим образом:
• осуществляется выбор двух операндов (например, коэффициента и данных);
• производится перемножение операндов и суммирование результата умножения с полученными до этого результатами.
Вычислительной частью данной многофункциональной команды может быть любая безусловно выполняемая команда АЛУ, за исключением деления, или любая команда умножителя, кроме насыщения. Имеются и некоторые другие исключения.
Например, следующий операнд X должен загружаться в регистр MX0 из памяти данных, а новый операнд Y должен загружаться в регистр MY0 из памяти программ (для команд данного уровня несущественно, из внутренней или из внешней памяти). Кроме того, результат вычисления должен записываться в регистр результата (MR или AR), ни в коем случае не в регистр обратной связи (MF или AF).
Теперь рассмотрим команды одновременного чтения из памяти данных и памяти программ. Эти команды представляют собой разновидность описанного выше вида многофункциональных команд. Их особенностью является отсутствие вычислений. Эти команды задают только одновременную выборку двух операндов. Пример такой команды приведен ниже:
AX0=DM(I2,M0), AY0=PM(I4,M6);
В данном примере в качестве регистров-приемников используются входные регистры АЛУ. Как и в предыдущем типе многофункциональных команд, здесь операнды X должны извлекаться из памяти данных, а операнды Y - из памяти программ (и в том и в другом случае из внешней или из внутренней памяти процессоров с внутренней памятью на кристалле).
Существуют многофункциональные команды выполнения вычислений с одновременным считыванием из памяти. Если, в отличие от описанных выше многофункциональных команд, вместо двойного считывания из памяти, команда задает выполнение только одной операции считывания из одного типа памяти, то одновременно с выборкой данных из памяти можно произвести различного рода вычисления. К числу таких разрешенных вычислений относятся все операции АЛУ, за исключением деления, все операции умножителя-накопителя MAC и все операции устройства сдвига, за исключением непосредственного сдвига SHIFT IMMEDIATE. Все вычисления должны быть безусловными. Пример многофункциональной команды такого типа приведен ниже:
AR=AX0+AY0, AX0=DM(I0,M3);
В этом примере в АЛУ выполняется операция сложения, в то время как из памяти данных выбирается один операнд. Этот тип многофункциональных команд имеет те же ограничения, что и предыдущий. Значение, хранящееся в регистре AX0 и используемое как исходное в ходе вычислений, представляет собой содержимое этого регистра в начале цикла. В конце цикла в результате операции считывания данных из памяти в регистр AX0 загружается новое значение. По этой же причине регистром назначения (в этом примере AR) не может быть регистр, указанный в качестве регистра назначения при считывании данных из памяти.
Следующий пример демонстрирует выполнение вычислений с одновременной записью данных в память. Команда вычисления с одновременной записью в память имеет ту же структуру, что и команда вычисления с одновременным считыванием из памяти. Однако порядок операторов в строке данной команды меняется на обратный. Сначала выполняется запись данных в память, а затем вычисление. Вот пример такой команды:
DM(I0,M0)=AR, AR=AX0+AY0;
В приведенном примере исходным значением для записи в память (значение, содержащееся в регистре AR) является значение, хранящееся в этом регистре в начале цикла. В результате произведенного вычисления в этот же регистр загружается новое значение, которое является содержимым регистра AR в конце цикла. При постановке операторов данной команды в обратном порядке, что является неразрешенным действием, ассемблер генерирует предупреждение, подразумевающее, что в память записывается результат вычисления, а не предыдущее значение регистра, которое должно было записываться в память. Использование при этом одного и того же регистра не является обязательным, хотя именно таким образом чаще всего организуется конвейер операндов для вычисления. Для вычислительных операций с помощью таких команд действуют те же ограничения, что и для команд, рассмотренных выше. Разрешенными являются все операции АЛУ, кроме деления, все операции умножителя-накопителя MAC и все операции устройства сдвига, кроме непосредственного сдвига. Вычисления должны быть безусловными.
Наконец, рассмотрим команды выполнения вычислений с одновременной пересылкой данных между регистрами. Многофункциональные команды этого последнего типа задают выполнение пересылки данных из одного регистра данных в другой с одновременным выполнением какой-либо вычислительной операции. Большинство ограничений, рассмотренных для предыдущих типов многофункциональных команд, справедливо и для этих команд. Пример такой команды:
AR=AX0+AY0, AX0=MR2;
В данном примере операция сложения в АЛУ производится одновременно с загрузкой в регистр AX0 нового значения, взятого из регистра MR2. Как и в предыдущих примерах, для вычисления используется значение, содержащееся в регистре AX0 в начале цикла. Данные могут между всеми регистрами ввода или вывода АЛУ, умножителя-накопителя MAC и устройства сдвига, за исключением регистров обратной связи (AF и MF) и регистра SB. В рассмотренном примере новое значение загружается в регистр AX0 из регистра данных в конце цикла. Разрешенными являются все операции АЛУ, кроме деления, все операции умножителя-накопителя и все операции устройства сдвига, кроме непосредственного сдвига. Вычисления должны быть безусловными.
Ниже приводится полный список многофункциональных команд в соответствии с принятыми ранее условными обозначениями. Назначение команд приводится в тексте описания этих команд. Вычисление с пересылкой регистр-регистр:
| |, dreg = dreg;
| |
||
Вычисление с чтением из памяти:
| |,dreg = |DM(|I0|,|M0|)|;
| | | |I1| |M1| |
|| | |I2| |M2| |
| |I3| |M3| |
| |I4| |M4| |
| |I5| |M5| |
| |I6| |M6| |
| |I7| |M7| |
| |
|PM(|I4|,|M4|)|;
| |I5| |M5| |
| |I6| |M6| |
| |I7| |M7| |
Вычисление с записью в память:
|DM(|I0|,|M0|)|=dreg, | |;
| |I1| |M1| | | |
| |I2| |M2| | ||
| |I3| |M3| |
| |I4| |M4| |
| |I5| |M5| |
| |I6| |M6| |
| |I7| |M7| |
| |
|PM(|I4|,|M4|)|;
| |I5| |M5| |
| |I6| |M6| |
| |I7| |M7| |
Чтение памяти данных и программ:
|AX0|=DM(|I0|,|M0|), |AY0|=PM(|I4|,|M4|);
|AX1| |I1| |M1| |AY1| |I5| |M5|
|MX0| |I2| |M2| |MY0| |I6| |M6|
|MX1| |I3| |M3| |MY1| |I7| |M7|
Команда АЛУ или MAC с чтением памяти данных и программ:
||,|AX0|=DM(|I0|,|M0|), |AY0|=PM(|I4|,|M4|);
|| |AX1| |I1| |M1| |AY1| |I5| |M5|
|MX0| |I2| |M2| |MY0| |I6| |M6|
|MX1| |I3| |M3| |MY1| |I7| |M7|
В качестве регистров dreg могут быть использованы регистры: AX0, АХ1, AY0, AY1, AR, MX0, МХ1, MY0, MY1, MR0, MR1, MR2, SI, SE, SR0 и SR1.
Примечания:
где ¹ — должны использоваться только регистры результатов AR, MR, а не регистры обратных связей AF, MF; ² — не могут быть условными командами.
В системе команд сигнального процессора имеется несколько команд, которые нельзя отнести ни к одной из перечисленных ранее групп. Полный список этих команд в соответствии с принятыми ранее условными обозначениями приводится ниже.
Пустая команда:
NOP;
Модифицировать регистр адреса:
MODIFY(|I0|,|M0|);
|I1| |M1|
|I2| |M2|
|I3| |M3|
|I4| |M4|
|I5| |M5|
|I6| |M6|
|I7| |M7|
Управление стеками:
[|PUSH| STS] [,POP CNTR] [,POP PC] [,POP LOOP];
|POP |
Управление режимом:
|ENA| |SEC_REG | [, ...];
|DIS| |BIT_REV |
|AV_LATCH|
|AR_SAT |
|M_MODE |
|TIMER |
|G_MODE |
|INTS |
Ключевые слова означают следующие режимы:
SEC_REG — фоновый регистровый файл;
BIT_REV — бит-реверс адреса генератора DAG1;
AV_LATCH — защелка статуса переполнения АЛУ (AV);
AR_SAT — насыщение регистра AR;
M_MODE — режим размещения результата MAC;
TIMER — разрешить работу таймера;
G_MODE — разрешить режим «Go mode»;
INTS — разрешить прерывания.
NOP — это команда отсутствия операций. Она часто применяется для выполнения холостого цикла процессора в качестве задержки на один такт.
Команда MODIFY позволяет модифицировать указатель адреса в заданном регистре I на величину, которая содержится в заданном регистре M, не обращаясь при этом к памяти. Как и во всех других случаях, регистры I и M должны быть регистрами одного и того же генератора адреса данных; любой регистр I0–I3 может использоваться в комбинации с любым регистром M0–M3, а любой регистр I4–I7 — с любым регистром M4–M7.
Команды PUSH и POP позволяют непосредственно управлять содержимым стеков состояния, счетчика, счетчика программ (PC) и циклов. Некоторые из этих стеков автоматически заполняются и опустошаются во время обслуживания прерываний.
Команды управления режимом разрешают и блокируют режимы некоторых операций процессора. Эта команды управляют режимами реверсной адресации в генераторе адреса данных 1, фиксацией переполнения АЛУ, насыщением регистра результата АЛУ, выбором набора основных или теневых регистров, режимом GO для непрекращающейся работы процессора во время предоставления шины, режимом сдвига в умножителе для выполнения целочисленных или дробных вычислений и активизацией таймера. За операторами ENA или DIS может следовать через запятую сколько угодно идентификаторов. Команды ENA и DIS могут повторяться в командной строке. Кроме того, все семь режимов могут разрешаться, блокироваться или изменяться при помощи всего одной команды.