3584 МИКРОПРОЦЕССОРНЫЕ СИСТЕМЫ

ЛАБОРАТОРНАЯ РАБОТА № 1

Архитектура процессора NeuroMatrix® NM6403

и его функциональные возможности

Цель работы: ознакомление с общей архитектурой, функциональными особеннос­тями современного отечественного процессора NeuroMatrix® NM6403 (Л1879ВМ1) и его отличиями от других процессоров этого класса.

 

1. ПОРЯДОК ВЫПОЛНЕНИЯ РАБОТЫ

По материалам разделов 2 и 3 изучить структуру процессора и основных блоков, структуру микрокоманд (МК), порядок ввода данных, кодирование  и выполнение МК.

По материалам раздела 4 ознакомиться с порядком ввода микропрограмм и исходных данных.

Выполнить упражнения из раздела 5 по указанию преподавателя.

2. СТРУКТУРА ПРОЦЕССОРА

2.1. Внешний интерфейс процессора

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

Процессор NM6403 имеет четыре канала, по которым он может обмениваться данными с внешними устройствами (см. рис. 1).

 

Рис. 1. Схема каналов доступа к данным со стороны процессора NM6403

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

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

2.2. Общее описание внутренней структуры процессора

Процессор NM6403 имеет следующие внутренние блоки (рис. 2):

  • скалярный процессор (СП);
  • векторный процессор (ВП);
  • два DMA-сопроцессора, управляющие работой коммуникационных портов;
  • два таймера;
  • регистры управления интерфейсом доступа к внешней памяти.

 

Рис. 2. Блочная структура процессора NeuroMatrix NM6403

Процессор NM6403 имеет 64-разрядный интерфейс работы с внешней памятью. За одно обращение к памяти он позволяет записать или прочитать одно 64-разрядное число по каждой из шин.

2.2.1. Описание основных элементов скалярного процессора

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

СП позволяет осуществлять следующие операции:

- различные виды адресации с модификацией адресных регистров;

- чтение/запись в память как 32-разрядных слов, так и пар слов, образующих 64-разрядное число;

- все виды арифметических и логических операций над регистрами общего назначения с модификацией и без модификации флагов состояния;

- различные типы сдвигов, в том числе на произвольное количество битов;

- условные и безусловные переходы, в том числе отложенные переходы;

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

- пошаговое умножение;

- управление таймерами;

- настройка регистров управления доступом к внешней памяти на тип, используемый в конкретном устройстве;

- управление векторным процессором путем задания его конфигурации.

2.2.2 Описание основных элементов векторного процессора

ВП представляет собой специализированный матричный узел – операционное устройство (ОУ) для выполнения операций умножения с накоплением, арифметических и логических операций, маскирования, функций активации над векторами и матрицами.

Под векторами понимаются одномерные массивы однородных данных, расположенные в памяти в виде непрерывного блока. Матрица - это массив векторов.

Разрядность всех узлов ВП составляет 64 бита. ВП осуществляет обработку целочисленных данных, которые упакованы в 64-разрядные слова с помощью простой конкатенации (см. рис. 3). В общем случае слово упакованных данных представляет собой вектор D = {Dk...D1}, содержащий k элементов, суммарная разрядность которых составляет 64 бита. Причем в одном слове D могут быть упакованы данные, имеющие разную разрядность. Количество элементов k, упакованных в одном слове, зависит от их разрядностей и может принимать целочисленное значение в диапазоне от 1 до 64.

 

Рис. 3. Формат слова упакованных векторных данных

 

В состав ВП входят следующие компоненты.

Рабочая матрица - операционный узел, в котором осуществляются операции умножения с накоплением. С рабочей матрицей связана пара регистров, которые определяют её разбиение на столбцы и строки. Описание функционирования рабочей матрицы приведено в п. 2.3.1.

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

Векторное АЛУ - устройство, позволяющее совершать стандартный набор арифметических и логических операций над парами 64-разрядных слов, каждое из которых разделено на малоразрядные элементы. При арифметических операциях в случае переполнения внутри диапазона, отведенного под один малоразрядный элемент, блокируется перенос битов в соседний элемент. Более подробное описание работы векторного АЛУ приведено в п. 2.3.2.

Буфер весовых коэффициентов (wfifo) - очередь глубиной в 32 64-разрядных слова, организованная по принципу FIFO. В нее подгружаются весовые коэффициенты, и в ней они хранятся, прежде чем происходит их загрузка в теневую матрицу. Загрузка данных и их выгрузка из wfifo могут осуществляться по частям, то есть, например, в wfifo можно загрузить сначала 8 слов, а затем еще 24, но так, чтобы не произошло переполнения.

Буфер внутренней памяти (ram) - очередь глубиной в 32 64-разрядных слова, организованная по принципу FIFO. Используется как один из аргументов в операциях умножения с накоплением, а также в операциях на векторном АЛУ. В ram может быть загружено от 1 до 32 слов. Буфер может использоваться многократно, однако в операциях должны участвовать все данные, хранящиеся в ram. Не допускается использование только части хранящихся там данных;

Псевдобуфер шины данных (data) используется для обозначения данных, находящихся на шине данных непосредственно в процессе их загрузки из памяти в ВП. Позволяет обрабатывать данные на проходе. Псевдобуфер имеет глубину в 32 64-разрядных слова и организован по принципу FIFO. Используется как один из аргументов в операциях умножения с накоплением, а также в операциях на векторном АЛУ.

Буфер накопления результатов (afifo) - очередь глубиной в 32 64-разрядных слова, организованная по принципу FIFO. Результат любой операции на ВП сохраняется в afifo. Может также использоваться как один из аргументов в операциях умножения с накоплением и в операциях на векторном АЛУ. Для того чтобы получить доступ к результатам вычислений на векторном процессоре, хранящимся в afifo, необходимо выгрузить их в память.

Векторный регистр (vr) - 64-разрядный регистр, используемый в качестве определенного операнда в операции умножения с накоплением. Можно представить его, как буфер, состоящий из заданного количества одинаковых слов. Может быть загружен из СП. Доступен только на запись.

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

Устройства, обеспечивающие выполнение операции маскирования над входными векторами. Более подробное описание выполнения операции маскирования дано в разделе п. 2.3.3.

Устройство, обеспечивающее циклический сдвиг вправо на один бит слова данных, подаваемого на вход X рабочей матрицы в операции взвешенного суммирования. Более подробное описание работы устройства циклического сдвига дано в п. 2.3.5.

2.3. Основные вычислительные блоки векторного процессора

Основные блоки векторного процессора определяют возможности процессора NM6403 при выполнении следующих операций:

  • умножение с накоплением, называемое также взвешенным суммированием;
  • арифметические и логические операции на векторном АЛУ;
  • маскирование данных;
  • функции активации;
  • сдвиг операнда Х при выполнении взвешенного суммирования.

Помимо этого, приведен порядок выполнения преобразований над данными, если эти преобразования заданы в одной векторной команде.

2.3.1. Взвешенное суммирование

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

Математически операция взвешенного суммирования, выполняемая на операционном узле ВП, записывается следующим образом:

 

где          Zi - элемент выходного вектора Xj - элемент данных, поступающих на вход X операционного узла ВП, Yi - частичная сумма, накопленная на предыдущем шаге взвешенного суммирования, Wij - весовой коэффициент, расположенный в соответствующей ячейке рабочей матрицы процессора, M - количество столбцов и N - количество строк рабочей матрицы соответственно.

Рабочая матрица имеет два входа X и Y (см. рис. 5). На эти входы подаются данные, расположенные во внешней памяти либо во внутренних буферах ram и afifo, работающих по принципу FIFO. Данные из буферов или из памяти могут быть поданы как на вход X, так и на вход Y. То есть, например, вектор 64-разрядных слов, хранящийся в ram, может быть передан на обработку в операционный узел через вход X и/или Y. Для управления потоком данных из внешней памяти используется логический буфер data. В качестве входа Y может также быть использован векторный регистр vr.  Кроме этого, в качестве входов могут выступать так называемые "нулевые" устройства, что означает, что данные на вход не поступают.

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

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

 

 

Рис. 4. Схематичное представление операции взвешенного суммирования

Разбиение матрицы на строки определяется регистром sb2. Этот же регистр определяет разбиение 64-разрядных слов входных данных, поступающих на вход X. В sb2 предварительно записывается слово, определяющее границы разбиения.

Разбиение рабочей матрицы на столбцы задается регистром nb2. Он же определяет разбиение 64-разрядных данных на входе Y и разрядности результатов вычислений, содержащихся в буфере afifo.

Рис. 5 показывает, какие действия может выполнить ВП с помощью одной процессорной инструкции. Допустим, в буфер данных ram было предварительно загружено из памяти 32 длинных слова. При операции взвешенного суммирования из памяти по очереди считываются слова входных данных, каждое из которых направляется на вход X операционного узла ВП. Параллельно из буфера ram считывается очередное слово и направляется на вход Y. Каждый элемент, составляющий слово на входе X, умножается на весовой коэффициент, находящийся в соответствующей ячейке рабочей матрицы, результаты умножения складываются в пределах столбца, а затем к ним добавляется значение элемента, находящегося на соответствующей позиции в слове, поступившем на вход Y. Результат операции записывается в буфер afifo.

Данные, находящиеся в буферах FIFO векторного процессора, хранятся в 64-разрядных словах. Для них на этом этапе разбиение на элементы не определено. Такое разбиение появляется только тогда, когда они поступают на вход X или Y рабочей матрицы или на вход векторного АЛУ. В зависимости от того, на какой вход, Х или Y, поступают данные, они делятся на элементы тем или иным образом.

 

Рис. 5. Схема выполнения операции взвешенного суммирования на ВП

Например, если разбиение входа X – 8 бит, то есть каждое слово представляется как 8 элементов по 8 бит, а разбиение Y – 16 бит, то в зависимости от того, куда будут направлены данные, хранящиеся в ram, на вход X или Y, они будут трактоваться либо как массив 8- либо 16- битных элементов.

2.3.2. Выполнение операций на векторном АЛУ

Арифметические и логические операции на векторном АЛУ выполняются над наборами 64-разрядных слов, подаваемых на входы X и Y операционного узла ВП. Эти наборы подаются на входы из буферов ram, afifo либо из памяти (data). Данные, хранящиеся, например, в ram, могут быть переданы на обработку в операционное устройство как через вход X, так и через вход Y. Кроме этого, в качестве входов могут выступать так называемые "нулевые" устройства, что означает, что данные на вход не поступают. Результат вычислений всегда попадает в afifo.

Входы X и Y векторного АЛУ являются равноправными. Разбиение данных, поступающих на эти входы, на элементы определяется регистром nb2. Регистр sb2 не оказывает никакого влияния на вычисления в векторном АЛУ. В этом состоит особенность работы векторного АЛУ по сравнению с рабочей матрицей.

Операции на векторном АЛУ выполняются с учетом разбиения входных данных на элементы (см. рис. 6). Это означает, что в местах разбиения 64-разрядных слов на элементы ставятся "перегородки", которые в случае переполнения блокируют перенос старшего бита в соседнее поле, занимаемое другим элементом, а также препятствуют распространению знака за пределы границ элемента. При блокировках переноса переносимый бит теряется.

 

Рис. 6. Выполнение вычислений на векторном АЛУ

 

На рис. 7 приведен пример сложения двух 64-разрядных слов, разбитых на 8 элементов по 8 бит каждый, на векторном АЛУ процессора NM6403.

 

Рис. 7. Сложение двух 64-разрядных слов на векторном АЛУ

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

2.3.3. Операция маскирования

Для выполнения маскирования в операционном узле ВП существует специальное устройство. Оно имеет три входа и два выхода (см. рис. 8). Данные, подаваемые на входы Х и Y рабочей матрицы или векторного АЛУ, сначала проходят через это устройство и только после этого попадают в рабочую матрицу или на векторное АЛУ. Если код векторной ко­манды не включает операцию маскирования, то данные, поступающие на входы X и Y уст­ройства, пропускаются на выход без изменений. Если в коде команды присутствует запрос на маскирование, то используется также третий вход в устройство, на который подается вектор масок. Он может считываться из памяти (data), либо из буферов ram или afifo.

Векторные команды маскирования, как и все остальные векторные команды процессора NM6403, выполняются от 1 до 32 тактов. На каждом такте на входы X, Y и на вход вектора масок подаётся по одному 64-разрядному слову.

Маскирование с векторным умножением

В системе команд процессора существует векторная команда, например:

маска      X             Y

rep 32 data = [ar0++] with vsum ram, data, afifo;

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

ü     побитовая операция AND слова со входа X и слова маски: X and MASK. Эта операция оставляет в результирующем векторе X только те биты, которые в маске были равны единице;

ü     побитовая операция AND слова со входа Y и инвертированного слова маски: Y and not MASK. Эта операция оставляет в результирующем векторе Y только те биты, которые в маске были равны нулю;

ü     выполнение операции взвешенного суммирования над маскированными данными.

 

Рис. 8. Положение устройства маскирования в ОУ ВП

Логическое маскирование

В случае совмещения операции маскирования с обработкой на векторном АЛУ, задаваемой командой:

маска        X            Y

rep 32 data = [ar0++] with mask ram, data, afifo;

над данными на каждом шаге выполняется следующее преобразование:

(X and MASK) or (Y and not MASK)

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

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

 

 

Рис. 9. Выполнение операции маскирования

 

2.3.4. Обработка данных функцией активации

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

Всего существует два типа функций активации:

ü     пороговая функция (см. рис. 10,а);

ü     функция насыщения (см. рис. 10,б).

Рис. 10. Типы встроенных функций активации процессора NM6403

 

Основную роль в управлении функциями активации играют регистры f1cr и f2cr.

Активация выполняется перед тем, как данные попадут в рабочую матрицу или на векторное АЛУ. Ей могут быть подвергнуты данные, поступающие либо на вход X, либо на вход Y, либо на оба входа сразу.

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

Арифметическая активация

Функция насыщения выполняется только в составе команды взвешенного суммирования (в том числе и с маскированием) и в арифметических командах на векторном АЛУ. Действие, которое она выполняет, называется также арифметической активацией. Второе название удобно, поскольку позволяет очертить круг векторных команд, совместно с которыми может выполняться функция насыщения. Это векторные команды процессора, содержащие арифметические действия.

Логическая активация

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


Регистры
f1cr и f2cr

Регистр f1cr задает пороги функций активации для данных, поступающих на вход X ВП, а f2cr - для данных входа Y.

Регистры f1cr и f2cr делят 64-разрядные слова входных данных на элементы. Над каждым элементом выполняется функция активации, то есть активации подвергаются одновременно и независимо все элементы, составляющие длинное слово.

Разбиение на элементы, записанное в f1cr и f2cr, может не совпадать с тем, которое задается на входах X и Y регистрами sb2 и nb2 соответственно при операциях умножения с накоплением или регистром nb2 при выполнении вычислений на векторном АЛУ. Однако в большинстве случаев программист устанавливает разбиение в f1cr и f2cr таковым, чтобы оно совпадало с разбиением, заданным регистрами nb2, sb2.

Разбиение данных на элементы с помощью f1cr (f2cr)

Далее в описании будет использоваться регистр f1cr, однако все сказанное ниже, если это специально не оговорено, справедливо и для f2cr.

Разбиение на элементы задается в регистре f1cr путем перепада значений соседних битов с 1 в 0 при движении от младших битов к старшим (см. рис. 11).

 

Рис. 11. Разбиение 64-битного слова на элементы с помощью f1cr (f2cr)

Поскольку деление на элементы происходит только при переходе от 1 к 0, наименьший размер элемента составляет 2 бита (у элемента самый младший бит должен быть равен 0, а самый старший - 1).

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

Использование f1cr (f2cr) в функции насыщения (арифметическая активация)

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

Имеется в виду, что есть регистр f1cr и есть входные данные, поступающие на вход X ВП. Слова входных данных, так же как и f1cr, имеют разрядность 64 бита, то есть каждому биту слова данных можно сопоставить бит регистра f1cr.

Как уже было сказано, переход от 1 к 0 в f1cr ведет к разделению слова входных данных на элементы. Дальнейшие рассуждения относятся к битам регистра f1cr, соответствующим полю элемента данных.

Самому старшему биту поля элемента всегда соответствует единица в f1cr (см. рис. 11). Количество единиц в f1cr вправо от него определяет порог насыщения.

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

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

f1cr:                                       ...11100000... ← верхний порог равен 31 (0x1F)

поле данных: ...00010110... ← значение элемента 22 (0x16)

после активации: ...00010110... ← значение элемента 22 (0x16)

Значение элемента данных равно 22, что меньше, чем 31. Поэтому данные будут переданы на выход функции активации без изменений.

Если все три старших бита значения элемента данных равны единице, то:

f1cr:                                       ...11100000... ← нижний порог равен -32 (0xE0)

поле данных: ...11110110... ← значение элемента -10 (0xF6)

после активации: ...11110110... ← значение элемента -10 (0xF6)

Значение элемента данных равно -10, что больше, чем -32. В этом случае данные также будут переданы на выход функции активации без изменений.

Когда биты поля элемента, соответствующие единичным битам f1cr, имеют неодинаковые значения, различаются два случая. Первый: старший бит поля элемента равен 0 (положительное значение), второй: старший бит поля элемента равен 1 (отрицательное значение).

В первом случае арифметическая функция активации осуществляет следующее преобразование:

f1cr:                                       ...11100000... ← верхний порог равен 31 (0x1F)

поле данных: ...01010110... ← значение элемента 86 (0x56)

после активации: ...00011111... ← значение элемента 31 (0x1F)

Значение элемента данных равно 86, что больше, чем 31. При этом функция активации заменит значение элемента на положительное пороговое.

Во втором случае арифметическая функция активации осуществляет следующее преобразование:

f1cr:                                       ...11100000... ← нижний порог равен -32 (0xE0)

поле данных: ...11010110... ← значение элемента -42 (0xD6)

после активации: ...11100000... ← значение элемента -32 (0xE0)

Значение элемента данных равно -42, что меньше, чем -32. В этом случае на выходе функции активации значение будет заменено на отрицательное пороговое.

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

Использование f1cr (f2cr) в пороговой функции (логическая активация).

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

Имеется в виду, что есть регистр f1cr и есть входные данные, поступающие на вход X ВП. Слова входных данных, так же как и f1cr, имеют разрядность 64 бита, то есть каждому биту слова данных можно сопоставить бит регистра f1cr. Переход от 1 к 0 в f1cr ведет к разделению слова входных данных на элементы.

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

f1cr:                                       ...0|10000000|1...

поле данных: .....00010110... ← значение 22 (0x16)

после активации: .....00000000... ← результат 0.

Значение элемента данных равно 22 (положительное). Поэтому результат обработки логической функцией активации равен 0.

Для отрицательных чисел:

f1cr:                                       ...0|10000000|1...

поле данных: .....10010110... ← значение -106 (0x96)

после активации: .....11111111... ← результат -1 (0хFF).

Значение элемента данных равно -106 (отрицательное). Поэтому результат обработки логической функцией активации равен -1.

