Как написать процессор на verilog

Обновлено: 03.07.2024

Добавим в счетчик функцию загрузки.
Загрузка осуществляется командой Counter_load.

В отдельном модуле создаем 4bit'ный регистр (аккумулятор).

Добавим в общую схему аккумулятор Acc, мультиплексор MUX2 и сумматор sum.
Сумматор прибавляет к числу в аккумуляторе Acc числа из памяти.
На сигнальные входы мультиплексора подаются числа data_in и sum.
Далее число из мультиплексора MUX2 загружается в аккумулятор Acc.

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

Замним двухвходовой мультиплексор четырёхвходовым.

Подключим к аккумулятору устройство вывода (4bit'ный регистр), также подключим к аккумулятору 2 флага:

1. Флаг «Ноль» — это лог. элемент 4ИЛИ-НЕ. Флаг поднимается, если содержимое Асс равно нулю.
2. Флаг «Ноль или Положительное число» — это лог. элемент НЕ на старшем разряде четырёхразрядного аккумулятора. Флаг поднимается, если содержимое Асс больше или равно нулю.


Добавим три команды
1. загрузка содержимого аккумулятора в устройство вывода data_out
2. загрузка адреса в счётчик, если поднят флаг «ноль» (JMP if Acc=0)
3. загрузка адреса в счётчик, если поднят флаг «ноль или положительное число» (JMP if Acc>=0)

Поместим команды и адреса в одно ОЗУ, а данные — в другое.

Схему можно скачать отсюда .

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

Отмечу, что загрузка числа в аккумулятор Асс должна производиться после переключения мультиплексора MUX (для команд ADD, SUB, LDA), по спаду тактового сигнала.

Т.о. в нашем компьютере следующая система команд

48х — ADD добавить число из ОЗУ к Асс
50х — SUB вычесть число, хранящееся в ОЗУ из Асс
80x — STA сохранить число из аккумулятора Асс в ОЗУ по адресу х
58х — LDA загрузить число из адреса х в Асс
04х — BRA безусловный переход в ячейку с адресом x
02х — BRZ переход в ячейку с адресом x, если Асс=0 (условный переход)
01x — BRP переход в ячейку с адресом x, если Асс>=0 (условный переход)
40х — INP загрузить число из data_input в Асс
20х — OUT загрузить число из Асс в data_out

Команды HLT у нас не будет.

Возьмём для примера алгоритм поиска максимального из двух чисел с сайта

Quartus II можно скачать с официального сайта .

При регистрации в разделе My Primary Job Function is* необходимо выбрать пункт Student.
Далее необходимо скачать драйвер для программатора (драйвер для usb-blaster'a можно установить из C:altera. quartusdriversusb-blaster).

Справочник статей

1. Требования к дизайну

Большинство современных компьютеров, будь то мэйнфреймы или микрокомпьютеры, имеют базовую структуру архитектуры фон Неймана, то есть компьютер состоит из пяти частей: контроллера, арифметического блока, памяти и устройства ввода / вывода. Инструкции и программы хранятся в памяти.


Центральный процессор, также называемый микропроцессором (микропроцессор), является ядром компьютерной системы. В основном выполняйте следующие задачи: (1) извлекайте инструкции из памяти и команды декодирования, (2) выполняйте простые операции арифметической логики, (3) передайте данные между процессором и памятью или вводом / выводом, (4) выполняйте программу Контроль и т. Д. [1].

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

2. Аппаратный состав


Изображение является ключевым компонентом микрокомпьютерной системы, включая процессор, память, шину данных и адреса. Центральный процессор состоит в основном из арифметико-логического блока (ALU, Arithmetic Logic Unit), аккумулятора (аккумулятора), общих регистров (регистров), счетчика программ (ПК, счетчика программ), регистра команд (IR, регистра команд), селектора адресов (мультиплексор адресов). )сочинение. Память здесь относится к основной памяти, которая разделена на оперативную память RAM (Radom Access Memory) и постоянную память ROM (Read Only Memory). Доступ к основной памяти и процессору осуществляется через шину, которая имеет два типа: адресная шина и адрес данных.

2.1 Память

2.1.1 ROM

ПЗУ используется для хранения инструкций, которые должны быть выполнены. Для введения инструкций см. Главу 3.

ПЗУ, инструкции только для чтения. Примите входной адрес, когда сигнал считывания и сигнал разрешения высоки, выводится соответствующая команда сохранения адреса, в противном случае выход остается в состоянии высокого импеданса. Адрес и данные имеют размер 8 бит, а размер адресуемой и внутренней памяти составляет 256 байт.


Синтез RTL (уровень передачи регистра, уровень передачи регистра), как показано выше.

2.2.2 RAM

Храните данные, читаемые и записываемые.


Вид RTL такой же, как и выше.

2.2 CPU

2.2.1 PC

Счетчик программ, иногда называемый регистром адресов команд (регистр адресов команд, IAR), соответствует Intel
Регистр указателя инструкций в процессоре X86. Его функция заключается в сохранении адреса смещения следующей инструкции, которая должна быть выполнена в текущем сегменте кода. В этой статье ПК автоматически изменяется контроллером, так что он всегда содержит адрес следующей инструкции, которая должна быть выполнена. Следовательно, ПК является регистром, используемым для управления потоком выполнения последовательности команд [2].

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


Вид RTL такой же, как и выше.

2.2.2 Аккумулятор

Аккумулятор используется для хранения промежуточного результата расчета.

Асинхронно очищается. Нарастающий фронт тактовых импульсов срабатывает, и текущий входной сигнал выводится, когда включен высокий уровень.


Представление RTL такое же, как и выше, видно, что это Q-триггер.

2.2.3 Селектор адресов

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

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


Вид RTL такой же, как и выше.

2.2.4 ALU

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


Вид RTL такой же, как и выше.

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

Регистр общего назначения, результат вывода ALU и вывод операнда регистром команд могут быть сохранены по конкретному адресу в регистре. Выведите данные, хранящиеся в регистре, на шину данных.

Когда сигнал записи действителен, входные данные (вывод из АЛУ) сохраняются по конкретному адресу в регистре. Когда сигнал чтения действителен, данные в определенном месте в регистре выводятся (на шину данных). Размер регистра составляет 32 байта.


Вид RTL такой же, как и выше.

2.2.6 IR

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


Вид RTL такой же, как и выше.

2.3 Внутренняя структура (всего)


На рисунке показана принципиальная схема внутренней структуры системы, показывающая взаимосвязь соединений между различными компонентами, шина данных и адресная шина являются ядром шинной системы. Адресная шина соединяет выход селектора адресов, входные клеммы ПЗУ и ОЗУ. Адресная шина соединена с выходом ПЗУ / ОЗУ, входом ИК и АЛУ и выходом общего регистра. Контроллер (вверху слева на рисунке) является блоком управления системы, подробнее см. Главу 4.

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


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

3. Набор инструкций

Мы определяем два типа типов длины набора команд RISC: короткую инструкцию и длинную инструкцию:



Среди них код инструкции представлен тремя битами, и всего существует 8 видов команд. Краткая инструкция имеет в общей сложности 8 битов, три старших бита - это код команды, а младшие пять бит - адрес общего регистра. Длинная команда составляет 16 битов, каждая длинная команда выбирается дважды, каждый раз, когда берутся 8 битов, первые 8 битов берутся первыми, формат такой же, как у короткой команды, а старшие 3 бита являются кодом команды, а нижние 5 битов являются общим адресом регистра; Младшие 8 бит берутся дважды, чтобы указать адрес ПЗУ или ОЗУ, в зависимости от кода инструкции.

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

INS Binary Description Type Comment
NOP 000 No operation Short Нет операции
LDO 001 Loads the contents of the ROM address into the REG address Long REG[reg_addr]<-ROM[ROM_addr]
LDA 010 Loads the contents of the RAM address into the REG address Long REG[reg_addr]<-RAM[RAM_addr]
STO 011 Store intermediate results into RAM address Long RAM[RAM_addr]<-REG[reg_addr]
PRE 100 Prefetch Data from REG address Short ACCUM<-REG[reg_addr]
ADD 101 Adds the contents of the REG address or integer to the accumulator Short accumulator<-REG[reg_addr]+ ACCUM
LDM 110 Load Multiple Short REG[reg_addr]<-ACCUM
HLT 111 Halt Short Стоп команда

4. Контроллер

Контроллер является ядром системы и выполняет следующие функции: выбор инструкций, организация очередей команд, операнды чтения и записи, управление шиной и т. Д. Здесь (конечный автомат Мили) используется для реализации контроллера. Инструкции хранятся в ПЗУ для выполнения. Контроллер принимает внешние часы и сигналы сброса. Контроллер передает состояние на основе текущего состояния и входа.

4.1 Диаграмма переходов между состояниями


В соответствии с задачей инструкции мы разработали диаграмму перехода состояний, как показано на рисунке выше, слева направо, состояние Sidle, S0

S12. Значение каждого состояния таково:

Source State Description Comment
S0 Load ir1 Извлечь инструкцию 1 (первая из короткой инструкции или длинной инструкции)
S1 PC+1 +1 за каждый выполненный ПК
S2 HLT время простоя
S3 Load ir2 Извлечь инструкцию 2
S4 PC+1 +1 за каждый выполненный ПК
S5 ROM/RAM to REG LDA/LDO
S6 Protect Защита от записи
S7 Read REG СТО Фаза 1
S8 Write RAM СТО Фаза 2
S9 Read REG PRE / ADD, Фаза 1
S10 Write ACCUM PRE / ADD, Фаза 2
S11 Write REG LDM
S12 Protect LDM
Sidle Reset перезагрузка

Переходы между различными состояниями:

S0 S1 S2 S3 S4 S5 S6 S7 S8 S9 S10 S11 S12 Sidle
S0 1 0 0 0 0 0 0 0 0 0 0 0 0 1
S1 0 1 0 0 0 0 0 0 0 0 0 0 0 1
S2 0 0 1 0 0 0 0 0 0 0 0 0 0 1
S3 0 0 0 1 0 0 0 0 0 0 0 0 0 1
S4 0 0 0 0 1 0 0 0 0 0 0 0 0 1
S5 0 0 0 0 0 1 0 0 0 0 0 0 0 1
S6 0 0 0 0 0 0 1 0 0 0 0 0 0 1
S7 0 0 0 0 0 0 0 1 0 0 0 0 0 1
S8 0 0 0 0 0 0 0 0 1 0 0 0 0 1
S9 0 0 0 0 0 0 0 0 0 1 0 0 0 1
S10 0 0 0 0 0 0 0 0 0 0 1 0 0 1
S11 0 0 0 0 0 0 0 0 0 0 0 1 0 1
S12 0 0 0 0 0 0 0 0 0 0 0 0 1 1
Sidle 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Source State Destination State Condition
S0 S1
S1 S0 (!ins[0]).(!ins[1]).(!ins[2])
S1 S3 (!ins[0]).(ins[1]).(!ins[2]) + (ins[0]).(!ins[2])
S1 S11 (!ins[0]).(ins[1]).(ins[2])
S1 S9 (!ins[1]).(ins[2])
S1 S2 (ins[0]).(ins[1]).(ins[2])
S2 S2
S3 S4
S4 S7 (!ins[0]).(!ins[1]) + (!ins[0]).(ins[1]).(ins[2]) + (ins[0]).(!ins[1]).(ins[2]) + (ins[0]).(ins[1])
S4 S5 (!ins[0]).(ins[1]).(!ins[2]) + (ins[0]).(!ins[1]).(!ins[2])
S5 S6
S6 S0
S7 S8
S8 S0
S9 S10
S10 S0
S11 S12
S12 S0
Sidle S0

Например, мы можем видеть переход состояний S0 и S1:



Пожалуйста, обратитесь к приложению fsm.pdf 。

Что касается реализации verilog проиллюстрированного конечного автомата, здесь принята классическая трехэтапная структура: регистр состояния, комбинационная логика следующего состояния и комбинационная логика вывода ,

4.2 Регистр состояния FSM

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

4.3 Следующая логика комбинации состояний в FSM

Ответственный за передачу состояния управления, здесь следующее состояние и текущее состояние state И вход ins Все связанные, принадлежащие к конечному автомату типа Мили.

4.4 Логика комбинации выходов FSM

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

Из-за длины, см. Приложение.

5. Тест и результаты

Чтобы проверить правильность работы процессора RISC, чип тестируется ниже.

5.1 Тестовые инструкции

Инструкции, хранящиеся в ПЗУ, следующие:

Инструкции выполняются по порядку, и окончательный результат заключается в добавлении трех чисел по 65, 66 и 67 битов в ПЗУ и сохранении их в ОЗУ [2], то есть при добавлении трех чисел одновременно в ОЗУ [1] Добавьте сумму первых двух чисел.

5.2 Test-Bench

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

Нужно только дать ЦП два сигнала, чтобы стимулировать тактовую частоту clk И сигнал асинхронного сброса rst 。

5.3 Форма волны


В соответствии с результатами моделирования ModelSIM, аккумулятор выводит конечный результат 179. Как показано на рисунке выше, перед последней командой останова (при 6300ps на рисунке) адрес addr равен 2, данные - 179, записывается значение оперативной памяти, сигнал разрешения равен 1, и окончательный сигнал Результат записывается в ОЗУ [2], и результат инструкции правильный.

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


Как показано на рисунке, из формы сигнала видно, что выполнение длинной команды LDO занимает 6 тактов, а команда NOP - два такта, что согласуется с диаграммой перехода состояний. Полученные результаты соответствуют требованиям к выходным данным команды тестирования.


Как показано на рисунке, два наиболее важных двух момента используются для проверки правильности функции. Из формы сигнала видно, что соответствующие результаты 126 и 179 вычислений записываются в адрес ОЗУ в первой и второй позициях соответственно, и соответствующие управляющие сигналы являются нормальными. То есть для достижения функции, которую мы разработали. Для получения дополнительной информации о сигналах см. Прикрепленный исходный файл.

6. Заключение

Ссылки

[1] Чжоу Хэцинь, Ву Сюцин. Принципы микрокомпьютера и интерфейсные технологии (третье издание). Научно-технический университет китайской прессы, 2008.

В статье описан очередной велосипед процессор.
Вместо обычных RISC/СISC процессор не обладает набором инструкций как таковым, только единственная инструкция копирования.
Подобные процессоры есть у Maxim серия MAXQ.

Для начала опишем ROM, память программ

двухпортовую RAM для памяти данных

и сам процессор

Ему, как минимум, нужен регистр счётчика команд, а также один вспомогательный регистр, ну и регистр IO порта, чтобы было что показать наружу из нашего процессора.

Счётчик команд и будет адресом для памяти программ.

Память программ удвоенной ширины содержит два адреса: куда и откуда скопировать данные в двухпортовой памяти данных.

Обозначим специальные адреса: счётчик команд, генератор констант, проверка на 0 (для условных переходов), операций сложения/вычитания и порт ввода-вывода, в данном случае пока только вывода.

Шины данных двух портов памяти не просто соединены между собой, а через мультиплексоры, которые и будут заодно выполнять роль АЛУ.
Один мультиплексор — на шине данных порта чтения, чтобы вместо памяти по определённым адресам читать счётчик команд (для относительных переходов), IO, и т.д.
Второй — на шине данных порта записи, чтобы не только перекладывать данные в памяти, но ещё и при записи по определённым адресам изменять их.

Вспомогательный регистр reg_reg, который используется для арифметических действий не доступен напрямую, но в него копируется результат выполнения каждой инструкции.
Таким образом для сложения двух значений из памяти надо одно из них сначала прочитать куда угодно, например, скопировать самого в себя (и заодно в reg_reg), а следующая команда записи по адресу сумматора запишет туда уже сумму с предыдущим значением.
Генератор констант записывает в себя адрес, а не значение памяти по этому адресу.
Для безусловных переходов надо просто скопировать нужный адрес в reg_pc, а для условных переходов зарезервируем ещё один адрес TST, который превращает любое ненулевое значение в 1, и заодно увеличивает счётчик команд на 2 вместо 1 для пропуска следующей за ним команды, если результат не 0.

Вот собственно и весь процессор.

Assembler

Теперь напишем для него простую программу, которая просто выдаёт последовательно значения в порт, и останавливается на 5.
Писать ассемблер самому, даже такой простой (весь синтаксис A = B), было лень, поэтому вместо этого за основу был взят готовый язык Lua, который очень хорошо подходит для построения различных Domain Specific Language на его основе, заодно на халяву получим готовый Lua препроцессор.
Cначала объявление специальных адресов, запись в которые изменяет данные и переменная счётчика по адресу 7

Вместо макросов можно использовать обычные функции Lua, правда из-за того что метатаблица окружения _G была изменена для отлавливания присваиваний (см. ниже), заодно отвалились и глобальные переменные: объявление нелокальной переменной some_variable = 0xAA наш ассемблер посчитает "своим" и попробует разобрать, вместо этого для объявлений глобальной переменной препроцессора придётся использовать rawset(_G, some_variable, 0xAA), который не трогает метаметоды.

Метки будем обозначать словом label и строковыми константами, в Lua в случае единственного строкового аргумента у функции скобки можно опустить.

Обнулим счётчик и регистр порта:

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

Добавляем недостающее до переполнения в 0 и, если там не ноль, переходим в начало, пропуская CG="exit", иначе заканчиваем в бесконечном цикле "exit".

А теперь и собственно сам ассемблер asm.lua, как положено в 20 строк:

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

В Lua нет метаметода для присвоения, но есть метаметоды для индексации существующих значений и для добавления новых, в том числе и для таблицы глобального окружения _G.
Так как __newindex срабатывает только для несуществующих в таблице значений, то вместо добавления новых элементов в _G, надо их куда-то перепрятать, не добавляя в _G, и, соответственно, достать оттуда, когда к ним обратились через __index.
Если имя уже существует, то добавляем данную инструкцию к остальным.

Ну и после выполнения программы ассемблера, когда сборщик мусора наконец придёт за массивом с нашей программой output, просто напечатаем её, заодно заменяя текстовые метки на правильные адреса.

Запустив lua53 test.lua > rom.txt (или онлайн) получим программу для процессора в машинных кодах.

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

Ещё один простой процессор на verilog - 1

Просимулировав с помощью iverilog -o test.vvp test.v откроем получившийся test.vcd в GTKWave:

порт считает до пяти, а потом процессор зацикливается.

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

На рисунке 1 изображено цифровое устройство, состоящее из нескольких 16-разрядных регистров, мультиплексора, сумматора/вычитателя, счетчика и устройства управления. Данные поступают в систему через 16-разрядную шину DIN.

Эти данные, через 16-разрядный мультиплексор, поступают в различные регистры: R0, …, R7 и A. Мультиплексор позволяет передавать данные из одного регистра в другой. Выход мультиплексора называется шиной (bus), т.к. этот термин часто употребляется для каналов передачи данных от одной системы в другую.

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


Рисунок 1. Процессорный модуль – структура

Система может выполнять различные операции на каждом такте, которые задаются устройством управления. Это устройство определяет месторасположение исходных данных, которые должны быть поданы на шину, и в какой из регистров они должны быть загружены. Например, если устройство управление получило сигналы R0 out и Ain, это значит, что мультиплексор должен передать содержимое R0 на шину, а по следующему активному фронту сигнала тактовой частоты эти данные должны быть записаны в регистр A.

Подобная система называется процессором. Он выполняет действия, описанные в виде команд. В таблице 1 представлен перечень команд, поддерживаемых разрабатываемым нами процессором. В левом столбце указаны имена команд и их операнды. Используемый синтаксис RX ← [RY] говорит о том, что содержимое регистра RY должно быть загружено в регистр RX. Команда mv (пересылка) позволяет копировать данные из одного регистра в другой. Команда mvi (непосредственная загрузка), представленная выражением RX ← D говорит о том, что 16-разрядная константа D должна быть загружена в регистр RX.


Таблица 1. Система команд процессора

Для выполнения таких команд, как сложение и вычитание понадобится более одного такта, из-за большого количества необходимых пересылок данных. Для этого устройство управления использует двухразрядный счетчик, показанный на рисунке1, управляющий порядком следования команд. Процессор выполняет команду, пришедшую на вход DIN, по сигналу Run и отвечает сигналом Done, когда команда выполнена. В таблице 2 приведены управляющие сигналы, которые выдаются во время выполнения команд из таблицы 1, и соответствующие им тактовые моменты времени. Обратите внимание, только управляющие команды сохраняются в регистре IRin во время нулевого такта, поэтому он и не показан в данной таблице.


Таблица 2. Управляющие сигналы, устанавливаемые при выполнении команд

Задание 1

1. Создайте проект, реализующий процессорное устройство.

2. Проверьте работоспособность устройства с помощью моделирующей программы (с использованием тестового файла).

Задание 2

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


Рисунок 2. Добавление блока памяти к процессорному устройству.

2. В данном устройстве счетчик Counter используется для формирования адреса блока памяти Memory. Процессорное устройство и блок памяти работают каждый со своей тактовой частотой – Pclock и MClock, соответственно.

3. Для записи программы в блок памяти создайте тестовый файл инициализации содержимого блока памяти .

4. Проверьте работоспособность устройства с помощью моделирующей программы.

Читайте также: