Пример микроархитектуры
243
Отметим, что в любом случае регистр МРС может принять только одно из двух
возмол.чых значений:
1. Значение NEXT_ADDRESS.
2. Значение NEXT_ADDRESS со старшим битом, соединенным с логической
единицей операцией ИЛИ.
Других значений не существует. Если старший бит значения NEXT_ADDRESS
уже равен 1, нет смысла использовать JAMN или JAMZ.
Отметим, что если все биты JAM равны 0, то адрес следующей команды — про-
сто 9-битный номер в поле NEXT_ADDRESS- Если JAMN или JAMZ равны 1, то
существует два потенциально возможных адреса следующей микрокоманды: NEXT_
ADDRESS и NEXT_ADDRESS, соединенный операцией ИЛИ с 0x100 (предпо-
лагается, что NEXT_ADDRESS<OxFF). (Отметим, что Ох указывает, что число,
следующее за ним, дается в шестнадцатеричной системе счисления). Это проил-
люстрировано рис. 4.6. Текущая микрокоманда с адресом 0x75 содержит поле
NEXT_ADDRESS=0x92, причем бит JAMZ установлен на 1. Следовательно, сле-
дующий адрес микрокоманды зависит от значения бита Z, сохраненного при пре-
дыдущей операции АЛУ. Если бит Z равен 0, то следующая микрокоманда имеет
адрес 0x92. Если бит Z равен 1, то следующая микрокоманда имеет адрес 0x192.
Третий бит в поле JAM — JMPC. Если он установлен, то 8 битов регистра MBR
поразрядно связываются операцией ИЛИ с 8 младшими битами поля NEXT_
ADDRESS из текущей микрокоманды. Результат отправляется в регистр МРС.
На рис. 4.5 значком «ИЛИ» обозначена схема, которая выполняет операцию ИЛИ
над MBR и NEXT_ADDRESS, если бит JMPC равен 1, и просто отправляет
NEXT_ADDRESS в регистр МРС, если бит JMPC равен 0. Если JMPC равен 1, то
младшие 8 битов поля NEXT_ADDRESS равны 0. Старший бит может быть 0 или 1,
поэтому значение поля NEXT_ADDRESS обычно 0x000 или 0x100. Почему ино-
гда используется 0x000, а иногда — 0x100, мы обсудим позже.
Адрес
0x75
0x92
0x192
Add r
0x92
JAM
001
Биты управления трактом данных
;
;
Набор
битов JAMZ
Один из этих
адресов
последует
за 0x73
в зависимости
O T Z
Рис. 4.6. Микрокоманда с битом JAMZ, равным 1, указывает на две потенциальные
последующие микрокоманды
Возможность выполнять операцию ИЛИ над MBR и NEXT_ADDRESS и со-
хранять результат в регистре МРС позволяет реализовывать межуровневые пере-
ходы. Отметим, что по битам, находящимся в регистре MBR, можно определить
любой адрес из 256 возможных. Регистр MBR содержит код операции, поэтому
использование JMPC приведет к единственно возможному выбору следующей
244 Глава 4. Микроархитектурный уровень
микрокоманды. Этот метод позволяет осуществлять быстрый переход у функции,
соответствующей вызванному коду операции.
Для того чтобы продолжить чтение этой главы, очень важно понимать принци-
пы синхронизации машины, поэтому повторим их еще раз. Синхронизирующий
сигнал делится на подциклы, хотя внешние изменения этого сигнала происходят
только на заднем фронте, с которого начинается цикл, и на нарастающем фронте,
который загружает регистры и триггеры N и Z. Посмотрите еще раз на рис. 4.2.
Во время подцикла 1, который инициируется задним фронтом сигнала, адрес,
находящийся в данный момент в регистре МРС, загружается в регистр MIR. Во вре-
мя подцикла 2 регистр MIR выдает сигналы и в шину В загружается выбранный
регистр. Во время подцикла 3 происходит работа АЛУ и схемы сдвига. Во время
подцикла 4 стабилизируются значения шины С, шин памяти и АЛУ. На нарастаю-
щем фронте сигнала загружаются регистры из шины С, загружаются триггеры N и Z,
а регистры MBR и MDR получают результаты работы памяти, начавшейся в кон-
це предыдущего цикла (если эти результаты вообще имеются). Как только регистр
MBR получает свое значение, загружается регистр МРС. Это происходит где-то в
середине отрезка между нарастающим и задним фронтами, но уже после загрузки
MBR/MDR. Он может загружаться уровнем сигнала (но не фронтом сигнала) либо
загружаться через фиксированный отрезок времени после нарастающего фронта.
Все это означает, что регистр МРС не получает своего значения до тех пор, пока не
будут готовы регистры MBR, N и Z, от которых он зависит. На заднем фронте сиг-
нала, когда начинается новый цикл, регистр МРС может обращаться к памяти.
Отметим, что каждый цикл является самодостаточным. В каждом цикле опре-
деляется, значение какого регистра должно поступать на шину В, что должны де-
лать АЛУ и схема сдвига, куда нужно сохранить значение шины С, и, наконец,
каким должно быть следующее значение регистра МРС.
Следует сделать еще одно замечание по поводу рис. 4.5. До сих пор мы считали
МРС регистром, который состоит из 9 битов и загружается на высоком уровне сиг-
нала. В действительности этот регистр вообще не нужен. Все его входные сигналы
можно непосредственно связать с управляющей памятью. Поскольку они имеют-
ся в управляющей памяти на заднем фронте синхронизирующего сигнала, когда
выбирается и считывается регистр MIR, этого достаточно. Их не нужно хранить в
регистре МРС. По этой причине МРС может быть реализован в виде
виртуально-
го регистра,
который представляет собой просто место скопления сигналов и по-
хож скорее на коммутационное поле, чем на настоящий регистр. Если МРС сде-
лать виртуальным регистром, то процедура синхронизации сильно упрощается:
теперь события происходят только на нарастающем фронте и заднем фронте сиг-
нала. Но если вам проще считать МРС реальным регистром, то такой подход тоже
вполне допустим.
Пример архитектуры команд: IJVM
Чтобы продолжить описание нашего примера, введем уровень набора команд, ко-
торые должна интерпретировать микропрограмма машины IJVM (см. рис. 4.5). Для
удобства уровень архитектуры команд мы иногда будем называть
макроархитек-
Пример архитектуры команд. IJVM
245
турой,
чтобы противопоставить его микроархитектуре. Однако перед тем как при-
ступать к описанию IJVM, мы немного отвлечемся.
Стек
Во всех языках программирования есть понятие процедур с локальными перемен-
ными Эти переменные доступны во время выполнения процедуры, но перестают
быть доступными после окончания процедуры. Возникает вопрос: где должны хра-
ниться такие переменные?
К сожалению, предоставить каждой переменной абсолютный адрес в памяти
невозможно. Проблема заключается в том, что процедура может вызывать себя
сама. Мы рассмотрим такие рекурсивные процедуры в главе 5. А пока достаточно
сказать, что если процедура вызывается дважды, то хранить ее переменные под
конкретными адресами в памяти нельзя, поскольку второй вызов нарушит резуль-
таты первого.
Вместо этого используется другая стратегия. Для переменных резервируется
особая область памяти, которая называется
стеком,
но отдельные переменные не
получают в нем абсолютных адресов. Какой-либо регистр, скажем, LV, указывает
на базовый адрес локальных переменных для текущей процедуры. Рассмотрим
рис. 4.7,
а.
В данном случае вызывается процедура А с локальными переменными
а1,а2и аЗ,
и для этих переменных резервируется участок памяти, начинающийся
с адреса, который указывается регистром LV. Другой регистр, SP, указывает на
старшее слово локальных переменных процедуры А. Если значение регистра LV
равно 100, а слова состоят из 4 байтов, то значение SP будет 108. Для обращения к
переменной нужно вычислить ее смещение от адреса LV. Структура данных меж-
ду LV и SP (включая оба указанных слова) называется
фреймом локальных
переменных.
SP
LV
аЗ
а2
а1
SP-«~
LV -»~
108
104
100
Ь4
ЬЗ
Ь2
Ы
аЗ
а2
а1
S P - -
LV -»-
d
Ь4
ЬЗ
Ь2
Ы
аЗ
а2
а1
SP
LV
d5
d4
d3
d2
d1
a3
a2
a1
Рис, 4.7. Стек для хранения локальных переменных во время процедуры
А
(а); после того как
процедура
А
вызывает процедуру
В
(б), после того как процедура
В
вызывает процедуру С (в);
после того как процедуры С и в прекращаются, а процедура
А
вызывает процедуру
D
(г)
А теперь давайте посмотрим, что происходит, если процедура
А
вызывает дру-
гую процедуру,
В.
Где должны храниться 4 локальные переменные процедуры
В
(Ы, Ь2, ЬЗ, Ь4)?
Ответ: в стеке, расположенном над стеком для процедуры
Л,
как
показано на рис. 4.7,
6.
Отметим, что после вызова процедуры регистр LV указыва-
ет уже на локальные переменные процедуры
В.
Обращаться к локальным пере-
менным процедуры
В
можно по их сдвигу от LV. Если процедура
В
вызывает про-
246
Глава 4. Микроархитектурный уровень
цедуру
С,
то регистры LV и SP снова переопределяются и указывают на местона-
хождение локальных переменных процедуры
С,
как показано на рис. 4.7,
в.
Когда процедура С завершается,
В
снова активизируется и стек возвращается в
прежнее состояние (см. рис. 4.7,
б),
так что LV теперь указывает на локальные пере-
менные процедуры
В.
Когда процедура
В
завершается, стек переходит в исходное
состояние (см. рис. 4.7,
а).
При любых условиях LV указывает на базовый адрес
стекового фрейма для текущей процедуры, a SP — на верхнее слово этого фрейма.
Предположим, что процедура
А
вызывает процедуру
D,
которая содержит
5 локальных переменных. Соответствующий стек показан на рис. 4.7,
г.
Локаль-
ные переменные процедуры
D
используют участок памяти процедуры
В
и часть
стека процедуры
С.
В памяти с такой организацией размещаются только текущие
процедуры. Когда процедура завершена, отведенный для нее участок памяти осво-
бождается.
Но стек используется не только для хранения локальных переменных, а также
и для хранения операндов во время вычисления арифметических выражений.
Такой стек называется
стеком операндов.
Предположим, что перед вызовом про-
цедуры
В
процедура Л должна произвести следующее вычисление:
а1=а2+аЗ
Чтобы вычислить эту сумму, можно поместить
а2
в стек, как показано на
рис. 4.8,
а.
Тогда значение регистра SP увеличится на число, равное количеству
байтов в слове (скажем, на 4), и будет указывать на адрес первого операнда. Затем
в стек помещается переменная
аЗ,
как показано на рис. 4.8,
б.
Отметим, что назва-
ния процедур и переменных выбираются пользователем, а названия регистров и
кодов операций встроены. Названия процедур и переменных мы выделяем в тексте
курсивом.
Теперь можно произвести вычисление, выполнив команду, которая выталки-
вает два слова из стека, складывает их и помещает результат обратно в стек, как
показано на рис. 4.8,
в.
После этого верхнее слово можно вытолкнуть из стека и по-
местить его в локальную переменную
а1,
как показано на рис. 4.8,
г.
SP
LV
аЗ
а2
а1
SP"
LV
а2
аЗ
а2
а1
SP-
LV
а2+аЗ
аЗ
а2
а1
SP-
LV
аЗ
а2
а2+аЗ
а б в г
Рис. 4.8. Использование стека операндов для арифметических действий
Фреймы локальных переменных и стеки операндов могут смешиваться. Напри-
мер, когда вызывается функция i при вычислении выражения x
2
+f(x), часть этого
выражения (х
2
) может находиться в стеке операндов. Результат вычисления функ-
ции остается в стеке над х
2
, чтобы следующая команда сложила их.
Следует упомянуть, что все машины используют стек для хранения локальных
переменных, но не все используют его для операндов. В большинстве машин нет
стека операндов, но у JVM и IJVM он есть. Стековые операции мы рассмотрим
подробно в главе 5.
Пример архитектуры команд: IJVM 247
Модель памяти IJVM
А теперь мы можем рассмотреть архитектуру
IJVM.
Она состоит из памяти, кото-
рую можно рассматривать либо как массив из 4 294 967 296 байтов (4 Гбайт), либо
как массив из 1 073 741 824 слов, каждое из которых содержит 4 байта. В отличие
от большинства архитектур команд, виртуальная машина Java не совершает об-
ращений к памяти, видимых на уровне команд, но здесь существует несколько не-
явных адресов, которые составляют основу для указателя. Команды IJVM могут
обращаться к памяти только через эти указатели. Определены следующие области
памяти:
1.
Набор констант.
Эта область состоит из констант, цепочек и указателей на
другие области памяти, на которые можно делать ссылку. Данная область
загружается в тот момент, когда программа загружается из памяти, и после
этого не меняется. Существует неявный регистр СРР (Constant Pool Pointer —
указатель набора констант), который содержит адрес первого слова набора
констант.
2.
Фрейм локальных переменных.
Эта область предназначена для хранения пе-
ременных во время выполнения процедуры. Она называется
фреймом ло-
кальных переменных.
В начале этого фрейма располагаются параметры (или
аргументы) вызванной процедуры. Фрейм локальных переменных не вклю-
чает в себя стек операндов. Он помещается отдельно. Исходя из соображений
производительности, мы поместили стек операндов прямо над фреймом
локальных переменных. Существует неявный регистр, который содержит
адрес первой переменной фрейма. Мы назовем этот регистр LV (Local
Variable — локальная переменная). Параметры вызванной процедуры хра-
нятся в начале фрейма локальных переменных.
3.
Стек операндов.
Стек операндов не должен превышать определенный раз-
мер, который заранее вычисляется компилятором Java. Пространство стека
операндов располагается прямо над фреймом локальных переменных, как
показано на рис. 4.9. В данном случае стек операндов удобно считать частью
фрейма локальных переменных. В любом случае существует виртуальный
регистр, который содержит адрес верхнего слова стека. Отметим, что в от-
личие от регистров СРР и LV этот указатель меняется во время выполнения
процедуры, поскольку операнды помещаются в стек и выталкиваются из него.
4.
Область процедур.
Наконец, существует область памяти, в которой содер-
жится программа. Есть виртуальный регистр, содержащий адрес команды,
которая будет вызвана следующей. Этот указатель называется счетчиком
команд, или PC (Program Counter). В отличие от других участков памяти,
область процедуры представляет собой массив байтов.
Следует сделать одно примечание по поводу указателей. Регистры СРР, LV и SP
указывают на
слова,
а не на
байты,
и смещения происходят на определенное число
слов. Например, LV, LV+1 и LV+2 указывают на первые три слова из фрейма ло-
кальных переменных, a LV, LV+4 и LV+8 — на слова, расположенные на расстоя-
нии четырех слов (16 байтов) друг от друга.