Логическая функция активации (пороговая функция) переводит неотрицательные значения элементов полей данных в 0, отрицательные в –1.

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

Таблица. 1

Разрядность элемента

Количество элементов в слове

Значение константы в f1cr (f2cr)

64 бита

1

f1crh = 80000000h

32 бита

2

80000000h

21 бит

3

4000020000100000hl

16 бит

4

80008000h

10 бит

6

0802008020080200hl

8 бит

8

80808080h

4 бита

16

88888888h

2 бита

32

AAAAAAAAh

2.3.5. Циклический сдвиг вправо операнда Х при взвешенном суммировании

Данные, поступающие на вход Х при операции взвешенного суммирования или маскирования, могут быть подвергнуты циклическому смещению на один бит вправо. Это смещение необходимо для того, чтобы компенсировать неудобства, которые возникают из-за невозможности разбиения данных, подаваемых на вход X рабочей матрицы, на элементы нечетной разрядности, в частности на элементы разрядности 1 бит. Так как регистр sb2 не позволяет задать побитовое разбиение данных на входе Х, необходим дополнительный преобразователь для того, чтобы обеспечить обработку старших битов при двухбитовом разбиении. Это преобразование обеспечивает циклический сдвиг вправо на один бит. В этом случае старшие биты двухбитовых слов становятся младшими.

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

Циклический сдвиг вправо осуществляется на один бит. При этом разбиение на элементы не учитывается, то есть сдвигается целиком все слово. Его младший бит становится самым старшим (63-м), первый - нулевым, второй - первым и т.д.

Циклический сдвиг работает при любом разбиении матрицы на строки, влияет только на данные, поступающие на вход Х, встречается только в командах взвешенного суммирования и логического маскирования. Активизируется устройство циклического сдвига при использовании в векторной команде ключевого слова shift, расположенного перед операндом X.

2.3.6. Порядок выполнения преобразований над данными на ВП

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

 

Рис. 12. Порядок выполнения преобразований над данными в ВП

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

Примечание. На Рис. 12, b регистр сдвига может использоваться только в операции логического маскирования (см. 2.3.3. Операция маскирования).

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

3. Описание и порядок использования программы NMCalculator

3.1. Общие сведения о программе

NMCalculator – интерактивный помощник разработчикам программного обеспечения для процессора Л1879ВМ1. Программа может быть полезна и программистам, осваивающим язык ассемблера и методы программирования векторного узла процессора. Данное приложение является, в некотором смысле, программной моделью векторного узла. В программе нет полного соответствия реальным аппаратным ресурсам процессора Л1879ВМ1, однако, такое абстрагирование упрощает процесс разработки программ, позволяя программисту сосредоточиться на решении основной задачи. NMCalculator предоставляет разработчику возможность сформировать векторную команду и сразу же получить результат. Программа способна эмулировать векторные команды, в которых задействуются векторное АЛУ или рабочая матрица (операции взвешенного суммирования). Пользовательский интерфейс программы состоит из двух частей:

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

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

3.2. Формирование векторной команды

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

Рекомендуется соблюдать следующий порядок формирования команды.

1. Определить операционное устройство: либо рабочая матрица (операция взвешенного суммирования), либо векторное АЛУ. В первом случае – установить флажок "Vsum".

2. Если необходимо, то подключить регистры маскирования и активации. При работе с векторным АЛУ задать требуемую арифметико-логическую операцию в группе переключателей "Operations".

3. Задать необходимое разбиение матрицы.

4. Ввести входные значения.

Действия пользователя по формированию команды отображаются в поле "Command". Обозначения, принятые при выводе команды, не соответствуют реальным устройствам про­цессора Л1879ВМ1. Например, команда маскирования может выглядеть следующим образом:

rep 1 with mask M, shift X, activate Y

В реальной команде вместо операндов M, X и Y необходимо использовать существующие аппаратные ресурсы (data, ram, afifo и т. д.).

3.3. Разбиение рабочей матрицы и запись весовых коэффициентов

Для разбиения матрицы на строки и столбцы можно либо воспользоваться выпадающим списком стандартных разбиений (nbl, nbh, sbl, sbh), либо вводить константы разбиения в окна ввода, либо разбить матрицу в визуализаторе с помощью кнопок-переключателей. Все изменения в матрице отображаются в визуализаторе и в окнах ввода. Для удобства 64-разрядные регистры разбиения (nb1 и sb) разделены на младшие и старшие части. При необходимости, можно скопировать значение младшей части регистра в старшую часть (кнопка "->"). Следует учитывать, что при работе с арифметико-логическими командами (в отличие от команд взвешенного суммирования) разбиение можно задать только в регистрах nbl и nbh, то есть векторы на входах X и Y будут иметь одинаковое разбиение. В операции взвешенного суммирования разбиение на столбцы (nbl, nbh) может отличаться от разбиения на строки (sbl, sbh).  Весовые коэффициенты можно задать щелчком мыши в визуализаторе матрицы, устанавливая/сбрасывая необходимые биты. Альтерна­тивный способ – вызов окна диалога из всплывающего меню. В этом случае можно задать значение всей строке либо одному весовому коэффициенту. В окне диалога предусмотрен ввод шестнадцатеричных и десятичных значений. Для просмотра всех введённых весов можно воспользоваться генератором кода (кнопка "Code"). Весовые коэффициенты будут записаны в секцию данных в массиве Weights.

3.4. Установка входных значений

Входные значения (Entry X, Entry Y), маску (Mask X, Mask Y), пороги для функции активации можно задать щелчком мыши в визуализаторе матрицы на соответствующих регистрах, устанавливая/сбрасывая необходимые биты либо вводя значения в соответствующие окна ввода.

3.5. Генератор кода

Сформированная пользователем команда, входные значения, константы разбиения и другая введённая пользователем информация используются для генерирования листинга ассемблерного кода. В листинге определены секция данных (значения, определённые пользователем) и секция кода – пример реализации функции с вызовом векторной команды. Генератор кода включается кнопкой "Code".

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

4. Порядок выполнения работы

1. Запустите программу NMCalculator, разверните её окно на весь экран и выберите в главном меню: Setup->Step of grid->5 для 15” монитора или Setup->Step of grid->6 для 17” монитора.

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

ü     Выберите операцию взвешенного суммирования, установив флажок на индикаторе vsum.

ü     Разбейте рабочую матрицу на 16 строк по 4 бита. Для этого выберите из выпадающего списка поля sbl нужное значение; оно будет автоматически скопировано и в старшую часть этого регистра.

ü     В результате такого разбиения получилась матрица из 16 ячеек. Теперь в каждую ячейку запишите весовой коэффициент, равный 1. Для этого, щёлкая правой кнопкой мыши на ячейке, выберите пункт всплывающего меню Set element… и в появившемся окне введите 1.

На этом конфигурирование векторного процессора заканчивается и он настроен на выполнение определённой функции. Остаётся подать исходные данные на входы Х и Y рабочей матрицы, и мы получим результат вычислений в регистре afifo. Введите в поле Х 64-разрядную константу 1111111111111111h, а вход Y пока использовать не будем. Для того чтобы ввести число, вовсе не надо отсчитывать 16 цифр, т.к. больше чем 16 цифр программа в любом случае не даст ввести. Кроме того, следует помнить, что во все окна редактирования данные вводятся и выводятся через них в 16-ричной системе счисления. При этом если введено/выведено менее 16 цифр, то предполагается, что слева число дополняется недостающими нулями. Итак, посмотрим, что же мы получили в результате. Если всё сделали правильно, то в afifo будет число 10h (16), т.е. это просто сумма 16 4-разрядных тетрад, каждая из которых равна 1h (0001b). Далее, выполните следующие действия:

ü     Подайте на вход Y 2h. Как видите, это значение прибавляется к результату.

ü     Измените значение на входе X таким образом, чтобы результат снова стал равным 10h. Занесите в отчет это значение.

ü     Верните начальные значения входов Х (=1111111111111111h) и Y (=0h).

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

ü     Удалите одну единицу со входа Х. Какую, по-вашему, единицу Вы удалили – старшую или младшую? Как при этом изменится результат? Запомните это для себя.

ü     Теперь повторите последние 3 пункта, но весовой коэффициент надо установить в ноль не нижней ячейки, а самой верхней. Обратите внимание, что при удалении одной единицы со входа Х, т.е. замене его старшей тетрады на ноль, результат в afifo уже не уме­ньшается на 1, как прежде. Объясните, почему это происходит. Пояснения занесите в отчет.

ü      
Верните начальные значения входов Х (=1111111111111111h), Y (=0h) и конфигурацию рабочей матрицы.

ü     Разбейте рабочую матрицу на 2 столбца по 32 бита каждый. Задайте значения весовых коэффициентов рабочей матрицы в левом столбце следующими: самый нижний -1, второй снизу 2h, а остальные оставьте равными 0. Для того чтобы ввести -1, необязательно переводить её в ffffffffh, можно вводить прямо в десятичной системе, установив переклю­чатель в окне ввода весового коэффициента на Decimal. Проанализируем результат: 100000010. Младшие 8 цифр (00000010) – это результат прошлой операции по сложению всех тетрад входа Х плюс младшие 32 разряда входа Y. А вот старшие 8 цифр (00000001) – это результат новой операции, которая равна удвоенному значению второй тетрады за вычетом первой тетрады и плюс старшие 32 разряда входа Y. Таким образом, мы запро­граммировали векторный узел процессора на одновременное выполнение двух операций над одними и теми же входными данными. Это является одной из отличительных черт процес­сора NM6403,  которая позволяет добиваться очень высокой производительности и широко используется на практике. Реально можно совмещать и большее число различных операций. Ограничение существует лишь в том, что при возникновении переполнения в одном из столбцов оно теряется и никак не фиксируется, так что программист должен сам оценивать и задавать необходимую ему разрядность данных от 1 до 64 (по столбцам) и от 2 до 64 (по строкам), даже не обязательно кратную 2. Например, можно разбить матрицу на три столбца следующим образом: 5/27/32 или 1/2/21/40. Допустимо использование не всей матрицы.

