Часть II. Программирование

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

Глава 6. Первая программа

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

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

Введите текст этой программы в компьютер с помощью любого текстового редактора и сохраните в файле с именем 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 главного меню симулятора.

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

Глава 7. Порт BDMA

В этой главе описывается порт байтового прямого доступа к памяти процессора, под названием 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. Но прежде чем начать освоение данной процедуры, необходимо подробнее познакомиться с этим интерфейсом. С этого мы продолжим изучение сигнального процессора в следующей главе.

Глава 8. Порт 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, и процессор запустит загруженную программу.

Глава 9. Адаптер LPT-IDMA

Теперь, после знакомства с портом 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 без всяких переделок.

Все схемы и проект прошивки для ПЛИС прилагаются в виде соответствующих файлов на компакт-диске этой книги.

Глава 10. Менеджер памяти

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

Для того чтобы можно было использовать адаптер 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. Кроме того, можно изменить имя загружаемого файла в самой программе.

Глава 11. Директивы ассемблера

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

На примере программ, рассмотренных ранее, было показано, что программа может включать в себя строки различных директив. Директивы управляют процессом ассемблирования. Они обрабатываются препроцессором, но в отличие от инструкций не генерируют при ассемблировании код. Любая директива ассемблера начинается с точки и заканчивается точкой с запятой. Некоторые директивы имеют параметры и аргументы. Параметры следуют сразу за директивой и разделяются косой чертой, аргументы следуют после параметров. Директивы ассемблера могут содержать однострочный комментарий, который не может быть продолжен на следующей строке. Если требуется многострочный комментарий, необходимо начать новый комментарий на следующей строке. В общем случае запись директив выглядит следующим образом:

.ДИРЕКТИВА [/ПАРАМЕТР1][/ПАРАМЕТР2] ... [АРГУМЕНТ]; {Комментарий}

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

Помимо директив ассемблера, в среде разработки программ для ADSP-2181 существует несколько директив для системного конфигуратора. Ранее говорилось о том, что системный конфигуратор можно не использовать, если имеется готовый файл описания архитектуры с расширением ach для конкретного устройства, построенного на определенном типе сигнального процессора. Этот файл используется компоновщиком и программой эмулятора для того, чтобы определить размер памяти системы, тип памяти процессора (ОЗУ или ПЗУ), какая часть памяти является внутренней и внешней и какая периферия отображена в памяти. При необходимости, можно воспользоваться системным конфигуратором с целью получения нового файла описания архитектуры для нового устройства или другого типа процессора.

Перед запуском системного конфигуратора необходимо создать файл с расширением sys, содержащим директивы описания устройства (системы). Запуск системного конфигуратора осуществляется командой:

bld21 имя_файла[sys] [-c]

Здесь имя_файла — это имя исходного файла системной конфигурации. Имя файла может быть указано с расширением. Если расширение отсутствует, системный конфигуратор добавляет по умолчанию расширение sys. Системный конфигуратор создает выходной файл описания архитектуры с именем входного файла и расширением ach. Ключ -с делает системный конфигуратор чувствительным к регистру символов, различающим использование верхнего или нижнего регистра (прописных или строчных) символов. Если ключ -с не используется, то на выходе системного конфигуратора будет создан файл с символами верхнего регистра. Для вызова справки системного конфигуратора, наберите команду:

bld21 -help

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

Рассмотрим более подробно основные директивы системного конфигуратора и ассемблера для сигнальных процессоров семейства ADSP.

Директива SYSTEM

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

.SYSTEM имя_системы;

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

- прописные латинские буквы от «A» до «Z»;

- строчные латинские буквы от «a» до «z»;

- знак подчеркивания «_».

Имена не должны начинаться с цифры и могут иметь длину до 32 знаков. Ниже приведено несколько примеров имен с описанием их назначения:

Main_prog
{Программный модуль на языке ассемблер}

var1
{Переменная};

buff
{Буфер данных};

met173
{Программная метка};

DAC
{Порт, отображенный в памяти}.

Директива ADSP21XX

Эта директива определяет, какой тип процессора семейства 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

Эта директива определяет логическое состояние вывода MMAP процессора в системе. Она принимает одну из двух форм:

.MMAP0 {Вывод MMAP=0}

.MMAP1 {Вывод MMAP=1}

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

Директива SEG

Директива 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 принимает одну из двух форм:

.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. Директива имеет форму записи:

.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

Директива 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 определяет соответствие между именем константы и ее значением. Данная директива имеет следующую форму записи:

.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 имеет следующую форму записи:

.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 указывает редактору связей на необходимость разместить все структуры данных модуля в определенном сегменте памяти данных. Сегменты должны быть предварительно определены в файле описания архитектуры системного конфигуратора. Директивы 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

Директива 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 имеет формат:

.GLOBAL внутренний_символ 1, внутренний_символ 2,...;

