Изменения в документе:
05.12.09: мелкие стилистические правки
--------- 1.04
04.12.09: проведены некоторые несущественные для содержания правки
04.12.09: дополнения в описании адресного интерпретатора
04.12.09: дополнения в системе команд
03.12.09: исправлена таблица системы команд (приведена в соответствие с реальным VHDL кодом процессора)
--------- 1.03
31.10.09: изменена система обозначений процессора E16 -> 1E16, Е32 -> 1E32, E64 -> 3E64
31.10.09: добавлена информация о многоядерных вариантах процессора 3Е16, 3Е32, 3Е64
31.10.09: введены изменения в описание системы команд в соответствии с текущим состоянием проекта
--------- 1.02
04.07.09: добавлено содержание со ссылками на разные части документа и простейшей навигацией
04.07.09: изменен формат данного документа для просмотра на менее широких мониторах
--------- 1.01
03.07.09: исправлено описание команды ONE
03.07.09: изменена таблица результатов разводки
03.07.09: расширено описание адресного интерпретатора
03.07.09: добавлены ссылки на код процессора и его обсуждение на форуме
--------- 1.00
02.07.09: первая версия этого описания
Содержание
Введение
Архитектура процессора
Адресный интерпретатор
Результаты разводки процессора в Quartus II
Система команд процессора
Многоядерный вариант процессора EQUINOX
к содержанию
Введение
Название процессора EQUINOX происходит от дня, в который он был задуман − день равноденствия.
Разработка полностью российская. Автор - Макарченко И.П. (Ivan Mak aka WingLion).
Исходный код процессора на языке AHDL (ALTERA Hardware Design Language) свободен для доступа и обсуждается здесь: http://fforum.winglion.ru/viewtopic.php?t=1517
Ссылка на зип-файл с исходным кодом последней версии: http://winglion.ru/equinox/fcpu7.zip
Кодовые названия (1E16, 1E32, 1E64) содержaт в себе указание на варианты ширины шины данных и адреса − 16, 32 и 64. В AHDL описании ширина шин задается как параметр и должна быть кратна четырем, т.к. система команд процессора является четырехбитной.
к содержанию
Архитектура процессора
Процессор основан на стековой архитектуре. EQUINOX имеет два аппаратных стека: стек данных и стек возвратов, глубина которых задается параметром AHDL-описания. В начальной конфигурации она составляет 8 элементов стека данных и 8 элементов стека возвратов. Стек данных предназначен для хранения обрабатываемых данных, а стек возвратов используется для адресов возврата из подпрограмм и временного хранения данных.
Два внутренних регистра процессора по сути являются вершинами соответствующих стеков: PC - вершина стека возвратов, TR - вершина стека данных.
|
Рис.1 Архитектура процессора EQUINOX.
|
Рис.2 Обобщенный базовый формат команды процессора EQUINOX. Для вариантов E16, E32 и E64, соответственно, 4, 8 и 16 опкодов в слове.
Команды загружаются в процессор группами, упакованными в машинное слово, и исполняются последовательно от младшей тетрады бит к старшей.
Если среди загруженных команд появляются литеральные команды, требующие непосредственных операндов, они загружаются в процессе исполнения группы команд в тот момент, когда приходит время исполнения литеральной команды. Данные для нее берутся из следующих адресов кода программы. Если в группе несколько литеральных команд, данные для них следуют за кодовой группой последовательно в соответствии с положением литеральных команд в группе.
Так, группа команд LIT LIT ! NOP будет закодирована в слово 0A11 вслед за которым в коде будут находиться два литерала, которые будут взяты командами LIT и положены в стек данных, после чего произойдет исполнение команды !. Затем по команде NOP произойдет загрузка следующей группы команд из адреса кода, следующего за последним операндом. Таким образом одной группой команд в стек могут быть загружены до 4 операндов для 16-битного варианта процессора, до 8 операндов для 32-битного и до 16 операндов для 64-битного.
Регистр команд процессора устроен подобно регистру сдвига, сдвигающего за один такт сразу 4 бита, таким образом в младших битах регистра сдвига на каждом такте оказывается какая-либо команда - либо одна из тех, что была загружена в регистр команд ранее, либо команда NOP с кодом 0, которая вдвигается в регистр сдвига с обратной стороны на каждом такте, когда нет загрузки регистра команд. Именно поэтому в систему команд процессора введена такая, казалось бы ненужная команда, как NOP. В действительности она осуществляет загрузку регистра команд и, таким образом, обеспечивает непрерывное поступление команд на исполнительную часть процессора.
Команды с префиксами состоят из двух четверок бит (нибблов) - префикса и модификатора. Если префикс попадает в старшую тетраду командного слова, его модификатор оказывается равным нулю(!). В данный момент модификаторы используются не все. Те модификаторы, которые не описаны в таблице системы команд, остаются зарезервированными.
Исполнение команд с префиксами сдвигает регистр команд сразу на две четверки бит, и на следующем такте, после появления префикса, исполняется команда, следующая за префиксом и его модификатором.
| |
к содержанию
Адресный интерпретатор
Процессор разработан таким образом, чтобы Адресный интерпретатор не требовал для своей работы дополнительных манипуляций. Использование адресного интерпретатора обусловлено в первую очередь уменьшением объема кода и увеличением скорости исполнения последовательности вызовов подпрограмм в подобном режиме.
Объем кода уменьшается за счет отсутствия кодов команды CALL перед каждым адресом в последовательности вызовов. Увеличение скорости исполнения обеспечивается аппаратной реализацией команды NEXT, которая по сути исполняет за один такт команды RET и CALL. Адрес перехода для последней берется из кода программы верхнего уровня, адрес которого сохранен в стеке возвратов в момент исполнения первого CALL.
Вызов адресного интерпретатора производится командой CALL, вслед за которой следуют адреса исполняемых программ. Каждое слово, исполняемое адресным интерпретатором должно заканчиваться командой NEXT. В этом случае по окончании выполнения программы производится немедленный вызов следующей программы. Если программа закончится командой RET, то произойдет выход из адресного интерпретатора и возврат к командному режиму исполнения программы.
Вложенная адресная интерпретация допустима и возникает, если в выполняемой программе появляется новый вызов CALL.
|
| |
Комментарии к схеме работы адресного интерпретатора:
1. Вызов адресного интерпретатора производится командой CALL за которой следует список адресов, по которым производится вызовы функций. Каждая функция заканчивается командой NEXT, которая выбирает следующий адрес из списка адресов верхнего уровня и передает его на исполнение.
2. В режиме обычного подпрограммного механизма подпрограммы завершаются командой RET. Возврат происходит на код, следующий после адреса вызова. Все команды, которые находятся после CALL в том же слове, где и код CALL - игнорируются. Адрес четверки, определяющий текущую команду в слове - отсутствует, а четверки выделяются путем сдвига командного слова в регистре команд.
3. Если команда RET встречается в слове из списка адресной интерпретации, то она работает подобно команде CODE которая переключает исполнение из режима адресного интерпретатора в нормальный режим, в котором исполняется непосредственно процессорный код.
4. Aдрес подпрограммы с единственной командой RET играет роль команды выхода из режиме адресного интерпретатора.
5. Если надо оформить некую подпрограмму, как слово, вызываемое через адресный интерпретатор, ее надо заключить в "оболочку", вызыващую эту подпрограмму отдельно и заканчивающуюся командой NEXT.
MyWORD: код подпрограммы
RET \ стандартное завершение подпрограммы
CALL MyWORD вызов подпрограммы из обычного кода
-- \ сюда происходит возврат
MyWORD_A: \ слово MyWORD оформленное для исполнения через адресный интерпретатор
CALL MyWORD вызов слова
NEXT \ сюда происходит возврат из MyWORD после чего
\ происходит переход на следующее слово из списка адресной интерпретации
\ список адресной интерпретации с использованием вызова подпрограммы MyWORD
WORDxx
MyWORD_A
WORDyy
|
к содержанию
Результаты разводки процессора в Quartus II
Результаты разводки процессора на Quartus II 9.0 Web Edition в различных режимах для разных ПЛИС.
|
Режим | Чип | Объем, LE-/-EMB (% объема чипа) | частота, MHz | глубина стеков |
.1E12 | EP2C20F484C7 | 597-/-0 (3%) | 164.12 | 8 |
.1E12 | EP2C20F484C7 | 501-/-0 (3%) | 164.12 | 4 |
| | | | |
.1E16 | EP2C20F484C7 | 794-/-0 (4%) | 159.08 | 8 |
.1E16 | EP2C20F484C7 | 667-/-0 (4%) | 162.26 | 4 |
.1E16 | EP1K30QC208-1 | 997-/-0 (58%) | 78.74 | 8 |
.1E16mul* | EP2C20F484C7 | 794-/-2 (4%) | 97.21 | 8 |
.1E16 | Stratix II GX | 413ALUT | 263.30 | 8 |
.1E16 | Stratix III | 407ALUT | 411.18 | 8 |
.1E16 | EP3C40Q240C8 | 811 | 178.16 | 8 |
| | | | |
.1Е32 | EP2C20F484C7 | 1517-/-0 (8%) | 129.37 | 8 |
.1Е32 | EP2C20F484C7 | 1261-/-0 (7%) | 131.58 | 4 |
.1Е32mul* | EP2C20F484C7 | 1608-/-6 (9%) | 70.88 | 8 |
.1E32 | Stratix II GX | 785ALUT | 230.89 | 8 |
.1E32 | Stratix III | 779ALUT | 345.78 | 8 |
.1E32 | EP3C40Q240C8 | 1553 | 148.96 | 8 |
| | | | |
.1Е64 | EP2C20F484C7 | 3010-/-0(16%) | 98.74 | 8 |
.1Е64 | EP2C20F484C7 | 2566-/-0(16%) | 100.39 | 4 |
.1Е64mul* | EP2C20F484C7 | 3342-/-20(18%) | 56.24 | 8 |
* Суффикс mul в режиме означает, что в системе команд
присутствует команда умножения (при ее отключении увеличивается предельная рабочая частота, что может быть важно в ряде случаев) |
|
к содержанию
Система команд процессора
Таблица 1. Команды процессоров Е16(E32,E64) |
|
код | Мнемоника | краткое описание | стек данных | стек возвратов |
0, | NOP | нет операции | DS: ( −− ) | RS: ( −− ) |
1, | LIT | загрузка литерала | DS: ( −− lit ) | RS: ( −− ) |
2, | CALL | вызов подпрограммы (адресного интерпретатора) | ( −− ) | ( −− PC+1 ) |
3, | RET | возврат из подпрограммы | ( −− ) | ( Adr −− ) |
4, | IF | условный переход, если вершина стека данных равна 0 | ( Cond −− ) | ( −− ) |
5, | DUP# | префикс операции типа DUP (глубина использования стека увеличивается) | ( −− Xxx ) | ( −− ) |
5,0, | DUP | дублировать вершину стека данных | ( Data −− Data Data ) | ( −− ) |
5,1, | OVER | копировать подвершину стека данных | ( Data2 Data1 −− Data2 Data1 Data2 ) | ( −− ) |
5,2, | TRUE | положить на вершину стека данных число 0 | ( −− 0 ) | ( −− ) |
5,3, | FALSE | положить на вершину стека данных число −1 | ( −− −1 ) | ( −− ) |
5,4, | ONE | положить на вершину стека данных число 1 | ( −− 1 ) | ( −− ) |
5,x, | DUP | RESERVED - дублировать вершину стека данных | ( Data −− Data Data ) | ( −− ) |
6 | SWAP# | префикс операции типа SWAP (глубина использования стека не меняется) | (−− ) | ( −− ) |
6,0, | SWAP | поменять местами два верхних элемента стека | ( Data2 Data1 −− Data1 Data2 ) | ( −− ) |
6,1, | INC | увеличить верхний элемент на единицу | ( Data1 −− Data1+1 ) | ( −− ) |
6,2, | DEC | уменьшить верхний элемент на единицу | ( Data1 −− Data1−1 ) | ( −− ) |
6,3, | MUL | перемножить два верхних элемента стека | ( Data2 Data1 −− Data2 × Data1 ) | ( −− ) |
6,4, | BSWAP | поменять местами старшую и младшую половины в верхнем элементе стека | ( Data1 −− Data1* ) | ( −− ) |
6,5, | LCONV | логическое конвертирование | ( Data1 −− 0 если Data1<>0 | −1 иначе ) | ( −− ) |
6,x, | SWAP | RESERVED - поменять местами два верхних элемента стека | ( Data2 Data1 −− Data1 Data2 ) | ( −− ) |
7 | DROP# | префикс операции типа DROP (глубина использования стека уменьшается) | ( −− ) | ( −− ) |
7,0, | DROP | удалить верхний элемент стека | ( Data2 Data1 −− Data2 ) | ( −− ) |
7,1, | ADD | сложить два верхних элемента стека
| ( Data2 Data1 −− Data2+Data1 ) | ( −− ) |
7,2, | SUB | Вычесть верхний элемент из нижнего | ( Data2 Data1 −− Data2−Data1 ) | ( −− ) |
7,3, | AND | поразрядное AND верхнего и нижнего элементов стека | ( Data2 Data1 −− Data2 and Data1 ) | ( −− ) |
7,4, | OR | поразрядное OR верхнего и нижнего элементов стека | ( Data2 Data1 −− Data2 or Data1 ) | ( −− ) |
7,5, | XOR | поразрядное XOR верхнего и нижнего элементов стека | ( Data2 Data1 −− Data2 xor Data1 ) | ( −− ) |
7,x, | DROP | RESERVED - удалить верхний элемент стека | ( Data2 Data1 −− Data2 ) | ( −− ) |
8, | @ | разыменовать - прочитать данное из памяти по адресу с вершины стека | ( Addr −− Mem[Addr] ) | ( −− ) |
9, | ! | присвоить - записать в память по адресу с вершины стека данное из подвершины стека | ( Data Addr −− ) | ( −− ) |
А, | >R | переместить вершину стека данных на стек возвратов | ( Data −− ) | ( −− Data ) |
B, | R> | переместить вершину стека возвратов на стек данных | ( −− Data ) | ( Data −− ) |
C, | MOVE | копировать блок данных** | ( −− ) | ( −− ) |
D, | NEXT | закончить исполнение слова и перейти на следующее (операция эквивалентная RET+CALL) | ( −− ) | (AdInt −− AdInt+1) |
E, | NOP* | RESERVED | ( −− ) | ( −− ) |
F, | NOP* | RESERVED | ( −− ) | ( −− ) |
|
к содержанию
Многоядерный вариант процессора EQUINOX
Доступ к внутренней памяти ПЛИС требует временных затрат. В синхроном режиме данные из ячейки по выставленному адресу появляются на выходе памяти только через два такта. Один такт на защелкивание адреса и один на защелкивание выходных данных. Такова реальность ПЛИС фирмы ALTERA. В связи с этим при построении встроенной системы на основе форт-процессора оказывается, что невозможно получить процессор, исполняющий одну команду за один такт, если длительность этого такта равна пределу, на котором работает внутреннее ОЗУ. Время доступа к ОЗУ составляет 2 такта плюс один такт на выполнение операций с полученными из памяти данными, и получается, что минимальный цикл процессора составляет 3 такта, что не так уж плохо при условии, что тактовая частота, на которой работает память в ПЛИС довольно высока - сотни мегагерц.
Тем не менее, для получения более высоких показателей скорости обработки информации имеет смысл конвееризовать процессор таким образом, чтобы за один такт исполнялась одна команда. Для этого был разработан трехъядерный фортпроцессор, у которого каждый такт исполняется команда одного из трех ядер. Остальные при этом ждут поступления данных из ОЗУ для того чтобы в свой рабочий такт выполнить свою команду.
Для получения такого эффекта все регистры процессора были утроены, а логика оставлена без изменений. Регистры соединены в конвеерное кольцо и данные в них каждый такт смещаются на одну позицию. В первой позиции при смещении данных по конвееру происходит их обработка логической схемой процессора. Таким образом одна логическая схема "обслуживает" столько ядер, сколько имеется позиций конвеера. Перемещение данных происходит синхронно, поэтому на каждой ступени каждый такт находятся данные одного из ядер.
Скорость работы каждого ядра такого многоядерного процессора составляет 1/N от частоты синхронизации конвеера, т.е. при трех ступенях - она составляет 1/3 общей частоты. Суммарная же скорость работы равна ровно тактовой частоте, что существенно выше, чем при одноядерном построении процессора.
Варианты многоядерных процессоров обозначаются кодами xE16,xE32,xE64, где x - количество ядер. В данный момент реально в ПЛИС существует вариант 3E16, а варианты 3Е32 и 3E64 только компилировались для проверки, но не были использованы в работе.
В данный момент (декабрь 2009г) проверка работы процессора EQUINOX ведется на варианте 3E16 с одним включенным ядром.
|