ü     Измените слово на входе Х на следующее: 11h. Впишите результат операции в отчет и объясните его.

ü     Измените слово на входе Х на следующее: 1h. Впишите результат операции в отчет и объясните его.

3. Теперь вам предстоит самостоятельно запрограммировать рабочую матрицу на вычисление 64-разрядного вектора, который в последующих пунктах будет вами использоваться. Для этого потребуются числа дней рождения вашей бригады. Исходя из того, что в месяце максимальное число дней 31 = 1Fh, для разрядности входных данных достаточно 8 бит. Числа нужно перевести в 16-ричную систему и, при необходимости, дополнить слева нулём до двух 16-ричных цифр. Затем эти числа записываются по порядку в поле Х. Например, если после перевода получились числа 1Ah и Ch, то в поле Х их надо ввести так: 1A0C или так: C1A, но не так: 1AC. Это важно, потому, что иначе данные не будут соответствовать разбиению матрицы и результат будет отличен от ожидаемого. Теперь разбейте рабочую матрицу на 64 ячейки (8 бит х 8 бит). В каждом столбце будут выполняться свои операции, которые  перечислены ниже в порядке от младших разрядов к старшим. Для их решения необходимо задать нужные весовые коэффициенты и, при необходимости, соответствующие разряды слова на входе Y. В результате получится искомый вектор, отдельные части которого являются решениями этих задач. Итак, задачи:

1. Вычислите сумму всех дней рождения.

2. Из самого большого дня рождения вычтите остальные.

3. Из суммы всех дней рождения, кроме самого малого, вычтите самое малое, умноженное на 3h.

4. Вычислите сумму произведений всех дней рождений на соответствующие им месяцы.

5. Вычислите сумму всех дней рождения за вычетом их количества.

6. Вычислите сумму всех дней рождения плюс их количество.

7. Младший байт входа Х.

8. Противоположное значение старшего байта входа Х, отличного от 00h.

Значение входов Х, Y, выхода в afifo и конфигурацию рабочей матрицы (в виде таблицы весовых коэффициентов) занесите в отчет в 16-ричной системе.

9. Кнопкой Clear all очистите конфигурацию процессора. Результат прошлой операции занесите в поле Х. Сейчас, необходимо, с помощью операции взвешенного суммирования поменять местами тетрады в слове Х. Подумайте, как это сделать. Значение выхода в afifo и конфигурацию рабочей матрицы (в виде таблицы весовых коэффициентов) занесите в отчет.

10. Кнопкой Clear all очистите конфигурацию процессора. Теперь рассмотрим операции векторного АЛУ. Векторное АЛУ, в отличие от скалярного, работает над 64-разрядными данными. Чтобы его использовать, снимите флажок vsum. Помните, что разбиение на элементы при использовании векторного АЛУ одинаково для входов Х и Y и задаётся регистром nb. Занесите в поле Y результат, полученный в пункте 3, поле Х оставьте равным 0, а разбиение задайте произвольно. Выберите операцию вычитания. В результате получим: afifo = XY. Значения входов Х, Y, выхода в afifo и выполняемую операцию занесите в отчет в 16-ричной системе. Рекомендуется записывать в столбик – цифра под цифрой.

11. Сложите на векторном АЛУ результат прошлой операции с результатом пункта 3, не меняя разбиение. Занесите данные, результат и операцию в отчет. Результат должен быть равен 0h.

12. Проинвертируйте побитно на векторном АЛУ результат пункта 3. Подумайте, какие для этого нужно выбрать операции, значения входов Х, Y и разбиение на элементы. Занесите данные, результат и операцию в отчет в двоичной системе. Убедитесь в правильности результата.

13. Поочерёдно выполните побитовые операции and, or и xor над результатами предыдущего пункта и пункта 3. Все данные, результаты и операции занесите  в отчет в двоичной системе. Убедитесь в правильности результатов.

14. Кнопкой Clear all очистите конфигурацию процессора. Рассмотрим операцию маскирования. Выполните операцию маскирования с векторным умножением (см. п 2.3.3), настроив процессор согласно первому примеру в пункте 1, но на Y подайте не 0, а то же, что на Х (1111111111111111h). При этом вектор масок задайте равным: ffffffffffffffffh и 0h. Все данные, результаты и операции занесите  в отчет. Убедитесь в правильности результатов.

15. Кнопкой Clear all очистите конфигурацию процессора. Выполните операцию логического маскирования (см. п 2.3.3), задав на вход Х результат пункта 3, на вход Y – 8888888888888888h и Mask = f0f0f0f0f0f0f0f0h. Все данные, результаты и операции занесите  в отчет. Убедитесь в правильности результатов.

16. Кнопкой Clear all очистите конфигурацию процессора. Рассмотрим обработку данных функцией активации. Выполните операцию арифметической активации входа Х (см. п 2.3.4), складывая Х (=f31f05) с нулём на векторном АЛУ. При этом задайте разбиение на элементы по 8 бит с верхним порогом насыщения, равным 15, и соответственно нижним, равным -16. Все данные, результаты и операции занесите  в отчет в двоичной системе. Убедитесь в правильности результатов.

17. Кнопкой Clear all очистите конфигурацию процессора. Выполните операцию логической активации входа Х (см. п 2.3.4), приняв Х равным результату пункта 3. Используйте операцию and с Y = ffffffffffffffffh. При этом разбиение на элементы задайте равным 16 элементам по 4 бита каждый (см. таблицу). Все данные, результаты и операции занесите  в отчет в двоичной системе. Убедитесь в правильности результатов.

18. Кнопкой Clear all очистите конфигурацию процессора. Рассмотрим операцию циклического сдвига вправо операнда Х (см. п. 2.3.5). Задайте всей матрице как одной ячейке весовой коэффициент, равный 1h, чтобы она просто складывала Х и Y. Выполните операцию взвешенного суммирования операнда Х (=0000000000000001h) и Y (=0h) два раза: с и без операции shift. Все данные, результаты и операции занесите  в отчет. Убедитесь в правильности результатов.

ЛАБОРАТОРНАЯ РАБОТА № 2

 

Программирование процессора

NeuroMatrix® NM6403

Цель работы: ознакомление с языком ассемблера процессора NeuroMatrix® NM6403 (Л1879ВМ1) и возможностями, которые он предоставляет программисту.

 

1. ПОРЯДОК ВЫПОЛНЕНИЯ РАБОТЫ

По материалам разделов 2 и 3 изучить структуру формата ассемблерных инструкций структуру микрокоманд (МК), порядок ввода данных, кодирование  и выполнение МК.

По материалам раздела 4 ознакомиться с порядком ввода микропрограмм и исходных данных.

2. ЭЛЕМЕНТЫ ЯЗЫКА АССЕМБЛЕРА ПРОЦЕССОРА NM6403

2.1. Формат ассемблерных инструкций

Процессор NM6403 работает с машинными командами 32-и 64-разрядного формата, причем в одной машинной команде содержится две операции процессора. В этом смысле NM6403 представляет собой скалярный микропроцессор со статической Very Long Instruction Word (VLIW) - архитектурой. При этом используются короткие и длинные инструкции. Причем короткие инструкции не содержат константы и имеют разрядность 32 бита. А длинные инструкции содержат в коде команды 32-разрядную константу. Поэтому их разрядность составляет 64 бита. Процессор адресуется к 32-разрядным словам. На хранение коротких инструкций отводится одна ячейка памяти, для длинных – две.

Примечание. Длинные инструкции всегда располагаются по четным адресам.

Если начало длинной инструкции при компиляции ассемблером приходится на нечетный адрес, перед ней автоматически вставляется пустая команда nul.

Процессор NM6403 за одно обращение к памяти считывает либо две коротких инструкции, либо одну длинную, поэтому регистр pc, определяющий адрес следующей считываемой инструкции, всегда имеет четное значение.

Все инструкции процессора NM6403 делятся на два типа:

ü     скалярные инструкции, которые управляют работой скалярного RISC-ядра, таймеров, осуществляют загрузку/чтение всех регистров (доступных по чтению/записи), за исключением векторных регистров, образующих очереди FIFO;

ü     векторные инструкции, которые управляют работой векторного процессора.

Каждая инструкция процессора NM6403 состоит из двух частей, называемых условно «левой» и «правой». Обе части инструкции выполняются процессором одновременно за один такт. В левой части инструкции записываются только адресные операции, в правой - все арифметическо-логические, не связанные с вычислением адресов и обращением к памяти. Левая и правая части инструкции соединяются воедино с помощью ключевого слова with.

Пример скалярной инструкции процессора:

gr0 = [ar0++] with gr1 = gr3 + gr0;

gr1 = gr3 + gr0 – правая часть инструкции, арифметическая операция.

gr0 = [ar0++] – левая часть инструкции, адресная операция.

В языке ассемблера левая или правая часть инструкции может быть опущена, однако поскольку процессор не может выполнить только левую или только правую часть команды, вместо опускаемой части при компиляции автоматически добавляется пустая операция nul. То есть ассемблерная инструкция, записанная как  gr0 = [ar0++]; трактуется ассемблером как  gr0 = [ar0++] with nul:

Аналогично левая часть gr1 = gr3 + gr0; рассматривается как nul with gr1 = gr3 + gr0;

Для улучшения читаемости программы, в случае если левая или правая часть инструкции не используется, связка with может опускаться.

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

2.2. Векторные инструкции  процессора

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

rep 32 data = [ar1++] with vsum , data, afifo;

Левая и правая части векторной инструкции разделены ключевым словом with, поле количества повторений (подчеркнуто) определяет, сколько длинных слов будет обработано данной командой. В большинстве случаев векторная команда будет выполняться столько тактов, каково значение счетчика, поскольку операция над длинным словом в векторном процессоре выполняется за один такт.

В случае если левая часть инструкции опущена, поле повторения и слово-связка with остаются при написании инструкции, например:

rep 16 with ram - 1; // правильная инструкция

Любые другие формы записи инструкции, как то

rep 16 ram - 1; или with ram - 1; // содержат ошибки

являются ошибочными, о чем сообщит компилятор.

2.3. Типы скалярных команд

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

 

Таблица. 1

Левая часть скалярной инструкции

Правая часть скалярной

инструкции

команды загрузки/записи регистров;

команды пересылки значений регистров;

команды адресной арифметики;

специальные скалярные команды;

команды безусловного и условного перехода;

команды безусловного и условного обращения к подпрограмме;

команды возврата из подпрограммы или прерывания;

пустая команда

арифметические операции;

логические операции;

сдвиговые операции;

пустая операция

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

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

gr4 = [ar0++] with gr0 = gr1 and not gr2;

В её левой части происходит инициализация регистра gr4 содержимым ячейки памяти, на которую указывает регистр ar0 с постинкрементацией адреса. В правой части выполняется трехоперандная логическая операция. Содержимое регистров gr1 и gr2 подвергается побитовой логической операции and not, а результат записывается в регистр gr0.

2.4. Типы векторных команд

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

Пример векторной инструкции с левой и правой частями:

rep 32 ram = [ar0++] with vsum , data, afifo;

Ключевое слово with разделяет левую и правую части. Счетчик повторений rep число присутствует в каждой векторной команде за исключением стоящих отдельно ftw и wtw. В левой части векторной команды выполняется операция загрузки данных во внутренний буфер ram векторного процессора, в правой данные, проходящие в ram по шине данных, дублируются и направляются в рабочую матрицу для выполнения взвешенного суммирования.

Таблица 2

Левая часть векторной инструкции

Правая часть векторной

инструкции

команды загрузки данных в векторный процессор;

команды выгрузки данных из векторного процессора;

специальные векторные команды;

пустая векторная операция

 

взвешенное суммирование (матричное умножение);

маскирование;

арифметические операции;

логические операции;

операция циклического сдвига;

операции активации операндов;

выгрузка управляющих векторных регистров;

пустая операция

2.5. Регистры процессора

Все регистры процессора NM6403 можно разделить на следующие группы:

1) адресные регистры;           2) регистры общего назначения;

3) специальные регистры;     4) векторные регистры.

2.5.1. Основные регистры

К основным регистрам процессора относятся 8 адресных регистров: ar0 - ar7(sp) и 8 регистров общего назначения: gr0 - gr0, которые используются в большинстве вычислительных операций процессора. Все они 32-разрядные, доступны как по чтению, так и по записи.

Адресные регистры

Адресные регистры делятся на две равноправные группы. В первую входят ar0-ar3, а во вторую - ar4..ar7. Это связано с наличием двух адресных устройств в процессоре.

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

Примеры использования адресных регистров:

ar0 = ar5; // копирование.

ar2 = ar3 + gr3; // модификация.

[ar4++] = gr7 with gr7 -= gr4 ; // запись в //память.

Адресный регистр ar7 используется процессором в качестве указателя стека адресов возврата sp(Stack Pointer). Это означает, что ar7 модифицируется автоматически, когда происходит вызов функции или прерывания, а также возврат из функции или из прерывания.

Регистры общего назначения

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

[gr0] = gr4; // запись значения регистра gr4 в //память

// по адресу, хранящемуся в gr0

Адресные регистры обладают в этом смысле значительно более широкими возможностями.

Примеры использования регистров общего назначения:

gr0 = gr5; // копирование.

gr2 = gr1 + gr3; // модификация.

[ar4++] = gr7 with gr7 -= gr4 ; // запись в //память

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

2.5.2. Регистровые пары

Каждому адресному регистру поставлен в соответствие регистр общего назначения с тем же номером. Таким образом, образуются пары, которые в дальнейшем будут называться регистровыми парами. Регистровые пары часто используются в инструкциях процессора, осуществляющих копирование, чтение/запись в память 64-разрядных данных, некоторые виды адресных операций.

Приведем пример загрузки из памяти в регистровую пару 64-разрядного значения:

ar0, gr0 = [ar1++]; // чтение 64-разрядного //слова.

Еще один пример использования регистровых пар встречается в некоторых методах адресации, например:

[ar0+=gr0] = gr4; // запись в память с //модификацией.

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

ar0, gr1 = [ar1++]; // неправильная инструкция.

[ar0+=gr1] = gr4;   // неправильная инструкция.

 


3. ПРИМЕРЫ ПРОСТЕЙШИХ ПРОГРАММ

Пример 1: Простая программа на языке Ассемблера

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

global __main: label; // объявление глобальной //метки

begin ".textAAA"      // начало секции кода

<__main>      // определение глобальной метки

gr0 = 1;   // загрузка константы в первый общий //регистр

gr1 = 2;  // загрузка константы во второй общий //регистр

gr7 = gr0 + gr1; // нахождение суммы

return; // возврат из функции, возвращаемое значение //хранится в gr7

end ".textAAA"; // признак окончания секции //кода

Комментарии. Пример начинается с объявления глобальной (global) метки __main (два подчёркивания перед словом main обязательны), которая будет в данном случае определять адрес в памяти той команды, с которой начинается тело основной программы. Причем объявление метки может происходить в любом месте ассемблерного файла, однако для лучшей читаемости кода рекомендуется выносить его за пределы секций.

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

За объявлением глобальной метки следует секция кода. Секция кода начинается с открывающей скобки begin и заканчивается закрывающей скобкой end. Имена секций при открывающей и закрывающей скобках должны совпадать.

Примечание Рекомендуется имя секции кода начинать с префикса text, например: «textMyCodeSection». Дизассемблер (программа dump.exe), разбирая первые символы имени секции, поймёт, что это код программы, и представит её содержимое в виде дизассемблированных инструкций. В противном случае он оставит содержимое секции в виде бинарного кода.

За открывающей скобкой begin “.textAAA” следует определение метки:

<__main>

Метка помечает ту команду, которая следует после неё до ближайшей ";". В приведённом выше примере меткой помечается инструкция gr0 = 1;.

Инструкции: gr0 = 1;  gr1 = 2;

представляют собой команды инициализации регистров общего назначения gr0 и gr1.

Инструкция:   gr7 = gr0 + gr1;

выполняет арифметическую операцию суммирования содержимого регистров gr1 и gr2, а результат заносит в регистр gr7.

Регистр gr7 используется для хранения возвращаемого значения при выходе из функции.

Тело программы заканчивается командой возврата из подпрограммы: return;

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

Пример 2:Организация циклов и доступ к памяти

Пример демонстрирует простейший метод организации цикла инструкциями <Loop> - начало цикла и   if > goto Loop; - конец цикла при заполнении массива данных возрастающими значениями.

global __main: label; // объявление глобальной //метки

nobits ".MyData1" // секция //неинициализированных данных

global C:word[16];      // объявили массив из 16 32-//разрядных слов

end ".MyData1";

begin ".textAAA"      // начало секции кода.

<__main>

ar0 = C;    // в ar0 загрузили адрес массива С.

gr0 = 0;    // в gr0 загрузили значение 0.

gr1 = 16; // в gr1 загрузили значение 16, //равное количеству итераций в цикле

<Loop>

[ar0++] = gr0;     // в память по адресу ar0 //записали содержимое gr0, а затем увеличили // адрес //на 1 (пост-инкрементация)

gr0++;          // увеличили значение gr0 на 1

gr1--;    // уменьшили значение gr1 на 1, таким

// образом установили флаг в регистре

// pswr для дальнейшей проверки

if > goto Loop;    // если условие выполнено,

// осуществляется переход на метку Loop.

return;

end ".textAAA"; // признак окончания секции //кода.

Комментарии. В примере массив С в цикле последовательно заполняется возрастающими значениями. Цикл организован путем перехода на заданную метку при выполнении определенных условий (с помощью команд условного перехода).

Команда

if > goto Loop;

осуществляет переход на метку Loop, в случае если условие “>” (больше) выполнено (все сравнения осуществляются с нулём). Эта команда проверяет значение флагов, выставленных предшествующей операцией. В данном случае такой операцией является gr1-- (в примере gr1 является счетчиком цикла. В процессоре NM6403 нет специального регистра–счётчика циклов). Установка флагов происходит только при выполнении арифметическо-логической операции в правой части скалярной команды.

Пример 3:Оптимизация выполнения цикла

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

global __main: label; // объявление глобальной //метки.

nobits ".MyData1" // секция //неинициализированных данных.

global C:word[16]; // объявление массива из 16 //32-разрядных слов

end ".MyData1";

begin ".textAAA"   // начало секции кода

<__main>

ar0 = C;  // в ar0 загружается адрес массива С.

gr0 = 0;  // в gr0 загружается значение 0

gr1 = 16; // в gr1 загружается значение 16, //равное количеству  итераций в цикле

gr1--;  // переменная цикла уменьшается на 1 //для входа в цикл с правильно выставленными //условными флагами

