![](/user_photo/1489_ZGosh.jpg)
Structured Computer Organization (Архитектура компьютера) / computer_organization_2003
.pdf![](/html/1489/253/html_NIFD2oRbYJ.h1Nk/htmlconvd-Pm2c6r251x1.jpg)
Пример архитектуры команд IJVM |
253 |
Листинг4.2. Программа на языке ассемблерJava
1 |
ILOAD j |
//i=J+k |
2ILOAD k
3IADD
4ISTORE l
5 |
ILOAD l |
//if(1—3) |
6BIPUSH 3
7IFJCMPEQ LI
8 |
ILOAD j |
//J-J-l |
9BIPUSH 1
10ISUB
11ISTORE j
12GOTO L2
13 LI BIPUSH 0 |
//k-0 |
14ISTORE k
15L2
Листинг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 После выполнения первой команды ILOAD j помешается в стек (См на рисунке прямоугольник над цифрой 1 ) Цифра 1 означает, что выполнена команда 1 После выполнения второй команды ILOAD в стеке оказываются уже два слова, как показано в прямоугольнике над цифрой 2 После выполнения команды IADD в стеке остается только одно слово, которое представляет собой сумму j+k. Когда верхнее слово выталкивается из стека и сохраняется в i, стек снова становится пустым
Команда 5 (ILOAD) начинает оператор if. Эта команда помещает i в стек Затем идет константа 3 (в команде 6) После сравнения стек снова становится пустым (7) Команда 8 является началом фрагмента el se Он продолжается вплоть до команды 12, когда совершается переход к метке L2.
![](/html/1489/253/html_NIFD2oRbYJ.h1Nk/htmlconvd-Pm2c6r252x1.jpg)
254 Глава4.Микроархитектурныйуровень
10 |
11 |
12 |
14 |
15 |
Рис. 4.12. Состояние стека после выполнения каждой команды в программе, приведенной в листинге 4.2
Пример реализации микроархитектуры
Мы подробно описали, что такое микроархитектура и макроархитектура. Осталось осуществить реализацию. Другими словами, нам предстоит узнать, что собой представляет и как работает программа микроархитектурного уровня, интерпретирующая команды макроархитектуры. Прежде чем ответить на эти вопросы, мы должны изложить систему обозначений, которую мы будем использовать для описания.
Микрокоманды и их запись
В принципе мы могли бы описать работу управляющей памяти с помощью двоичной системы счисления, по 36 битов в слове. Но гораздо удобнее ввести систему обозначений, с помощью которой можно передать суть рассматриваемых вопросов, и при этом не вдаваться в ненужные подробности. Важно понимать, что язык, который мывыбираем,предназначендлятого,чтобыпроиллюстрироватьосновныепринципы работы программы, а не для того, чтобы использовать его в новых проектах. Если бы нашей целью было практическое применение языка, мы бы ввели совсем другую запись, чтобы максимально повысить гибкость программы. При этом была бы очень важна проблема выбора адресов, поскольку адреса в памяти не упорядочены. Насколько эффективным будет выбор адресов, зависит от способностей разработчика. Поэтому мы введем простой символический язык, который полностью описывает каждую операцию, но не объясняет полностью, как определяются все адреса.
Наша система обозначений показывает все действия, которые происходят на одной линии за один цикл. Теоретически для описания этих операций мы могли бы использовать язык высокого уровня. Однако контроль циклов очень важен, поскольку этодает возможность выполнять несколько операций одновременно. Кроме того, такой контроль необходим для того, чтобы можно было проанализировать каждый цикл, понять все операции и проверить их. Если целью разработки является повышение скорости и производительности, то имеет значение каждый цикл. При практической реализации в программу включается множество различных приемов для экономии циклов. В такой экономии есть большая выгода: четырехцикловая команда, которую можно сократить на два цикла, будет после этого выполняться в два раза быстрее. И такое повышение скорости достигается каждый раз, когда мы выполняем эту команду.
Один из возможных подходов — просто выдать список сигналов, которые должны активизироваться в каждом цикле. Предположим, что в одном цикле мы хотим
![](/html/1489/253/html_NIFD2oRbYJ.h1Nk/htmlconvd-Pm2c6r253x1.jpg)
Пример реализации микроархитектуры |
255 |
увеличить значение SP на единицу. Мы также хотим инициировать операцию чтения и хотим, чтобы следующая команда находилась в управляющей памяти в ячейке 122. Тогда мы могли бы написать:
ReadRegister=SP, ALIMNC, WSP, Read, NEXT_ADDRESS=122
Здесь WSP значит «записать регистр SP». Эта запись полная, но она сложна для понимания. Вместо этого мы соединим эти операции и передадим в записи результат действий:
SP-SP+1, rd
Назовем наш микроассемблер высокого уровня «MAL» (Micro Assembly Language — микроассемблер). По-французски «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
![](/html/1489/253/html_NIFD2oRbYJ.h1Nk/htmlconvd-Pm2c6r254x1.jpg)
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
![](/html/1489/253/html_NIFD2oRbYJ.h1Nk/htmlconvd-Pm2c6r255x1.jpg)
Пример реализации микроархитектуры |
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 МГц.
![](/html/1489/253/html_NIFD2oRbYJ.h1Nk/htmlconvd-Pm2c6r256x1.jpg)
258 Глава 4. Микроархитектурный уровень
Чтобы микроассемблер установил бит JAMZ, нужно написать следующее: if(Z) goto LI, else goto L2
Поскольку аппаратное обеспечение требует, чтобы 8 младших битов этих адресов совпадали, задача микроассемблера состоит в том, чтобы присвоить им такие адреса. С другой стороны, L2 может находиться в любом из младших 256 слов управляющей памяти, поэтому микроассемблер без труда найдет подходящую пару.
Часто эти два утверждения сочетаются. Например,
Z=TOS. if(Z) goto LI. else goto L2
В результате такой записи MAL породит микрокоманду, в которой значение регистра TOS пропускается через АЛУ, но при этом нигде не сохраняется, так что это значение устанавливает бит Z. Сразу после загрузки из АЛУ бит Z соединяется со старшим битом регистра МРС через схему ИЛИ, вследствие чего адрес следующей микрокоманды будет вызван или из L2, или из Ы. Значение регистра МРС стабилизируется, и он сможет использоватьегодля вызоваследующей микрокоманды.
Наконец, нам нужна специальная запись, чтобы использовать бит JMPC:
goto (MBR OR value)
Эта запись сообщает микроассемблеру, что нужно использовать value (значение) для поля NEXT^ADDRESS и установить битJMPC, так чтобы MBR соединялся через схему ИЛИ с регистром МРС вместе со значением NEXT_ADDRESS. Если value равно 0, достаточно написать:
goto (MBR)
Отметим, что только 8 младших битов регистра MBR соединяются с регистром МРС (см. рис. 4.5), поэтому вопрос о знаковом расширении тут не возникает. Также отметим, что используется то значение MBR, которое доступно в конце текущего цикла.
Реализация IJVM с использованием Mic-1
Сейчас мы уже дошли до того момента, когда можно соединить все части вместе. В табл. 4.4-приводится микропрограмма, которая работает на микроархитектуре Mic-1 и интерпретирует IJVM. Программа очень короткая — всего 112 микрокоманд. Таблица состоит из трех столбцов. В первом столбце записано символическое обозначение микрокоманды, во втором — сама микрокоманда, а в третьем — комментарий. Как мы уже говорили, последовательность микрокоманд не обязательно соответствует последовательности адресов в управляющей памяти.
Выбор названий большинства регистров, изображенных нарис. 4.1, должен стать очевидным. Регистры СРР (Constant Pool Pointer — указатель набора констант), LV (Local Variable pointer — указатель фрейма локальных переменных) и SP (Stack Pointer — указатель стека) содержат указатели адресов набора констант, фрейма локальных переменных и верхнего элемента в стеке соответственно, а регистр PC (Program Counter — счетчик команд) содержит адрес байта, который нужно вызвать следующим из потока команд. Регистр MBR (Memory Buffer Register — буферный регистр памяти) — это 1-байтовый регистр, который содержит байты потока команд, поступающих из памяти для интерпретации. TOS и ОРС — дополнительные регистры. Они будут описаны ниже.
![](/html/1489/253/html_NIFD2oRbYJ.h1Nk/htmlconvd-Pm2c6r257x1.jpg)
Пример реализации микроархитектуры |
259 |
В определенные моменты в каждом из этих регистров обязательно находится определенное значение. Однако каждый из них также может использоваться в качестве временного регистра в случае необходимости. В начале и конце каждой команды регистр TOS (Top Of Stack register — регистр вершины стека) содержит значение адреса памяти, на который указывает SP. Это значение избыточно, поскольку его всегда можно считать из памяти, но если хранить это значение в регистре, то обращение к памяти не требуется. Для некоторых команд использование регистра TOS, напротив, влечет за собой больше обращений к памяти. Например, команда POP отбрасывает верхнее слово стека и, следовательно, должна вызвать новое значение вершины стека из памяти и записать его в регистр TOS.
ОРС — временный регистр. У него нет определенного заданного назначения. В нем, например, может храниться адрес кода операции для команды перехода, пока значение PC увеличивается, чтобы получить доступ к параметрам. Он также используется в качестве временного регистра в командах условного перехода.
Как и все интерпретаторы, микропрограмма, приведенная в табл. АЛ, включает в себя основной цикл, который вызывает, декодирует и выполняет команды интерпретируемой программы (в данном случае команды IJVM). Основной цикл начинается со строки Mainl, а именно с инварианта (утверждения), что в регистр PC уже загружен адрес ячейки памяти, в которой содержится код операции. Более того, этот код операции уже вызван из памяти в регистр MBR. Когда мы вернемся к этой ячейке, мы должны быть уверены, что значение PC уже обновлено и указывает на код следующей операции, а сам код операции уже вызван из памяти в MBR.
Такая последовательность действий имеет место в начале каждой команды, поэтому важно сделать ее как можно более короткой. Разрабатывая аппаратное и программное обеспечение микроархитектуры Mic-1, мы смогли сократить основной цикл до одной микрокоманды. Каждый раз, когда выполняется эта микрокоманда, код операции, которую нужно выполнить, уже находится в регистре MBR. Эта микрокоманда, во-первых, осуществляет переход к микрокоду, который выполняет данную операцию, а во-вторых, вызывает следующий после кода операции байт, который может быть либо операндом, либо кодом операции.
А теперь мы можем объяснить главную причину, почему в каждой микрокоманде явным образом указывается следующая микрокоманда и почему последовательность команд может и не соответствовать порядку их расположения в памяти. Все адреса управляющей памяти, соответствующие кодам операций, должны быть зарезервированы для первого слова интерпретатора соответствующей команды. Так, из табл. 4.2 мы видим, что программа, которая интерпретирует команду POP, начинается в ячейке 0x57, а программа, которая интерпретирует команду DUP, начинается в ячейке 0x59. (Как язык MAL узнает, что команду POP нужно поместить в ячейку 0x57, — одна из загадок Вселенной. Предположительно, где-то существует файл, который сообщает ему об этом.)
К сожалению, программа, интерпретирующая команду POP, включает в себя три микрокоманды, поэтому если их расположить в памяти последовательно, то эта программа смешается с началом команды DUP. Поскольку все адреса управляющей памяти, которые соответствуют кодам операций, зарезервированы, то все микрокоманды, идущие после первой микрокоманды в каждой последовательности, должны размещаться в промежутках между зарезервированными адресами. По этой
![](/html/1489/253/html_NIFD2oRbYJ.h1Nk/htmlconvd-Pm2c6r258x1.jpg)
260 Глава 4. Микроархитектурный уровень
причине происходит очень много «скачков», и было бы нерационально каждый раз вставлять микрокоманду перехода, чтобы перепрыгнуть от одной последовательности адресов к другой.
Чтобы понять, как работает интерпретатор, предположим, что регистр MBR содержит значение 0x60, то есть код операции IADO (см. табл. 4.2). В основном цикле, который состоит из одной микрокоманды, выполняется следующее:
1. Значение регистра PC увеличивается, и теперь он содержит адрес первого байта после кода операции.
2.Начинается передача следующего байта в регистр MBR. Этот байт понадобится рано или поздно либо в качестве операнда текущей команды IJVM, либо в качестве кода следующей операции (как в случае с командой IADD, у которой нет операндов).
3.Совершается переход к адресу, который содержался в регистре MBR в начале цикла Main 1. Номер адреса равен значению кода операции, которая выполняется в данный момент. Этот адрес был помещен туда предыдущей микрокомандой. Отметим, что значение, которое вызывается из памяти во время этой микрокоманды, не играет никакой роли в межуровневом переходе.
Здесь начинается вызов следующего байта, поэтому он будет доступен уже к концу третьей микрокоманды. В этот момент он, может быть, и не нужен, но он в любом случае когда-нибудь понадобится, поэтому не будет никакого вреда в том, что вызов начнется именно здесь.
Таблица 4.4. Микропрограмма для Mic-1
Микрокоманда |
Операции |
Комментарий |
|
Maini |
PC=PC+1; fetch; goto(MBR) |
MBR содержит код операции; получение |
|
|
|
следующего байта; отсылка |
|
пор! |
goto Maini |
Ничего не происходит |
|
iaddi |
|
||
MAR=SP=SP-1;rd |
Чтениеслова,идущегопослеверхнегословастека |
||
iadd2 |
H=TOS |
||
Н = вершина стека |
|||
|
|
||
iadd3 |
MDR=TOS=MDR+H;wr; |
Суммированиедвухверхнихслов;запись |
|
|
goto Main 1 |
суммывверхнююпозициюстека |
|
isubi |
MAR=SP=SP-1.rd |
Чтение слова, идущего после верхнего слова стека |
|
isub2 |
H=TOS |
Н = вершина стека |
|
isub3 |
MDR=TOS=MDR-H;wr; |
Вычитание;записьрезультатаввершинустека |
|
|
goto Maini |
|
|
iandi |
MAR=SP=SP-1;rd |
Чтениеслова,идущегопослеверхнегословастека |
|
iand2 |
H=TOS |
Н = вершина стека |
|
iand3 |
MDR=TOS=MDRHH,wr; |
ОперацияИ;записьрезультатаввершинустека |
|
|
goto Maini |
|
|
ior1 |
MAR=SP=SP-1;rd |
Чтениеслова,идущегопослеверхнегослова |
|
стека |
|
|
|
ior2 |
H=TOS |
Н = вершина стека |
|
ior3 |
М0Р=ТО5=МОРИЛИН; |
Операция ИЛИ; запись результата в вершину стека |
|
|
wr;gotoMaini |
|
|
duol |
MAR=SP=SP+1 |
УвеличениеSPна 1 и копирование результата |
|
|
|
в регистр MAR |
![](/html/1489/253/html_NIFD2oRbYJ.h1Nk/htmlconvd-Pm2c6r259x1.jpg)
![](/html/1489/253/html_NIFD2oRbYJ.h1Nk/htmlconvd-Pm2c6r260x1.jpg)