mirror of
https://github.com/MPSU/APS.git
synced 2025-09-16 09:40:10 +00:00
Initial commit
This commit is contained in:
351
Labs/04. Primitive programmable device/README.md
Normal file
351
Labs/04. Primitive programmable device/README.md
Normal file
@@ -0,0 +1,351 @@
|
||||
# Лабораторная работа 4 "Примитивное программируемое устройство"
|
||||
|
||||
В этой лабораторной работе на основе ранее разработанных блоков памяти и АЛУ ты соберешь простой учебный процессор с архитектурой `CYBERcobra 3000 Pro 2.1`. Это нужно для более глубокого понимания принципов работы программируемых устройств, чтобы проще было понять архитектуру RISC-V в будущем.
|
||||
|
||||
## Допуск к лабораторной работе
|
||||
|
||||
Для выполнения этой лабораторной работы, необходимо в полной мере освоить следующие элементы синтаксиса языка verilog:
|
||||
|
||||
1. Описание модулей, их создание внутри других модулей и оператор непрерывного присваивания `assign` ([Modules.md](../../Basic%20Verilog%20structures/Modules.md)).
|
||||
2. Описание мультиплексоров: с помощью `тернарного оператора`, блоков `case` и `if/else`. Знать особенности использования этих блоков и особенности синтеза комбинационной логики внутри блока `always` ([Multiplexors.md](../../Basic%20Verilog%20structures/Multiplexors.md)).
|
||||
3. Описание регистров ([Registers.md](../../Basic%20Verilog%20structures/Registers.md)).
|
||||
4. Оператор конкатенации ([Concatenation.md](../../Basic%20Verilog%20structures/Concatenation.md)).
|
||||
5. Отладку проекта по временной диаграмме ([Debug manual.md](../../Vivado%20Basics/Debug%20manual.md)).
|
||||
|
||||
## Цель
|
||||
|
||||
Реализовать простейшее программируемое устройство с архитектурой `CYBERcobra 3000 Pro 2.1`
|
||||
|
||||
## Ход работы
|
||||
|
||||
1. Изучить принцип работы процессоров (соответствующий раздел [#теории](#теория-про-программируемое-устройство))
|
||||
2. Познакомиться с архитектурой и микроархитектурой `CYBERcobra 3000 Pro 2.1` (раздел про эту [#архитектуру](#архитектура-cybercobra-3000-pro-21-и-ее-микроархитектура))
|
||||
3. Изучить необходимые для описания процессора конструкции verilog (раздел [#инструменты](#инструменты-для-реализации-процессора))
|
||||
4. Реализовать процессор с архитектурой `CYBERcobra 3000 Pro 2.1` ([#задание по разработке аппаратуры](#задание-по-реализации-процессора))
|
||||
5. Проверить работу процессора в ПЛИС.
|
||||
|
||||
Доп. задание, выполняемое дома:
|
||||
|
||||
6. Написать программу для процессора и на модели убедиться в корректности ее выполнения ([#задание по разработке программы](#задание-по-проверке-процессора))
|
||||
|
||||
## Теория про программируемое устройство
|
||||
|
||||
В обобщенном виде, процессор включает в себя память, АЛУ, устройство управления и интерфейсную логику для организации ввода/вывода. Также, в процессоре есть специальный регистр `PC` (**Program Counter** – счетчик команд), который хранит в себе число – адрес ячейки памяти, в которой лежит инструкция, которую нужно выполнить. Инструкция тоже представляет собой число, в котором закодировано `что нужно сделать` и `с чем это нужно сделать`.
|
||||
|
||||
Алгоритм работы процессора следующий:
|
||||
|
||||
1. Из памяти считывается инструкция по адресу `PC`
|
||||
2. Устройство управления дешифрует полученную инструкцию (то есть определяет какую операцию нужно сделать, где взять операнды и куда разместить результат)
|
||||
3. Декодировав инструкцию, устройство управления выдает всем блокам процессора (АЛУ, регистровый файл, мультиплексоры) соответствующие управляющие сигналы, тем самым выполняя эту инструкцию.
|
||||
4. Изменяется значение `PC`.
|
||||
5. Цикл повторяется с `п.1`.
|
||||
|
||||
Любая инструкция приводит к изменению состояния памяти. В случае процессора с архитектурой `CYBERcobra 3000 Pro 2.1` есть два класса инструкций: одни изменяют содержимое регистрового файла — это инструкции записи. Другие изменяют значение `PC` — это инструкции перехода. В первом случае используются вычислительные инструкции и инструкции загрузки данных из других источников. Во-втором случае используются инструкции перехода.
|
||||
|
||||
Если процессор обрабатывает вычислительную инструкцию, то `PC` перейдет к следующей по порядку инструкции. На лабораторной работе, посвященной памяти, мы сделали память инструкций с [побайтовой адресацией](../03.%20Register%20file%20and%20memory/README.md#1-память-инструкций). Это означает, что каждый байт памяти имеет свой собственный адрес. Поскольку длина инструкции составляет `4 байта`, для перехода к следующей инструкции `PC` должен быть увеличен на `4` (`PC = PC + 4`). При этом, регистровый файл сохранит результат некоторой операции на АЛУ или данные со входного порта.
|
||||
|
||||
Если же обрабатывается инструкция перехода, то возможно два варианта. В случае безусловного или успешного условного перехода, значение `PC` увеличится на значение константы закодированной внутри инструкции `PC = PC + const*4` (иными словами, `const` говорит о том, через сколько инструкций перепрыгнет `PC`, `const` может быть и отрицательной). В случае же неуспешного условного перехода `PC`, как и после вычислительных команд, просто перейдет к следующей инструкции, то есть `PC = PC + 4`.
|
||||
|
||||
> Строго говоря `PC` меняется при выполнении любой инструкции (кроме случая `const = 0`, то есть перехода инструкции на саму себя `PC = PC + 0*4`). Разница в том, на какое значение `PC` изменится. В вычислительных инструкциях это всегда адрес следующей инструкции, программа не управляет `PC`, он "сам знает", что ему делать. В инструкциях перехода программа и контекст определяют, что произойдет с `PC`.
|
||||
|
||||
## Архитектура CYBERcobra 3000 Pro 2.1 и ее микроархитектура
|
||||
|
||||
В качестве первого разрабатываемого программируемого устройства предлагается использовать архитектуру специального назначения `CYBERcobra 3000 Pro 2.1`, которая была разработана в **МИЭТ**. Главным достоинством данной архитектуры является простота ее понимания и реализации. Главным ее минусом является неоптимальность ввиду неэффективной реализации кодирования инструкций, что приводит к наличию неиспользуемых битов в программах. Но это неважно, так как основная цель разработки процессора с архитектурой `CYBERcobra 3000 Pro 2.1` — это более глубокое понимание принципов работы программируемых устройства, которое поможет при разработке более сложного процессора с архитектурой **RISC-V**.
|
||||
|
||||

|
||||
|
||||
Простота архитектуры `CYBERcobra 3000 Pro 2.1` проявляется, в том числе, за счет отсутствия памяти данных. Это значит, что данные c которыми работает программа могут храниться только в регистровом файле. Также в таком процессоре почти полностью отсутствует устройство управления (формально оно существует, но состоит только из проводов и пары логических вентилей).
|
||||
|
||||
Архитектурой предусмотрена поддержка 19 инструкций (5 типов команд):
|
||||
|
||||
Первые два типа содержат 16 инструкций, которые выполняются на АЛУ:
|
||||
|
||||
- 10 вычислительных
|
||||
- 6 операций сравнения для условного перехода
|
||||
|
||||
Кроме того, есть инструкции:
|
||||
|
||||
- безусловного перехода
|
||||
- загрузки константы
|
||||
- загрузки данных с внешнего устройства.
|
||||
|
||||
К классу инструкций записи, то есть тех, которые меняют значение регистрового файла, можно отнести: 10 вычислительных, загрузки константы и загрузки данных с внешнего устройства. К классу инструкций перехода: 6 операций сравнения для условного перехода и безусловный переход.
|
||||
|
||||
### Последовательное считывание инструкций
|
||||
|
||||
Будем рассматривать архитектуру (функции процессора) и микроархитектуру (реализация процессора) одновременно, прослеживая рассуждения их разработчика.
|
||||
|
||||
Для начала реализуем базовый функционал, подключив счетчик команд `PC` к памяти инструкций `instr_mem` и сумматору, прибавляющему 4 к `PC`. Выход сумматора подключим ко входу `PC`.
|
||||
|
||||
Каждый раз, когда будет происходить тактовый импульс (переключение `clk_i` из 0 в 1), значение `PC` будет увеличиваться на `4`, тем самым указывая на следующую инструкцию. Последовательное считывание программы из памяти готово.
|
||||
|
||||
Так как операции будут выполняться только над данными в регистровом файле, то его можно сразу подключить к АЛУ, соединив порты чтения `read_data1_o` и `read_data2_o` со входами операндов АЛУ, а результат операции АЛУ подключив к порту на запись `write_data_i`. Полученный результат изображен на картинке ниже.
|
||||
|
||||

|
||||
|
||||
Для компактности схемы, названия портов регистрового файла сокращено (`RA1` обозначает `read_addr1_i` и т.п.).
|
||||
|
||||
### Кодирование вычислительных инструкций
|
||||
|
||||
Чтобы добавить поддержку каких-либо инструкций, необходимо договориться **как** они будут кодироваться (эта часть относится к вопросам архитектуры). Вычислительные инструкции требуют следующую информацию:
|
||||
|
||||
1. по каким адресам регистрового файла лежат операнды?
|
||||
2. по какому адресу будет сохранен результат?
|
||||
3. какая операция должна быть выполнена?
|
||||
|
||||
Для этого в инструкции были выбраны следующие поля: 5 бит (`[27:23]`) для кодирования операции на АЛУ, два раза по 5 бит для кодирования адресов операндов в регистровом файле (`[22:18]` и `[17:13]`) и 5 бит для кодирования адреса результата (`[4:0]`). Ниже демонстрируется деление 32-битной инструкции на поля `alu_op`, `RA1`, `RA2` и `WA`.
|
||||
|
||||

|
||||
|
||||
``` C
|
||||
reg_file[WA] ← reg_file[RA1] {alu_op} reg_file[RA2]
|
||||
```
|
||||
|
||||
Запись выше является некоторой формализацией выполняемой функции, которая как бы отвечает на вопрос "а что, собственно, будет сделано?". В регистр по адресу WA (`reg_file[WA]`) будет записан (`←`) результат операции alu_op (`{alu_op}`) между регистрами по адресам RA1 (`reg_file[RA1]`) и RA2 (`reg_file[RA1]`).
|
||||
|
||||
### Реализация вычислительных инструкций
|
||||
|
||||
Чтобы процессор правильно реагировал на эти инструкции, требуется подключить ко входам адреса регистрового файла и управляющему входу АЛУ соответствующие биты выхода `read_data` памяти инструкции (**Instruction Memory**). В таком случае, когда `PC` будет указывать на ячейку памяти, в которой лежит, например, следующая 32-битная инструкция:
|
||||
|
||||
```text
|
||||
0000 00111 00100 01000 00000000 11100
|
||||
|alu_op| RA1 | RA2 | | WA
|
||||
```
|
||||
|
||||
будет выполнена операция `reg_file[28] = reg_file[4] | reg_file[8]`, потому что `alu_op = 00111`, что соответствует операции **логического ИЛИ**, `WA = 11100`, то есть 28-ой регистр, `RA1 = 00100` (4-ый регистр) и `RA2 = 01000` (8-ой регистр). Ниже иллюстрируется фрагмент микроархитектуры, поддерживающий вычислительные операции на АЛУ. Так как пока что другие инструкции не поддерживаются, то вход `WE` регистрового файла всегда равен `1` (это временно).
|
||||
|
||||

|
||||
|
||||
### Реализация загрузки константы в регистровый файл
|
||||
|
||||
Информация как-то должна попадать в регистровый файл, для этого добавим инструкцию загрузки константы по адресу `WA`. Чтобы аппаратура могла различать, когда ей нужно выполнять операцию на АЛУ, а когда загружать константу, назначим один бит инструкции определяющим "что именно будет записано в регистровый файл": результат с АЛУ или константа из инструкции. За это будет отвечать 28-ой бит инструкции `WS` (**Write Source**). Если `WS == 1`, значит выполняется вычислительная инструкция, а если `WS == 0`, значит нужно загрузить константу в регистровый файл.
|
||||
|
||||
Сама константа имеет разрядность **23 бита** ([27:5] биты инструкции) и должна быть **знакорасширена** до 32-х бит, то есть к 23-битной константе нужно приклеить слева 9 раз 23-ий знаковый бит константы (см. [оператор конкатенации](../../Basic%20Verilog%20structures/Concatenation.md)).
|
||||
|
||||
Пример: если [27:5] биты инструкции равны:
|
||||
|
||||
```text
|
||||
10100000111100101110111
|
||||
```
|
||||
|
||||
то после знакорасширения константа примет вид:
|
||||
|
||||
```text
|
||||
11111111110100000111100101110111
|
||||
```
|
||||
|
||||
(если бы старший бит был равен нулю, то константа заполнилась бы слева нулями, а не единицами).
|
||||
|
||||
Нет ничего страшного в том, что биты константы попадают на те же поля, что и `alu_op`, `RA1` и `RA2`, потому что когда выполняется инструкция загрузки константы не важно что будет выдавать АЛУ в этот момент (ведь благодаря мультиплексору на вход регистрового файла приходит константа). А значит не важно и что приходит в этот момент на АЛУ в качестве операндов и кода операции. Ниже демонстрируется деления 32-битной инструкции на поля `alu_op`, `RA1`, `RA2`, `WA`, `WS` и `const`, **с перекрытием полей**.
|
||||
|
||||

|
||||
|
||||
``` C
|
||||
reg_file[WA] ← const
|
||||
```
|
||||
|
||||
Так как вход записи уже занят результатом операции АЛУ, его потребуется мультиплексировать со значением константы из инструкции, которая предварительно **знакорасширяется** в блоке `SE`. На входе `WD` регистрового файла появляется мультиплексор, управляемый 28-м битом инструкции, который и определяет что будет записано: константа или результат вычисления на АЛУ.
|
||||
|
||||
Например, в такой реализации следующая 32-битная инструкция поместит константу `-1` в регистр по адресу `5`:
|
||||
|
||||
```text
|
||||
000 0 11111111111111111111111 00101
|
||||
|WS| RF_const | WA |
|
||||
```
|
||||
|
||||
Далее приводится фрагмент микроархитектуры, поддерживающий вычислительные операции на АЛУ и загрузку констант из инструкции в регистровый файл.
|
||||
|
||||

|
||||
|
||||
### Реализация загрузки в регистровый файл данных с внешних устройств
|
||||
|
||||
Чтобы процессор мог взаимодействовать с внешним миром добавим возможность загрузки данных с внешних устройств в регистр по адресу `WA`. Появляется третий тип инструкции, который определяет третий источник ввода для регистрового файла. Одного бита `WS` для выбора одного из трех источников будет недостаточно, поэтому расширим это поле до 2 бит. Теперь, когда `WS == 0` будет загружаться константа, когда `WS == 1` – будет загружаться результат вычисления АЛУ, а при `WS == 2` будут загружаться данные с внешних устройств. Остальные поля в данной инструкции не используются.
|
||||
|
||||

|
||||
|
||||
``` C
|
||||
reg_file[WA] ← sw_i
|
||||
```
|
||||
|
||||
По аналогии с загрузкой констант увеличиваем входной мультиплексор до 4 входов и подключаем к нему управляющие сигналы – `[29:28]` биты инструкции. Последний вход используется, чтобы разрешить неопределенность на выходе при `WS == 3`(`default`-вход, см. [мультиплексор](../../Basic%20Verilog%20structures/Multiplexors.md)).
|
||||
|
||||
Выход OUT подключается к первому порту на чтение регистрового файла. Значение на выходе OUT будет определяться содержимым ячейки памяти по адресу `RA1`. Ниже приводится фрагмент микроархитектуры, поддерживающий вычислительные операции на АЛУ, загрузку констант из инструкции в регистровый файл и загрузку данных с внешних устройств.
|
||||
|
||||

|
||||
|
||||
### Реализация условного перехода
|
||||
|
||||
С реализованным набором инструкций полученное устройство нельзя назвать процессором – пока что это продвинутый калькулятор. Добавим поддержку инструкции условного перехода, при выполнении которой программа будет перепрыгивать через заданное количество команд. Чтобы аппаратура отличала эту инструкцию от других будем использовать 30-ый бит `B` (`branch`). Если `B == 1`, значит это инструкция условного перехода и, если условие перехода выполняется, к `PC` надо прибавить константу. Если `B == 0`, значит это какая-то другая инструкция и к `PC` надо прибавить четыре.
|
||||
|
||||

|
||||
|
||||
Для вычисления результата условного перехода, нам необходимо выполнить операцию на АЛУ и посмотреть на сигнал `flag`. Если он равен 1, переход выполняется, в противном случае — не выполняется. А значит, нам нужны операнды `A`, `B`, и `alu_op`. Кроме того, нам необходимо указать насколько мы сместимся относительно текущего значения `PC` (константу смещения, `offset`). Для передачи этой константы лучше всего подойдут незадействованные биты инструкции `[12:5]`.
|
||||
|
||||
Обратим внимание на то, что `PC` 32-битный и должен быть всегда кратен четырем (`PC` не может указывать кроме как в начало инструкции, а каждая инструкция длиной в 32 бита). Чтобы константа смещения указывала на число инструкций, а не число байт, необходимо увеличить её в 4 раза. Это можно сделать, если приклеить к ней справа два нулевых бита (так же как в десятичной системе можно умножить число на 10<sup>2</sup>=100 если дописать справа от него два нуля). Кроме того, чтобы разрядность константы совпадала с разрядностью `PC`, необходимо знакорасширить её до 32 бит.
|
||||
|
||||
Приведенный ниже Си-подобный псевдо-код (далее мы назовем его псевдоассемблером) демонстрирует кодирование инструкций с новым полем `B`:
|
||||
|
||||
``` C
|
||||
if (reg_file[RA1] {alu_op} reg_file[RA2])
|
||||
PC ← PC + const * 4
|
||||
else
|
||||
PC ← PC + 4
|
||||
```
|
||||
|
||||
Так как второй вход сумматора счетчика команд занят числом 4, то для реализации условного перехода этот вход надо мультиплексировать с константой. Мультиплексор при этом управляется 30-ым битом `B`, который и определяет, что будет прибавляться к `PC`.
|
||||
|
||||
Сигнальные линии, которые управляют АЛУ и подают на его входы операнды уже существуют. Поэтому на схему необходимо добавить только логику управления мультиплексором на входе сумматора счетчика команд так. Эта логика работает следующим образом:
|
||||
|
||||
1. Если сейчас инструкция условного перехода
|
||||
2. И если условие перехода выполнилось
|
||||
|
||||
то к `PC` прибавляется знакорасширенная константа, умноженная на 4. В противном случае, к `PC` прибавляется 4.
|
||||
|
||||
Так как теперь не любая инструкция приводит к записи в регистровый файл, появляется необходимость управлять входом `WE` так, чтобы при операциях условного перехода запись в регистровый файл не производилась. Это можно сделать, подав на WE значение `!B` (запись происходит, если сейчас **не операция условного перехода**)
|
||||
|
||||

|
||||
|
||||
### Реализация безусловного перехода
|
||||
|
||||
Осталось добавить поддержку инструкции безусловного перехода, для идентификации которой используется оставшийся 31-ый бит `J`(jump). Если бит `J == 1`, то это безусловный переход, и мы прибавляем к `PC` знакорасширенную константу смещения, умноженную на 4 (как это делали и в условном переходе).
|
||||
|
||||

|
||||
|
||||
``` C
|
||||
PC ← PC + const*4
|
||||
```
|
||||
|
||||
Для реализации безусловного перехода, нам необходимо добавить дополнительную логику управления мультиплексором перед сумматором. Итоговая логика его работы звучит так:
|
||||
|
||||
1. Если сейчас инструкция безусловного перехода
|
||||
2. ИЛИ если сейчас инструкция условного перехода
|
||||
3. И если условие перехода выполнилось
|
||||
|
||||
Кроме того, при безусловном переходе в регистровый файл так же ничего не пишется. А значит, необходимо обновить логику работы сигнала разрешения записи `WE`, который будет равен 0 если сейчас инструкция условного или безусловного перехода.
|
||||
|
||||
Ниже приводится итоговый вариант микроархитектуры процессора `CYBERcobra 3000 Pro 2.1`
|
||||
|
||||

|
||||
|
||||
### Финальный обзор
|
||||
|
||||
Итого, архитектура `CYBERcobra 3000 Pro 2.1` поддерживает 5 типов инструкций, которые кодируются следующим образом (иксами помечены биты, которые не задействованы в данной инструкции):
|
||||
|
||||
1. 10 вычислительных инструкций `0 0 01 alu_op RA1 RA2 xxxx xxxx WA`
|
||||
2. Инструкция загрузки константы `0 0 00 const WA`
|
||||
3. Инструкция загрузки из внешних устройств `0 0 10 xxx xxxx xxxx xxxx xxxx xxxx WA`
|
||||
4. Безусловный переход `1 0 xx xxx xxxx xxxx xxxx const xxxxx`
|
||||
5. 6 инструкций условного перехода `0 1 xx alu_op RA1 RA2 const x xxxx`
|
||||
|
||||
При кодировании инструкций используются следующие поля:
|
||||
|
||||
- J – однобитный сигнал указывающий на выполнение безусловного перехода
|
||||
- B – однобитный сигнал указывающий на выполнение условного перехода
|
||||
- WS – двухбитный сигнал указывающий источник данных для записи в регистровый файл:
|
||||
- 0 – константа из инструкции
|
||||
- 1 – результат с АЛУ
|
||||
- 2 – внешние данные
|
||||
- 3 – не используется
|
||||
- alu_op – 5-битный сигнал кода операции АЛУ (в соответствии с таблицей из лабораторной по АЛУ)
|
||||
- RA1 и RA2 – 5-битные адреса операндов из регистрового файла
|
||||
- offset – 8-битная константа для условного / безусловного перехода
|
||||
- const — 23-битная константа для загрузки в регистровый файл
|
||||
- WA – 5-битный адрес регистра, в который будет записан результат
|
||||
|
||||
Напишем простую программу для этого процессора, которая в бесконечном цикле увеличивает значение первого регистра на 1. Сначала напишем программу на псевдоассемблере (используя предложенную мнемонику):
|
||||
|
||||
``` C
|
||||
reg_file[1] ← -1 // загрузить константу `-1` регистр 1
|
||||
reg_file[2] ← sw_i // загрузить значение с входа sw_i в регистр 2
|
||||
reg_file[3] ← 1 // загрузить константу `1` регистр 3
|
||||
|
||||
reg_file[1] ← reg_file[1] + reg_file[3] // сложить регистр 1 с регистром 3 и
|
||||
// поместить результат в регистр 1
|
||||
|
||||
if (reg_file[1] < reg_file[2]) // если значение в регистре 1 меньше
|
||||
// значения в регистре 2,
|
||||
PC ← PC – 1 // возврат на 1 инструкцию назад
|
||||
|
||||
out_o = reg_file[1], PC ← PC + 0 // бесконечное повторение этой инструкции
|
||||
// с выводом на out_o значения в регистре 1
|
||||
```
|
||||
|
||||
Теперь в соответствии с кодировкой инструкций переведем программу в машинные коды:
|
||||
|
||||
```text
|
||||
0 0 00 11111111111111111111111 00001
|
||||
0 0 10 00000000000000000000000 00010
|
||||
0 0 00 00000000000000000000001 00011
|
||||
0 0 01 00000 00001 00011 00000000 00001
|
||||
0 1 00 11110 00001 00010 11111111 00000
|
||||
1 0 00 00000 00001 00000 00000000 00000
|
||||
```
|
||||
|
||||
Полученную программу можно помещать в память программ и выполнять на процессоре.
|
||||
|
||||
## Инструменты для реализации процессора
|
||||
|
||||
Так как все модули процессора написаны, основная часть кода описания процессора будет связана с подключением этих модулей друг к другу. Подробнее о подключении модулей сказано в [Modules.md](../../Basic%20Verilog%20structures/Modules.md).
|
||||
|
||||
Для реализации блоков знакорасширения с умножением на 4 подходит использование оператора конкатенации ([Concatenation.md](../../Basic%20Verilog%20structures/Modules.md)).
|
||||
|
||||
## Задание по реализации процессора
|
||||
|
||||
Разработать процессор `CYBERcobra`, объединив ранее разработанные модули:
|
||||
|
||||
- Память инструкций (проинициализированную в двоичном формате файлом `example.txt`)
|
||||
- Регистровый файл
|
||||
- Арифметико-логическое устройство
|
||||
- 32-битный сумматор
|
||||
|
||||
Кроме того, необходимо описать регистр счетчика команд и логику его работы, в соответствии с ранее представленной микроархитектурой.
|
||||
|
||||
```SystemVerilog
|
||||
module CYВЕRcоbrа (
|
||||
inрut logic clk_i,
|
||||
inрut logic rst_i,
|
||||
inрut logic [15:0] sw_i,
|
||||
оutрut logic [31:0] out_o
|
||||
);
|
||||
|
||||
// тут твой код, о котором говорится чуть выше
|
||||
|
||||
endmodule
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Порядок выполнения задания
|
||||
|
||||
1. Внимательно ознакомьтесь с [заданием](#задание-по-реализации-процессора). В случае возникновения вопросов, проконсультируйтесь с преподавателем.
|
||||
2. Реализуйте модуль `CYBERcobra`. Для этого:
|
||||
1. В `Design Sources` проекта с предыдущих лаб, создайте `SystemVerilog`-файл `cybercobra.sv`.
|
||||
2. Опишите в нем модуль процессора с таким же именем и портами, как указано в [задании](#задание-по-реализации-процессора) (обратите внимание на регистр имени модуля).
|
||||
1. В первую очередь, необходимо создать счетчик команд и все вспомогательные провода. При создании, **следите за разрядностью**.
|
||||
2. Затем, необходимо создать экземпляры модулей: памяти инструкции, АЛУ, регистрового файла и сумматора. При подключении сигналов сумматора, надо **обязательно** надо подать нулевое значение на входной бит переноса. Выходной бит переноса подключать не обязательно.
|
||||
3. После этого, необходимо описать оставшуюся логику:
|
||||
1. Программного счетчика
|
||||
2. Сигнала управления мультиплексором, выбирающим слагаемое для программного счетчика
|
||||
3. Сигнала разрешения записи в регистровый файл
|
||||
4. Мультиплексор, выбирающий слагаемое для программного счетчика
|
||||
5. Мультиплексор, выбирающий источник записи в регистровый файл.
|
||||
3. После описания модуля, его необходимо проверить с помощью [`тестового окружения`](../../Basic%20Verilog%20structures/Testbench.md).
|
||||
1. Тестовое окружение находится [`здесь`](tb_cybercobra.sv).
|
||||
2. Программа, которой необходимо проинициализировать память инструкций находится [`здесь`](example.txt). Алгоритм работы программы приведен в разделе [`Финальный обзор`](#финальный-обзор).
|
||||
3. Для запуска симуляции воспользуйтесь [`этой инструкцией`](../../Vivado%20Basics/Run%20Simulation.md).
|
||||
4. Перед запуском симуляции убедитесь, что выбран правильный модуль верхнего уровня.
|
||||
5. **Во время симуляции, вы должны прожать "Run All" и убедиться, что в логе есть сообщение о завершении теста!**
|
||||
6. В этот раз, в конце не будет сообщения о том, работает ли ваше устройство или в нем есть ошибки. Вы должны самостоятельно проверить работу модуля, перенеся его внутренние сигналы на временную диаграмму, и [проверив](../../Vivado%20Basics/Debug%20manual.md) логику их работы.
|
||||
4. Добавьте в проект модуль верхнего уровня ([nexys_cybercobra_demo.sv](board%20files/nexys_cybercobra_demo.sv)), соединяющий процессор с периферией в ПЛИС. Описание работы модуля находится [здесь](board%20files)
|
||||
5. Замените содержимое файла, инициализирующего память инструкций новой программой, которая размещена [`здесь`](board%20files/demo.txt).
|
||||
6. Убедитесь, что у файла, инициализирующего память инструкций выставлен тип `Memory Initialization Files`, а не `Memory File`.
|
||||
7. Подключите к проекту файл ограничений ([nexys_a7_100t.xdc](board%20files/nexys_a7_100t.xdc)), если тот еще не был подключен, либо замените его содержимое данными из файла к этой лабораторной работе.
|
||||
8. Проверьте работу процессора в ПЛИС.
|
||||
|
||||
---
|
||||
|
||||
После выполнения задания по реализации процессора, необходимо также выполнить [индивидуальное задание](Индивидуальное%20задание) по написанию двоичной программы под созданный вами процессор.
|
||||
|
||||
---
|
||||
|
||||
Дерзайте!
|
11
Labs/04. Primitive programmable device/board files/README.md
Normal file
11
Labs/04. Primitive programmable device/board files/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Проверка работы CYBERcobra на ПЛИС
|
||||
|
||||
Если вы не понимаете, что лежит в этой папке, или если надо вспомнить, как прошить ПЛИС, можно воспользоваться [`этой инструкцией`](../../../Vivado%20Basics/Program%20nexys%20a7.md)
|
||||
|
||||
Файл [`nexys_cybercobra_demo.v`](nexys_cybercobra_demo.v), который нужно запускать с [`демонстрационным файлом инструкций`](demo.txt), является демонстрацией возможностей кобры, реализующий лишь декодирование выходных значений в формат для отображения на семисегментных индикаторах, а вся логика работы реализована инструкциями в текстовом файле.
|
||||
|
||||
Сначала выводится приветствие `≡ALOHA≡`, меняя положение восьми правых переключателей, последовательно нажимая на кнопку `BTND` (на рисунке выделена синим цветом), можно включать или выключать `один` из выбранных сегментов. Кнопка `CPU RESET` (на рисунке выделена красным цветом) возвращает все исходное состояние. Попробуйте погасить все слово, а потом снова его зажечь.
|
||||
|
||||
Файл [`nexys_cybercobra.v`](nexys_cybercobra.v), который нужно запускать с `вашим файлом инструкций`, так же реализует лишь декодирование выходных значений в формат для отображения на семисегментных индикаторах, но кнопка `BTND` задает тактирующий сигнал, следовательно, нажимая на нее, вы можете пошагово переходить по инструкциям, контролируя правильность работы устройства, для удобства можете в тестовом окружении выставить такое же входное значение, как переключатели sw[15:0] на плате.
|
||||
|
||||

|
380
Labs/04. Primitive programmable device/board files/demo.txt
Normal file
380
Labs/04. Primitive programmable device/board files/demo.txt
Normal file
@@ -0,0 +1,380 @@
|
||||
21
|
||||
42
|
||||
07
|
||||
00
|
||||
02
|
||||
02
|
||||
00
|
||||
00
|
||||
01
|
||||
40
|
||||
84
|
||||
10
|
||||
63
|
||||
94
|
||||
11
|
||||
00
|
||||
01
|
||||
60
|
||||
04
|
||||
10
|
||||
0c
|
||||
00
|
||||
06
|
||||
00
|
||||
0d
|
||||
40
|
||||
01
|
||||
00
|
||||
0e
|
||||
02
|
||||
00
|
||||
00
|
||||
2f
|
||||
00
|
||||
00
|
||||
00
|
||||
0c
|
||||
40
|
||||
b0
|
||||
10
|
||||
0d
|
||||
40
|
||||
b4
|
||||
10
|
||||
0e
|
||||
40
|
||||
b8
|
||||
10
|
||||
0f
|
||||
40
|
||||
bc
|
||||
10
|
||||
10
|
||||
00
|
||||
10
|
||||
00
|
||||
11
|
||||
80
|
||||
01
|
||||
00
|
||||
12
|
||||
14
|
||||
00
|
||||
00
|
||||
73
|
||||
00
|
||||
00
|
||||
00
|
||||
22
|
||||
00
|
||||
00
|
||||
00
|
||||
43
|
||||
00
|
||||
00
|
||||
00
|
||||
84
|
||||
00
|
||||
00
|
||||
00
|
||||
05
|
||||
01
|
||||
00
|
||||
00
|
||||
06
|
||||
02
|
||||
00
|
||||
00
|
||||
07
|
||||
04
|
||||
00
|
||||
00
|
||||
08
|
||||
08
|
||||
00
|
||||
00
|
||||
09
|
||||
10
|
||||
00
|
||||
00
|
||||
ea
|
||||
1f
|
||||
00
|
||||
00
|
||||
0b
|
||||
00
|
||||
04
|
||||
20
|
||||
00
|
||||
00
|
||||
04
|
||||
30
|
||||
00
|
||||
00
|
||||
04
|
||||
30
|
||||
00
|
||||
00
|
||||
04
|
||||
30
|
||||
00
|
||||
00
|
||||
04
|
||||
30
|
||||
00
|
||||
00
|
||||
04
|
||||
30
|
||||
00
|
||||
00
|
||||
04
|
||||
30
|
||||
00
|
||||
00
|
||||
04
|
||||
30
|
||||
00
|
||||
00
|
||||
04
|
||||
30
|
||||
00
|
||||
00
|
||||
04
|
||||
30
|
||||
c0
|
||||
7e
|
||||
a9
|
||||
7e
|
||||
0b
|
||||
40
|
||||
ad
|
||||
13
|
||||
20
|
||||
45
|
||||
2c
|
||||
7c
|
||||
40
|
||||
65
|
||||
2c
|
||||
7c
|
||||
60
|
||||
85
|
||||
2c
|
||||
7c
|
||||
80
|
||||
a5
|
||||
2c
|
||||
7c
|
||||
a0
|
||||
c5
|
||||
2c
|
||||
7c
|
||||
c0
|
||||
e5
|
||||
2c
|
||||
7c
|
||||
e0
|
||||
05
|
||||
2d
|
||||
7c
|
||||
00
|
||||
26
|
||||
2d
|
||||
7c
|
||||
14
|
||||
60
|
||||
86
|
||||
13
|
||||
00
|
||||
02
|
||||
50
|
||||
7c
|
||||
14
|
||||
40
|
||||
86
|
||||
13
|
||||
00
|
||||
02
|
||||
50
|
||||
7c
|
||||
14
|
||||
20
|
||||
86
|
||||
13
|
||||
00
|
||||
02
|
||||
50
|
||||
7c
|
||||
14
|
||||
00
|
||||
86
|
||||
13
|
||||
00
|
||||
02
|
||||
50
|
||||
7c
|
||||
14
|
||||
e0
|
||||
85
|
||||
13
|
||||
00
|
||||
02
|
||||
50
|
||||
7c
|
||||
14
|
||||
c0
|
||||
85
|
||||
13
|
||||
00
|
||||
02
|
||||
50
|
||||
7c
|
||||
14
|
||||
a0
|
||||
85
|
||||
13
|
||||
00
|
||||
02
|
||||
50
|
||||
7c
|
||||
14
|
||||
80
|
||||
85
|
||||
13
|
||||
00
|
||||
02
|
||||
50
|
||||
7c
|
||||
00
|
||||
04
|
||||
04
|
||||
bc
|
||||
01
|
||||
60
|
||||
06
|
||||
13
|
||||
c0
|
||||
03
|
||||
04
|
||||
bc
|
||||
01
|
||||
40
|
||||
06
|
||||
13
|
||||
80
|
||||
03
|
||||
04
|
||||
bc
|
||||
01
|
||||
20
|
||||
06
|
||||
13
|
||||
40
|
||||
03
|
||||
04
|
||||
bc
|
||||
01
|
||||
00
|
||||
06
|
||||
13
|
||||
00
|
||||
03
|
||||
04
|
||||
bc
|
||||
01
|
||||
e0
|
||||
05
|
||||
13
|
||||
c0
|
||||
02
|
||||
04
|
||||
bc
|
||||
01
|
||||
c0
|
||||
05
|
||||
13
|
||||
80
|
||||
02
|
||||
04
|
||||
bc
|
||||
01
|
||||
a0
|
||||
05
|
||||
13
|
||||
40
|
||||
02
|
||||
04
|
||||
bc
|
||||
01
|
||||
80
|
||||
05
|
||||
13
|
||||
00
|
||||
02
|
||||
04
|
||||
bc
|
||||
01
|
||||
60
|
||||
06
|
||||
12
|
||||
c0
|
||||
01
|
||||
04
|
||||
bc
|
||||
01
|
||||
40
|
||||
06
|
||||
12
|
||||
80
|
||||
01
|
||||
04
|
||||
bc
|
||||
01
|
||||
20
|
||||
06
|
||||
12
|
||||
40
|
||||
01
|
||||
04
|
||||
bc
|
||||
01
|
||||
00
|
||||
06
|
||||
12
|
||||
00
|
||||
01
|
||||
04
|
||||
bc
|
||||
01
|
||||
e0
|
||||
05
|
||||
12
|
||||
c0
|
||||
00
|
||||
04
|
||||
bc
|
||||
01
|
||||
c0
|
||||
05
|
||||
12
|
||||
80
|
||||
00
|
||||
04
|
||||
bc
|
||||
01
|
||||
a0
|
||||
05
|
||||
12
|
||||
40
|
||||
00
|
||||
04
|
||||
bc
|
||||
01
|
||||
80
|
||||
05
|
||||
12
|
||||
60
|
||||
17
|
||||
04
|
||||
bc
|
@@ -0,0 +1,211 @@
|
||||
## This file is a general .xdc for the Nexys A7-100T
|
||||
## To use it in a project:
|
||||
## - uncomment the lines corresponding to used pins
|
||||
## - rename the used ports (in each line, after get_ports) according to the top level signal names in the project
|
||||
|
||||
# Clock signal
|
||||
set_property -dict { PACKAGE_PIN E3 IOSTANDARD LVCMOS33 } [get_ports { CLK100 }]; #IO_L12P_T1_MRCC_35 Sch=clk100mhz
|
||||
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports {CLK100}];
|
||||
|
||||
|
||||
#Switches
|
||||
set_property -dict { PACKAGE_PIN J15 IOSTANDARD LVCMOS33 } [get_ports { SW[0] }]; #IO_L24N_T3_RS0_15 Sch=sw[0]
|
||||
set_property -dict { PACKAGE_PIN L16 IOSTANDARD LVCMOS33 } [get_ports { SW[1] }]; #IO_L3N_T0_DQS_EMCCLK_14 Sch=sw[1]
|
||||
set_property -dict { PACKAGE_PIN M13 IOSTANDARD LVCMOS33 } [get_ports { SW[2] }]; #IO_L6N_T0_D08_VREF_14 Sch=sw[2]
|
||||
set_property -dict { PACKAGE_PIN R15 IOSTANDARD LVCMOS33 } [get_ports { SW[3] }]; #IO_L13N_T2_MRCC_14 Sch=sw[3]
|
||||
set_property -dict { PACKAGE_PIN R17 IOSTANDARD LVCMOS33 } [get_ports { SW[4] }]; #IO_L12N_T1_MRCC_14 Sch=sw[4]
|
||||
set_property -dict { PACKAGE_PIN T18 IOSTANDARD LVCMOS33 } [get_ports { SW[5] }]; #IO_L7N_T1_D10_14 Sch=sw[5]
|
||||
set_property -dict { PACKAGE_PIN U18 IOSTANDARD LVCMOS33 } [get_ports { SW[6] }]; #IO_L17N_T2_A13_D29_14 Sch=sw[6]
|
||||
set_property -dict { PACKAGE_PIN R13 IOSTANDARD LVCMOS33 } [get_ports { SW[7] }]; #IO_L5N_T0_D07_14 Sch=sw[7]
|
||||
set_property -dict { PACKAGE_PIN T8 IOSTANDARD LVCMOS18 } [get_ports { SW[8] }]; #IO_L24N_T3_34 Sch=sw[8]
|
||||
set_property -dict { PACKAGE_PIN U8 IOSTANDARD LVCMOS18 } [get_ports { SW[9] }]; #IO_25_34 Sch=sw[9]
|
||||
set_property -dict { PACKAGE_PIN R16 IOSTANDARD LVCMOS33 } [get_ports { SW[10] }]; #IO_L15P_T2_DQS_RDWR_B_14 Sch=sw[10]
|
||||
set_property -dict { PACKAGE_PIN T13 IOSTANDARD LVCMOS33 } [get_ports { SW[11] }]; #IO_L23P_T3_A03_D19_14 Sch=sw[11]
|
||||
set_property -dict { PACKAGE_PIN H6 IOSTANDARD LVCMOS33 } [get_ports { SW[12] }]; #IO_L24P_T3_35 Sch=sw[12]
|
||||
set_property -dict { PACKAGE_PIN U12 IOSTANDARD LVCMOS33 } [get_ports { SW[13] }]; #IO_L20P_T3_A08_D24_14 Sch=sw[13]
|
||||
set_property -dict { PACKAGE_PIN U11 IOSTANDARD LVCMOS33 } [get_ports { SW[14] }]; #IO_L19N_T3_A09_D25_VREF_14 Sch=sw[14]
|
||||
set_property -dict { PACKAGE_PIN V10 IOSTANDARD LVCMOS33 } [get_ports { SW[15] }]; #IO_L21P_T3_DQS_14 Sch=sw[15]
|
||||
|
||||
### LEDs
|
||||
#set_property -dict { PACKAGE_PIN H17 IOSTANDARD LVCMOS33 } [get_ports { LED[0] }]; #IO_L18P_T2_A24_15 Sch=led[0]
|
||||
#set_property -dict { PACKAGE_PIN K15 IOSTANDARD LVCMOS33 } [get_ports { LED[1] }]; #IO_L24P_T3_RS1_15 Sch=led[1]
|
||||
#set_property -dict { PACKAGE_PIN J13 IOSTANDARD LVCMOS33 } [get_ports { LED[2] }]; #IO_L17N_T2_A25_15 Sch=led[2]
|
||||
#set_property -dict { PACKAGE_PIN N14 IOSTANDARD LVCMOS33 } [get_ports { LED[3] }]; #IO_L8P_T1_D11_14 Sch=led[3]
|
||||
#set_property -dict { PACKAGE_PIN R18 IOSTANDARD LVCMOS33 } [get_ports { LED[4] }]; #IO_L7P_T1_D09_14 Sch=led[4]
|
||||
#set_property -dict { PACKAGE_PIN V17 IOSTANDARD LVCMOS33 } [get_ports { LED[5] }]; #IO_L18N_T2_A11_D27_14 Sch=led[5]
|
||||
#set_property -dict { PACKAGE_PIN U17 IOSTANDARD LVCMOS33 } [get_ports { LED[6] }]; #IO_L17P_T2_A14_D30_14 Sch=led[6]
|
||||
#set_property -dict { PACKAGE_PIN U16 IOSTANDARD LVCMOS33 } [get_ports { LED[7] }]; #IO_L18P_T2_A12_D28_14 Sch=led[7]
|
||||
#set_property -dict { PACKAGE_PIN V16 IOSTANDARD LVCMOS33 } [get_ports { LED[8] }]; #IO_L16N_T2_A15_D31_14 Sch=led[8]
|
||||
#set_property -dict { PACKAGE_PIN T15 IOSTANDARD LVCMOS33 } [get_ports { LED[9] }]; #IO_L14N_T2_SRCC_14 Sch=led[9]
|
||||
#set_property -dict { PACKAGE_PIN U14 IOSTANDARD LVCMOS33 } [get_ports { LED[10] }]; #IO_L22P_T3_A05_D21_14 Sch=led[10]
|
||||
#set_property -dict { PACKAGE_PIN T16 IOSTANDARD LVCMOS33 } [get_ports { LED[11] }]; #IO_L15N_T2_DQS_DOUT_CSO_B_14 Sch=led[11]
|
||||
#set_property -dict { PACKAGE_PIN V15 IOSTANDARD LVCMOS33 } [get_ports { LED[12] }]; #IO_L16P_T2_CSI_B_14 Sch=led[12]
|
||||
#set_property -dict { PACKAGE_PIN V14 IOSTANDARD LVCMOS33 } [get_ports { LED[13] }]; #IO_L22N_T3_A04_D20_14 Sch=led[13]
|
||||
#set_property -dict { PACKAGE_PIN V12 IOSTANDARD LVCMOS33 } [get_ports { LED[14] }]; #IO_L20N_T3_A07_D23_14 Sch=led[14]
|
||||
#set_property -dict { PACKAGE_PIN V11 IOSTANDARD LVCMOS33 } [get_ports { LED[15] }]; #IO_L21N_T3_DQS_A06_D22_14 Sch=led[15]
|
||||
|
||||
### RGB LEDs
|
||||
#set_property -dict { PACKAGE_PIN R12 IOSTANDARD LVCMOS33 } [get_ports { LED16_B }]; #IO_L5P_T0_D06_14 Sch=led16_b
|
||||
#set_property -dict { PACKAGE_PIN M16 IOSTANDARD LVCMOS33 } [get_ports { LED16_G }]; #IO_L10P_T1_D14_14 Sch=led16_g
|
||||
#set_property -dict { PACKAGE_PIN N15 IOSTANDARD LVCMOS33 } [get_ports { LED16_R }]; #IO_L11P_T1_SRCC_14 Sch=led16_r
|
||||
#set_property -dict { PACKAGE_PIN G14 IOSTANDARD LVCMOS33 } [get_ports { LED17_B }]; #IO_L15N_T2_DQS_ADV_B_15 Sch=led17_b
|
||||
#set_property -dict { PACKAGE_PIN R11 IOSTANDARD LVCMOS33 } [get_ports { LED17_G }]; #IO_0_14 Sch=led17_g
|
||||
#set_property -dict { PACKAGE_PIN N16 IOSTANDARD LVCMOS33 } [get_ports { LED17_R }]; #IO_L11N_T1_SRCC_14 Sch=led17_r
|
||||
|
||||
##7 segment display
|
||||
set_property -dict { PACKAGE_PIN T10 IOSTANDARD LVCMOS33 } [get_ports { CA }]; #IO_L24N_T3_A00_D16_14 Sch=ca
|
||||
set_property -dict { PACKAGE_PIN R10 IOSTANDARD LVCMOS33 } [get_ports { CB }]; #IO_25_14 Sch=cb
|
||||
set_property -dict { PACKAGE_PIN K16 IOSTANDARD LVCMOS33 } [get_ports { CC }]; #IO_25_15 Sch=cc
|
||||
set_property -dict { PACKAGE_PIN K13 IOSTANDARD LVCMOS33 } [get_ports { CD }]; #IO_L17P_T2_A26_15 Sch=cd
|
||||
set_property -dict { PACKAGE_PIN P15 IOSTANDARD LVCMOS33 } [get_ports { CE }]; #IO_L13P_T2_MRCC_14 Sch=ce
|
||||
set_property -dict { PACKAGE_PIN T11 IOSTANDARD LVCMOS33 } [get_ports { CF }]; #IO_L19P_T3_A10_D26_14 Sch=cf
|
||||
set_property -dict { PACKAGE_PIN L18 IOSTANDARD LVCMOS33 } [get_ports { CG }]; #IO_L4P_T0_D04_14 Sch=cg
|
||||
#set_property -dict { PACKAGE_PIN H15 IOSTANDARD LVCMOS33 } [get_ports { DP }]; #IO_L19N_T3_A21_VREF_15 Sch=dp
|
||||
set_property -dict { PACKAGE_PIN J17 IOSTANDARD LVCMOS33 } [get_ports { AN[0] }]; #IO_L23P_T3_FOE_B_15 Sch=an[0]
|
||||
set_property -dict { PACKAGE_PIN J18 IOSTANDARD LVCMOS33 } [get_ports { AN[1] }]; #IO_L23N_T3_FWE_B_15 Sch=an[1]
|
||||
set_property -dict { PACKAGE_PIN T9 IOSTANDARD LVCMOS33 } [get_ports { AN[2] }]; #IO_L24P_T3_A01_D17_14 Sch=an[2]
|
||||
set_property -dict { PACKAGE_PIN J14 IOSTANDARD LVCMOS33 } [get_ports { AN[3] }]; #IO_L19P_T3_A22_15 Sch=an[3]
|
||||
set_property -dict { PACKAGE_PIN P14 IOSTANDARD LVCMOS33 } [get_ports { AN[4] }]; #IO_L8N_T1_D12_14 Sch=an[4]
|
||||
set_property -dict { PACKAGE_PIN T14 IOSTANDARD LVCMOS33 } [get_ports { AN[5] }]; #IO_L14P_T2_SRCC_14 Sch=an[5]
|
||||
set_property -dict { PACKAGE_PIN K2 IOSTANDARD LVCMOS33 } [get_ports { AN[6] }]; #IO_L23P_T3_35 Sch=an[6]
|
||||
set_property -dict { PACKAGE_PIN U13 IOSTANDARD LVCMOS33 } [get_ports { AN[7] }]; #IO_L23N_T3_A02_D18_14 Sch=an[7]
|
||||
|
||||
##Buttons
|
||||
set_property -dict { PACKAGE_PIN C12 IOSTANDARD LVCMOS33 } [get_ports { resetn }]; #IO_L3P_T0_DQS_AD1P_15 Sch=cpu_resetn
|
||||
#set_property -dict { PACKAGE_PIN N17 IOSTANDARD LVCMOS33 } [get_ports { BTNC }]; #IO_L9P_T1_DQS_14 Sch=btnc
|
||||
#set_property -dict { PACKAGE_PIN M18 IOSTANDARD LVCMOS33 } [get_ports { BTNU }]; #IO_L4N_T0_D05_14 Sch=btnu
|
||||
#set_property -dict { PACKAGE_PIN P17 IOSTANDARD LVCMOS33 } [get_ports { BTNL }]; #IO_L12P_T1_MRCC_14 Sch=btnl
|
||||
#set_property -dict { PACKAGE_PIN M17 IOSTANDARD LVCMOS33 } [get_ports { BTNR }]; #IO_L10N_T1_D15_14 Sch=btnr
|
||||
set_property -dict { PACKAGE_PIN P18 IOSTANDARD LVCMOS33 } [get_ports { BTND }]; #IO_L9N_T1_DQS_D13_14 Sch=btnd
|
||||
|
||||
|
||||
##Pmod Headers
|
||||
##Pmod Header JA
|
||||
#set_property -dict { PACKAGE_PIN C17 IOSTANDARD LVCMOS33 } [get_ports { JA[1] }]; #IO_L20N_T3_A19_15 Sch=ja[1]
|
||||
#set_property -dict { PACKAGE_PIN D18 IOSTANDARD LVCMOS33 } [get_ports { JA[2] }]; #IO_L21N_T3_DQS_A18_15 Sch=ja[2]
|
||||
#set_property -dict { PACKAGE_PIN E18 IOSTANDARD LVCMOS33 } [get_ports { JA[3] }]; #IO_L21P_T3_DQS_15 Sch=ja[3]
|
||||
#set_property -dict { PACKAGE_PIN G17 IOSTANDARD LVCMOS33 } [get_ports { JA[4] }]; #IO_L18N_T2_A23_15 Sch=ja[4]
|
||||
#set_property -dict { PACKAGE_PIN D17 IOSTANDARD LVCMOS33 } [get_ports { JA[7] }]; #IO_L16N_T2_A27_15 Sch=ja[7]
|
||||
#set_property -dict { PACKAGE_PIN E17 IOSTANDARD LVCMOS33 } [get_ports { JA[8] }]; #IO_L16P_T2_A28_15 Sch=ja[8]
|
||||
#set_property -dict { PACKAGE_PIN F18 IOSTANDARD LVCMOS33 } [get_ports { JA[9] }]; #IO_L22N_T3_A16_15 Sch=ja[9]
|
||||
#set_property -dict { PACKAGE_PIN G18 IOSTANDARD LVCMOS33 } [get_ports { JA[10] }]; #IO_L22P_T3_A17_15 Sch=ja[10]
|
||||
|
||||
##Pmod Header JB
|
||||
#set_property -dict { PACKAGE_PIN D14 IOSTANDARD LVCMOS33 } [get_ports { JB[1] }]; #IO_L1P_T0_AD0P_15 Sch=jb[1]
|
||||
#set_property -dict { PACKAGE_PIN F16 IOSTANDARD LVCMOS33 } [get_ports { JB[2] }]; #IO_L14N_T2_SRCC_15 Sch=jb[2]
|
||||
#set_property -dict { PACKAGE_PIN G16 IOSTANDARD LVCMOS33 } [get_ports { JB[3] }]; #IO_L13N_T2_MRCC_15 Sch=jb[3]
|
||||
#set_property -dict { PACKAGE_PIN H14 IOSTANDARD LVCMOS33 } [get_ports { JB[4] }]; #IO_L15P_T2_DQS_15 Sch=jb[4]
|
||||
#set_property -dict { PACKAGE_PIN E16 IOSTANDARD LVCMOS33 } [get_ports { JB[7] }]; #IO_L11N_T1_SRCC_15 Sch=jb[7]
|
||||
#set_property -dict { PACKAGE_PIN F13 IOSTANDARD LVCMOS33 } [get_ports { JB[8] }]; #IO_L5P_T0_AD9P_15 Sch=jb[8]
|
||||
#set_property -dict { PACKAGE_PIN G13 IOSTANDARD LVCMOS33 } [get_ports { JB[9] }]; #IO_0_15 Sch=jb[9]
|
||||
#set_property -dict { PACKAGE_PIN H16 IOSTANDARD LVCMOS33 } [get_ports { JB[10] }]; #IO_L13P_T2_MRCC_15 Sch=jb[10]
|
||||
|
||||
##Pmod Header JC
|
||||
#set_property -dict { PACKAGE_PIN K1 IOSTANDARD LVCMOS33 } [get_ports { JC[1] }]; #IO_L23N_T3_35 Sch=jc[1]
|
||||
#set_property -dict { PACKAGE_PIN F6 IOSTANDARD LVCMOS33 } [get_ports { JC[2] }]; #IO_L19N_T3_VREF_35 Sch=jc[2]
|
||||
#set_property -dict { PACKAGE_PIN J2 IOSTANDARD LVCMOS33 } [get_ports { JC[3] }]; #IO_L22N_T3_35 Sch=jc[3]
|
||||
#set_property -dict { PACKAGE_PIN G6 IOSTANDARD LVCMOS33 } [get_ports { JC[4] }]; #IO_L19P_T3_35 Sch=jc[4]
|
||||
#set_property -dict { PACKAGE_PIN E7 IOSTANDARD LVCMOS33 } [get_ports { JC[7] }]; #IO_L6P_T0_35 Sch=jc[7]
|
||||
#set_property -dict { PACKAGE_PIN J3 IOSTANDARD LVCMOS33 } [get_ports { JC[8] }]; #IO_L22P_T3_35 Sch=jc[8]
|
||||
#set_property -dict { PACKAGE_PIN J4 IOSTANDARD LVCMOS33 } [get_ports { JC[9] }]; #IO_L21P_T3_DQS_35 Sch=jc[9]
|
||||
#set_property -dict { PACKAGE_PIN E6 IOSTANDARD LVCMOS33 } [get_ports { JC[10] }]; #IO_L5P_T0_AD13P_35 Sch=jc[10]
|
||||
|
||||
##Pmod Header JD
|
||||
#set_property -dict { PACKAGE_PIN H4 IOSTANDARD LVCMOS33 } [get_ports { JD[1] }]; #IO_L21N_T3_DQS_35 Sch=jd[1]
|
||||
#set_property -dict { PACKAGE_PIN H1 IOSTANDARD LVCMOS33 } [get_ports { JD[2] }]; #IO_L17P_T2_35 Sch=jd[2]
|
||||
#set_property -dict { PACKAGE_PIN G1 IOSTANDARD LVCMOS33 } [get_ports { JD[3] }]; #IO_L17N_T2_35 Sch=jd[3]
|
||||
#set_property -dict { PACKAGE_PIN G3 IOSTANDARD LVCMOS33 } [get_ports { JD[4] }]; #IO_L20N_T3_35 Sch=jd[4]
|
||||
#set_property -dict { PACKAGE_PIN H2 IOSTANDARD LVCMOS33 } [get_ports { JD[7] }]; #IO_L15P_T2_DQS_35 Sch=jd[7]
|
||||
#set_property -dict { PACKAGE_PIN G4 IOSTANDARD LVCMOS33 } [get_ports { JD[8] }]; #IO_L20P_T3_35 Sch=jd[8]
|
||||
#set_property -dict { PACKAGE_PIN G2 IOSTANDARD LVCMOS33 } [get_ports { JD[9] }]; #IO_L15N_T2_DQS_35 Sch=jd[9]
|
||||
#set_property -dict { PACKAGE_PIN F3 IOSTANDARD LVCMOS33 } [get_ports { JD[10] }]; #IO_L13N_T2_MRCC_35 Sch=jd[10]
|
||||
|
||||
##Pmod Header JXADC
|
||||
#set_property -dict { PACKAGE_PIN A14 IOSTANDARD LVCMOS33 } [get_ports { XA_N[1] }]; #IO_L9N_T1_DQS_AD3N_15 Sch=xa_n[1]
|
||||
#set_property -dict { PACKAGE_PIN A13 IOSTANDARD LVCMOS33 } [get_ports { XA_P[1] }]; #IO_L9P_T1_DQS_AD3P_15 Sch=xa_p[1]
|
||||
#set_property -dict { PACKAGE_PIN A16 IOSTANDARD LVCMOS33 } [get_ports { XA_N[2] }]; #IO_L8N_T1_AD10N_15 Sch=xa_n[2]
|
||||
#set_property -dict { PACKAGE_PIN A15 IOSTANDARD LVCMOS33 } [get_ports { XA_P[2] }]; #IO_L8P_T1_AD10P_15 Sch=xa_p[2]
|
||||
#set_property -dict { PACKAGE_PIN B17 IOSTANDARD LVCMOS33 } [get_ports { XA_N[3] }]; #IO_L7N_T1_AD2N_15 Sch=xa_n[3]
|
||||
#set_property -dict { PACKAGE_PIN B16 IOSTANDARD LVCMOS33 } [get_ports { XA_P[3] }]; #IO_L7P_T1_AD2P_15 Sch=xa_p[3]
|
||||
#set_property -dict { PACKAGE_PIN A18 IOSTANDARD LVCMOS33 } [get_ports { XA_N[4] }]; #IO_L10N_T1_AD11N_15 Sch=xa_n[4]
|
||||
#set_property -dict { PACKAGE_PIN B18 IOSTANDARD LVCMOS33 } [get_ports { XA_P[4] }]; #IO_L10P_T1_AD11P_15 Sch=xa_p[4]
|
||||
|
||||
##VGA Connector
|
||||
#set_property -dict { PACKAGE_PIN A3 IOSTANDARD LVCMOS33 } [get_ports { VGA_R[0] }]; #IO_L8N_T1_AD14N_35 Sch=vga_r[0]
|
||||
#set_property -dict { PACKAGE_PIN B4 IOSTANDARD LVCMOS33 } [get_ports { VGA_R[1] }]; #IO_L7N_T1_AD6N_35 Sch=vga_r[1]
|
||||
#set_property -dict { PACKAGE_PIN C5 IOSTANDARD LVCMOS33 } [get_ports { VGA_R[2] }]; #IO_L1N_T0_AD4N_35 Sch=vga_r[2]
|
||||
#set_property -dict { PACKAGE_PIN A4 IOSTANDARD LVCMOS33 } [get_ports { VGA_R[3] }]; #IO_L8P_T1_AD14P_35 Sch=vga_r[3]
|
||||
#set_property -dict { PACKAGE_PIN C6 IOSTANDARD LVCMOS33 } [get_ports { VGA_G[0] }]; #IO_L1P_T0_AD4P_35 Sch=vga_g[0]
|
||||
#set_property -dict { PACKAGE_PIN A5 IOSTANDARD LVCMOS33 } [get_ports { VGA_G[1] }]; #IO_L3N_T0_DQS_AD5N_35 Sch=vga_g[1]
|
||||
#set_property -dict { PACKAGE_PIN B6 IOSTANDARD LVCMOS33 } [get_ports { VGA_G[2] }]; #IO_L2N_T0_AD12N_35 Sch=vga_g[2]
|
||||
#set_property -dict { PACKAGE_PIN A6 IOSTANDARD LVCMOS33 } [get_ports { VGA_G[3] }]; #IO_L3P_T0_DQS_AD5P_35 Sch=vga_g[3]
|
||||
#set_property -dict { PACKAGE_PIN B7 IOSTANDARD LVCMOS33 } [get_ports { VGA_B[0] }]; #IO_L2P_T0_AD12P_35 Sch=vga_b[0]
|
||||
#set_property -dict { PACKAGE_PIN C7 IOSTANDARD LVCMOS33 } [get_ports { VGA_B[1] }]; #IO_L4N_T0_35 Sch=vga_b[1]
|
||||
#set_property -dict { PACKAGE_PIN D7 IOSTANDARD LVCMOS33 } [get_ports { VGA_B[2] }]; #IO_L6N_T0_VREF_35 Sch=vga_b[2]
|
||||
#set_property -dict { PACKAGE_PIN D8 IOSTANDARD LVCMOS33 } [get_ports { VGA_B[3] }]; #IO_L4P_T0_35 Sch=vga_b[3]
|
||||
#set_property -dict { PACKAGE_PIN B11 IOSTANDARD LVCMOS33 } [get_ports { VGA_HS }]; #IO_L4P_T0_15 Sch=vga_hs
|
||||
#set_property -dict { PACKAGE_PIN B12 IOSTANDARD LVCMOS33 } [get_ports { VGA_VS }]; #IO_L3N_T0_DQS_AD1N_15 Sch=vga_vs
|
||||
|
||||
##Micro SD Connector
|
||||
#set_property -dict { PACKAGE_PIN E2 IOSTANDARD LVCMOS33 } [get_ports { SD_RESET }]; #IO_L14P_T2_SRCC_35 Sch=sd_reset
|
||||
#set_property -dict { PACKAGE_PIN A1 IOSTANDARD LVCMOS33 } [get_ports { SD_CD }]; #IO_L9N_T1_DQS_AD7N_35 Sch=sd_cd
|
||||
#set_property -dict { PACKAGE_PIN B1 IOSTANDARD LVCMOS33 } [get_ports { SD_SCK }]; #IO_L9P_T1_DQS_AD7P_35 Sch=sd_sck
|
||||
#set_property -dict { PACKAGE_PIN C1 IOSTANDARD LVCMOS33 } [get_ports { SD_CMD }]; #IO_L16N_T2_35 Sch=sd_cmd
|
||||
#set_property -dict { PACKAGE_PIN C2 IOSTANDARD LVCMOS33 } [get_ports { SD_DAT[0] }]; #IO_L16P_T2_35 Sch=sd_dat[0]
|
||||
#set_property -dict { PACKAGE_PIN E1 IOSTANDARD LVCMOS33 } [get_ports { SD_DAT[1] }]; #IO_L18N_T2_35 Sch=sd_dat[1]
|
||||
#set_property -dict { PACKAGE_PIN F1 IOSTANDARD LVCMOS33 } [get_ports { SD_DAT[2] }]; #IO_L18P_T2_35 Sch=sd_dat[2]
|
||||
#set_property -dict { PACKAGE_PIN D2 IOSTANDARD LVCMOS33 } [get_ports { SD_DAT[3] }]; #IO_L14N_T2_SRCC_35 Sch=sd_dat[3]
|
||||
|
||||
##Accelerometer
|
||||
#set_property -dict { PACKAGE_PIN E15 IOSTANDARD LVCMOS33 } [get_ports { ACL_MISO }]; #IO_L11P_T1_SRCC_15 Sch=acl_miso
|
||||
#set_property -dict { PACKAGE_PIN F14 IOSTANDARD LVCMOS33 } [get_ports { ACL_MOSI }]; #IO_L5N_T0_AD9N_15 Sch=acl_mosi
|
||||
#set_property -dict { PACKAGE_PIN F15 IOSTANDARD LVCMOS33 } [get_ports { ACL_SCLK }]; #IO_L14P_T2_SRCC_15 Sch=acl_sclk
|
||||
#set_property -dict { PACKAGE_PIN D15 IOSTANDARD LVCMOS33 } [get_ports { ACL_CSN }]; #IO_L12P_T1_MRCC_15 Sch=acl_csn
|
||||
#set_property -dict { PACKAGE_PIN B13 IOSTANDARD LVCMOS33 } [get_ports { ACL_INT[1] }]; #IO_L2P_T0_AD8P_15 Sch=acl_int[1]
|
||||
#set_property -dict { PACKAGE_PIN C16 IOSTANDARD LVCMOS33 } [get_ports { ACL_INT[2] }]; #IO_L20P_T3_A20_15 Sch=acl_int[2]
|
||||
|
||||
##Temperature Sensor
|
||||
#set_property -dict { PACKAGE_PIN C14 IOSTANDARD LVCMOS33 } [get_ports { TMP_SCL }]; #IO_L1N_T0_AD0N_15 Sch=tmp_scl
|
||||
#set_property -dict { PACKAGE_PIN C15 IOSTANDARD LVCMOS33 } [get_ports { TMP_SDA }]; #IO_L12N_T1_MRCC_15 Sch=tmp_sda
|
||||
#set_property -dict { PACKAGE_PIN D13 IOSTANDARD LVCMOS33 } [get_ports { TMP_INT }]; #IO_L6N_T0_VREF_15 Sch=tmp_int
|
||||
#set_property -dict { PACKAGE_PIN B14 IOSTANDARD LVCMOS33 } [get_ports { TMP_CT }]; #IO_L2N_T0_AD8N_15 Sch=tmp_ct
|
||||
|
||||
##Omnidirectional Microphone
|
||||
#set_property -dict { PACKAGE_PIN J5 IOSTANDARD LVCMOS33 } [get_ports { M_CLK }]; #IO_25_35 Sch=m_clk
|
||||
#set_property -dict { PACKAGE_PIN H5 IOSTANDARD LVCMOS33 } [get_ports { M_DATA }]; #IO_L24N_T3_35 Sch=m_data
|
||||
#set_property -dict { PACKAGE_PIN F5 IOSTANDARD LVCMOS33 } [get_ports { M_LRSEL }]; #IO_0_35 Sch=m_lrsel
|
||||
|
||||
##PWM Audio Amplifier
|
||||
#set_property -dict { PACKAGE_PIN A11 IOSTANDARD LVCMOS33 } [get_ports { AUD_PWM }]; #IO_L4N_T0_15 Sch=aud_pwm
|
||||
#set_property -dict { PACKAGE_PIN D12 IOSTANDARD LVCMOS33 } [get_ports { AUD_SD }]; #IO_L6P_T0_15 Sch=aud_sd
|
||||
|
||||
##USB-RS232 Interface
|
||||
#set_property -dict { PACKAGE_PIN C4 IOSTANDARD LVCMOS33 } [get_ports { UART_TXD_IN }]; #IO_L7P_T1_AD6P_35 Sch=uart_txd_in
|
||||
#set_property -dict { PACKAGE_PIN D4 IOSTANDARD LVCMOS33 } [get_ports { UART_RXD_OUT }]; #IO_L11N_T1_SRCC_35 Sch=uart_rxd_out
|
||||
#set_property -dict { PACKAGE_PIN D3 IOSTANDARD LVCMOS33 } [get_ports { UART_CTS }]; #IO_L12N_T1_MRCC_35 Sch=uart_cts
|
||||
#set_property -dict { PACKAGE_PIN E5 IOSTANDARD LVCMOS33 } [get_ports { UART_RTS }]; #IO_L5N_T0_AD13N_35 Sch=uart_rts
|
||||
|
||||
##USB HID (PS/2)
|
||||
#set_property -dict { PACKAGE_PIN F4 IOSTANDARD LVCMOS33 } [get_ports { PS2_CLK }]; #IO_L13P_T2_MRCC_35 Sch=ps2_clk
|
||||
#set_property -dict { PACKAGE_PIN B2 IOSTANDARD LVCMOS33 } [get_ports { PS2_DATA }]; #IO_L10N_T1_AD15N_35 Sch=ps2_data
|
||||
|
||||
##SMSC Ethernet PHY
|
||||
#set_property -dict { PACKAGE_PIN C9 IOSTANDARD LVCMOS33 } [get_ports { ETH_MDC }]; #IO_L11P_T1_SRCC_16 Sch=eth_mdc
|
||||
#set_property -dict { PACKAGE_PIN A9 IOSTANDARD LVCMOS33 } [get_ports { ETH_MDIO }]; #IO_L14N_T2_SRCC_16 Sch=eth_mdio
|
||||
#set_property -dict { PACKAGE_PIN B3 IOSTANDARD LVCMOS33 } [get_ports { ETH_RSTN }]; #IO_L10P_T1_AD15P_35 Sch=eth_rstn
|
||||
#set_property -dict { PACKAGE_PIN D9 IOSTANDARD LVCMOS33 } [get_ports { ETH_CRSDV }]; #IO_L6N_T0_VREF_16 Sch=eth_crsdv
|
||||
#set_property -dict { PACKAGE_PIN C10 IOSTANDARD LVCMOS33 } [get_ports { ETH_RXERR }]; #IO_L13N_T2_MRCC_16 Sch=eth_rxerr
|
||||
#set_property -dict { PACKAGE_PIN C11 IOSTANDARD LVCMOS33 } [get_ports { ETH_RXD[0] }]; #IO_L13P_T2_MRCC_16 Sch=eth_rxd[0]
|
||||
#set_property -dict { PACKAGE_PIN D10 IOSTANDARD LVCMOS33 } [get_ports { ETH_RXD[1] }]; #IO_L19N_T3_VREF_16 Sch=eth_rxd[1]
|
||||
#set_property -dict { PACKAGE_PIN B9 IOSTANDARD LVCMOS33 } [get_ports { ETH_TXEN }]; #IO_L11N_T1_SRCC_16 Sch=eth_txen
|
||||
#set_property -dict { PACKAGE_PIN A10 IOSTANDARD LVCMOS33 } [get_ports { ETH_TXD[0] }]; #IO_L14P_T2_SRCC_16 Sch=eth_txd[0]
|
||||
#set_property -dict { PACKAGE_PIN A8 IOSTANDARD LVCMOS33 } [get_ports { ETH_TXD[1] }]; #IO_L12N_T1_MRCC_16 Sch=eth_txd[1]
|
||||
#set_property -dict { PACKAGE_PIN D5 IOSTANDARD LVCMOS33 } [get_ports { ETH_REFCLK }]; #IO_L11P_T1_SRCC_35 Sch=eth_refclk
|
||||
#set_property -dict { PACKAGE_PIN B8 IOSTANDARD LVCMOS33 } [get_ports { ETH_INTN }]; #IO_L12P_T1_MRCC_16 Sch=eth_intn
|
||||
|
||||
##Quad SPI Flash
|
||||
#set_property -dict { PACKAGE_PIN K17 IOSTANDARD LVCMOS33 } [get_ports { QSPI_DQ[0] }]; #IO_L1P_T0_D00_MOSI_14 Sch=qspi_dq[0]
|
||||
#set_property -dict { PACKAGE_PIN K18 IOSTANDARD LVCMOS33 } [get_ports { QSPI_DQ[1] }]; #IO_L1N_T0_D01_DIN_14 Sch=qspi_dq[1]
|
||||
#set_property -dict { PACKAGE_PIN L14 IOSTANDARD LVCMOS33 } [get_ports { QSPI_DQ[2] }]; #IO_L2P_T0_D02_14 Sch=qspi_dq[2]
|
||||
#set_property -dict { PACKAGE_PIN M14 IOSTANDARD LVCMOS33 } [get_ports { QSPI_DQ[3] }]; #IO_L2N_T0_D03_14 Sch=qspi_dq[3]
|
||||
#set_property -dict { PACKAGE_PIN L13 IOSTANDARD LVCMOS33 } [get_ports { QSPI_CSN }]; #IO_L6P_T0_FCS_B_14 Sch=qspi_csn
|
@@ -0,0 +1,84 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module nexys_CYBERcobra_dz(
|
||||
input CLK100,
|
||||
input resetn,
|
||||
input BTND,
|
||||
input [15:0] SW,
|
||||
output CA, CB, CC, CD, CE, CF, CG,
|
||||
output [7:0] AN
|
||||
);
|
||||
|
||||
CYBERcobra dut(
|
||||
.clk_i(btn),
|
||||
.rst_i(!resetn),
|
||||
.sw_i(SW[15:0]),
|
||||
.out_o(out)
|
||||
);
|
||||
|
||||
localparam pwm = 1000;
|
||||
reg [9:0] counter;
|
||||
reg [3:0] semseg;
|
||||
reg [7:0] ANreg;
|
||||
reg CAr, CBr, CCr, CDr, CEr, CFr, CGr;
|
||||
reg btn;
|
||||
wire [31:0] out;
|
||||
|
||||
assign AN[7:0] = ANreg[7:0];
|
||||
assign {CA, CB, CC, CD, CE, CF, CG} = {CAr, CBr, CCr, CDr, CEr, CFr, CGr};
|
||||
|
||||
|
||||
always @(posedge CLK100) begin
|
||||
if (!resetn) begin
|
||||
counter <= 'b0;
|
||||
ANreg[7:0] <= 8'b11111111;
|
||||
{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1111111;
|
||||
btn <= BTND;
|
||||
end
|
||||
else begin
|
||||
btn <= BTND;
|
||||
if (counter < pwm) counter = counter + 'b1;
|
||||
else begin
|
||||
counter = 'b0;
|
||||
ANreg[1] <= ANreg[0];
|
||||
ANreg[2] <= ANreg[1];
|
||||
ANreg[3] <= ANreg[2];
|
||||
ANreg[4] <= ANreg[3];
|
||||
ANreg[5] <= ANreg[4];
|
||||
ANreg[6] <= ANreg[5];
|
||||
ANreg[7] <= ANreg[6];
|
||||
ANreg[0] <= !(ANreg[6:0] == 7'b1111111);
|
||||
end
|
||||
case (1'b0)
|
||||
ANreg[0]: semseg <= out[3 : 0];
|
||||
ANreg[1]: semseg <= out[7 : 4];
|
||||
ANreg[2]: semseg <= out[11: 8];
|
||||
ANreg[3]: semseg <= out[15:12];
|
||||
ANreg[4]: semseg <= out[19:16];
|
||||
ANreg[5]: semseg <= out[23:20];
|
||||
ANreg[6]: semseg <= out[27:24];
|
||||
ANreg[7]: semseg <= out[31:28];
|
||||
endcase
|
||||
case (semseg)
|
||||
4'h0: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0000001;
|
||||
4'h1: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1001111;
|
||||
4'h2: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0010010;
|
||||
4'h3: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0000110;
|
||||
4'h4: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1001100;
|
||||
4'h5: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0100100;
|
||||
4'h6: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0100000;
|
||||
4'h7: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0001111;
|
||||
4'h8: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0000000;
|
||||
4'h9: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0000100;
|
||||
4'hA: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0001000;
|
||||
4'hB: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1100000;
|
||||
4'hC: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0110001;
|
||||
4'hD: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1000010;
|
||||
4'hE: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0110000;
|
||||
4'hF: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0111000;
|
||||
default: {CAr,CBr,CCr,CDr, CEr, CFr, CGr} <= 7'b0111111;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
@@ -0,0 +1,77 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module nexys_CYBERcobra(
|
||||
input CLK100,
|
||||
input resetn,
|
||||
input BTND,
|
||||
input [15:0] SW,
|
||||
output CA, CB, CC, CD, CE, CF, CG,
|
||||
output [7:0] AN
|
||||
);
|
||||
|
||||
CYBERcobra dut(
|
||||
.clk_i(CLK100),
|
||||
.rst_i(!resetn),
|
||||
.sw_i({7'b0,splash,SW[7:0]}),
|
||||
.out_o(out)
|
||||
);
|
||||
|
||||
localparam pwm = 1000;
|
||||
reg [9:0] counter;
|
||||
reg [3:0] semseg;
|
||||
reg [7:0] ANreg;
|
||||
reg CAr, CBr, CCr, CDr, CEr, CFr, CGr;
|
||||
reg [3:0] btn;
|
||||
reg [10:0] btn_reg;
|
||||
wire splash;
|
||||
wire [31:0] out;
|
||||
|
||||
assign AN[7:0] = ANreg[7:0];
|
||||
assign {CA, CB, CC, CD, CE, CF, CG} = {CAr, CBr, CCr, CDr, CEr, CFr, CGr};
|
||||
assign splash = ((btn == 4'b1111) ^ btn_reg[10]) && (btn == 4'b1111);
|
||||
|
||||
always @(posedge CLK100) begin
|
||||
if (!resetn) begin
|
||||
counter <= 'b0;
|
||||
ANreg[7:0] <= 8'b11111111;
|
||||
{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1111111;
|
||||
btn <= 4'b0;
|
||||
btn_reg <= 0;
|
||||
end
|
||||
else begin
|
||||
btn <= (btn << 1'b1) + BTND;
|
||||
btn_reg <= (btn_reg << 1'b1) + (btn == 4'b1111);
|
||||
if (counter < pwm) counter = counter + 'b1;
|
||||
else begin
|
||||
counter = 'b0;
|
||||
ANreg[1] <= ANreg[0];
|
||||
ANreg[2] <= ANreg[1];
|
||||
ANreg[3] <= ANreg[2];
|
||||
ANreg[4] <= ANreg[3];
|
||||
ANreg[5] <= ANreg[4];
|
||||
ANreg[6] <= ANreg[5];
|
||||
ANreg[7] <= ANreg[6];
|
||||
ANreg[0] <= !(ANreg[6:0] == 7'b1111111);
|
||||
end
|
||||
case (1'b0)
|
||||
ANreg[0]: semseg <= out[3 : 0];
|
||||
ANreg[1]: semseg <= out[7 : 4];
|
||||
ANreg[2]: semseg <= out[11: 8];
|
||||
ANreg[3]: semseg <= out[15:12];
|
||||
ANreg[4]: semseg <= out[19:16];
|
||||
ANreg[5]: semseg <= out[23:20];
|
||||
ANreg[6]: semseg <= out[27:24];
|
||||
ANreg[7]: semseg <= out[31:28];
|
||||
endcase
|
||||
case (semseg)
|
||||
4'h1: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1110001; //L
|
||||
4'h3: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0110110; //?
|
||||
4'h8: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0000001; //O
|
||||
4'hA: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0001000; //A
|
||||
4'hC: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1001000; //H
|
||||
default: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1111111; //
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
24
Labs/04. Primitive programmable device/example.txt
Normal file
24
Labs/04. Primitive programmable device/example.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
E1
|
||||
FF
|
||||
FF
|
||||
0F
|
||||
02
|
||||
00
|
||||
00
|
||||
20
|
||||
23
|
||||
00
|
||||
00
|
||||
00
|
||||
01
|
||||
60
|
||||
04
|
||||
10
|
||||
E0
|
||||
5F
|
||||
04
|
||||
4F
|
||||
00
|
||||
00
|
||||
04
|
||||
80
|
52
Labs/04. Primitive programmable device/tb_cybercobra.sv
Normal file
52
Labs/04. Primitive programmable device/tb_cybercobra.sv
Normal file
@@ -0,0 +1,52 @@
|
||||
`timescale 1ns / 1ps
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Company: MIET
|
||||
// Engineer: Nikita Bulavin
|
||||
//
|
||||
// Create Date:
|
||||
// Design Name:
|
||||
// Module Name: tb_cybercobra
|
||||
// Project Name: RISCV_practicum
|
||||
// Target Devices: Nexys A7-100T
|
||||
// Tool Versions:
|
||||
// Description: tb for CYBERcobra 3000 Pro 2.1
|
||||
//
|
||||
// Dependencies:
|
||||
//
|
||||
// Revision:
|
||||
// Revision 0.01 - File Created
|
||||
// Additional Comments:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
module tb_CYBERcobra();
|
||||
|
||||
CYBERcobra dut(
|
||||
.clk_i(clk),
|
||||
.rst_i(rstn),
|
||||
.sw_i (sw_i ),
|
||||
.out_o(OUT)
|
||||
);
|
||||
|
||||
wire [31:0] OUT;
|
||||
reg clk;
|
||||
reg rstn;
|
||||
reg [15:0] sw_i;
|
||||
|
||||
initial clk <= 0;
|
||||
always #5 clk = ~clk;
|
||||
|
||||
initial begin
|
||||
$display( "\nStart test: \n\n==========================\nCLICK THE BUTTON 'Run All'\n==========================\n"); $stop();
|
||||
rstn = 1'b1;
|
||||
#10;
|
||||
rstn = 1'b0;
|
||||
sw_i = 16'b100001000; //значение, до которого считает счетчик
|
||||
//#260;
|
||||
//sw_i = 15'b0;
|
||||
#10000;
|
||||
$display("\n The test is over \n See the internal signals of the CYBERcobra on the waveform \n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
@@ -0,0 +1,313 @@
|
||||
# Написание программы под процессор CYBERcobra
|
||||
|
||||
Чтобы максимально "прочувствовать" принцип работы созданного вами процессора, вам необходимо написать один из [вариантов программ](#индивидуальные-задания). Вариант выдается преподавателем.
|
||||
|
||||
Порядок выполнения задания следующий:
|
||||
|
||||
1. В первую очередь необходимо ознакомиться с заданием и изучить пример, приведенный в конце задания. Если возникли вопросы по заданию или примеру, свяжитесь с преподавателем. Чем лучше вы понимаете что от вас ожидают, тем проще будет выполнить задание.
|
||||
2. Составьте алгоритм работы программы (буквально возьмите листочек, и нарисуйте блок-схему). Прежде чем вы погрузитесь в увлекательное приключение с ноликами и единицами, вам нужно составить четкую карту вашего путешествия.
|
||||
3. Проверьте вашу блок-схему на данных из примера. Если все сошлось, проверьте вашу блок-схему на других данных. Не забывайте про краевые случаи (отрицательные числа, деление на ноль, переполнения и прочее — для каждого задания они могут быть разными).
|
||||
4. После того как вы убедились в работоспособности алгоритма на всех возможных данных, наступает время претворить его в виде двоичной программы.
|
||||
5. Программа описывается в текстовом файле. Для удобства был написан специальный конвертер, который будет принимать на вход текстовый файл с комментариями и двоичным кодом, разделенным пробелами, а на выход выдавать текстовый файл, которым можно будет проинициализировать память инструкций. Подробнее о конвертере смотрите в разделе [cyberconverter](#cyberconverter). Пример текстового файла, который сможет принять конвертер:
|
||||
|
||||
```text
|
||||
//J B WS ALUop RA1 RA2 const WA
|
||||
0 0 00 11111111111111111111111 00001 // загрузить константу -1 регистр 1
|
||||
0 0 10 00000000000000000000000 00010 // загрузить значение с входа sw_i в регистр 2
|
||||
0 0 00 00000000000000000000001 00011 // загрузить константу 1 регистр 3
|
||||
0 0 01 00000 00001 00011 00000000 00001 // сложить регистр 1 с регистром 3 и поместить результат в регистр 1
|
||||
0 1 00 11110 00001 00010 11111111 00000 // если значение в регистре 1 меньше значения в регистре 2, возврат на 1 инструкцию назад
|
||||
1 0 00 00000 00001 00000 00000000 00000 // бесконечное повторение этой инструкции с выводом на out_o значения в регистре 1
|
||||
```
|
||||
|
||||
6. При реализации условных переходов следует иметь в виду пару правил:
|
||||
1. Блок ветвления (аналог `if/else`) состоит из двух наборов инструкций:
|
||||
1. инструкции блока `if` (если условие выполнилось)
|
||||
2. инструкции блока `else` (если условие не выполнилось)
|
||||
|
||||
При этом сразу за инструкцией ветвления описываются инструкции блока `else` (т.к. в случае не выполнения условия перехода, `PC` перейдет к следующей инструкции).
|
||||
Для того, чтобы после выполнения инструкций блока `else` не начались исполняться инструкции блока `if`, в конце блока этих инструкций необходимо добавить безусловный переход на инструкцию, следующую за инструкциями блока `if`.
|
||||
2. Если вы реализуете не ветвление (аналог блока `if/else`), а только проверку условия для выполнения какого-то блока инструкций (аналог блока `if` без блока `else`), вы должны помнить, что блок `else` все равно есть, просто в этом блоке нет инструкций. Однако вы, как и в прошлом правиле, должны добавить безусловный переход на инструкцию, следующую за инструкциями блока `if`.
|
||||
Этого можно избежать, если инвертировать ваше условие. В этом случае, если ваше инвертированное условие выполнится, вы сможете сразу пропустить нужное количество инструкций и начать исполнять инструкцию за пределами вашего блока `if`. Если инвертированное условие не выполнится (т.е. выполнится исходное условие), `PC` перейдет к следующей инструкции, где и будут находиться ваши инструкции блока `if`.
|
||||
Звучит достаточно запутанно, поэтому давайте рассмотрим пару примеров. Сначала мы запишем нашу идею на языке Си, а затем перенесем её в двоичный код под архитектуру CYBERcobra:
|
||||
|
||||
```C
|
||||
if(reg[1]==reg[5])
|
||||
{
|
||||
reg[2] = 10;
|
||||
reg[3] = 15;
|
||||
}
|
||||
else
|
||||
{
|
||||
reg[2] = 7;
|
||||
goto if_end; // Поскольку в памяти программы блок else будет идти
|
||||
// сразу за инструкцией условного перехода, необходимо
|
||||
// добавить в конце инструкцию безусловного перехода,
|
||||
// чтобы не начать исполнять инструкции блока if.
|
||||
}
|
||||
if_end:
|
||||
```
|
||||
|
||||
Мы хотим проверить на равенство значения в регистровом файле по адресам `1` и `5`. Если это так, записать значения `10` и `15` по адресам `2` и `3` соответственно. В противном случае, записать значение `7` по адресу `2`.
|
||||
Это можно реализовать следующей двоичной программой:
|
||||
|
||||
```text
|
||||
//J B WS ALUop RA1 RA2 const WA
|
||||
0 1 00 11000 00010 00101 00000011 00000 // Если регистры 2 и 5 равны,
|
||||
// перемести PC на 3 инструкции вперед
|
||||
// (перешагни через две
|
||||
// инструкции блока else)
|
||||
//---------------------------------------
|
||||
// блок else
|
||||
//---------------------------------------
|
||||
0 0 00 00000000000000000000111 00010 // reg[2] = 7
|
||||
1 0 00 00000 00000 00000 00000011 00000 // goto if_end
|
||||
//---------------------------------------
|
||||
|
||||
|
||||
//---------------------------------------
|
||||
// блок if
|
||||
//---------------------------------------
|
||||
0 0 00 00000000000000000001010 00010 // reg[2] = 10
|
||||
0 0 00 00000000000000000001111 00011 // reg[3] = 15
|
||||
//---------------------------------------
|
||||
|
||||
0 0 00 00000000000000000000000 00000 // некая инструкция с меткой if_end
|
||||
// куда будет перемещен PC после
|
||||
// выполнения блока else
|
||||
```
|
||||
|
||||
Рассмотрим второй пример, где нет блока `else`:
|
||||
|
||||
```C
|
||||
if(reg[1] == reg[5])
|
||||
{
|
||||
reg[2] = 10;
|
||||
reg[3] = 15;
|
||||
}
|
||||
```
|
||||
|
||||
Как упоминалось ранее, можно реализовать этот условный переход по той же схеме (тогда пример программы на Си примет вид):
|
||||
|
||||
```C
|
||||
if(reg[1] == reg[5])
|
||||
{
|
||||
reg[2] = 10;
|
||||
reg[3] = 15;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto if_end;
|
||||
}
|
||||
if_end:
|
||||
```
|
||||
|
||||
А можно инвертировать условие:
|
||||
|
||||
```C
|
||||
if(reg[1] != reg[5])
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
reg[2] = 10;
|
||||
reg[3] = 15;
|
||||
}
|
||||
```
|
||||
|
||||
В этом случае, нет нужды делать безусловный переход на инструкцию, следующую за инструкциями блока `if`, т.к. там нет никаких инструкций.
|
||||
Такое условие можно реализовать следующей двоичной программой:
|
||||
|
||||
```text
|
||||
//J B WS ALUop RA1 RA2 const WA
|
||||
0 1 00 11001 00010 00101 00000011 00000 // Если регистры 2 и 5 НЕ РАВНЫ,
|
||||
// перемести PC на 3 инструкции вперед
|
||||
// (перешагни через две
|
||||
// инструкции блока else)
|
||||
//---------------------------------------
|
||||
// блок else
|
||||
//---------------------------------------
|
||||
0 0 00 00000000000000000001010 00010 // reg[2] = 7
|
||||
0 0 00 00000000000000000001111 00011 // reg[3] = 15
|
||||
//---------------------------------------
|
||||
```
|
||||
|
||||
7. В двоичном программировании, реализация циклов лучше всего делается аналогом `do while` в Си (если вы уверены, что первая итерация цикла гарантированно пройдет условие выхода из цикла). В этом случае, вы сперва описываете тело цикла, а затем через условный переход возвращаетесь обратно к началу тела цикла. Если условие не выполнилось, вы автоматически выйдете из цикла.
|
||||
8. Для того, чтобы в конце выполнения программы было легко увидеть результат выполнения, в конец программы необходимо добавить инструкцию безусловного перехода, поле `const` которой равно нулю. В этом случае, будет выполняться `PC=PC+0` что приведет к повторению этой инструкции снова и снова. При этом в поле `RA1` необходимо указать адрес регистра, где хранится результат. На временной диаграмме это отобразится так, что в какой-то момент все сигналы процессора "замрут", а на выходе `out_o` окажется результат, вычисленный вашей программой.
|
||||
9. После того, как вы написали программу, её необходимо проверить. Для этого сперва необходимо преобразовать её к формату, принимаемому памятью инструкций с помощью программы [`cyberconverter`](#cyberconverter). При необходимости, заменить данные в файле, инициализирующем память инструкций актуальными данными.
|
||||
10. Если ваша программа использует данные с внешних устройств, нужно выставить проверяемое вами значение в модуле `testbench` на вход `sw_i` в месте подключения модуля `CYBERcobra`.
|
||||
11. Проверка работы программы осуществляется аналогично проверке модуля `CYBERcobra` — вы достаете внутренние сигналы модуля, и смотрите за поведением сигналов: `PC`, `read_data` памяти инструкций, `flag` АЛУ, содержимым регистрового файла. Проверяете, что в конце на выходе `out_o` размещено корректное значение.
|
||||
12. В случае, если вы не успели довести свой процессор до работоспособного состояния, вы можете проверить программу на готовом процессоре из ветки [`Я-не-смог`](https://github.com/MPSU/APS/tree/%D0%AF-%D0%BD%D0%B5-%D1%81%D0%BC%D0%BE%D0%B3).
|
||||
|
||||
## cyberconverter
|
||||
|
||||
[cyberconverter](cyberconverter.cpp) — это программа, которая преобразует текстовый файл с инструкциями архитектуры CYBERcobra в текстовый файл, который сможет принять память инструкций.
|
||||
|
||||
cyberconverter может обрабатывать файлы, содержащие комментарии (начинающиеся с `//`), пробелы и пустые строки, а так же наборы символов `0` и `1`. Комментарии, пробелы и пустые строки удаляются, после чего оставшиеся строки из 32 нулей и единиц нарезаются на четверки по 8 бит, конвертируются в шестнадцатиричные значения и записываются в выходной файл.
|
||||
|
||||
cyberconverter принимает до двух аргументов. Порядок запуска следующий:
|
||||
|
||||
1. Вызов справки:
|
||||
|
||||
```bash
|
||||
cyberconverter -h
|
||||
cyberconverter --help
|
||||
```
|
||||
|
||||
2. Преобразование программы, записанной в файле `test.txt`, с записью результата в файл `program.txt`:
|
||||
|
||||
```bash
|
||||
cyberconverter test.txt program.txt
|
||||
```
|
||||
|
||||
3. Если не указан второй аргумент, результат будет записан в файл:
|
||||
`<имя_исходного_файла>_converted.<расширение исходного файла>`:
|
||||
|
||||
```bash
|
||||
cyberconverter test.txt
|
||||
```
|
||||
|
||||
Результат будет записан в файл `test_converted.txt`.
|
||||
|
||||
4. Если программа будет запущена без аргументов, то исходным файлом будет считаться файл `program.txt`.
|
||||
|
||||
В случае отсутствия исходного файла, наличия неподдерживаемых символов или неверной длины инструкции будет выведено сообщение об ошибке.
|
||||
|
||||
## Индивидуальные задания
|
||||
|
||||
1. Вычислить [циклический сдвиг](https://ru.wikipedia.org/wiki/Битовый_сдвиг#Циклический_сдвиг) вправо `a >> sw_i`.
|
||||
Пример: `a = 0...01011`, `sw_i = 0...010`.
|
||||
Результат вычислений: `out_o = 110...010`.
|
||||
|
||||
2. Вычислить `a - sw_i` без использования операции вычитания.
|
||||
Пример: `sw_i = 0...011`, `a = 0...0100`.
|
||||
Результат вычислений: `out_o = 0...001`.
|
||||
|
||||
3. Вычислить [циклический сдвиг](https://ru.wikipedia.org/wiki/Битовый_сдвиг#Циклический_сдвиг) влево `a << sw_i`.
|
||||
Пример: `a = 10...01011`, `sw_i = 0...10`.
|
||||
Результат вычислений: `out_o = 0...0101110`.
|
||||
|
||||
4. Поменять местами `[7:0]` и `[15:8]` биты числа `sw_i`. Вывести результат в `out_o`.
|
||||
Пример: `sw_i = 0...010100000_1110010`.
|
||||
Результат вычислений: `out_o = 0...011100101_10100000`.
|
||||
|
||||
5. Вычислить приблизительное значение длины вектора `(a;sw_i)`. Вычисляется как `max + min/2`, где `max` и `min` — это большее и меньшее из чисел `a` и `sw_i` соответственно.
|
||||
Пример: `a = 0...011`, `sw_i = 0...0100`.
|
||||
Результат вычислений: `out_o = 0...0101`.
|
||||
---
|
||||
|
||||
6. Вычислить `a * sw_i` посредством суммы `sw_i` значений `a`. Вывести результат в `out_o`.
|
||||
Пример: `a = 5`, `sw_i = 4`. `5 * 4 == 5 + 5 + 5 + 5 = 20`.
|
||||
Результат вычислений: `out_o = 0...010100`.
|
||||
|
||||
7. Если `sw_i[1:0] == 00`, то в `out_o` выводится `a`, если `sw_i[1:0] == 01`, то `b`, если `sw_i[1:0] == 10`, то `c`, если `sw_i[1:0] == 11`, то `d`.
|
||||
Пример: `a = 0...00`, `b = 0...010`, `c = 0...011`, `d = 0...001`, `sw_i[1:0] = 01`.
|
||||
Результат вычислений: `out_o = 0...010`.
|
||||
|
||||
8. Посчитать длину окружности при заданном радиусе `sw_i`, считая, что `pi = 3`. Вывести результат в `out_o`.
|
||||
Пример: `sw_i = 0...010`.
|
||||
Результат вычислений: `out_o = 0...01100`.
|
||||
|
||||
9. Если `sw_i` является степенью двойки, то вывести `out_o = 0...01`, в противном случае, `out_o = 0...0`.
|
||||
Пример 1: `sw_i = 0...0100`. Результат вычислений: `out_o = 0...01`.
|
||||
Пример 2: `sw_i = 0...0110`. Результат вычислений: `out_o = 0...00`.
|
||||
|
||||
10. Найти количество нулей в двоичном представлении числа `sw_i`. Вывести результат в `out_o`.
|
||||
Пример: `sw_i = 1...10110_0010`.
|
||||
Результат вычислений: `out_o = 0...0101`.
|
||||
|
||||
11. Найти наибольший двоичный разряд числа `sw_i`, значение которого равно `1`. Если такого нет, вывести `32`. Вывести результат в `out_o`.
|
||||
Пример: `sw_i = 0...0110`.
|
||||
Результат вычислений: `out_o = 0...010`.
|
||||
|
||||
12. Сформировать число, состоящее из чётных двоичных разрядов числа `sw_i`. Вывести результат в `out_o`.
|
||||
Пример: `sw_i = 0...011_1011_1000`.
|
||||
Результат вычислений `out_o = 0...01_0100`.
|
||||
|
||||
13. Найти количество единиц в двоичном представлении числа `sw_i` (обрати внимание, `sw_i` – знаковое число). Вывести результат в `out_o`.
|
||||
Пример: `sw_i = 0...0101_0110`.
|
||||
Результат вычислений: `out_o = 0...0100`.
|
||||
|
||||
14. Найти количество двоичных разрядов, в которых различаются числа `sw_i` и `a`. Вывести результат в `out_o`.
|
||||
Пример: `sw_i = 0...0110`, `a = 0...01110`.
|
||||
Результат вычислений: `out_o = 0...01`.
|
||||
|
||||
15. Вывести в `out_o` подряд все единицы входного числа `sw_i`.
|
||||
Пример: `sw_i = 0...01011011011`.
|
||||
Результат вычислений:`out_o` = `0...01111111`.
|
||||
|
||||
16. Вывести в `out_o` значение выражения: `out = (k*a + b) + c`, где `k` — это `sw_i[15:12]`, `a` — это `sw_i[11:8]`, `b` — это `sw_i[7:4]`, `c` — это `sw_i[3:0]`.
|
||||
Пример: `sw_i = 0...0001_0000_0100_0011`.
|
||||
Результат вычислений: `out_o = 0...01010`.
|
||||
|
||||
---
|
||||
|
||||
17. Найти остаток от деления `sw_i` на `a`. Вывести результат в `out_o`.
|
||||
Пример: `sw_i = 0...0101`, `a = 0...010`.
|
||||
Результат вычислений: `out_o = 0...01`.
|
||||
|
||||
18. Найти и вывести в `out_o` количество вхождений `a[2:0]` в `sw_i` без пересечений.
|
||||
Пример: `a[2:0] = 010`, `sw_i = 0...01101_0101`.
|
||||
Результат вычислений: `out_o = 0...01`.
|
||||
|
||||
19. Определить, сколько раз встречается `11` в двоичном представлении `sw_i` без пересечений. Вывести результат в `out_o`.
|
||||
Пример: `sw_i = 0...01110`.
|
||||
Результат вычислений: `out_o = 0...01`.
|
||||
|
||||
20. Вывести в `out_o` результат целочисленного деления `a/sw_i`.
|
||||
Пример: `sw_i = 0...010`, `a = 0...0111`.
|
||||
Результат вычислений: `out_o = 0...011`.
|
||||
|
||||
21. Вывести в `out_o` сумму `sw_i[3:0]` + `sw_i[7:4]` + `sw_i[11:8]` + `sw_i[15:12]`.
|
||||
Пример: `sw_i[15:0] = 0001_0010_0011_0000`.
|
||||
Результат вычислений: `out_o = 0...0110`.
|
||||
|
||||
22. В числе `sw_i` заменить справа-налево каждое `00` на `11`. Вывести результат в `out_o`.
|
||||
Пример: `sw_i = 1...101000`.
|
||||
Результат вычислений: `out_o = 1...101011`.
|
||||
|
||||
---
|
||||
|
||||
23. Поменять местами чётные биты числа `sw_i` с нечётными битами этого числа (то есть соседние биты поменять местами). Вывести результат в `out_o`.
|
||||
Пример: `sw_i = 0...01010_0111`.
|
||||
Результат вычислений: `out_o = 0...0101_1011`.
|
||||
|
||||
24. Инвертировать первые `sw_i` бит числа `a`. Вывести результат в `out_o`.
|
||||
Пример: `sw_i = 0...011`, `a = 0...01010_0011`.
|
||||
Результат вычислений: `out_o = 0...01010_0100`.
|
||||
|
||||
25. Вывести n-ый член [последовательности Фибоначчи](https://ru.wikipedia.org/wiki/Числа_Фибоначчи) Fn. n = `sw_i`. Вывести результат в `out_o`.
|
||||
Пример: `sw_i = 0...0100`.
|
||||
Результат вычислений: `out_o = 0...010`.
|
||||
|
||||
26. Поменять в числе `a` разряды `i = sw_i[4:0]` и `j = sw_i[9:5]`. Вывести результат в `out_o`.
|
||||
Пример: `a = 0...01001`, `sw_i[9:0] = 00000_00001`. Значит, в числе `а` необходимо поменять местами `а[0]` и `a[1]`.
|
||||
Результат вычислений: `out_o = 0...01010`.
|
||||
|
||||
---
|
||||
|
||||
27. Вычислить `a * sw_i` с использованием операций сложений и сдвига ("в столбик"). Вывести результат в `out_o`.
|
||||
[Пример](https://en.wikipedia.org/wiki/Binary_multiplier#Binary_long_multiplication): `a = 0...01011`, `sw_i[9:0] = 0...01110`.
|
||||
Результат вычислений: `out_o = 0...010011010`.
|
||||
```
|
||||
1011 (11 в двоичном виде)
|
||||
x 1110 (14 в двоичном виде)
|
||||
======
|
||||
0000 (это 1011 x 0)
|
||||
1011 (это 1011 x 1, сдвинутое на 1 влево)
|
||||
1011 (это 1011 x 1, сдвинутое на 2 влево)
|
||||
+ 1011 (это 1011 x 1, сдвинутое на 2 влево)
|
||||
=========
|
||||
10011010 (154 в двоичном виде)
|
||||
```
|
||||
|
||||
28. Вывести в `out_o` n-ый член [арифметической прогрессии](https://ru.wikipedia.org/wiki/Арифметическая_прогрессия) aN, где `a1 = a`, `d = sw_i[15:8]`, `n = sw_i[7:0]` (d и n неотрицательные).
|
||||
Пример: `sw_i[15:8] = 0000_0010`, `sw_i[7:0] = 0000_0011`, `a = 0...01`.
|
||||
Результат вычислений: `out_o = 0...0101`.
|
||||
|
||||
<!-- 25. *Зажечь все светодиоды на 50% яркости ([подсказка](http://wiki.amperka.ru/конспект-arduino:шим)) -->
|
||||
|
||||
29. Удалить все вхождения `sw_i[2:0]` из `a` со сдвигом вправо (заполняя удаленные области).
|
||||
Пример: `a = 0...010011010`, `sw_i[2:0] = 101`.
|
||||
Результат вычислений: `out_o = 0...010010`
|
@@ -0,0 +1,137 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
void print_help(const std::string program_name)
|
||||
{
|
||||
using namespace std;
|
||||
cout << "Usage: " << program_name << " [input file] [output file]\n\n";
|
||||
cout << "Convert CYBERcobra program file into $readmemh acceptable file.\n";
|
||||
cout << "CYBERcobra program file may contain only comments (starting with \"//\"),\n";
|
||||
cout << "whitespaces and binary digits '0' or '1'.\n";
|
||||
cout << "This program will erase this parts from every line and then convert\n";
|
||||
cout << "32-bits binary strings into 4 little endian 8-bit strings in hex-format.\n\n";
|
||||
cout << "If output file omitted, the <input_file_base>_converted.<input_file_ext>\n";
|
||||
cout << "will be produced.\n\n";
|
||||
cout << "If input file omitted, program.txt will be used.\n\n";
|
||||
cout << "Example:\n\n";
|
||||
cout << program_name << " open \"program.txt\" and produce \"program_converted.txt\"\n";
|
||||
cout << program_name << " test.txt open \"test.txt\" and produce \"test_converted.txt\"\n";
|
||||
cout << program_name << " test.txt myname.dat open \"test.txt\" and produce \"myname.dat\"\n";
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
using namespace std;
|
||||
/*
|
||||
Parse argument list and print help message if needed
|
||||
*/
|
||||
string ifname;
|
||||
string ofname;
|
||||
string start;
|
||||
string end;
|
||||
string filename = argv[0];
|
||||
size_t dot_pos;
|
||||
filename = filename.substr(filename.find_last_of("/\\") + 1);
|
||||
switch (argc)
|
||||
{
|
||||
case 1:
|
||||
ifname = "program.txt";
|
||||
ofname = "program_converted.txt";
|
||||
break;
|
||||
case 2:
|
||||
if (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))
|
||||
{
|
||||
print_help(filename);
|
||||
return 0;
|
||||
}
|
||||
ifname = argv[1];
|
||||
dot_pos = ifname.find(".");
|
||||
if(dot_pos != string::npos)
|
||||
{
|
||||
start = ifname.substr(0, dot_pos);
|
||||
end = ifname.substr(dot_pos, ifname.size() - dot_pos);
|
||||
ofname = start + "_converted" + end;
|
||||
}
|
||||
else
|
||||
{
|
||||
ofname = ifname + "_converted";
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
ifname = argv[1];
|
||||
ofname = argv[2];
|
||||
break;
|
||||
default:
|
||||
print_help(filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Program body
|
||||
*/
|
||||
// Open input and output files
|
||||
ifstream ifs(ifname);
|
||||
if(!ifs)
|
||||
{
|
||||
cerr << "Unable to open file: \"" << ifname << "\"" << endl;
|
||||
return -1;
|
||||
}
|
||||
ofstream ofs(ofname);
|
||||
|
||||
if (!ofs.is_open())
|
||||
{
|
||||
cerr << "Unable to open file: \"" << ofname << "\"" << endl;
|
||||
return -1;
|
||||
}
|
||||
string str;
|
||||
size_t line_counter = 0;
|
||||
while (getline(ifs, str))
|
||||
{
|
||||
line_counter++;
|
||||
// trim line from comments and whitespaces, skip empty lines after trimming
|
||||
auto comment_pos = str.find("//");
|
||||
if(comment_pos != string::npos)
|
||||
{
|
||||
str.erase(comment_pos);
|
||||
}
|
||||
str.erase(remove_if(str.begin(), str.end(), ::isspace), str.end());
|
||||
if(!str.size())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if(str.size()!=32)
|
||||
{
|
||||
cerr << "line " << line_counter << " length is not equal 32 after trimming comments and whitespaces" << endl;
|
||||
return -2;
|
||||
}
|
||||
// split 32-bits binary line into 4 little-endian hex lines and write them into file
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
{
|
||||
// For every 8-bit part of 32-bit line get int representation.
|
||||
// If illegal character found, throw error.
|
||||
size_t valid_char_num;
|
||||
string byte_substr = str.substr(8*(3-i), 8);
|
||||
int cur_byte = std::stoi(byte_substr, &valid_char_num, 2);
|
||||
if(valid_char_num != 8)
|
||||
{
|
||||
cerr << "Illegal character '" << byte_substr.at(valid_char_num) <<
|
||||
"' found in line " << line_counter << ": \"" << str << "\"\n";
|
||||
cerr << "Should be only '0' or '1'." << endl;
|
||||
return -3;
|
||||
}
|
||||
char hex_byte_str[3];
|
||||
// convert int representation into hex string
|
||||
snprintf(hex_byte_str, 3, "%02x", cur_byte);
|
||||
ofs << hex_byte_str << "\n";
|
||||
}
|
||||
}
|
||||
ifs.close();
|
||||
ofs.close();
|
||||
|
||||
return 0;
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
//J B WS ALUop RA1 RA2 const WA
|
||||
0 0 00 11111111111111111111111 00001 // загрузить константу -1 регистр 1
|
||||
0 0 10 00000000000000000000000 00010 // загрузить значение с входа sw_i в регистр 2
|
||||
0 0 00 00000000000000000000001 00011 // загрузить константу 1 регистр 3
|
||||
0 0 01 00000 00001 00011 00000000 00001 // сложить регистр 1 с регистром 3 и поместить результат в регистр 1
|
||||
0 1 00 11110 00001 00010 11111111 00000 // если значение в регистре 1 меньше значения в регистре 2, возврат на 1 инструкцию назад
|
||||
1 0 00 00000 00001 00000 00000000 00000 // бесконечное повторение этой инструкции с выводом на out_o значения в регистре 1
|
Reference in New Issue
Block a user