<Loop>

// если условие выполнено, осуществляется //отложенный переход на метку Loop

if > delayed goto Loop with gr1--;

// две следующих инструкции выполняются до //того, как произойдёт переход

[ar0++] = gr0 with gr0++ noflags;

nul;

// -- здесь произойдёт переход на метку Loop --

return; // сюда перейдёт программа, когда //условие не выполнится

end ".textAAA"; // признак окончания секции //кода.

Комментарии. В языке ассемблера введено два типа команд перехода. К первому относятся команды обычного перехода, ко второму - отложенного. Такое разделение введено искусственно, для удобства программирования.

От момента выбора команды перехода и до того, как состоится реальный переход, проходит от одного до трёх тактов. За это время процессор успевает выбрать дополнительно одну - три инструкции, следующих непосредственно за инструкцией перехода. Назовём такие инструкции отложенными. Упрощённая схема выполнения перехода подразумевает, что компилятор сам рассчитывает количество отложенных инструкций и заполняет их пустыми командами (nul). Если программист для выполнения перехода использует инструкцию без ключевого слова delayed, например:

if > goto Loop;

то две инструкции nul будут автоматически добавлены компилятором.

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

if > delayed goto Loop with gr1--;

будут выполнены две отложенные инструкции:

[ar0++] = gr0 with gr0++ noflags;

null;

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

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

if > delayed goto Loop with gr1--;

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

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

Пример 4: Копирование массива данных на скалярном процессоре

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

global __main: label; // объявление глобальной //метки.

data ".MyData"    // секция инициализированных //данных

// массив А из 16 64-разрядных слов заполняется //начальными значениями

global A: long[16] = (0l,1l,2l,3l,4l,5hl,6l,7l, 8l,9l,10l,0Bhl,0Chl,13l,14l,15l);

end ".MyData";

nobits ".MyData1" // секция //неинициализированных данных.

global B:long[16];    // объявляется массив В //из 16 64-разрядных слов

global C:long[16];    // объявляется массив С //из 16 64-разрядных слов

end ".MyData1";

begin ".textAAA" // начало секции кода

<__main>

// простое копирование массива данных на //скалярном процессоре

ar0 = A;

ar1 = B;

gr1 = 32; // счётчик цикла (32 цикла для //копирования 16 64-bit слов)

gr1--; // устанавливается флаг для первого /вхождения в цикл

<Loop>

//если условие выполнено, осуществляется отложенный //переход на метку Loop

if > delayed goto Loop with gr1--;

// чтение из памяти 32-разрядного слова

gr2 = [ar0++];

// запись в память 32-разрядного слова

[ar1++] = gr2;

// копирование массива данных с помощью //регистровых пар

ar0 = A;

ar1 = B;

gr1 = 16; // счётчик цикла (16 циклов для //копирования 16 64-bit слов)

gr1--; // устанавливается флаг для первого //вхождения в цикл

<Loop1>

// если условие выполнено, осуществляется отложенный //переход на метку Loop1

if > delayed goto Loop1 with gr1--;

// чтение из памяти 64-разрядного слова

gr2,ar2 = [ar0++];

// запись в память 64-разрядного слова

[ar1++] = ar2,gr2;

return;

end ".textAAA"; // признак окончания секции //кода.

Комментарии. В первой части примера копирование данных осуществляется через один 32-разрядный регистр. На первом шаге в регистр заносится слово из памяти, на втором оно копируется из регистра в память по другому адресу. В данном случае значения адресных регистров каждый раз увеличиваются на единицу. Поскольку необходимо скопировать массив из шестнадцати 64-разрядных слов, а за один цикл копирования через регистр переносится одно 32-разрядное число (младшая или старшая половина 64-разрядного слова), то для того, чтобы скопировать весь массив необходимо выполнить тридцать два цикла.

Во второй части примера копирование происходит через регистровую пару ar2, gr2 (в регистровой паре каждому адресному регистру поставлен в соответствие регистр общего назначения с тем же номером). За один цикл чтения/записи переносится целиком 64-разрядное слово, поэтому количество циклов копирования равно шестнадцати.

При чтении из памяти в регистровую пару ar2,gr2 = [ar0++]; всегда младшая часть 64-разрядного слова попадает в arX, старшая – в grX независимо от того, в каком порядке перечислены регистры в паре. Те же правила действуют при записи содержимого регистровой пары в память. По младшему адресу всегда записывается содержимое регистра arX, по старшему - grX. Таким образом, команда [ar1++] = gr2,ar2; запишет данные в память в том же порядке, в каком они были считаны, независимо от того, в какой последовательности перечислены регистры регистровой пары.

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

Процессор автоматически распознаёт, какой тип доступа к памяти используется в заданной инструкции – 32-или 64-разрядный.

Наличие в инструкции регистровой пары или 64-разрядного регистра управления приводит к тому, что доступ к памяти ведётся 64-разрядными словами. Но поскольку единица адресации - 32-разрядное слово, то при 64-разрядном доступе простая инкрементация адресного регистра приводит к увеличению его значения на два, например:

gr2 = [ar0++];      // ar0 увеличивается на 1

ar2,gr2 = [ar0++]; // ar0 увеличивается на 2

Пример 5: Копирование массива данных на векторном процессоре

Пример демонстрирует копирование массива 32-разрядных слов с помощью векторного процессора.

global __main: label;  // объявление глобальной метки.

data ".MyData"     // секция инициализированных //данных

// массив А из 16 32-разрядных слов заполняется //начальными значениями

globalA:word[16]= (0,1,2,3,4,5h,6,7,8,9,10,0Bh,0Ch,13,14, 15);

end ".MyData";

nobits ".MyData1"  // секция //неинициализированных данных.

global B:word[16];  // объявляется массив В из //16 32-разрядных слов

global C:word[16];  // объявляется массив С из //16 32-разрядных слов

end ".MyData1";

begin ".textAAA"      // начало секции кода.

<__main>

// копирование массивов данных с помощью //векторного процессора

ar0 = A;

ar1 = C;

// массив А подаётся на векторное АЛУ и попадает в //afifo без изменений

rep 8 data = [ar0++] with data;

// сохранение во внешней памяти содержимого afifo,

//заполненного предыдущей векторной инструкцией.

rep 8 [ar1++] = afifo;

return;

end ".textAAA"; // признак окончания секции //кода.

Комментарии. Обязательным атрибутом векторной инструкции является количество повторений, определяющее, какое количество 64-разрядных векторов данных обрабатывается данной инструкцией. В этом смысле векторные инструкции являются SIMD (Single Instruction Multiple Data) -инструкциями, выполняя одно и то же действие над несколькими векторами данных.

 

Команда

rep 8 [ar1++] = afifo;

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

Содержимое afifo не может быть выгружено в регистры процессора или регистровые пары, только в память.

Пример 6: Операция взвешенного суммирования

Демонстрируется пример использования устройства умножения, теневой и рабочей матрицы, входящих в состав векторного процессора NeuroMatrix. В примере рассмотрено использование операции взвешенного суммирования для перестановки байтов внутри 64-разрядного вектора данных.

global __main: label; // объявление глобальной //метки.

data ".MyData" //секция инициализированных //данных

// исходный вектор

A: long = 8877665544332211hl;

// место для хранения результата вычислений

B: long = 0l;

// массив Matr содержит значения для //заполнения матрицы весовых коэффициентов

Matr: long[8] = (0100000000000000hl,

0001000000000000hl,

0000010000000000hl,

0000000100000000hl,

0000000001000000hl,

0000000000010000hl,

0000000000000100hl,

0000000000000001hl);

end ".MyData";

begin ".textAAA" // начало секции кода

<__main>

ar1 = Matr;

nb1 = 80808080h; // матрица делится на 8 //столбцов по 8 бит

sb = 03030303h; // матрица делится на 8 строк

// весовые коэффициенты загружаются в буфер //wfifo

rep 8 wfifo = [ar1++];

ftw; // весовые коэффициенты пересылаются в //теневую матрицу с перекодировкой. Эта инструкция //всегда выполняется 32 такта

wtw; // весовые коэффициенты копируются из //теневой матрицы в рабочую

ar2 = A;

ar4 = B;

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

rep 1 data = [ar2] with vsum , data, 0;

// результат операции выгружается из afifo в //память

rep 1 [ar4] = afifo;

return;

end ".textAAA"; // признак окончания секции //кода.

Комментарии. Задачей данного примера является перестановка порядка элементов в 64-разрядном векторе из состояния A = 8877665544332211hl в состояние В = 1122334455667788hl. Эта перестановка выполняется на устройстве умножения векторного процессора с помощью операции взвешенного суммирования. Основная идея этого преобразования поясняется на рис. 1:

 

Рис. 1. Перестановка элементов вектора на матричном умножителе

Команда rep 8 wfifo = [ar1++]; осуществляет загрузку весовых коэффициентов из памяти в регистр-контейнер wfifo. Загрузку можно осуществлять и по частям, но так, чтобы не произошло переполнения. Контейнер wfifo имеет глубину в тридцать два 64-разрядных слова.

Команда ftw; выполняет перекодировку весовых коэффициентов, расположенных в wfifo, в специальный вид, в котором они хранятся в теневой матрице. Эта операция всегда выполняется за 32 такта, однако она может выполняться параллельно с другими векторными инструкциями.

Команда wtw; копирует весовые коэффициенты из теневой матрицы в рабочую.

Инструкция

rep 1 data = [ar2] with vsum , data, 0;

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

Пример 7: Операция взвешенного суммирования (окончание)