Пример:

.VAR/PM/RAM buff[10]; {Создать буфер buff размерностью

 10 элементов}

.GLOBAL buff; {Объявить буфер видимым из других модулей}

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

Директива ENTRY

Директива ENTRY позволяет обращаться к программным меткам в других модулях. Это позволяет использовать метку для вызова подпрограммы или межмодульных переходов. Директива ENTRY имеет формат:

.ENTRY метка 1, метка 2, ...;

Пример:

.ENTRY met; {Делает метку met доступной из других модулей}

С тех пор как метка объявлена директивой ENTRY, другие модули могут обращаться к ней, идентифицируя ее как внешнюю с помощью директивы EXTERNAL.

Директива EXTERNAL

Директива EXTERNAL позволяет программному модулю обращаться к глобальным структурам данных (переменным, буферам и портам) и программным меткам, объявленным в других модулях.

Структура должна быть определена до этого с помощью директивы GLOBAL или ENTRY в тех модулях, где она впервые объявлена. Другие модули должны использовать директиву EXTERNAL для открытия доступа к внешним структурам. Директива имеет формат:

.EXTERNAL структура 1, структура 2, ...;

Пример:

.EXTERNAL met; {Метка находится в другом модуле}

Директива INCLUDE

Директива 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 создает в программе макрос (набор инструкций). Макрос используется для повторения часто используемых последовательностей инструкций в исходном тексте программы. Передачей аргументов макросу реализуется подобие подпрограммы, которая может быть использована в различных программах. Допускается делать вложение макросов. Макровложения ограничены только размером свободной оперативной памяти. Вложенные макросы должны быть объявлены в определенной последовательности. Первым объявляется внутренний макрос. Внешний макрос объявляется последним. Все константы, используемые в макросах, должны быть объявлены перед объявлением макросов. Макрос определяется двумя директивами. Первая директива имеет формат:

.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 задает программные метки, используемые в макросе. Данная директива указывает ассемблеру создавать уникальную версию метки при каждом включении макроса. Это предотвращает ошибку дублирования меток в случае, когда макрос вызывается несколько раз в одном программном модуле. Директива имеет формат записи:

.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 вставляет разделители страниц. После такой директивы листинг программы продолжится с новой страницы. Данная директива, как и последующие, применяется для выделения программных фрагментов и оформления листинга программы. Пример ее использования:

.NEWPAGE;

Директива PAGELENTH

Директива PAGELENTH регулярно вставляет разделитель страниц после указанного количества строк. Формат ее записи:

.PAGELENGTH число_строк;

Директива LEFTMARGIN

Директива LEFTMARGIN оставляет левое поле с указанным числом столбцов (знакомест символов). Формат ее записи:

.LEFTMARGIN число_столбцов;

Директива INDENT

Директива INDENT осуществляет отступ строк программы в исходном коде с указанным числом столбцов. Формат ее записи:

.INDENT число_столбцов;

Директива PAGEWIDTH

Директива PAGEWIDTH определяет ширину строк листинга с указанным числом столбцов. Формат ее записи:

.PAGEWIDTH число_столбцов;

Директивы NEWPAGE и PAGELENGTH могут быть использованы для нумерации страниц, в то время как директивы LEFTMARGIN, INDENT и PAGEWIDTH используются для того, чтобы сделать каждую страницу удобной для чтения. Эти директивы могут быть помещены в любом месте исходного файла.

Директива ENDMOD

Директива ENDMOD указывает на завершение программного модуля. Программа ассемблера останавливается, когда достигает директивы ENDMOD. Формат ее записи:

.ENDMOD;

Глава 12. Форматы данных

В этой главе говорится о форматах представления данных в сигнальном процессоре при выполнении вычислительных операций.

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

С форматом представления данных в процессоре тесно связаны такие понятия, как целочисленная и плавающая арифметика. Другими словами, арифметические операции, выполняемые процессором, могут производиться с целыми или с вещественными числами. Известно, что вещественные числа имеют целую и дробную части, отделяемые друг от друга запятой. Если запятая не меняет своего положения в формате представления чисел при выполнении арифметических операций, говорят, что данные представлены в формате с фиксированной запятой. Например: 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

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

В зависимости от типа решаемой задачи программист сам определяет, какой именно формат лучше использовать.

Глава 13. Система команд

В этой главе начинается изложение системы команд сигнального процессора.

Сигнальные процессоры семейства 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.

Примечания:

¹ — любая команда АЛУ (исключая DIVS и DIVQ);

¹ — любая команда умножителя/накопителя;

² — любая команда устройства сдвига (исключая непосредственный сдвиг);

где ¹ — должны использоваться только регистры результатов 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 могут повторяться в командной строке. Кроме того, все семь режимов могут разрешаться, блокироваться или изменяться при помощи всего одной команды.

Загрузка...