Пример архитектуры команд IJVM
253
Листинг 4.2.
Программа на языке ассемблер Java
1 ILOAD j //i=J+k
2 ILOAD k
3 IADD
4 ISTORE l
5 I LOAD l //if (1—3)
6 BIPUSH 3
7 IFJCMPEQ LI
8 ILOAD j //J-J-l
9 BIPUSH 1
10 ISUB
11 ISTORE j
12 GOTO L2
13 LI BIPUSH 0 //k-0
14 ISTORE k
15 L2
Листинг 4.3.
Программа IJVM в шестнадцатеричном коде
0x15 0x02
0x15 0x03
0x60
0x36 0x01
0x15 0x01
0x10 0x03
0x9F 0x00 OxOD
0x15 0x02
0x10 0x01
0x64
ОхЗб 0x02
0xA7 0x00 0x07
0x10 0x00
0x36 0x03
Скомпилированная программа проста. Сначала j и к помещаются в стек, скла-
дываются, а результат сохраняется в i Затем i и константа 3 помещаются в стек
и сравниваются Если они равны, то совершается условный переход к
L1,
где к
получает значение 0 Если они не равны, то выполняется часть программы после
IF_ICMPEQ После этого осуществляется переход к
L2,
где сливаются части el se
и then
Стек операндов для программы IJVM, приведенной в листинге 4.2, изображен
на рис. 4 12 До начала выполнения программы стек пуст, что показывает гори-
зонтальная черта над цифрой 0 После выполнения первой команды I LOAD j поме-
шается в стек (См на рисунке прямоугольник над цифрой 1 ) Цифра 1 означает,
что выполнена команда 1 После выполнения второй команды ILOAD в стеке оказы-
ваются уже два слова, как показано в прямоугольнике над цифрой 2 После выпол-
нения команды IADD в стеке остается только одно слово, которое представляет со-
бой сумму j+k. Когда верхнее слово выталкивается из стека и сохраняется в i,
стек снова становится пустым
Команда 5 (ILOAD) начинает оператор if. Эта команда помещает i в стек
Затем идет константа 3 (в команде 6) После сравнения стек снова становится пу-
стым (7) Команда 8 является началом фрагмента el se Он продолжается вплоть
до команды 12, когда совершается переход к метке
L2.
254
Глава 4. Микроархитектурный уровень
10
11
12
14
Рис. 4.12. Состояние стека после выполнения каждой команды
в программе, приведенной в листинге 4.2
15
Пример реализации микроархитектуры
Мы подробно описали, что такое микроархитектура и макроархитектура. Осталось
осуществить реализацию. Другими словами, нам предстоит узнать, что собой пред-
ставляет и как работает программа микроархитектурного уровня, интерпретирую-
щая команды макроархитектуры. Прежде чем ответить на эти вопросы, мы долж-
ны изложить систему обозначений, которую мы будем использовать для описания.
Микрокоманды и их запись
В принципе мы могли бы описать работу управляющей памяти с помощью двоич-
ной системы счисления, по 36 битов в слове. Но гораздо удобнее ввести систему
обозначений, с помощью которой можно передать суть рассматриваемых вопросов,
и при этом не вдаваться в ненужные подробности. Важно понимать, что язык, кото-
рый мы выбираем, предназначен для того, чтобы проиллюстрировать основные прин-
ципы работы программы, а не для того, чтобы использовать его в новых проектах.
Если бы нашей целью было практическое применение языка, мы бы ввели совсем
другую запись, чтобы максимально повысить гибкость программы. При этом была
бы очень важна проблема выбора адресов, поскольку адреса в памяти не упорядоче-
ны. Насколько эффективным будет выбор адресов, зависит от способностей разра-
ботчика. Поэтому мы введем простой символический язык, который полностью опи-
сывает каждую операцию, но не объясняет полностью, как определяются все адреса.
Наша система обозначений показывает все действия, которые происходят на
одной линии за один цикл. Теоретически для описания этих операций мы могли
бы использовать язык высокого уровня. Однако контроль циклов очень важен, по-
скольку это дает возможность выполнять несколько операций одновременно. Кроме
того, такой контроль необходим для того, чтобы можно было проанализировать
каждый цикл, понять все операции и проверить их. Если целью разработки явля-
ется повышение скорости и производительности, то имеет значение каждый цикл.
При практической реализации в программу включается множество различных при-
емов для экономии циклов. В такой экономии есть большая выгода: четырехцик-
ловая команда, которую можно сократить на два цикла, будет после этого выпол-
няться в два раза быстрее. И такое повышение скорости достигается каждый раз,
когда мы выполняем эту команду.
Один из возможных подходов — просто выдать список сигналов, которые долж-
ны активизироваться в каждом цикле. Предположим, что в одном цикле мы хотим
Пример реализации микроархитектуры 255
увеличить значение SP на единицу. Мы также хотим инициировать операцию
чтения и хотим, чтобы следующая команда находилась в управляющей памяти
в ячейке 122. Тогда мы могли бы написать:
ReadRegister=SP, ALIMNC, WSP, Read, NEXT_ADDRESS=122
Здесь WSP значит «записать регистр SP». Эта запись полная, но она сложна
для понимания. Вместо этого мы соединим эти операции и передадим в записи
результат действий:
SP-SP+1, rd
Назовем наш микроассемблер высокого уровня «MAL» (Micro Assembly Lan-
guage — микроассемблер). По-французски «MAL» значит «болезнь» — это то, что
с вами случится, если вы будете писать слишком большие программы на этом язы-
ке. Язык MAL разработан для того, чтобы продемонстрировать основные характе-
ристики микроархитектуры. Во время каждого цикла могут записываться любые
регистры, но обычно записывается только один. Значение только одного регистра
может передаваться на шину В и в АЛУ. На шине А может быть +1, 0, -1 и ре-
гистр Н. Следовательно, для обозначения определенной операции мы можем ис-
пользовать простой оператор присваивания, как в языке Java. Например, чтобы
копировать регистр SP в регистр MDR, мы можем написать:
MDR=SP
Чтобы показать, что мы используем какую-либо функцию АЛУ, мы можем на-
писать, например:
MDR-H+SP
Эта строка означает, что значение регистра Н складывается со значением реги-
стра SP и результат записывается в регистр MDR. Операция сложения коммута-
тивна (это значит, что порядок операндов не имеет значения), поэтому данное выше
выражение можно записать в виде:
MDR=SP+H
и при этом породить ту же 36-битную микрокоманду, хотя, строго говоря, Н явля-
ется левым операндом АЛУ.
Мы должны использовать только допустимые операции. Самые важные опера-
ции приведены в табл. 4.3, где SOURCE — значение любого из регистров MDR,
PC, MBR, MBRU, SP, LV, CPP, TOS и ОРС (MBRU (MBR Unsigned) - это значе-
ние регистра MBR без знака). Все эти регистры могут выступать в качестве источ-
ников значений для АЛУ (они поступают в АЛУ через шину В). Сходным образом
DEST может обозначать любой из следующих регистров: MAR, MDR, PC, SP, LV,
CPP, TOS, ОРС и Н. Любой из этих регистров может быть пунктом назначения
для выходного сигнала АЛУ, который передается к регистрам по шине С. Многие,
казалось бы, разумные утверждения недопустимы. Например, выражение
MDR=SP+MDR
выглядит вполне корректно, но эту операцию нельзя выполнить в тракте данных,
изображенном на рис. 4.5, за один цикл. Такое ограничение существует, поскольку
для операции сложения (в отличие от увеличения или уменьшения на 1) один из
операндов должен быть значением регистра Н. Точно так же, выражение
H=H-MDR
2 5 6
Глава 4. Микроархитектурный уровень
могло бы пригодиться, но оно невозможно, поскольку единственным возможным
источником вычитаемого является регистр Н. Ассемблер должен отбрасывать
выражения, которые кажутся пригодными, но в действительности недопустимы.
В нашей системе записи допускается использование нескольких операторов
присваивания. Например, чтобы прибавить 1 к регистру SP и сохранить получен-
ное значение в регистрах SP и MDR, нужно записать следующее:
SP=MDR=SP+1
Для обозначения процессов считывания из памяти и записи в память слов по
4 байта мы будем вставлять в микрокоманду слова rd и wr. Для вызова байта через
1-байтный порт используется команда fetch. Операции присваивания и операции
взаимодействия с памятью могут происходить в одном и том же цикле. То, что
происходит в одном цикле, записывается в одну строку.
Чтобы избежать путаницы, напомним еще раз, что Mic-1 может обращаться к
памяти двумя способами. При чтении и записи 4-байтных слов данных используют-
ся регистры М AR/MDR. Эти процессы показываются в микрокомандах словами rd
и wr соответственно. При чтении 1-байтных кодов операций из потока команд ис-
пользуются регистры PC/MBR. В микрокоманде это показывается словом fetch.
Оба типа операций взаимодействия с памятью могут происходить одновременно.
Однако один и тот же регистр не может получать значение из памяти и тракта
данных в одном и том же цикле. Рассмотрим кусок программы
MAR=SP. rd
MDR-H
В результате выполнения первой микрокоманды значение из памяти приписы-
вается регистру MDR в конце второй микрокоманды. Однако вторая микрокоман-
да в то же самое время приписывает другое значение регистру MDR. Эти две опе-
рации присваивания конфликтуют, поскольку результаты не определены.
Таблица 4.3.
Все допустимые операции. Любую из перечисленных операций можно
расширить, добавив «<<8», что означает сдвиг результата влево на 1 байт.
Например, часто используется операция H=MBR«8
DEST=H
DEST=SOURCE
DEST=H
DEST=SOURCE
DEST=H+SOURCE
DEST=H+SOURCE+1
DEST=H+1
DEST=SOURCE+1
DEST=SOURCE-H
DEST=SOURCE-1
DEST= -H
DEST=H И SOURCE
DEST=H ИЛИ SOURCE
DEST=O
DESTM
DEST=-1
Пример реализации микроархитектуры 257
Помните, что в каждой микрокоманде должен явно показываться адрес следу-
ющей микрокоманды Однако часто бывает так, что микрокоманда вызывается
только одной другой микрокомандой, а именно той микрокомандой, которая нахо-
дится в строке над ней. Чтобы упростить работу программиста, микроассемблер
обычно приписывает адрес каждой микрокоманде (порядок адресов может и не
соответствовать последовательности микрокоманд в управляющей памяти) и запол-
няет поле NEXT_ADDRESS, так что последовательность выполнения микрокоманд
соответствует последовательности строк микропрограммы.
Однако программисту иногда нужно совершить переход, условный или без-
условный. Запись безусловных переходов проста:
goto label
Такая запись может включаться в любую микрокоманду В ней явным образом
указывается имя следующей микрокоманды. Например, очень часто последова-
тельность микрокоманд заканчивается возвращением к первой команде основно-
го цикла, поэтому последняя команда в каждой такой последовательности содер-
жит запись
goto Mainl
Отметим, что в тракте данных происходят обычные операции даже во время
выполнения микрокоманд, которые содержат goto. В любой микрокоманде есть
поле NEXT_ADDRESS. Команда goto сообщает ассемблеру, что в это поле вместо
адреса микрокоманды, записанной в следующей строке, нужно поместить особое
значение. В принципе каждая строка должна содержать запись goto, но если нуж-
ный адрес — это адрес микрокоманды, записанной в следующей строке, goto может
опускаться для удобства.
Для условных переходов нам требуется другая запись Помните, что JAMN и
JAMZ используют биты N и Z соответственно. Например, иногда нужно прове-
рить, не равно ли значение регистра 0. Для этого можно было бы пропустить это
значение через АЛУ, сохранив его после этого в том же регистре. Тогда мы бы на-
писали:
T0S=TOS
Запись выглядит забавно, но выполняет необходимые действия (устанавлива-
ет триггер Z и записывает значение в регистре TOS). В целях удобочитаемости
микропрограммы мы расширили язык MAL, добавив два новых воображаемых
регистра N и Z, которым можно присваивать значения. Например, строка
Z=TOS
пропускает значение регистра TOS через АЛУ, устанавливая триггер Z (и N), но
при этом не сохраняет значение ни в одном из регистров. Использование регистра
Z или N в качестве пункта назначения показывает микроассемблеру, что нужно
установить все биты в поле С (см. рис. 4.4) на 0. Тракт данных проходит обычный
цикл, выполняются все обычные допустимые операции, но ни один из регистров
не записывается. Не важно, где находится пункт назначения' в регистре N или в ре-
гистре Z. Микрокоманды, которые при этом порождает микроассемблер, одинако-
вы. Программисты, выбравшие не тот регистр, в наказание будут неделю работать
на первом компьютере IBM PC с частотой 4,77 МГц.