Пример демонстрирует использование операции взвешенного суммирования для одновременного вычисления суммы и разности двух 32-разрядных элементов вектора.

global __main: label; // объявление глобальной //метки.

data ".MyData" //секция инициализированных //данных

// исходный вектор

A: long = 3333333322222222hl;

// место для хранения результата вычислений

B: long = 0l;// массив Matr содержит значения //для заполнения матрицы весовых коэффициентов

Matr: long[2] = (0000000100000001hl,

0FFFFFFFF00000001hl);

end ".MyData";

begin ".textAAA" // начало секции кода.

<__main>

ar1 = Matr;

nb1 = 80000000h; // разбиение матрицы на два //столбца по 32 бита

sb = 03h; // разбиение матрицы на две строки по //32 бита

// весовые коэффициенты загружаются в буфер //wfifo, одновременно с этим они транслируются в //теневую матрицу, а затем и в рабочую

rep 2 wfifo = [ar1++], ftw, wtw;

ar2 = A;

ar4 = B;

// операция взвешенного суммирования

rep 1 data = [ar2] with vsum , data, 0;

// результат операции выгружается из afifo в //память

rep 1 [ar4] = afifo;

return;

end ".textAAA"; // признак окончания секции //кода

Комментарии. Задачей данного примера является одновременное вычисление суммы и разности элементов 64-разрядного вектора. Это вычисление выполняется на устройстве умножения векторного процессора с помощью операции взвешенного суммирования.

Основная идея этого преобразования поясняется на рис. 2:

 

Рис. 2. Вычисление суммы и разности элементов вектора

 

Инструкция

rep 1 data = [ar2] with vsum , data, 0;

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


4. ПОРЯДОК ВЫПОЛНЕНИЯ РАБОТЫ

Практическая часть работы предполагает разработку несложной программы на ассемблере. Программа заполняет блок памяти возрастающими значениями, а затем выполняет некоторые манипуляции над ним. Блок памяти (или массив) будем рассматривать состоящим из элементов определённой разрядности. Для простоты будем брать только кратные двум разрядности (например, 2, 4, 8, 16, 32, 64 бит), чтобы блок всегда можно было представить из целого количества таких элементов. При этом элементы имеют порядковые номера от начала блока: 0, 1, 2,… Под четными/нечетными элементами будем понимать элементы с четными/нечетными порядковыми номерами. Ещё раз, следует обратить внимание на то, что нумерация начинается с нуля!, а ноль – это четное число. Также, следует уточнить, что понимается под положительными/отрицательными числами. Все числа, старший разряд которых равен 1, процессор воспринимает как отрицательные, а все остальные – как положительные.

В качестве помощника при написании программы будем использовать программу NMCalculator, которая позволяет сначала визуально задать команду и конфигурацию векторного процессора, а потом получить текст программы на ассемблере, из которого можно позаимствовать такие строки, как: объявление и инициализация массива весовых коэффициентов; команды конфигурирования векторного процессора, задающие разбиение на элементы, константы векторов маскирования и активации и, конечно же, саму векторную команду. Таким образом, нет необходимости копаться в справочниках по форматам всех команд процессора, а можно просто поставить переключатели на форме в нужное положение и к тому же сразу просмотреть результат этой команды, подав значения на входы X и Y.

Для отладки программы воспользуемся специальным отладчиком emudbg.exe, поставляемым вместе с пакетом разработчика для процессора NM6403.

Практическое задание.

1. Заполнить блок памяти размером в 8 64-разрядных слов (или, что то же самое, – 16 32-разрядных слов) элементами длиной 32 бита, возрастающими с INC на INC, где INC = текущий год * (номер бригады + количество человек в бригаде + сумма цифр номера группы). Написать программу для выполнения этого задания (см. далее разобранный пример программы), скомпилировать её в выполняемый файл (*.abs) и отладить её на отладчике emudbg.exe. Зафиксировать в отчете полученный блок памяти с указанием его начального и конечного адреса.

Для написания текста программы следует воспользоваться любым текстовым редактором не сохраняющим символы форматирования (например, notepad.exe). Файл следует сохранить с расширением *.asm.

Для компиляции полученного файла необходимо поместить его в каталог, где установлен пакет разработчика для NM6403. Это может быть каталог С:\Program Files\Module\Nmsdk\bin\ (или другой по указанию инженера). Из этого каталога следует запустить программу компилятора следующей командой:

nmcc -g step1.asm libc.lib –m,

где step1.asm – имя файла с исходными текстами вашей программы. Рекомендуется создать командный (*.bat) файл с этой командой, чтобы не вводить её каждый раз, а запускать уже этот файл. Если компиляция пройдёт успешно, то в этом же каталоге будут созданы несколько файлов с тем же именем, но с другими расширениями. Нас из них будут интересовать следующие: *.abs – выполняемый файл открываемый из отладчика emudbg.exe, *.map – файл карты памяти в котором можно просмотреть начальные адреса переменных (то, что объявляется в секциях данных) в памяти для наблюдения за их содержимым в процессе отладки. Если же в процессе компиляции будут обнаружены ошибки в тексте программы, компилятор сообщит о них выводом сообщений с указанием номера строки в которой обнаружена ошибка.

Для отладки скомпилированной программы следует запустить программу emudbg.exe из того же каталога. Затем, в главном меню выбрать пункт: Цель->Загрузить программу и в появившемся диалоговом окне указать *.abs-файл вашей программы. Чтобы просмотреть различные ресурсы процессора, память и дизассемблированный или исходный код программы, следует обратиться к соответствующим подпунктам меню Вид. Рекомендуется, одновременно следить как минимум за содержимым памяти, используемых программой скалярных и векторных регистров, конфигурацией рабочей матрицы и текущей выполняемой строкой исходного текста.

Для доступа к ним необходимо открыть следующие пункты меню Вид: Память, Регистры, Специфика целевой среды и Исходные тексты->Ваш файл *.asm. При этом память во многих случаях будет удобнее просматривать не 32-разрядными словами, а 64-разрядными. Для этого следует выполнить щелчок правой кнопкой мыши в окне Память->64-битные слова. На панели инструментов отладчика расположены кнопки быстрого доступа к наиболее используемым командам, всплывающие подсказки поясняют их назначение. Нам понадобятся: Шаг простой/Шаг обходящий – любой из них для выполнения текущей команды; Снять/поставить точку останова; Запуск/Анимация – любая из них для выполнения уже хорошо отлаженной части программы до точки останова, чтобы не использовать многократно Шаг простой/Шаг обходящий. Чтобы выгрузить программу из отладчика, но оставить конфигурацию процессора и содержимое памяти без изменений следует выбрать: Цель->Выгрузить программу. Если же необходим полный сброс процессора в исходное состояние, то следует выбрать: Цель->Перезагрузить цель.

2. Выполнить обработку полученного блока памяти (массива) в соответствии с вариантом для вашей бригады:

2.1 Вычислить контрольную сумму блока памяти (количество единичных бит) и записать её за концом блока.

Рекомендации по выполнению. Воспользуйтесь операцией взвешенного суммирования, разбив рабочую матрицу на 32 строки и 1 столбец. На первом этапе вычислите сумму четных единичных бит (нумерация, как и для элементов блока памяти, начинается с нуля, а ноль – четное число) блока памяти. Полученные восемь 64-разрядных частичных сумм запишите в память для их временного хранения. На втором этапе вычислите сумму нечетных единичных бит. На третьем этапе просуммируйте частичные суммы четных и нечетных бит. Выгрузите полученные восемь 64-разрядных частичных сумм в память для их временного хранения. На четвертом этапе просуммируйте полученные частичные суммы на скалярном процессоре (см. далее пример программы) и запишите результат в память. Для того чтобы получить доступ к четным/нечетным битам входа Х, необходимо воспользоваться блоком маскирования, задав соответствующие векторы масок:

EvenMask:   long = 05555555555555555hl;  //маска для четных бит   (0101).

OddMask:    long = 0aaaaaaaaaaaaaaaahl;  //маска для нечетных бит (1010).

Вектор масок следует хранить в ram, причем столько 64-разрядных слов, сколько раз выполняется векторная команда (в нашем случае – 8). Кроме того, при суммировании нечетных бит, необходимо подключить регистр сдвига, т.к. разрядность элементов входа Х – 2 бита. Во избежание путаницы следует четко представлять последовательность выполнения всех этих операций в процессоре (см. предыдущую работу).

2.2. Прибавить всем четным элементам разрядностью 16 бит сегодняшнее число. Затем вычислить сумму 0-го и 1-го 64-разрядных элементов и записать её сразу за концом блока.

Рекомендации по выполнению. Для выполнения первой части задания воспользуйтесь операцией взвешенного суммирования. Задайте необходимое разбиение рабочей матрицы и весовые коэффициенты. Составьте 64-разрядную константу, в которой четные 16-разрядные элементы равны сегодняшнему числу. Затем эту константу можно будет использовать в качестве операнда Y операции взвешенного суммирования, для этого её надо предварительно загрузить, к примеру, в регистр vr. Для выполнения второй части задания следует воспользоваться соответствующей операцией на векторном АЛУ. При этом не забывайте, что разбиение на элементы при использовании векторного АЛУ одинаково для входов Х и Y и задаётся регистром nb. Ответьте на вопрос – влияет ли разбиение на результат вашей операции  в векторном АЛУ?

2.3. Умножить каждый элемент блока памяти разрядностью 8 бит на количество человек в бригаде. Затем найти разность между 2-м и 0-м 32-разрядными элементами и записать её сразу за концом блока 64-разрядным словом с обнулением старших 32 разрядов этого слова.

Рекомендации по выполнению. Для выполнения первой части задания воспользуйтесь операцией взвешенного суммирования. Задайте необходимое разбиение рабочей матрицы и весовые коэффициенты. Для выполнения второй части задания следует воспользоваться соответствующей операцией на векторном АЛУ. При этом не забывайте, что разбиение на элементы при использовании векторного АЛУ одинаково для входов Х и Y и задаётся регистром nb. Ответьте на вопрос – влияет ли разбиение на результат вашей операции в векторном АЛУ?

2.4. Вычесть из каждого нечетного элемента разрядностью 32 бита предыдущий (четный) элемент. Затем найти результат логического «или»  0-го и 4-го элементов разрядностью 16 бит и записать его сразу за концом блока 64-разрядным словом с обнулением старших 48 разрядов этого слова.

Рекомендации по выполнению (см. п. 2.3).

2.5. Поменять местами соседние четные и нечетные 16-разрядные элементы. Затем найти результат логического «и» 0-го и 1-го 64-разрядных слов и записать его сразу за концом блока.

Рекомендации по выполнению (см. п. 2.3).

2.6. Найти сумму всех четных 32-разрядных элементов. Затем обнулить у всех 32-разрядных элементов блока биты, которые в полученной сумме равны нулю.

Рекомендации по выполнению. Для выполнения первой части задания воспользуйтесь скалярным АЛУ. Полученную 32-разрядную сумму выгрузите подряд два раза в память, чтобы получить 64-разрядное слово. Для выполнения второй части задания следует воспользоваться операцией логического «и» на векторном АЛУ с 64-разрядным словом, полученным на предыдущем этапе. Чтобы использовать это слово, его необходимо загрузить в ram, причем столько 64-разрядных слов, сколько раз выполняется векторная команда (в нашем случае – 8).

2.7. Проинвертировать побитно блок памяти, а затем найти результат логического «и» исходного и проинвертированного блока.

Рекомендации по выполнению. Воспользуйтесь операцией or или and в АЛУ, подключив инверсию входа Х, а на вход Y подав соответствующее значение в зависимости от используемой операции. В данном примере можно обойтись без выгрузки промежуточных результатов в память, воспользовавшись возможностью работы afifo одновременно как на чтение (из «головы»), так и на запись (в «хвост»).

2.8. Замените все неотрицательные 4-разрядные элементы нулём, а отрицательные – минус единицей. Затем замените все -1 на 1, а нулевые элементы оставьте без изменений.

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

2.9. Выполнить арифметическую активацию всех 4-разрядных элементов порогами [-4; 3] (см. предыдущую работу). Затем найти результат «исключающего или» 0-го и 7-го (последнего) 64-разрядных элементов и записать его сразу за концом блока.

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

2.10. Переставить в каждом 64-разрядном элементе 4-разрядные тетрады в обратном порядке, после чего проинвертировать побитно весь блок памяти.

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

2.11. Прибавить всем четным 16-разрядным элементам следующий за ними нечетный элемент, умноженный на 2 и уменьшенный на 1. После чего найти результат операции исключающего или над 0-м и 4-м 16-разрядными элементами блока памяти, и записать его в память 64-разрядным словом сразу за концом блока с обнулением оставшихся 48 старших бит слова. Затем найти сумму всех нечетных 8-разрядных элементов блока памяти в формате 32-разрядного слова.

3. Отладить по шагам полученную программу и убедиться в её правильности. Зафиксировать в отчете текст программы и результаты её работы.

Пример. В качестве примера, рассмотрим текст конечной программы с подробными комментариями.

global __main: label; // объявление глобальной метки.

data ".dataSeg"         // секция инициализированных данных.

Block:    word[16] = (0 dup 16); // объявление массива из 16 32-//разрядных слов c заполнением его нулевыми значениями

Result:     long = 0hl; // 64-разрядное слово для результатов //обработки,  в памяти оно будет расположено сразу за массивом Block

Entry_Y:               long = 00000ffff0000ffffhl; // служебная константа

Entry_Y1:             long = 000000000000ffffhl; // служебная //константа

INC:        word = 36072; // служебная константа.

Weights: long[4] = (

00000000000000001hl,

00000000000010002hl,

00000000100000000hl,

00001000200000000hl); // массив весовых коэффициентов

Weights1: long[8] = (

00000000000000000hl,

00000000000000001hl,

00000000000000000hl,

00000000000000001hl,

00000000000000000hl,

00000000000000001hl,

00000000000000000hl,

00000000000000001hl); // массив весовых коэффициентов

end ".dataSeg";

begin ".textAAA" // начало секции кода.

<__main>

//------заполнение массива Block значениями, возрастающими от //INC на INC--------

ar0 = Block;      // в ar0 загружается адрес массива Block.

gr1 = 16;          // в gr1 загружается значение 16,

// равное количеству итераций в цикле

gr2 = [INC];   // в gr2 загружается значение из памяти по адресу //INC, равное инкременту значений элементов массива

gr0 = gr2;        // первый элемент = инкременту

gr1--;          // переменная цикла уменьшается на 1 для входа в //цикл с правильно выставленными условными флагами

<Loop>

// если условие выполнено, осуществляется отложенный переход //на метку Loop

if > delayed goto Loop  with gr1--;

// две следующих инструкции выполняются до того, как //произойдёт переход

[ar0++] = gr0 with gr0+=gr2 noflags;

nul;

// конфигурирование векторного процессора (ВП)

nb1 = 080008000h; // 4 столбца по 16 бит.

sb = 020002h;         // 4 строки по 16 бит.

ar0 = Weights;  // в ar0 поместить адрес массива весовых //коэффициентов Weights.

rep 4 wfifo = [ar0++], ftw, wtw; // загрузка весовых коэффициентов //в ВП

// подготовка операндов X и Y

vr = [Entry_Y]; // загрузить в vr 64-разрядное слово из памяти

ar0 = Block; // в ar0 загружается адрес массива Block

// собственно, операция взвешенного суммирования

rep 8 data = [ar0++] with vsum , data, vr;

// выгрузка результатов в память из afifo

ar0 = Block; // в ar0 загружается адрес массива Block

rep 8 [ar0++] = afifo; // выгрузка данных

// операция xor над 0-м и 4-м 16-разрядными элементами блока //памяти, результат которой записывается в память 64-разрядным словом //сразу за концом блока с обнулением оставшихся старших бит слова

// конфигурирование векторного процессора (ВП)

nb1 = 080008000h; // 4 столбца по 16 бит

wtw; // переписать информацию из теневого регистра nb1 в //рабочий nb2

// подготовка операндов X и Y

ar1 = Block; // в ar1 загружается адрес массива Block

rep 1 data = [ar1] with data; //загрузка в afifo 0-го 64-разрядного //элемента массива

ar1++; // увеличение адреса на 2

ar1++; // для доступа к 1-му 64-разрядному элементу

//выполнение операции исключающего «или» операцией xor на //векторном АЛУ

rep 1 data = [ar1] with data xor afifo;

//обнуление старших 48 разрядов

ar2 = Entry_Y1; // поместить в ar2 адрес Entry_Y1

rep 1 data = [ar2] with data and afifo;

//запись результата 64-разрядным словом сразу за концом //массива Block, т.е. по адресу Result

ar0 = Result;

rep 1 [ar0] = afifo;

// нахождение суммы всех нечетных 8-разрядных элементов блока //памяти в формате 32-разрядного слова

// (1) нахождение частичных сумм для каждого 64-разрядного //элемента блока

// конфигурирование векторного процессора (ВП)

nb1 = 00h;      // один столбец

sb = 02020202h; // 8 строк по 8 бит

ar0 = Weights1; // в ar0 поместить адрес массива весовых //коэффициентов Weights

rep 8 wfifo = [ar0++], ftw, wtw; // загрузка весовых коэффициентов //в ВП

// подготовка операндов X и Y

ar0 = Block; // в ar0 загружается адрес массива Block

// нахождение суммы нечетных 8-разрядных элементов для //каждого 64-разрядного слова операцией взвешенного суммирования

rep 8 data = [ar0++] with vsum , data, 0;

// выгрузка временного результата в память на место массива //Block

ar0 = Block; // в ar0 загружается адрес массива Block

rep 8 [ar0++] = afifo;

// (2) суммирование 64-разрядных слов частичных сумм на //скалярном процессоре

ar1 = Block;      // в ar1 загружается адрес массива Block

gr2 = 0;            // искомая сумма будет накапливаться в gr2

gr1 = 8;  // в gr1 загружается значение 8, равное количеству //итераций в цикле

gr0=[ar1++];//в gr0 считывается первое четное 32-разрядное //слово с инкрементом адреса на 1

ar1++; // повторный инкремент адреса для доступа к четному 32-//разрядному слову

<Loop1>

gr0 = [ar1++] with gr2+=gr0; // накопление суммы с чтением в gr0 //из памяти

ar1++; // повторный инкремент адреса для доступа к четному 32-//разрядному слову

gr1--; // декремент счетчика цикла

//если условие выполнено, осуществляется переход на метку //Loop1

if > goto Loop1;

//1. Здесь произойдёт переход на метку Loop1 2. Сюда перейдёт программа, когда условие не выполнится 3. В gr2 находится искомая сумма всех нечетных 8-разрядных элементов массива.

return;

end ".textAAA"; // признак окончания секции кода

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

 

 

Y=

0

-1

0

-1

 

3

1

2

0

0

2

0

1

0

0

1

0

0

1

2

0

0

0

0

1

 

3

2

1

0

СОДЕРЖАНИЕ

Лабораторная работа №1                                                                         стр. 1

 

Лабораторная работа № 2                                                                        стр. 26

 
звезды заядлые игроки казино