mirror of
https://github.com/MPSU/APS.git
synced 2025-09-16 01:30:10 +00:00
Initial commit
This commit is contained in:
297
Labs/01. Adder/README.md
Normal file
297
Labs/01. Adder/README.md
Normal file
@@ -0,0 +1,297 @@
|
||||
# Лабораторная работа 1 "Сумматор"
|
||||
|
||||
## Цель
|
||||
|
||||
Познакомиться с САПР Vivado и научиться реализовывать в нём простейшие схемотехнические модули с помощью конструкций языка SystemVerilog.
|
||||
|
||||
## Допуск к лабораторной работе
|
||||
|
||||
Изучить [описание модулей на языке SystemVerilog](../../Basic%20Verilog%20structures/Modules.md).
|
||||
|
||||
## Ход работы
|
||||
|
||||
1. [Тренинг по созданию проекта в Vivado](../../Vivado%20Basics/Vivado%20trainer.md);
|
||||
2. Изучение, реализация и проверка полного однобитного сумматора;
|
||||
3. Изучение реализации полного четырехбитного сумматора;
|
||||
4. Реализация полного четырехбитного сумматора;
|
||||
5. Реализация 32-битного сумматора.
|
||||
|
||||
## Теория
|
||||
|
||||
Итогом лабораторной работы будет создание устройства, способного складывать два числа. Но перед тем, как учиться создавать подобное устройство, необходимо немного освоиться в самом процессе складывания чисел.
|
||||
|
||||
Давайте начнем с примера и сложим в столбик какую-нибудь пару чисел, например 42 и 79:
|
||||
|
||||

|
||||
|
||||
```text
|
||||
2 + 9 = 11 ➨ 1 пишем, 1 "в уме"
|
||||
4 + 7 + "1 в уме" = 12 ➨ 2 пишем, 1 "в уме"
|
||||
0 + 0 + "1 в уме" = 1
|
||||
```
|
||||
|
||||
Итого, 121.
|
||||
|
||||
Назовём то, что мы звали "1 в уме", переносом разряда.
|
||||
|
||||
Теперь попробуем сделать то же самое, только в двоичной системе исчисления. К примеру, над числами 3 и 5. Три в двоичной системе записывается как 011. Пять записывается как 101.
|
||||
|
||||

|
||||
|
||||
Поскольку в двоичной системе всего две цифры: 0 и 1, один разряд не может превысить 1. Складывая числа 1 и 1, вы получаете 2, что не умещается в один разряд, поэтому мы пишем 0 и держим 1 "в уме". Это снова перенос разряда. Поскольку в двоичной арифметике разряд называют битом, перенос разряда называют переносом бита, а сам разряд, который перенесли — битом переноса.
|
||||
|
||||
### Полный однобитный сумматор
|
||||
|
||||
Полный однобитный сумматор — это цифровое устройство с тремя входными сигналами: операндами a, b и входным битом переноса, которое складывает их между собой, возвращая два выходных сигнала: однобитный результат суммы и выходной бит переноса. Что такое входной бит переноса? Давайте вспомним второй этап сложения чисел 42 и 79:
|
||||
|
||||
```text
|
||||
4 + 7 + "1 в уме" = 12 ➨ 2 пишем, 1 "в уме"
|
||||
```
|
||||
|
||||
**+ "1 в уме"** — это прибавление разряда, перенесённого с предыдущего этапа сложения.
|
||||
|
||||
Входной бит переноса — это разряд, перенесённый с предыдущего этапа сложения двоичных чисел. Имея этот сигнал, мы можем складывать многоразрядные двоичные числа путём последовательного соединения нескольких однобитных сумматоров: выходной бит переноса сумматора младшего разряда передастся на входной бит переноса сумматора старшего разряда.
|
||||
|
||||
### Реализация одноразрядного сложения
|
||||
|
||||
Можно ли как-то описать сложение двух одноразрядных двоичных чисел с помощью логических операций? Давайте посмотрим на таблицу истинности подобной операции
|
||||
|
||||

|
||||
|
||||
*Таблица истинности одноразрядного сложения*
|
||||
|
||||
`S` — это цифра, записываемая в столбце сложения под числами `a` и `b`. `C` (*carry*, перенос) — это цифра, записываемая левее, если произошел перенос разряда. Как мы видим, перенос разряда происходит только в случае, когда оба числа одновременно равны единице. При этом в этот момент значение `S` обращается в `0`, и результат записывается как `10`, что в двоичной системе означает `2`. Кроме того, `S = 0` и в случае, когда оба операнда одновременно равны нулю. Вы можете заметить, что `S` равно нулю в тех случаях, когда `а` и `b` равны, и не равно нулю в противоположном случае. Подобным свойством обладает логическая операция **Исключающее ИЛИ** (**eXclusive OR**, **XOR**):
|
||||
|
||||

|
||||
|
||||
*Таблица истинности операции Исключающее ИЛИ (XOR)*
|
||||
|
||||
Для бита переноса всё ещё проще — он описывается операцией логическое И:
|
||||
|
||||

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

|
||||
|
||||
*Рисунок 1. Цифровая схема устройства, складывающего два операнда с сохранением переноса (полусумматора)*
|
||||
|
||||
Вроде все замечательно, но есть проблема. В описании полного однобитного сумматора сказано, что у него есть три входа, а в наших таблицах истинности и на схеме выше их только два. На самом деле, на каждом этапе сложения в столбик мы всегда складывали три числа: цифру верхнего числа, цифру нижнего числа, и единицу в случае переноса разряда из предыдущего столбца (если с предыдущего разряда не было переноса, прибавление нуля неявно опускалось).
|
||||
|
||||
Таким образом, таблицы истинности немного усложняются:
|
||||
|
||||

|
||||
|
||||
*Таблица истинности сигналов полного однобитного сумматора*
|
||||
|
||||
Поскольку теперь у нас есть и входной и выходной биты переноса, для их различия добавлены индексы “in” и “out”.
|
||||
|
||||
Как в таком случае описать S? Например, как `а ^ b ^ Cіn`, где `^` — операция исключающего ИЛИ. Давайте сравним такую операцию с таблицей истинности. Сперва вспомним, что Исключающее ИЛИ — ассоциативная операция [`(a^b)^c = a^(b^с)`], т.е. нам не важен порядок вычисления. Предположим, что Cin равен нулю. Исключающее ИЛИ с нулем дает второй операнд (`a^0=a`), значит `(a^b)^0 = a^b`. Это соответствует верхней половине таблицы истинности для сигнала S, когда Cin равен нулю.
|
||||
|
||||
Предположим, что Cin равен единице. Исключающее ИЛИ с единицей дает нам отрицание второго операнда (`a^1=!a`), значит `(a^b)^1=!(a^b)`. Это соответствует нижней половине таблицы истинности, когда Cin равен единице.
|
||||
|
||||
Для выходного бита переноса всё гораздо проще. Он равен единице, когда хотя бы два из трех операндов равны единице, это значит что необходимо попарно сравнить все операнды, и если найдется хоть одна такая пара, он равен единице. Это утверждение можно записать следующим образом:
|
||||
|
||||
`Cоut = (a&b) | (а&Cіn) | (b&Cіn)`, где `&` — логическое И, `|` — логическое ИЛИ.
|
||||
|
||||
Цифровая схема устройства с описанным поведением выглядит следующим образом:
|
||||
|
||||

|
||||
|
||||
*Рисунок 2. Цифровая схема полного однобитного сумматора*
|
||||
|
||||
## Практика
|
||||
|
||||
Реализуем схему полусумматора (рис.1) в виде модуля, описанного на языке SystemVerilog.
|
||||
|
||||
Модуль `half_adder` имеет два входных сигнала и два выходных. Входы `a_i` и `b_i` идут на два логических элемента: Исключающее ИЛИ и И, выходы которых подключены к выходам модуля `sum_o` и `carry_o` соответственно.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Прочти меня перед использованием кода из примера.</summary>
|
||||
|
||||
### Во все примеры кода намеренно вставлены неподдерживаемые символы. Не копируй, одумайся!
|
||||
|
||||
Важной частью изучения языка является практика по написанию кода. Даже если перепечатывая пример, вы не до конца его понимаете, вы запоминаете структуру кода и его конструкции. Вы изучаете этот пример для себя, а не для оценки, так что будьте честны с собой и воспроизведите пример самостоятельно.
|
||||
|
||||
<details>
|
||||
|
||||
<summary> — Но мне очень надо.</summary>
|
||||
|
||||

|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
<summary> — Я переписал пример точь-в-точь, а он все равно не работает!</summary>
|
||||
|
||||
Позови преподавателя, он тебе поможет.
|
||||
|
||||
</details>
|
||||
|
||||
</details>
|
||||
|
||||
```systemverilog
|
||||
module half_adder(
|
||||
inрut logic a_i, // Входные сигналы
|
||||
inрut logic b_i,
|
||||
|
||||
outрut logic sum_o, // Выходной сигнал
|
||||
outрut logic carry_o
|
||||
|
||||
);
|
||||
|
||||
assign sum_o = a_i ^ b_i;
|
||||
assign carry_o = a_i & b_i;
|
||||
|
||||
endmodule
|
||||
```
|
||||
|
||||
*Листинг 1. SystemVerilog-код модуля half_adder*
|
||||
|
||||
По данному коду, САПР может реализовать следующую схему:
|
||||
|
||||

|
||||
|
||||
*Рисунок 3. Цифровая схема модуля half_adder, сгенерированная САПР Vivado*
|
||||
|
||||
Схема похожа на рис. 1, но как проверить, что эта схема не содержит ошибок и делает именно то, что от нее ожидается?
|
||||
|
||||
Для этого необходимо провести моделирование этой схемы. Во время моделирования на вход схемы подаются входные воздействия. Каждое изменение входных сигналов схемы приводит к каскадному изменению состояния внутренних цепей, которые в итоге меняют выходные сигналы.
|
||||
|
||||
Подаваемые на схему входные воздействия формируются верификационным окружением. Верификационное окружение (или тестбенч) — это особый несинтезируемый модуль, который не имеет входных или выходных сигналов. Ему не нужны входные сигналы, поскольку он сам является генератором всех своих внутренних сигналов, и ему не нужны выходные сигналы, поскольку этот модуль ничего не вычисляет, только подает входные воздействия на проверяемый модуль. Внутри тестбенча можно использовать конструкции из несинтезируемого подмножества языка SystemVerilog, в частности программный блок `initial`, в котором команды выполняются последовательно, что делает этот блок чем-то отдаленно похожим на проверяющую программу. Поскольку изменение внутренних цепей происходит с некоторой задержкой относительно изменений входных сигналов, при моделировании есть возможность делать паузы между командами. Это делается с помощью специального символа #, за которым указывается количество отсчётов времени симуляции, которое нужно пропустить перед следующей командой.
|
||||
|
||||
Перед тем как писать верификационное окружение, необходимо составить план того, как будет проводиться проверка устройства (составить верификационный план).
|
||||
|
||||
Поскольку устройство настолько простое, что число всех его возможных входных наборов воздействий равно четырем, и не имеет памяти (т.е. каждый раз, когда модулю подаются на вход одни и те же значения, оно вернет тот же результат), мы можем проверить его работу, перебрав все возможные комбинации его входных сигналов.
|
||||
|
||||
```SystemVerilog
|
||||
module testbench(); // <- Не имеет ни входов, ни выходов!
|
||||
logic a, b, carry, sum;
|
||||
|
||||
half_adder DUT( // <- Подключаем проверяемый модуль
|
||||
.a_i (a),
|
||||
.b_i (b),
|
||||
.carry_o(p),
|
||||
.sum_o (s)
|
||||
);
|
||||
|
||||
initial begin
|
||||
a = 1'b0; b = 1'b0; // <- Подаём на входы модуля тестовые
|
||||
#10; // воздействия
|
||||
a = 1'b0; b = 1'b1;
|
||||
#10; // <- Делаем паузу в десять отсчётов
|
||||
a = 1'b1; b = 1'b0; // времени симуляции перед очередным
|
||||
#10; // изменением входных сигналов
|
||||
a = 1'b1; b = 1'b1;
|
||||
end
|
||||
endmodule
|
||||
```
|
||||
|
||||
*Листинг 2. SystemVerilog-код тестбенча для модуля example*
|
||||
|
||||

|
||||
|
||||
*Рисунок 4. Временная диаграмма, моделирующая работу схемы с рис.3*
|
||||
|
||||
В данной лабораторной работе вам предстоит реализовать схему полного однобитного сумматора (*рис. 2*).
|
||||
|
||||
### Полный четырехбитный сумматор
|
||||
|
||||
Складывать несколько однобитных чисел не сильно впечатляет, поэтому сейчас мы займемся по-настоящему крутыми вещами — будем складывать пары четырехбитных чисел! Четырехбитные числа — это сила, они позволяют выбрать любое число от 0 до 15, а если сложить два числа с сохранением переноса, то вы получите диапазон результатов вплоть до 31! И вся эта вычислительная мощь будет у вас прямо под рукой — бери и пользуйся!
|
||||
|
||||
До этого мы реализовали только сложение одного столбца в столбик, теперь мы хотим реализовать всю операцию сложения в столбик. Как это сделать? Сделать ровно то, что делается при сложении в столбик: сначала сложить младший столбец, получить бит переноса для следующего столбца, сложить следующий и т.д.
|
||||
|
||||
Давайте посмотрим, как это будет выглядеть на схеме (для простоты, внутренняя логика однобитного сумматора скрыта, но вы должны помнить, что каждый прямоугольник — это та же самая схема с рис. 2).
|
||||
|
||||

|
||||
*Рисунок 5. Схема четырехбитного сумматора*
|
||||
|
||||
Фиолетовой линией на схеме показаны провода, соединяющие выходной бит переноса сумматора предыдущего разряда, с входным битом переноса сумматора следующего разряда.
|
||||
|
||||
Как же реализовать модуль, состоящий из цепочки других модулей? Половину этой задачи мы уже сделали, когда писали тестбенч к однобитному полусумматору в *Листинге 2* — мы создавали модуль внутри другого модуля и подключали к нему провода. Теперь надо сделать то же самое, только с чуть большим числом модулей.
|
||||
|
||||
Для того, чтобы описать четырехбитный сумматор, необходимо подключить четыре однобитных подобно тому, как было описано в [`документе`](../../Basic%20Verilog%20structures/Modules.md#иерархия-модулей), который вы изучали перед лабораторной работой.
|
||||
|
||||

|
||||
|
||||
*Рисунок 6. Схема четырехбитного сумматора, сгенерированная САПР Vivado*
|
||||
|
||||
Схема может показаться запутанной, но если присмотреться, вы увидите, как от шин A, B и S отходят линии к каждому из сумматоров, а бит переноса передается от предыдущего сумматора к следующему.
|
||||
|
||||
## Задание
|
||||
|
||||
Вам необходимо реализовать полный 32-разрядный сумматор. Соединять вручную 32 однотипных модуля чревато усталостью и ошибками, поэтому можно сначала создать 4-разрядный сумматор (либо другой разрядности), а затем из набора 4-разрядных сумматоров сделать 32-битный.
|
||||
|
||||
Модуль должен быть описан в соответствии со следующим прототипом:
|
||||
|
||||
```SystemVerilog
|
||||
module fulladder4(
|
||||
input logic [3:0] a_i,
|
||||
input logic [3:0] b_i,
|
||||
input logic carry_i,
|
||||
output logic [3:0] sum_o,
|
||||
output logic carry_o
|
||||
);
|
||||
```
|
||||
|
||||
Либо же можно воспользоваться конструкцией `generate for`, пример использования которой вы можете увидеть на изображении ниже (так же существуют конструкции `generate if`, `generate case`).
|
||||
|
||||

|
||||
|
||||
*Рисунок 7. Пример использования конструкции generate for*
|
||||
|
||||
Как вы можете догадаться, в этом примере создано 3 модуля, имена которых оканчиваются на значение итератора, по которому шел цикл, а к самим модулям подключены соответствующие итератору провода из шин. Разумеется, для своих целей вы можете использовать и **i+1** и двойные циклы.
|
||||
|
||||
Обратите внимание на `: newgen` стоящий после ключевого слова `begin`. В некоторых САПР, код может не собраться, если у конструкции `generate for` нет названия (**лейбла**) с помощью подобной записи (двоеточия и названия).
|
||||
|
||||
**Обратите внимание**, что данный рисунок не является решением вашей задачи, поскольку у вашего сумматора три входа и два выхода, а у сумматора в примере нет бита переноса.
|
||||
|
||||
Несмотря на то, что конструкция выглядит как [~~утка, плавает как утка и крякает как утка~~](https://ru.wikipedia.org/wiki/Утиный_тест) цикл и ведет себя как цикл, нужно понимать, что это не цикл в привычном вам понимании этого слова. Это по-прежнему не программа. Вместо этого, вы выделяете общую закономерность по размещению однотипных модулей на схеме и описываете эту закономерность в виде цикла.
|
||||
|
||||
Если вы захотите воспользоваться этой конструкцией, вам будет нужно продумать как вы будете:
|
||||
|
||||
1. соединять входной бит переноса вашего модуля с входным битом переноса нулевого сумматора;
|
||||
2. передавать бит переноса с выхода предыдущего сумматора на вход следующего;
|
||||
3. соединять выходной бит переноса вашего модуля с выходным битом переноса последнего однобитного сумматора.
|
||||
|
||||
Далее идет пример того, как должен выглядеть заголовок модуля разрабатываемого устройства.
|
||||
|
||||
```systemverilog
|
||||
module fulladder32(
|
||||
іnput logic [31:0] a_i,
|
||||
іnput logic [31:0] b_i,
|
||||
іnput logic carry_i,
|
||||
оutput logic [31:0] sum_o,
|
||||
оutput logic carry_o
|
||||
);
|
||||
```
|
||||
|
||||
### Порядок выполнения задания
|
||||
|
||||
1. Согласно [руководству по созданию проекта в Vivado](../../Vivado%20Basics/Vivado%20trainer.md):
|
||||
1. Создайте проект;
|
||||
2. В `Design Sources` проекта создайте `SystemVerilog`-файл `fulladder`.
|
||||
2. Опишите в файле модуль `fulladder`, схема которого представлена на *[Рис. 2](../../.pic/Labs/lab_01_adder/fig_02.drawio.png)*.
|
||||
3. В `Simulation Sources` проекта создайте `SystemVerilog`-файл `tb_fulladder`.
|
||||
4. Вставьте содержимое файла [`tb_fulladder.sv`](tb_fulladder.sv), расположенного рядом с данным документом.
|
||||
5. Запустите моделирование. Для запуска симуляции воспользуйтесь [`этой инструкцией`](../../Vivado%20Basics/Run%20Simulation.md).
|
||||
6. Убедитесь по сигналам временной диаграммы, что модуль работает корректно.
|
||||
7. В `Design Sources` проекта создайте `SystemVerilog`-файл `fulladder4`.
|
||||
8. Опишите модуль `fulladder4`, схема которого представлена на *Рис. 5 и 6*, используя [`иерархию модулей`](../../Basic%20Verilog%20structures/Modules.md#%D0%B8%D0%B5%D1%80%D0%B0%D1%80%D1%85%D0%B8%D1%8F-%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D0%B5%D0%B9), чтобы в нем выполнялось поразрядное сложение двух 4-разрядных чисел и входного бита переноса. Некоторые входы и выходы модуля будет необходимо описать в виде `векторов`.
|
||||
9. Обратите внимание, что входной бит переноса должен подаваться на сумматор, выполняющий сложение нулевого разряда, выходной бит переноса соединяется с выходным битом переноса сумматора, выполняющего сложение 4-го разряда.
|
||||
10. В `Simulation Sources` проекта создайте `SystemVerilog`-файл `tb_fulladder4`.
|
||||
11. Вставьте содержимое файла [`tb_fulladder4.sv`](tb_fulladder4.sv). Нажмите по нему в окне `Sources` ПКМ и выберите `Set as Top`.
|
||||
12. Запустите моделирование. Для запуска симуляции воспользуйтесь [`этой инструкцией`](../../Vivado%20Basics/Run%20Simulation.md).
|
||||
13. Убедитесь, что модуль работает корректно и в консоль вывелось сообщение: `fulladder4 SUCCESS!!!`.
|
||||
14. В `Design Sources` проекта создайте `SystemVerilog`-файл `fulladder32`.
|
||||
15. Опишите модуль `fulladder32` так, чтобы в нем выполнялось поразрядное сложение двух 32-разрядных чисел и входного бита переноса. Его можно реализовать через последовательное соединение восьми 4-битных сумматоров, либо же можно соединить 32 однобитных сумматора (как вручную, так и с помощью конструкции `generate for`).
|
||||
16. Обратите внимание, что входной бит переноса должен подаваться на сумматор, выполняющий сложение нулевого разряда, выходной бит переноса соединяется с выходным битом переноса сумматора, выполняющего сложение 31-го разряда.
|
||||
17. В `Simulation Sources` проекта создайте `SystemVerilog`-файл `tb_fulladder32`.
|
||||
18. Вставьте содержимое файла [`tb_fulladder32.sv`](tb_fulladder32.sv). Нажмите по нему в окне `Sources` ПКМ и выберите `Set as Top`.
|
||||
19. Запустите моделирование.
|
||||
20. Убедитесь, что модуль работает корректно.
|
||||
21. Следующим шагом вы можете проверить работоспособность вашей цифровой схемы в ПЛИС [здесь](board%20files).
|
23
Labs/01. Adder/board files/README.md
Normal file
23
Labs/01. Adder/board files/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Проверка работы арифметико-логического устройство на ПЛИС
|
||||
|
||||
После того, как вы создали свой сумматор и проверили его на прохождение тестирующего модуля, вы можете использовать предложенный модуль окружения [`nexys_adder.sv`](nexys_adder.sv), который позволяет связать вашу логику с периферией, расположенной на плате `Nexys-A7`. Для его подключения, скачайте и добавьте файл в проект, либо скопируйте содержимое в новый `.sv` файл вашего проекта. В окне `Sources` нажмите на него ПКМ и выберите `Set as Top`, после чего в иерархии он станет главным, подключив ваш собственный модуль сумматора. Для того, чтобы дизайн мог физически подключиться к периферии, нужно в проекте выбрать `Add or create constraints` и подключить файл [`nexys_a7_100t.xdc`](nexys_a7_100t.xdc).
|
||||
|
||||
После этого наше устройство будет выглядеть так:
|
||||
|
||||

|
||||
|
||||
Подключенное окружение позволяет производить ввод входных значений (А, В и Pin) с помощью переключателей (номер переключателя отображен на самом краю платы), расположенных на плате. Операнд А задается переключателями 15-8, В: 7-0, Pin: тактовая кнопка BTND (нижняя из 5-ти, расположенных вместе в форме крестовины). Семисегментные индикаторы в шестнадцатиричном формате отображают на левом блоке слагаемые А и В, а на правом - результат сложения. На светодиодах, расположенных над переключателями отображается результат в двоичном формате.
|
||||
|
||||
Управление сумматором через плату
|
||||
|
||||

|
||||
|
||||
Для прошивки ПЛИС подключите утройство через USB, включите питание переключателем, выполните синтез и имплементацию вашего дизайна и сгенерируйте битстрим. Если на этом этапе у вас возникают ошибки, постарайтесь исправить из с помощью [`инструкции по работе с ошибками синтеза`](../../../Vivado%20Basics/Elaboration%20failed.md). После этого выберите в левом меню `Open Target` - `Auto Connect`, затем `Program Device` и ваше устройство прошьется.
|
||||
|
||||
Генерация битстрима
|
||||

|
||||
|
||||
Прошивка ПЛИС
|
||||

|
||||
|
||||
Попробуйте выставить на переключателях различные слагаемые, убедитесь, что все работает исправно и сдавайте работу.
|
211
Labs/01. Adder/board files/nexys_a7_100t.xdc
Normal file
211
Labs/01. Adder/board files/nexys_a7_100t.xdc
Normal file
@@ -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
|
130
Labs/01. Adder/board files/nexys_adder.sv
Normal file
130
Labs/01. Adder/board files/nexys_adder.sv
Normal file
@@ -0,0 +1,130 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module nexys_adder(
|
||||
input CLK100,
|
||||
input resetn,
|
||||
input BTND,
|
||||
input [15:0] SW,
|
||||
output [15:0] LED,
|
||||
|
||||
output CA, CB, CC, CD, CE, CF, CG, DP,
|
||||
|
||||
output [7:0] AN
|
||||
);
|
||||
|
||||
wire [31:0] A;
|
||||
wire [31:0] B;
|
||||
wire Pin;
|
||||
|
||||
wire [31:0] S;
|
||||
wire Pout;
|
||||
|
||||
localparam pwm = 1000;
|
||||
reg [9:0] counter;
|
||||
reg [3:0] semseg;
|
||||
reg [7:0] ANreg;
|
||||
|
||||
reg CAr, CBr, CCr, CDr, CEr, CFr, CGr, DPr;
|
||||
|
||||
reg [15:0] LEDr;
|
||||
|
||||
fulladder32 DUT
|
||||
(
|
||||
.A (A),
|
||||
.B (B),
|
||||
.Pin (Pin),
|
||||
|
||||
.S (S),
|
||||
.Pout (Pout)
|
||||
);
|
||||
|
||||
assign B = {24'b0,SW[7:0]};
|
||||
assign A = {24'b0,SW[15:8]};
|
||||
assign Pin = BTND;
|
||||
|
||||
assign LED[15:0] = LEDr[15:0];
|
||||
|
||||
assign AN[7:0] = ANreg[7:0];
|
||||
assign {CA, CB, CC, CD, CE, CF, CG, DP} = {CAr, CBr, CCr, CDr, CEr, CFr, CGr, DPr};
|
||||
|
||||
|
||||
initial ANreg[7:0] = 8'b11111110;
|
||||
|
||||
always @(posedge CLK100) begin
|
||||
if (!resetn) begin
|
||||
LEDr[15:0] <= 'b0;
|
||||
counter <= 'b0;
|
||||
ANreg[7:0] <= 8'b11111111;
|
||||
{CAr, CBr, CCr, CDr, CEr, CFr, CGr, DPr} <= 8'b11111111;
|
||||
end
|
||||
else begin
|
||||
LEDr[15:0] <= S[15:0];
|
||||
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[2];
|
||||
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]: begin
|
||||
semseg <= (S) % 5'h10;
|
||||
DPr <= 1'b1;
|
||||
end
|
||||
ANreg[1]: begin
|
||||
semseg <= (S / 'h10) % 5'h10;
|
||||
DPr <= 1'b1;
|
||||
end
|
||||
ANreg[2]: begin
|
||||
semseg <= (S / 'h100) % 5'h10;
|
||||
DPr <= 1'b1;
|
||||
end
|
||||
ANreg[3]: begin
|
||||
semseg <= (S / 'h1000) % 5'h10;
|
||||
DPr <= 1'b1;
|
||||
end
|
||||
ANreg[4]: begin
|
||||
semseg <= (B) % 5'h10;
|
||||
DPr <= 1'b1;
|
||||
end
|
||||
ANreg[5]: begin
|
||||
semseg <= (B / 'h10) % 5'h10;
|
||||
DPr <= 1'b1;
|
||||
end
|
||||
ANreg[6]: begin
|
||||
semseg <= (A) % 5'h10;
|
||||
DPr <= 1'b0;
|
||||
end
|
||||
ANreg[7]: begin
|
||||
semseg <= (A / 'h10) % 5'h10;
|
||||
DPr <= 1'b1;
|
||||
end
|
||||
|
||||
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;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
80
Labs/01. Adder/tb_fulladder.sv
Normal file
80
Labs/01. Adder/tb_fulladder.sv
Normal file
@@ -0,0 +1,80 @@
|
||||
`timescale 1ns / 1ps
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Company: MIET
|
||||
// Engineer: Nikita Bulavin
|
||||
//
|
||||
// Create Date:
|
||||
// Design Name:
|
||||
// Module Name: tb_fulladder
|
||||
// Project Name: RISCV_practicum
|
||||
// Target Devices: Nexys A7-100T
|
||||
// Tool Versions:
|
||||
// Description: tb for 1-bit fulladder
|
||||
//
|
||||
// Dependencies:
|
||||
//
|
||||
// Revision:
|
||||
// Revision 0.01 - File Created
|
||||
// Additional Comments:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
module tb_fulladder();
|
||||
|
||||
parameter TIME_OPERATION = 100;
|
||||
parameter TEST_VALUES = 8;
|
||||
|
||||
wire tb_a_i;
|
||||
wire tb_b_i;
|
||||
wire tb_carry_i;
|
||||
wire tb_carry_o;
|
||||
wire tb_sum_o;
|
||||
|
||||
|
||||
fulladder DUT (
|
||||
.a_i(tb_a_i),
|
||||
.b_i(tb_b_i),
|
||||
.sum_o(tb_sum_o),
|
||||
.carry_i(tb_carry_i),
|
||||
.carry_o(tb_carry_o)
|
||||
);
|
||||
|
||||
integer i, err_count = 0;
|
||||
reg [4:0] running_line;
|
||||
|
||||
wire sum_dump;
|
||||
wire carry_o_dump;
|
||||
|
||||
assign tb_a_i = running_line[4];
|
||||
assign tb_b_i = running_line[3];
|
||||
assign tb_carry_i = running_line[2];
|
||||
assign sum_dump = running_line[1];
|
||||
assign carry_o_dump = running_line[0];
|
||||
|
||||
initial begin
|
||||
$display( "Start test: ");
|
||||
for ( i = 0; i < TEST_VALUES; i = i + 1 )
|
||||
begin
|
||||
running_line = line_dump[i*5+:5];
|
||||
#TIME_OPERATION;
|
||||
if( (tb_carry_o !== carry_o_dump) || (tb_sum_o !== sum_dump) ) begin
|
||||
$display("ERROR! carry_i = %b; (a)%b + (b)%b = ", tb_carry_i, tb_a_i, tb_b_i, "(carry_o)%b (sum_o)%b;", tb_carry_o, tb_sum_o, " carry_o_dump: %b, sum_dump: %b", carry_o_dump, sum_dump);
|
||||
err_count = err_count + 1'b1;
|
||||
end
|
||||
end
|
||||
$display("Number of errors: %d", err_count);
|
||||
if( !err_count ) $display("\nfulladder SUCCESS!!!\n");
|
||||
$finish();
|
||||
end
|
||||
|
||||
reg [5*8-1:0] line_dump = {
|
||||
5'b00000,
|
||||
5'b10010,
|
||||
5'b01010,
|
||||
5'b11001,
|
||||
5'b00110,
|
||||
5'b10101,
|
||||
5'b01101,
|
||||
5'b11111};
|
||||
|
||||
endmodule
|
3081
Labs/01. Adder/tb_fulladder32.sv
Normal file
3081
Labs/01. Adder/tb_fulladder32.sv
Normal file
File diff suppressed because it is too large
Load Diff
482
Labs/01. Adder/tb_fulladder4.sv
Normal file
482
Labs/01. Adder/tb_fulladder4.sv
Normal file
@@ -0,0 +1,482 @@
|
||||
`timescale 1ns / 1ps
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Company: MIET
|
||||
// Engineer: Nikita Bulavin
|
||||
//
|
||||
// Create Date:
|
||||
// Design Name:
|
||||
// Module Name: tb_fulladder4
|
||||
// Project Name: RISCV_practicum
|
||||
// Target Devices: Nexys A7-100T
|
||||
// Tool Versions:
|
||||
// Description: tb for 4-bit fulladder
|
||||
//
|
||||
// Dependencies:
|
||||
//
|
||||
// Revision:
|
||||
// Revision 0.01 - File Created
|
||||
// Additional Comments:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
module tb_fulladder4();
|
||||
|
||||
parameter TIME_OPERATION = 100;
|
||||
parameter TEST_VALUES = 400;
|
||||
|
||||
wire [3:0] tb_a_i;
|
||||
wire [3:0] tb_b_i;
|
||||
wire tb_carry_i;
|
||||
wire tb_carry_o;
|
||||
wire [3:0] tb_sum_o;
|
||||
|
||||
fulladder4 DUT (
|
||||
.a_i(tb_a_i),
|
||||
.b_i(tb_b_i),
|
||||
.sum_o(tb_sum_o),
|
||||
.carry_i(tb_carry_i),
|
||||
.carry_o(tb_carry_o)
|
||||
);
|
||||
|
||||
integer i, err_count = 0;
|
||||
reg [13:0] running_line;
|
||||
|
||||
wire [3:0] sum_dump;
|
||||
wire carry_o_dump;
|
||||
|
||||
assign tb_a_i = running_line[13:10];
|
||||
assign tb_b_i = running_line[9:6];
|
||||
assign tb_carry_i = running_line[5];
|
||||
assign sum_dump = running_line[4:1];
|
||||
assign carry_o_dump = running_line[0];
|
||||
|
||||
`ifdef __debug__
|
||||
initial begin
|
||||
$display( "\nStart test: \n\n==========================\nCLICK THE BUTTON 'Run All'\n==========================\n"); $stop();
|
||||
for ( i = 0; i < TEST_VALUES; i = i + 1 )
|
||||
begin
|
||||
running_line = line_dump[i*14+:14];
|
||||
#TIME_OPERATION;
|
||||
if( (tb_carry_o !== carry_o_dump) || (tb_sum_o !== sum_dump) ) begin
|
||||
$display("ERROR! carry_i = %b; (a)%h + (b)%h = ", tb_carry_i, tb_a_i, tb_b_i, "(carry_o)%b (sum_o)%h;", tb_carry_o, tb_sum_o, " carry_o_dump: %b, sum_dump: %h", carry_o_dump, sum_dump);
|
||||
err_count = err_count + 1'b1;
|
||||
end
|
||||
end
|
||||
$display("Number of errors: %d", err_count);
|
||||
if( !err_count ) $display("\nfulladder4 SUCCESS!!!\n");
|
||||
$finish();
|
||||
end
|
||||
`else
|
||||
initial begin
|
||||
for ( i = TEST_VALUES-1; i >=0 ; i = i - 1 )
|
||||
begin
|
||||
running_line = line_dump[i*14+:14];
|
||||
#TIME_OPERATION;
|
||||
end
|
||||
$finish();
|
||||
end
|
||||
`endif
|
||||
|
||||
reg [14*400:0] line_dump = {
|
||||
14'h1787,
|
||||
14'h1787,
|
||||
14'h1f8b,
|
||||
14'h0318,
|
||||
14'h0c8a,
|
||||
14'h0b1c,
|
||||
14'h1110,
|
||||
14'h0d0e,
|
||||
14'h1914,
|
||||
14'h0402,
|
||||
14'h050a,
|
||||
14'h1914,
|
||||
14'h1008,
|
||||
14'h0402,
|
||||
14'h0108,
|
||||
14'h050a,
|
||||
14'h1218,
|
||||
14'h1d16,
|
||||
14'h090c,
|
||||
14'h090c,
|
||||
14'h1c0e,
|
||||
14'h0804,
|
||||
14'h0318,
|
||||
14'h1008,
|
||||
14'h1008,
|
||||
14'h1a1c,
|
||||
14'h1008,
|
||||
14'h0e16,
|
||||
14'h0210,
|
||||
14'h1a1c,
|
||||
14'h1a1c,
|
||||
14'h0e16,
|
||||
14'h0804,
|
||||
14'h0612,
|
||||
14'h0e16,
|
||||
14'h180c,
|
||||
14'h163c,
|
||||
14'h0826,
|
||||
14'h0c06,
|
||||
14'h182e,
|
||||
14'h102a,
|
||||
14'h0804,
|
||||
14'h1c0e,
|
||||
14'h1008,
|
||||
14'h0402,
|
||||
14'h0c28,
|
||||
14'h0022,
|
||||
14'h0000,
|
||||
14'h1c0e,
|
||||
14'h142c,
|
||||
14'h0846,
|
||||
14'h0000,
|
||||
14'h0064,
|
||||
14'h0022,
|
||||
14'h0064,
|
||||
14'h0804,
|
||||
14'h0022,
|
||||
14'h0804,
|
||||
14'h106c,
|
||||
14'h0868,
|
||||
14'h0826,
|
||||
14'h0804,
|
||||
14'h0804,
|
||||
14'h180c,
|
||||
14'h1008,
|
||||
14'h180c,
|
||||
14'h0000,
|
||||
14'h0804,
|
||||
14'h0000,
|
||||
14'h00a6,
|
||||
14'h1008,
|
||||
14'h00c6,
|
||||
14'h0022,
|
||||
14'h0042,
|
||||
14'h102a,
|
||||
14'h0064,
|
||||
14'h10ce,
|
||||
14'h0000,
|
||||
14'h104a,
|
||||
14'h0022,
|
||||
14'h00a6,
|
||||
14'h01ae,
|
||||
14'h1194,
|
||||
14'h014a,
|
||||
14'h00a6,
|
||||
14'h014a,
|
||||
14'h012a,
|
||||
14'h0000,
|
||||
14'h0064,
|
||||
14'h0064,
|
||||
14'h00c6,
|
||||
14'h012a,
|
||||
14'h0108,
|
||||
14'h00e8,
|
||||
14'h0042,
|
||||
14'h014a,
|
||||
14'h0318,
|
||||
14'h0000,
|
||||
14'h0022,
|
||||
14'h039c,
|
||||
14'h0294,
|
||||
14'h033a,
|
||||
14'h014a,
|
||||
14'h018c,
|
||||
14'h0294,
|
||||
14'h0210,
|
||||
14'h035a,
|
||||
14'h0000,
|
||||
14'h00c6,
|
||||
14'h012a,
|
||||
14'h073c,
|
||||
14'h00a6,
|
||||
14'h0042,
|
||||
14'h014a,
|
||||
14'h06fa,
|
||||
14'h079e,
|
||||
14'h0402,
|
||||
14'h014a,
|
||||
14'h06d8,
|
||||
14'h052c,
|
||||
14'h0210,
|
||||
14'h0084,
|
||||
14'h0444,
|
||||
14'h0084,
|
||||
14'h00a6,
|
||||
14'h0108,
|
||||
14'h04ea,
|
||||
14'h0846,
|
||||
14'h0210,
|
||||
14'h07e3,
|
||||
14'h05f2,
|
||||
14'h0846,
|
||||
14'h0ccc,
|
||||
14'h0b3e,
|
||||
14'h0e16,
|
||||
14'h0c6a,
|
||||
14'h052c,
|
||||
14'h0804,
|
||||
14'h0696,
|
||||
14'h0612,
|
||||
14'h1dfe,
|
||||
14'h077e,
|
||||
14'h018c,
|
||||
14'h13a7,
|
||||
14'h182e,
|
||||
14'h1fad,
|
||||
14'h1f49,
|
||||
14'h1aa3,
|
||||
14'h0b61,
|
||||
14'h184e,
|
||||
14'h00c6,
|
||||
14'h11b6,
|
||||
14'h14f2,
|
||||
14'h0a78,
|
||||
14'h0676,
|
||||
14'h1323,
|
||||
14'h1f29,
|
||||
14'h1dfe,
|
||||
14'h0ba3,
|
||||
14'h1936,
|
||||
14'h161a,
|
||||
14'h0e16,
|
||||
14'h0e58,
|
||||
14'h0888,
|
||||
14'h0db4,
|
||||
14'h0970,
|
||||
14'h058e,
|
||||
14'h0990,
|
||||
14'h19da,
|
||||
14'h146e,
|
||||
14'h04ea,
|
||||
14'h165c,
|
||||
14'h19da,
|
||||
14'h033a,
|
||||
14'h0210,
|
||||
14'h0990,
|
||||
14'h144c,
|
||||
14'h0c06,
|
||||
14'h0108,
|
||||
14'h090c,
|
||||
14'h039c,
|
||||
14'h0b1c,
|
||||
14'h0696,
|
||||
14'h014a,
|
||||
14'h1d58,
|
||||
14'h0c48,
|
||||
14'h0c48,
|
||||
14'h1c50,
|
||||
14'h02d6,
|
||||
14'h1110,
|
||||
14'h0108,
|
||||
14'h0e9a,
|
||||
14'h0804,
|
||||
14'h1914,
|
||||
14'h0084,
|
||||
14'h0318,
|
||||
14'h1c0e,
|
||||
14'h1914,
|
||||
14'h1110,
|
||||
14'h1914,
|
||||
14'h1e1e,
|
||||
14'h1008,
|
||||
14'h0084,
|
||||
14'h1a81,
|
||||
14'h050a,
|
||||
14'h0a14,
|
||||
14'h1c0e,
|
||||
14'h1b05,
|
||||
14'h1008,
|
||||
14'h0c06,
|
||||
14'h0210,
|
||||
14'h071a,
|
||||
14'h1e1e,
|
||||
14'h1e1e,
|
||||
14'h1e1e,
|
||||
14'h090c,
|
||||
14'h0108,
|
||||
14'h0210,
|
||||
14'h1c0e,
|
||||
14'h0a14,
|
||||
14'h0804,
|
||||
14'h180c,
|
||||
14'h1008,
|
||||
14'h0612,
|
||||
14'h180c,
|
||||
14'h161a,
|
||||
14'h0e16,
|
||||
14'h1008,
|
||||
14'h1e1e,
|
||||
14'h0210,
|
||||
14'h1a1c,
|
||||
14'h140a,
|
||||
14'h0634,
|
||||
14'h0c06,
|
||||
14'h0402,
|
||||
14'h0804,
|
||||
14'h0424,
|
||||
14'h0c06,
|
||||
14'h0424,
|
||||
14'h0424,
|
||||
14'h0826,
|
||||
14'h0c06,
|
||||
14'h102a,
|
||||
14'h0402,
|
||||
14'h180c,
|
||||
14'h0804,
|
||||
14'h0042,
|
||||
14'h106c,
|
||||
14'h0c48,
|
||||
14'h1870,
|
||||
14'h1008,
|
||||
14'h0868,
|
||||
14'h182e,
|
||||
14'h0042,
|
||||
14'h0042,
|
||||
14'h1870,
|
||||
14'h1008,
|
||||
14'h102a,
|
||||
14'h1008,
|
||||
14'h102a,
|
||||
14'h0022,
|
||||
14'h00a6,
|
||||
14'h1870,
|
||||
14'h0868,
|
||||
14'h10f0,
|
||||
14'h00c6,
|
||||
14'h104a,
|
||||
14'h00e8,
|
||||
14'h102a,
|
||||
14'h108c,
|
||||
14'h00a6,
|
||||
14'h0064,
|
||||
14'h0042,
|
||||
14'h0000,
|
||||
14'h0022,
|
||||
14'h10ae,
|
||||
14'h1194,
|
||||
14'h102a,
|
||||
14'h1132,
|
||||
14'h012a,
|
||||
14'h0022,
|
||||
14'h0042,
|
||||
14'h0000,
|
||||
14'h00a6,
|
||||
14'h00a6,
|
||||
14'h01ae,
|
||||
14'h0084,
|
||||
14'h012a,
|
||||
14'h0042,
|
||||
14'h0042,
|
||||
14'h0022,
|
||||
14'h014a,
|
||||
14'h0000,
|
||||
14'h00a6,
|
||||
14'h0000,
|
||||
14'h0274,
|
||||
14'h012a,
|
||||
14'h0274,
|
||||
14'h0022,
|
||||
14'h00e8,
|
||||
14'h014a,
|
||||
14'h0000,
|
||||
14'h0274,
|
||||
14'h0232,
|
||||
14'h00a6,
|
||||
14'h012a,
|
||||
14'h02f8,
|
||||
14'h0042,
|
||||
14'h079e,
|
||||
14'h071a,
|
||||
14'h06b8,
|
||||
14'h0232,
|
||||
14'h0612,
|
||||
14'h05b0,
|
||||
14'h014a,
|
||||
14'h0232,
|
||||
14'h0108,
|
||||
14'h0084,
|
||||
14'h01f0,
|
||||
14'h012a,
|
||||
14'h018c,
|
||||
14'h0990,
|
||||
14'h09b2,
|
||||
14'h0612,
|
||||
14'h0252,
|
||||
14'h056e,
|
||||
14'h0d72,
|
||||
14'h0000,
|
||||
14'h04c8,
|
||||
14'h0c06,
|
||||
14'h0c6a,
|
||||
14'h0edc,
|
||||
14'h09b2,
|
||||
14'h0042,
|
||||
14'h00a6,
|
||||
14'h0e58,
|
||||
14'h13c7,
|
||||
14'h094e,
|
||||
14'h165c,
|
||||
14'h182e,
|
||||
14'h0232,
|
||||
14'h0dd4,
|
||||
14'h0612,
|
||||
14'h18b2,
|
||||
14'h1f29,
|
||||
14'h1a61,
|
||||
14'h016c,
|
||||
14'h0afc,
|
||||
14'h140a,
|
||||
14'h05d0,
|
||||
14'h1110,
|
||||
14'h06b8,
|
||||
14'h05f2,
|
||||
14'h1e1e,
|
||||
14'h184e,
|
||||
14'h1301,
|
||||
14'h0e9a,
|
||||
14'h1787,
|
||||
14'h094e,
|
||||
14'h02d6,
|
||||
14'h08ca,
|
||||
14'h11b6,
|
||||
14'h06fa,
|
||||
14'h0f21,
|
||||
14'h0424,
|
||||
14'h16e3,
|
||||
14'h1aa3,
|
||||
14'h039c,
|
||||
14'h1194,
|
||||
14'h073c,
|
||||
14'h108c,
|
||||
14'h108c,
|
||||
14'h1b05,
|
||||
14'h108c,
|
||||
14'h1512,
|
||||
14'h184e,
|
||||
14'h0210,
|
||||
14'h0b5e,
|
||||
14'h0804,
|
||||
14'h180c,
|
||||
14'h090c,
|
||||
14'h1fcd,
|
||||
14'h148e,
|
||||
14'h0dd4,
|
||||
14'h129c,
|
||||
14'h079e,
|
||||
14'h0000,
|
||||
14'h0888,
|
||||
14'h108c,
|
||||
14'h1194,
|
||||
14'h1787,
|
||||
14'h0b1c,
|
||||
14'h0f83,
|
||||
14'h1f07,
|
||||
14'h0084,
|
||||
14'h0402,
|
||||
14'h0000,
|
||||
14'h1110,
|
||||
14'h108c,
|
||||
14'h1512};
|
||||
|
||||
endmodule
|
292
Labs/02. Arithmetic-logic unit/README.md
Normal file
292
Labs/02. Arithmetic-logic unit/README.md
Normal file
@@ -0,0 +1,292 @@
|
||||
# Лабораторная работа 2. Арифметико-логическое устройство
|
||||
|
||||
Так как основной задачей процессора является обработка цифровых данных, одним из его основных блоков является арифметико-логическое устройство (АЛУ). Задача АЛУ производить над входными данным арифметические и поразрядно логические операции.
|
||||
|
||||
## Цель
|
||||
|
||||
Используя навыки по описанию мультиплексоров, писать блок арифметико-логического устройства (АЛУ) на языке SystemVerilog.
|
||||
|
||||
## Допуск к лабораторной работе
|
||||
|
||||
Освоить [описание мультиплексора на языке SystemVerilog](../../Basic%20Verilog%20structures/Multiplexors.md).
|
||||
|
||||
## Общий ход выполнения работы
|
||||
|
||||
1. Изучить устройство и принцип работы АЛУ (раздел [#теория](#теория))
|
||||
2. Изучить языковые конструкции SystemVerilog для реализации АЛУ (раздел [#инструменты](#инструменты))
|
||||
3. Внимательно ознакомиться с заданием (раздел [#задание](#задание))
|
||||
4. Описать модуль АЛУ, проверить его предоставленным верификационным окружением.
|
||||
5. Проверить работу АЛУ в ПЛИС.
|
||||
|
||||
## Теория
|
||||
|
||||
Арифметико-логическое устройство (АЛУ, Arithmetic Logic Unit – ALU) – это блок процессора, выполняющий арифметические и поразрядно логические операции. Разница между арифметическими и логическими операциями в отсутствии у последних бита переноса, так как логические операции происходят между однобитными числами и дают однобитный результат, а в случае АЛУ (в рамках данной лабораторной работы) одновременно между 32-мя однобитными парами чисел. В логических операциях результаты значений отдельных битов друг с другом никак не связаны.
|
||||
|
||||
Также, кроме результата операций, АЛУ формирует флаги, которые показывают выполняется ли заданное условие. Например, выведет `1`, если один операнд меньше другого.
|
||||
|
||||
Обычно АЛУ представляет собой комбинационную схему (то есть без элементов памяти), на входы которой поступают информационные (операнды) и управляющие (код операции) сигналы, в ответ на что на выходе появляется результат заданной операции. АЛУ бывает не комбинационной схемой, но это скорее исключение.
|
||||
|
||||

|
||||
|
||||
*Рисунок 1. Структурное обозначение элемента АЛУ*
|
||||
|
||||
На рис. 1 изображен пример АЛУ, используемый в книге "Цифровая схемотехника и архитектура компьютера" Харрис и Харрис. На входы `A` и `B` поступают операнды с разрядностью *N*. На трехбитный вход `F` подается код операции. Например, если туда подать `000`, то на выходе `Y` появится результат операции *логическое И* между битами операндов `A` и `B`. Если на `F` подать `010`, то на выходе появится результат сложения. Это лишь пример, разрядность и коды могут отличаться в зависимости от количества выполняемых операций и архитектуры.
|
||||
|
||||
Существует несколько подходов к реализации АЛУ, отличающиеся внутренней организацией. Некоторые из подходов освещаются на [`лекции 3`](../../Lectures/03.%20Digital%20arithmetics.md). В лабораторных работах применяется повсеместно используемый подход мультиплексирования операций, то есть подключения нескольких операционных устройств (которые выполняют какие-то операции, например сложения, логическое И и т.п.) к мультиплексору, который будет передавать результат нужного операционного устройства на выходы АЛУ.
|
||||
|
||||
Рассмотрим на примере все того же АЛУ MIPS из книги Харрисов. На рис. 2, в левой его части, изображена внутренняя организация этого АЛУ, справа – таблица соответствия кодов операциям. На выходе схемы (внизу) стоит четырехвходовый мультиплексор, управляемый двумя из трех битов `F`. К его входам подключены *N* логических И (побитовое И *N*-разрядных операндов), *N* логических ИЛИ, *N*-разрядный сумматор и Zero Extend – устройство делающее из однобитного числа *N*-битное число, дополняя нулями слева.
|
||||
|
||||
К одному из входов этих операционных устройств подключен `A` без изменений, а ко второму подключен выход двухвходового мультиплексора, управляемого оставшимся битом *F*. То есть `F[2]` определяет, что будет вторым операндом: `B` или `~B`. Вдобавок `F[2]` подается на входной перенос сумматора, то есть, когда `F[2] == 1` на выходе сумматора появляется результат операции `A + ~B + 1`, что (с учетом [дополнительного кода](https://ru.wikipedia.org/wiki/Дополнительный_код)) эквивалентно `A – B` (о сумматорах, входных переносах и вычитании на [`лекции 3`](../../Lectures/03.%20Digital%20arithmetics.md)).
|
||||
|
||||

|
||||
|
||||
*Рисунок 2. Структурная схема АЛУ MIPS*
|
||||
|
||||
Посмотрим, что произойдет, если на вход `F` такого АЛУ подать `111`. Будет выполняться операция `SLT`(сокращение от `Set Less Then`) – выдать `1`, если `A` меньше `B`, в противном случае — выдать `0`. Биты `F[1:0]` переключат мультиплексор на выход блока Zero Extend. На вход Zero Extend поступает старший бит выхода сумматора, этот бит отвечает за знак результата. Так как `F[2] == 1`, сумматор вычисляет `A + ~B + 1`, то есть `A – B`, значит, если `A < B`, то результат вычитания будет отрицательный, а старший бит `Y[N-1] == 1`. Если `A` не меньше `B`, то разность будет неотрицательна, а `Y[N-1] == 0`, как и требуется от этой операции.
|
||||
|
||||

|
||||
|
||||
*Рисунок 3. Пример исполнения операции АЛУ*
|
||||
|
||||
Преимущество такой организации АЛУ в его простой модификации, настройке под нужные коды операций, читаемости кода и масштабируемости. Можно легко добавить или убрать требуемые операции. Подумайте, как бы вы обновили данную схему, если бы от вас потребовалось расширить её функционал операциями XOR (Исключающее ИЛИ) и (SGE операция "больше либо равно")?
|
||||
|
||||
## Инструменты
|
||||
|
||||
Как было сказано выше, АЛУ можно реализовать, [мультиплексируя](../../Basic%20Verilog%20structures/Multiplexors.md) результаты нескольких операционных устройств.
|
||||
|
||||
При описании очередной комбинации управляющего сигнала, выходу мультиплексора можно сразу присваивать необходимое логическое выражение (например результат побитового ИЛИ можно подать на выход сразу в виде выражения: `a | b`, однако в некоторых случаях выражения будут сложнее из-за различных особенностей реализации, о которых будет рассказано в задании).
|
||||
|
||||
### Параметры
|
||||
|
||||
Очень удобным на практике оказывается использование параметров. Параметры добавляют модулю гибкости, позволяя убрать "магические" константы из описания модулей, подставляя вместо них выразительное символьное имя. Параметры отдаленно схожи с макросами `#define` в языке Си, однако стоит понимать, параметры используются на этапе синтеза схемы, в отличие от макросов в языке Си, которые заменяются препроцессором еще до этапа компиляции.
|
||||
|
||||
Допустим, ваше устройство должно включить тостер, если на вход ему придет сигнал `32'haf3c5bd0`. Человек, не знакомый с устройством, при прочтении этого кода будет недоумевать, что это за число и почему используется именно оно. Однако, скрыв его за параметром `TOASTER_EN`, читающий поймет, что это код включения тостера. Кроме того, если некоторая константа должна использоваться в нескольких местах кода, то определив её через в виде параметра, можно будет менять её в одном месте, и она тут же поменяется везде.
|
||||
|
||||
Параметры позволяют влиять на структуру модуля. К примеру, описывая сумматор, можно параметризовать его разрядность и использовать этот параметр при описании модуля (например в блоке `generate for`). В этом случае вы сможете создавать множество сумматоров различных разрядностей, подставляя при создании нужное вам значение параметра.
|
||||
|
||||
Параметр может быть объявлен в модуле двумя способами:
|
||||
|
||||
- в прототипе модуля;
|
||||
- в теле описания модуля.
|
||||
|
||||
В первом случае для указания параметра после имени модуля ставится символ `#`, за которым в круглых скобках пишется ключевое слово `parameter` затем указывается тип параметра (по умолчанию знаковое 32-битное число), после чего указывается имя и (опционально) значение по умолчанию.
|
||||
|
||||
Пример:
|
||||
|
||||
```SystemVerilog
|
||||
module overflow #(parameter WIDTH = 32)(
|
||||
input logic [WIDTH-1 : 0] a, b,
|
||||
output logic overflow
|
||||
);
|
||||
|
||||
logic [WIDТН : 0] sum;
|
||||
|
||||
ass𝚒gn sum = a + b;
|
||||
ass𝚒gn overflow = sum[WIDTH];
|
||||
|
||||
endmodule
|
||||
```
|
||||
|
||||
Только таким способом объявления параметр может влиять на разрядность портов модуля.
|
||||
|
||||
В случае, если параметр не влияет на разрядность портов, его можно объявить в теле модуля:
|
||||
|
||||
```SystemVerilog
|
||||
module toaster(
|
||||
input logic [31:0] command,
|
||||
output logic power
|
||||
)
|
||||
|
||||
parameter TOASTER_EN = 32'haf3c5bd0;
|
||||
|
||||
assign power = command == TOASTER_EN;
|
||||
|
||||
endmodule
|
||||
```
|
||||
|
||||
В случае АЛУ будет удобно использовать параметры для обозначения кодов команд. Во-первых, для того чтобы в `case` не допустить ошибок, а во-вторых – чтобы можно было легко менять управляющие коды для повторного использования АЛУ в других проектах.
|
||||
|
||||
Сравните сами:
|
||||
|
||||
```SystemVerilog
|
||||
//parameter SLT = 5'b00011;
|
||||
//parameter BEQ = 5'b11000;
|
||||
|
||||
//...
|
||||
|
||||
always_comb
|
||||
case(ALUOp)
|
||||
//...
|
||||
5'b00011: //... // вообще же ничего не понятно
|
||||
5'b11000: //... // никуда не годится
|
||||
```
|
||||
|
||||
и
|
||||
|
||||
```SystemVerilog
|
||||
parameter SLT = 5'b00011;
|
||||
parameter BEQ = 5'b11000;
|
||||
|
||||
//...
|
||||
|
||||
аlwауs_comb
|
||||
case(ALUOp)
|
||||
//...
|
||||
SLT: //... // очень понятно
|
||||
BEQ: //... // так лаконично и красиво
|
||||
```
|
||||
|
||||
С параметрами гораздо взрослее, серьезнее и понятнее смотрится. Кстати, сразу на заметку: в SystemVerilog можно объединять группу параметров в **пакет**(package), а затем импортировать его внутрь модуля, позволяя переиспользовать параметры без повторного их прописывания для других модулей.
|
||||
|
||||
Делается это следующим образом.
|
||||
|
||||
Сперва создается SystemVerilog-файл, который будет содержать пакет (к примеру содержимое файла может быть таким):
|
||||
|
||||
```SystemVerilog
|
||||
package riscv_params_pkg;
|
||||
parameter ISA_WIDTH = 32;
|
||||
parameter ANOTHER_EX = 15;
|
||||
endpackage
|
||||
```
|
||||
|
||||
Далее, внутри модуля, которому нужны параметры из этого пакета, необходимо сделать соответствующий импорт этих параметров. Это можно сделать либо для каждого параметра отдельно, либо импортировать все параметры сразу:
|
||||
|
||||
```SystemVerilog
|
||||
module riscv_processor(
|
||||
//...Порты
|
||||
);
|
||||
|
||||
import riscv_params_pkg::ISA_WIDTH; // Если необходимо импортировать
|
||||
import riscv_params_pkg::ANOTHER_EX; // все параметры в пакете,эти две строчки
|
||||
// могут быть заменены закомментированной
|
||||
// ниже строкой:
|
||||
//import riscv_params_pkg::*;
|
||||
|
||||
endmodule
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
При реализации АЛУ, вам также потребуется использовать [операции сдвига](https://ru.wikipedia.org/wiki/Битовый_сдвиг), к которым относятся:
|
||||
|
||||
- `<<` — логический сдвиг влево
|
||||
- `>>` — логический сдвиг вправо
|
||||
- `>>>` — арифметический сдвиг вправо
|
||||
|
||||
<br><br><br><br><br>
|
||||
|
||||
---
|
||||
|
||||
### Особенности реализации сдвига
|
||||
|
||||
**Для ВСЕХ операций сдвига вы должны брать только 5 младших бит операнда B.**
|
||||
|
||||
Сами посмотрите: пятью битами можно описать 32 комбинации [0-31], а у операнда А будет использоваться ровно 32 бита. **Это обязательное требование**, поскольку старшие биты в дальнейшем будут использоваться по другому назначению и если вы упустите это, ваш будущий процессор станет работать неправильно.
|
||||
|
||||
---
|
||||
|
||||
<br><br><br><br><br>
|
||||
|
||||
## Задание
|
||||
|
||||
Необходимо на языке SystemVerilog реализовать АЛУ в соответствии со следующим прототипом:
|
||||
|
||||
```SystemVerilog
|
||||
|
||||
module аlu_r𝚒sсv (
|
||||
𝚒nput logic [31:0] a_i,
|
||||
𝚒nput logic [31:0] b_i,
|
||||
𝚒nput logic [4:0] alu_op_i,
|
||||
оutput logic flag_o,
|
||||
оutput logic [31:0] result_o
|
||||
);
|
||||
|
||||
import alu_opcodes_pkg::*; // импорт параметров, содержащих
|
||||
// коды операций для АЛУ
|
||||
|
||||
endmodule
|
||||
```
|
||||
|
||||
Для стандартного набора целочисленных операций архитектуры RISC-V требуется выполнять 16 различных операций. Для кодирования 16 операций было бы достаточно 4 бит, но в лабораторной работе предлагается использовать 5-битный код, что связано с особенностями кодирования инструкций (будет рассмотрено в лекциях 6 и 7). Видно, что старший бит кода операции указывает на то, является ли операция вычислительной или это операция сравнения.
|
||||
|
||||
|ALUOp|={flag, add/sub, aluop}|Result |Flag |Операция |
|
||||
|-----|-----------------------|-----------------------------------|-----------------------------------|---------------------------------------------------|
|
||||
| ADD | 0 0 000 |Result = А + В | Flаg = 0 | Сложение |
|
||||
| SUB | 0 1 000 |Result = А – В | Flаg = 0 | Вычитание |
|
||||
| SLL | 0 0 001 |Result = А << В | Flаg = 0 | Сдвиг влево |
|
||||
| SLTS| 0 0 010 |Result = А < В (знаковое сравнение)| Flаg = 0 | Знаковое сравнение |
|
||||
| SLTU| 0 0 011 |Result = А < В | Flаg = 0 | Беззнаковое сравнение |
|
||||
| XOR | 0 0 100 |Result = А ^ В | Flаg = 0 | Побитовое исключающее **ИЛИ** |
|
||||
| SRL | 0 0 101 |Result = А >> В | Flаg = 0 | Сдвиг вправо |
|
||||
| SRA | 0 1 101 |Result = А >>> В | Flаg = 0 | Арифметический сдвиг вправо (операнд А — знаковый)|
|
||||
| OR | 0 0 110 |Result = А \| В | Flаg = 0 | Побитовое логическое **ИЛИ** |
|
||||
| AND | 0 0 111 |Result = А & В | Flаg = 0 | Побитовое логическое **И** |
|
||||
| EQ | 1 1 000 |Result = 0 | Flаg = (А == В) | Выставить флаг, если **равны** |
|
||||
| NE | 1 1 001 |Result = 0 | Flаg = (А != В) | Выставить флаг, если **не равны** |
|
||||
| LTS | 1 1 100 |Result = 0 | Flаg = А < В (знаковое сравнение) | Знаковое сравнение **<** |
|
||||
| GES | 1 1 101 |Result = 0 | Flаg = А ≥ В (знаковое сравнение) | Знаковое сравнение **≥** |
|
||||
| LTU | 1 1 110 |Result = 0 | Flаg = А < В | Беззнаковое сравнение **<** |
|
||||
| GEU | 1 1 111 |Result = 0 | Flаg = А ≥ В | Беззнаковое сравнение **≥** |
|
||||
|
||||
**Выражения в этой таблице приведены для примера. Не все из них можно просто переписать — часть этих выражений надо дополнить. Чтобы вы не копировали выражения, в них вставлены неподдерживаемые символы.**
|
||||
|
||||
Обратите внимание на инструкции сравнения. У нас есть две похожие пары инструкций:
|
||||
|
||||
- `LTS`
|
||||
- `LTU`
|
||||
- `SLTS`
|
||||
- `SLTU`
|
||||
|
||||
Первая пара инструкций вычисляет "ветвительный" результат. Результат операции будет подан на выходной сигнал `Flag` и использован непосредственно при ветвлении.
|
||||
|
||||
Вторые две инструкции используются для получения "вычислительного" результата. Т.е. результат сравнения будет подан на выходной сигнал `Result` так же, как подается результат операции `ADD`, и будет использован в неких вычислениях, избегая при этом условного перехода.
|
||||
|
||||
К примеру, нам необходимо пройтись по массиву из миллиона элементов и убедиться, что все они были неотрицательны. Об этом будет сигнализировать переменная `num_of_err`, значение которой должно быть равно числу элементов массива, меньших нуля. Вычислить значение этой переменной можно двумя способами:
|
||||
|
||||
1. В каждой итерации цикла сделать ветвление: в одном случае инкрементировать переменную, в другом случае — нет (для ветвления использовать "ветвительную" операцию `LTS`).
|
||||
2. В каждой итерации цикла складывать текущее значение переменной с результатом "вычислительной" операции `SLTS`.
|
||||
|
||||
Операции ветвления очень сильно влияют (в худшую сторону) на производительность конвейерного процессора. В первом случае мы получим миллион операций ветвления, во втором — ни одной! Разумеется потом переменную `num_of_err` скорее всего сравнят с нулем что приведет к ветвлению, но при вычислении значения этой переменной ветвления можно будет избежать.
|
||||
|
||||
Различие между `SLTS` и `SLTU` (или `LTS` и `LTU`) заключается в том, как мы интерпретируем операнды: как знаковые числа (операции `STLS` и `LTS`) или как беззнаковые (операциии `SLTU` и `LTU`).
|
||||
|
||||
Предположим, мы сравниваем два двоичных числа: `1011` и `0100`. Если интерпретировать эти числа как беззнаковые, то это `11` и `4`, результат: `11 > 4`. Однако если интерпретировать эти числа как знаковые, то теперь это числа `-5` и `4` и в этом случае `-5 < 4`.
|
||||
|
||||
Как мы видим, результат одной и той же операции над одними и теми же двоичными числами может зависеть от того, каким образом мы интерпретируем эти двоичные числа. Для большинства операций в АЛУ это не важно: например, сложение будет работать одинаково в обоих случаях, благодаря свойствам дополнительного кода, а побитовые операции работают с отдельными битами двоичного числа. А вот для операции арифметического сдвига это важно — **операнд А в арифметическом сдвиге должен интерпретироваться как знаковый**.
|
||||
|
||||
По умолчанию SystemVerilog интерпретирует все сигналы как беззнаковые, если мы хотим изменить это поведение, необходимо воспользоваться конструкцией `$signed`.
|
||||
|
||||
Конструкция `$signed` говорит САПР интерпретировать число, переданное в качестве операнда, как знаковое.
|
||||
|
||||
```SystemVerilog
|
||||
аss𝚒gn Rеsult = $s𝚒gnеd(А) >>> В[4:0];
|
||||
```
|
||||
|
||||
В этом примере некоторому сигналу `Result` присваивают результат сдвига знакового числа `A` на значение количества бит получаемых из младших 5 бит сигнала `B`.
|
||||
|
||||
Так как используются не все возможные комбинации управляющего сигнала АЛУ, то **при описании через `case` не забывайте использовать `default`**. Если описать АЛУ как задумано, то получится что-то похожее на картинку ниже. Но не обязательно, зависит от вашего описания.
|
||||
|
||||

|
||||
|
||||
### Порядок выполнения задания
|
||||
|
||||
1. Добавьте в проект файл [`alu_opcodes_pkg.sv`](alu_opcodes_pkg.sv). Этот файл содержит объявление пакета `alu_opcodes_pkg`, в котором прописаны все опкоды АЛУ.
|
||||
2. В `Design Sources` проекта создайте `SystemVerilog`-файл `аlu_r𝚒sсv.sv`.
|
||||
3. Опишите в нем модуль АЛУ с таким же именем и портами, как указано в [задании](#задание).
|
||||
1. Поскольку у вас два выходных сигнала, зависящих от сигнала `alu_op_i`, вам потребуется описать два разных [мультиплексора](../../Basic%20Verilog%20structures/Multiplexors.md) (их лучше всего описывать через два отдельных блока `case`). При описании, используйте `default` на оставшиеся комбинации сигнала `alu_op_i`.
|
||||
2. Следите за разрядностью ваших сигналов.
|
||||
3. Для реализации АЛУ, руководствуйтесь таблицей с операциями, а не схемой в конце задания, которая приведена в качестве референса. Обратите внимание, в одной половине операций `flag_o` должен быть равен нулю, в другой `result_o` (т.е. всегда либо один, либо другой сигнал должен быть равен нулю). Именно поэтому удобней всего будет описывать АЛУ в двух разных блоках `case`.
|
||||
4. Вам не нужно переписывать опкоды из таблицы в качестве вариантов для блока `case`. Вместо этого используйте символьные имена с помощью параметров, импортированных из пакета `alu_opcodes_pkg`.
|
||||
5. При операции сложения вы **должны** использовать ваш 32-битный сумматор из первой лабораторной (описывая вычитание сумматор использовать не надо, можно использовать `-`).
|
||||
1. При подключении сумматора, на входной бит переноса необходимо подать значение `1'b0`. Если не подать значение на входной бит переноса, результат суммы будет не определен (т.к. не определено одно из слагаемых).
|
||||
2. Выходной бит переноса при подключении сумматора можно не указывать, т.к. он использоваться не будет.
|
||||
6. При реализации операций сдвига, руководствуйтесь [особенностями реализации сдвигов](#особенности-реализации-сдвига).
|
||||
4. После реализации модуля АЛУ его нужно будет проверить с помощью тестового окружения.
|
||||
1. Добавьте файл [`tb_miriscv_alu.sv`](tb_miriscv_alu.sv) в `Simulation sources`.
|
||||
2. Для запуска симуляции воспользуйтесь [`этой инструкцией`](../../Vivado%20Basics/Run%20Simulation.md).
|
||||
3. Перед запуском симуляции убедитесь, что в качестве top-level модуля выбран модуль `tb_miriscv_alu`.
|
||||
4. Убедитесь, что симуляция завершена (об этом будет соответствующее сообщение в консоли). По завершению симуляции, в случае отсутствия ошибок, будет выведено сообщение "SUCCESS", в противном случае будут выведены сообщения об этих ошибках.
|
||||
5. В случае, если были найдены ошибки, вы должны найти и исправить их. Для этого руководствуйтесь [документом](../../Vivado%20Basics/Debug%20manual.md).
|
||||
5. Добавьте в проект модуль верхнего уровня ([nexys_alu.sv](board%20files/nexys_alu.sv)), соединяющий АЛУ с периферией в ПЛИС. Описание модуля находится [здесь](board%20files)
|
||||
6. Подключите к проекту файл ограничений ([nexys_a7_100t.xdc](board%20files/nexys_a7_100t.xdc), файл ограничений, добавленный в первой лабораторной содержит другие данные, вам необходимо вставить содержимое файла для текущей лабы).
|
||||
7. Проверьте работу АЛУ в ПЛИС.
|
28
Labs/02. Arithmetic-logic unit/alu_opcodes_pkg.sv
Normal file
28
Labs/02. Arithmetic-logic unit/alu_opcodes_pkg.sv
Normal file
@@ -0,0 +1,28 @@
|
||||
package alu_opcodes_pkg;
|
||||
parameter ALU_OP_WIDTH = 5;
|
||||
|
||||
parameter ALU_ADD = 5'b00000;
|
||||
parameter ALU_SUB = 5'b01000;
|
||||
|
||||
parameter ALU_XOR = 5'b00100;
|
||||
parameter ALU_OR = 5'b00110;
|
||||
parameter ALU_AND = 5'b00111;
|
||||
|
||||
// shifts
|
||||
parameter ALU_SRA = 5'b01101;
|
||||
parameter ALU_SRL = 5'b00101;
|
||||
parameter ALU_SLL = 5'b00001;
|
||||
|
||||
// comparisons
|
||||
parameter ALU_LTS = 5'b11100;
|
||||
parameter ALU_LTU = 5'b11110;
|
||||
parameter ALU_GES = 5'b11101;
|
||||
parameter ALU_GEU = 5'b11111;
|
||||
parameter ALU_EQ = 5'b11000;
|
||||
parameter ALU_NE = 5'b11001;
|
||||
|
||||
// set lower than operations
|
||||
parameter ALU_SLTS = 5'b00010;
|
||||
parameter ALU_SLTU = 5'b00011;
|
||||
|
||||
endpackage
|
23
Labs/02. Arithmetic-logic unit/board files/README.md
Normal file
23
Labs/02. Arithmetic-logic unit/board files/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Проверка работы арифметико-логического устройства в ПЛИС
|
||||
|
||||
После того, как вы создали свое АЛУ и проверили его на прохождение тестирующего модуля, вы можете использовать предложенный модуль окружения [`nexys_alu.v`](nexys_alu.v), который позволяет связать вашу логику с периферией, расположенной на плате `Nexys-A7`. Для его подключения, скачайте и добавьте файл в проект, либо скопируйте содержимое в новый `.v` файл вашего проекта. В окне `Sources` нажмите на него ПКМ и выберите `Set as Top`, после чего в иерархии он станет главным, подключив ваш собственный модуль АЛУ. Для того, чтобы дизайн мог физически подключиться к периферии, нужно в проекте выбрать `Add Sources`, `Add or create constraints` и подключить файл [`nexys_a7_100t.xdc`](nexys_a7_100t.xdc). Если у вас уже подключен этот файл, необходимо заменить данные на те, которые предложены в текущей лабе.
|
||||
|
||||
После этого наше устройство будет выглядеть так:
|
||||
|
||||

|
||||
|
||||
Подключенное окружение позволяет производить ввод входных значений (А и В) и управляющего сигнала (ALUOp) с помощью переключателей (номер переключателя отображен на самом краю платы), расположенных на плате. А: 15-11, В: 10-6, ALUOp: 4-0, а переключатель №5 активирует семисегментные индикаторы, на которых отображается на левом блоке операнды А и В, а на правом - ALUOp. На светодиодах, расположенных над переключателями отображается выходное значение в двоичном формате, а 15-й светодиод отвечает за сигнал `Flag`
|
||||
|
||||
Управление АЛУ через плату
|
||||
|
||||

|
||||
|
||||
Для прошивки ПЛИС подключите утройство через USB, включите питание переключателем, выполните синтез и имплементацию вашего дизайна и сгенерируйте битстрим. Если на этом этапе у вас возникают ошибки, постарайтесь исправить из с помощью [`инструкции по работе с ошибками синтеза`](../../../Vivado%20Basics/Synthesis%20failed.md). После этого выберите в левом меню `Open Target` - `Auto Connect`, затем `Program Device` и ваше устройство прошьется.
|
||||
|
||||
Генерация битстрима
|
||||

|
||||
|
||||
Прошивка ПЛИС
|
||||

|
||||
|
||||
Попробуйте выставить на переключателях различные опкоды, такие как сложение, вычитание, сдвиг и сравнения, убедитесь, что все работает исправно и сдавайте работу.
|
211
Labs/02. Arithmetic-logic unit/board files/nexys_a7_100t.xdc
Normal file
211
Labs/02. Arithmetic-logic unit/board files/nexys_a7_100t.xdc
Normal file
@@ -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
|
96
Labs/02. Arithmetic-logic unit/board files/nexys_alu.sv
Normal file
96
Labs/02. Arithmetic-logic unit/board files/nexys_alu.sv
Normal file
@@ -0,0 +1,96 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module nexys_alu(
|
||||
input CLK100,
|
||||
input resetn,
|
||||
input [15:0] SW,
|
||||
output [15:0] LED,
|
||||
output CA, CB, CC, CD, CE, CF, CG,
|
||||
output [7:0] AN
|
||||
);
|
||||
import alu_opcodes_pkg::*;
|
||||
wire [4:0] operator_i;
|
||||
wire [31:0] operand_a_i;
|
||||
wire [31:0] operand_b_i;
|
||||
|
||||
wire [31:0] result_o;
|
||||
wire comparison_result_o;
|
||||
|
||||
localparam pwm = 1000;
|
||||
reg [9:0] counter;
|
||||
reg [3:0] semseg;
|
||||
reg [7:0] ANreg;
|
||||
reg CAr, CBr, CCr, CDr, CEr, CFr, CGr;
|
||||
reg [15:0] LEDr;
|
||||
reg minus;
|
||||
|
||||
alu_riscv DUT
|
||||
(
|
||||
.ALUOp (operator_i),
|
||||
.A (operand_a_i),
|
||||
.B (operand_b_i),
|
||||
|
||||
.Result (result_o),
|
||||
.Flag (comparison_result_o)
|
||||
);
|
||||
|
||||
assign operator_i = SW[4:0];
|
||||
assign operand_b_i = {{28{SW[10]}},SW[9:6]};
|
||||
assign operand_a_i = {{28{SW[15]}},SW[14:11]};
|
||||
|
||||
assign LED[15:0] = LEDr[15:0];
|
||||
|
||||
assign AN[7:0] = ANreg[7:0];
|
||||
assign {CA, CB, CC, CD, CE, CF, CG} = {CAr, CBr, CCr, CDr, CEr, CFr, CGr};
|
||||
|
||||
initial ANreg[7:0] = 8'b11111110;
|
||||
|
||||
always @(posedge CLK100) begin
|
||||
if (!resetn) begin
|
||||
LEDr[15:0] <= 'b0;
|
||||
counter <= 'b0;
|
||||
ANreg[7:0] <= 8'b11111111;
|
||||
{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1111111;
|
||||
end
|
||||
else begin
|
||||
LEDr[14:0] <= result_o[14:0];
|
||||
LEDr[15] <= comparison_result_o;
|
||||
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] <= !(SW[5] && (ANreg[6:0] == 7'b1111111));
|
||||
end
|
||||
case (1'b0)
|
||||
ANreg[0]: semseg <= result_o[31] ? ( ~result_o + 1 ) % 4'd10: (result_o ) % 4'd10;
|
||||
ANreg[1]: semseg <= result_o[31] ? ((~result_o + 1) / 'd10 ) % 4'd10: (result_o / 'd10 ) % 4'd10;
|
||||
ANreg[2]: semseg <= result_o[31] ? ((~result_o + 1) / 'd100 ) % 4'd10: (result_o / 'd100 ) % 4'd10;
|
||||
ANreg[3]: semseg <= result_o[31] ? ((~result_o + 1) / 'd1000) % 4'd10: (result_o / 'd1000) % 4'd10;
|
||||
ANreg[4]: semseg <= operand_b_i[31] ? ( ~operand_b_i + 1 ) % 4'd10: (operand_b_i ) % 4'd10;
|
||||
ANreg[5]: semseg <= operand_b_i[31] ? ((~operand_b_i + 1) / 'd10) % 4'd10: (operand_b_i / 'd10) % 4'd10;
|
||||
ANreg[6]: semseg <= operand_a_i[31] ? ( ~operand_a_i + 1 ) % 4'd10: (operand_a_i ) % 4'd10;
|
||||
ANreg[7]: semseg <= operand_a_i[31] ? ((~operand_a_i + 1) / 'd10) % 4'd10: (operand_a_i / 'd10) % 4'd10;
|
||||
endcase
|
||||
minus <= (operator_i == ALU_ADD || operator_i == ALU_SUB || operator_i == ALU_SLTS || operator_i == ALU_SRA || operator_i == ALU_LTS || operator_i == ALU_GES);
|
||||
case (semseg)
|
||||
4'd0: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= (((!ANreg[5] & operand_b_i[31]) || (!ANreg[7] & operand_a_i[31]) || (!ANreg[3] & result_o[31])) && minus) ? 7'b1111110: 7'b0000001;
|
||||
4'd1: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= (((!ANreg[5] & operand_b_i[31]) || (!ANreg[7] & operand_a_i[31]) || (!ANreg[3] & result_o[31])) && minus) ? 7'b1001110: 7'b1001111;
|
||||
4'd2: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0010010;
|
||||
4'd3: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0000110;
|
||||
4'd4: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1001100;
|
||||
4'd5: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0100100;
|
||||
4'd6: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0100000;
|
||||
4'd7: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0001111;
|
||||
4'd8: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0000000;
|
||||
4'd9: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0000100;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
10104
Labs/02. Arithmetic-logic unit/tb_miriscv_alu.sv
Normal file
10104
Labs/02. Arithmetic-logic unit/tb_miriscv_alu.sv
Normal file
File diff suppressed because it is too large
Load Diff
379
Labs/03. Register file and memory/README.md
Normal file
379
Labs/03. Register file and memory/README.md
Normal file
@@ -0,0 +1,379 @@
|
||||
# Лабораторная работа 3 "Регистровый файл и внешняя память"
|
||||
|
||||
Процессор — это программно-управляемое устройство выполняющее обработку информации и управление этим процессом. Очевидно, программа, которая управляет процессором, должна где-то храниться. Данные, с которыми процессор работает, тоже должны быть в доступном месте. Нужна память!
|
||||
|
||||
## Цель
|
||||
|
||||
Описать на языке SystemVerilog элементы памяти для будущего процессора:
|
||||
|
||||
- память команд (Instruction Memory);
|
||||
- память данных (Data Memory);
|
||||
- регистровый файл (Register File).
|
||||
|
||||
## Допуск к лабораторной работе
|
||||
|
||||
Для успешного выполнения лабораторной работы, вам необходимо освоить:
|
||||
|
||||
- приведенные способы описания [мультиплексоров](../../Basic%20Verilog%20structures/Multiplexors.md)
|
||||
- способы описания [регистров](../../Basic%20Verilog%20structures/Registers.md)
|
||||
- [конкатенацию](../../Basic%20Verilog%20structures/Concatenation.md).
|
||||
|
||||
## Ход работы
|
||||
|
||||
1. Изучить способы организации памяти (раздел [#теория про память](#теория-про-память)).
|
||||
2. Изучить конструкции SystemVerilog для реализации запоминающих элементов (раздел [#инструменты](#инструменты-для-реализации-памяти)).
|
||||
3. В проекте с прошлой лабораторной реализовать модули: Instruction Memory, Data Memory и Register File ([#задание](#задание-по-реализации-памяти)).
|
||||
4. Проверить с помощью тестового окружения корректность их работы.
|
||||
5. Проверить работу регистрового файла в ПЛИС.
|
||||
|
||||
## Теория про память
|
||||
|
||||
Память — это устройство для упорядоченного хранения и выдачи информации. Различные запоминающие устройства отличаются способом и организацией хранения данных. Базовыми характеристиками памяти являются:
|
||||
|
||||
- V — объем (количество бит данных, которые единовременно может хранить память);
|
||||
- a — разрядность адреса (ширина шины адреса, определяет адресное пространство — количество адресов отдельных ячеек памяти);
|
||||
- d — разрядность хранимых данных (разрядность ячейки памяти, как правило совпадает с разрядностью входных/выходных данных).
|
||||
|
||||
В общем случае `V = 2^a * d`.
|
||||
|
||||
Для объема памяти в 1 KiB ([кибибайт](https://ru.wikipedia.org/wiki/%D0%9A%D0%B8%D0%B1%D0%B8%D0%B1%D0%B0%D0%B9%D1%82), 1024 байта или 8192 бита) разрядность адреса может быть, например, 10 бит (что покрывает 2^10 = 1024 адреса), тогда разрядность хранимых данных должна быть 8 бит. 1024 * 8 = 8192, то есть 1 кибибайт. Если разрядность адреса, например, 8 бит (что покрывает 2^8 = 256 адресов), то разрядность данных `d = V / 2^a` это 8192 / 256 = 32 бита.
|
||||
|
||||
Однако, может быть такое, что не все ячейки памяти реально реализованы на кристалле микросхемы, то есть некоторые адреса существуют, но по ним не имеет смысла обращаться, а объем памяти, соответственно, не равен `V ≠ 2^a * d` — он меньше. Подобные случаи будут рассмотрены отдельно.
|
||||
|
||||
Память можно разделить на категории: ПЗУ (постоянное запоминающее устройство) и ОЗУ (оперативное запоминающее устройство). Из ПЗУ можно только считывать информацию, которая попадает в ПЗУ до начала использования памяти и не может изменяться в процессе работы. Из ОЗУ можно считывать и записывать информацию. В самом простом случае ПЗУ имеет один вход адреса `addr` и один выход считываемых данных `read_data`. На вход `addr` подается адрес требуемой ячейки памяти, на выходе `read_data` появляются данные, которые хранятся по этому адресу.
|
||||
|
||||
Для ОЗУ требуется больше сигналов. Кроме входного `addr` и выходного `read_data` добавляются: входные данные для записи `write_data`, сигнал синхронизации `clk`, который определяет момент записи данных и сигнал разрешения на запись `write_enable`, который контролирует нужно ли записывать данные или только считывать. Для того, чтобы записать информацию в такую память необходимо:
|
||||
|
||||
- выставить адрес `addr` в который планируется запись данных,
|
||||
- выставить сами данные для записи на вход `write_data`,
|
||||
- установить сигнал `write_enable` в состояние разрешения записи (как правило это 1) и
|
||||
- дождаться нужного фронта `clk` — в этот момент данные будут записаны по указанному адресу. При этом, на выходе `read_data` будут старые данные, хранящиеся по адресу `addr`. На одном такте происходит одновременное считывание информации и запись новой.
|
||||
|
||||
Так же возможна реализация, в которой вход `read_data` и выход `write_data` объединены в единый вход/выход `data`. В этом случае операции чтения и записи разделены во времени и используют для этого один единый порт ввода-вывода (`inout`, двунаправленный порт) `data`.
|
||||
|
||||

|
||||
|
||||
*Рисунок 1. Примеры блоков ПЗУ и ОЗУ*
|
||||
|
||||
Кроме того, различают память с **синхронным** и **асинхронным** чтением. В первом случае, перед выходным сигналом шины данных ставится дополнительный регистр, в который по тактовому синхроимпульсу записываются запрашиваемые данные. Такой способ может очень сильно сократить **критический путь** цифровой схемы, но требует дополнительный такт на доступ в память. В свою очередь, асинхронное чтение позволяет получить данные не дожидаясь очередного синхроимпульса, но такой способ увеличивает критический путь.
|
||||
|
||||
Еще одной характеристикой памяти является количество доступных портов. Количество портов определяет к скольким ячейкам памяти можно обратиться одновременно. Проще говоря, сколько входов адреса существует. Все примеры памяти рассмотренные выше являются **однопортовыми**, то есть у них один порт. Например, если у памяти 2 входа адреса `addr1` и `addr2` — это **двухпортовая память**. При этом не важно, можно ли по этим адресам только читать/писать или выполнять обе операции.
|
||||
|
||||
Регистровый файл, который будет реализован в рамках данной работы, является **трехпортовым**, и имеет 2 порта на чтение и 1 порт на запись.
|
||||
|
||||
С точки зрения аппаратной реализации память в ПЛИС может быть **блочной**, **распределенной** или **регистровой**. **Блочная память** — это аппаратный блок памяти, который можно сконфигурировать под свои нужды. **Распределенная** и **регистровая** память (в отличие от блочной) реализуется на **конфигурируемых логических блоках** (см. [как работает ПЛИС](../../Introduction/How%20FPGA%20works.md)). Такая память привязана к расположению конфигурируемых логических блоков ПЛИС и как бы равномерно распределена по всему кристаллу. Вместо реализации логики конфигурируемые логические блоки используются для нужд памяти. Чтобы понять почему это возможно, рассмотрим структуру логического блока:
|
||||
|
||||

|
||||
|
||||
*Рисунок 2. Структурная схема логического блока в ПЛИС*
|
||||
|
||||
В логическом блоке есть **таблицы подстановки**(Look Up Table, LUT), которые представляют собой не что иное как память, которая переконфигурируется под нужды хранения, а не реализацию логики. Таким образом, трехвходовой LUT может выступать в роли восьмиразрядной памяти.
|
||||
|
||||
Однако LUT будет сложно приспособить под многопортовую память: посмотрим на схему еще раз: три входа LUT формируют адрес одной из восьми ячеек. Это означает, что среди этих восьми ячеек нельзя обратиться к двум из них одновременно.
|
||||
|
||||
Для реализации многопортовой памяти небольшого размера лучше воспользоваться расположенным в логическом блоке D-триггере (**DFF** на *рис. 2*). Не смотря на то, что D-триггер позволяет воспроизвести только 1 разряд элемента памяти, он не ограничивает реализацию по портам.
|
||||
|
||||
Таким образом, плюс распределенной памяти относительно регистровой заключается в лучшей утилизации ресурсов: на восемь разрядов памяти у нас уйдет один трехвходовой LUT, в то время как для восьми разрядов регистровой памяти у нас уйдет 8 D-триггеров.
|
||||
|
||||
Минусом является ограниченность в реализации многопортовой памяти.
|
||||
|
||||
Сравним блочную память с распределенной/регистровой: поскольку большой объем памяти съест много логических блоков при реализации распределенной/регистровой памяти, такую память лучше делать в виде блочной.
|
||||
|
||||
В то же время, к плюсам распределенной/регистровой памяти можно отнести возможность синтезировать память с асинхронным портом на чтение, чем мы и воспользуемся при реализации однотактного процессора (если бы порт чтения памяти был синхронным, нам потребовалось ждать один такт, чтобы получить инструкцию из памяти инструкций или данные из регистрового файла, что затруднило бы реализацию однотактного процессора, где каждая инструкция должна выполняться ровно за один такт).
|
||||
|
||||
Обычно синтезатор сам понимает, какой вид памяти подходит под описанную схему на языке SystemVerilog.
|
||||
|
||||
В случае, если под описанную схему подходит несколько видов памяти, есть возможность выбрать конкретную вручную, причем способы могут различаться от производителя к производителю, поэтому за подробностями лучше обращаться к документации. Например у Xilinx за это отвечает [следующий раздел](https://docs.xilinx.com/r/en-US/ug901-vivado-synthesis/RAM-HDL-Coding-Techniques) документации по синтезу.
|
||||
|
||||
## Инструменты для реализации памяти
|
||||
|
||||
### Описание памяти на языке SystemVerilog
|
||||
|
||||
Память на языке SystemVerilog объявляется [подобно регистрам](../../Basic%20Verilog%20structures/Registers.md), используя ключевое слово `logic`. Но, кроме разрядности (разрядности ячеек памяти, в данном случае) после имени регистра (памяти, в данном случае) указывается количество создаваемых ячеек либо в виде натурального числа, либо в виде диапазона адресов этих ячеек.:
|
||||
|
||||
```SystemVerilog
|
||||
logic [19:0] memory1 [16]; // memory1 и memory2 являются полностью
|
||||
logic [19:0] memory2 [0:15]; // идентичными памятями.
|
||||
|
||||
logic [19:0] memory3 [15:0]; // memory3 будет такой же памятью, что и
|
||||
// предыдущие, но на временной диаграмме
|
||||
// Vivado при ее отображении сперва будут
|
||||
// идти ячейки, начинающиеся со старших
|
||||
// адресов (что в рамках данного курса
|
||||
// лабораторных работ будет скорее минусом).
|
||||
|
||||
|
||||
logic [19:0] memory3 [1:16]; // А вот memory3 хоть и совпадает по
|
||||
// размеру с предыдущими реализациями,
|
||||
// но отличается по адресному пространству
|
||||
// обращение по нулевому адресу выдаст
|
||||
// недетерминированный результат. Это не
|
||||
// значит что память будет плохой или
|
||||
// дефектной, просто надо учитывать эту её
|
||||
// особенность.
|
||||
```
|
||||
|
||||
В приведенном листинге `logic [19:0] memory1 [16];` создается память с шестнадцатью (от 0-го до 15-го адреса) 20-битными ячейками памяти. В таком случае говорят, что ширина памяти 20 бит, а глубина 16. Для адресации такой памяти потребуется адрес с разрядностью ceil(log2(16)) = 4 бита (`ceil` — операция округления вверх). Это однопортовая память.
|
||||
|
||||
Для обращения к конкретной ячейке памяти используются квадратные скобки с указанием нужного адреса `memory[addr]`. Грубо говоря, то, что указывается в квадратных скобках будет подключено ко входу адреса памяти `memory`.
|
||||
|
||||
Реализация асинхронного подключения к выходу памяти осуществляется оператором `assign`. А, если требуется создать память с синхронным чтением, то присваивание выходу требуется описать внутри блока`always_ff`.
|
||||
|
||||
Так как запись в память является синхронным событием, то описывается она в конструкции `always_ff`. При этом, как и при описании регистра, можно реализовать управляющий сигнал разрешения на запись через блок вида `if(write_enable)`.
|
||||
|
||||
```SystemVerilog
|
||||
module mem16_20 ( // создать блок с именем mem16_20
|
||||
input logic clk, // вход синхронизации
|
||||
input logic [3:0] addr, // адресный вход
|
||||
input logic [19:0] write_data, // вход данных для записи
|
||||
input logic write_enable, // сигнал разрешения на запись
|
||||
output logic [19:0] async_read_data// асинхронный выход считанных данных
|
||||
output logic [19:0] sync_read_data // синхронный выход считанных данных
|
||||
);
|
||||
|
||||
logic [19:0] memory [0:15]; // создать память с 16-ю
|
||||
// 20-битными ячейками
|
||||
|
||||
// асинхронное чтение
|
||||
assign read_data = memory[addr]; // подключить к выходу async_read_data
|
||||
// ячейку памяти по адресу addr
|
||||
// (асинхронное чтение)
|
||||
|
||||
// синхронное чтение
|
||||
always_ff(@posedge clk) begin // поставить перед выходом sync_read_data
|
||||
sync_read_data <= memory[addr]; // регистр, в который каждый такт будут
|
||||
end // записываться считываемые данные
|
||||
|
||||
// запись
|
||||
always_ff @ (posedge clk) begin // каждый раз по фронту clk
|
||||
if(write_enable) begin // если сигнал write_enable == 1, то
|
||||
memory[addr] <= write_data; // в ячейку по адресу addr будут записаны
|
||||
// данные сигнала write_data
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
```
|
||||
|
||||
В случае реализации ПЗУ нет необходимости в описании входов для записи. В таком случае описание памяти занимает всего пару строк. Чтобы инициализировать такую память (то есть поместить в нее начальные значения, чтобы было что из нее читать), требуемое содержимое нужно добавить к прошивке, вместе с которой данные попадут в ПЛИС. Для этого в проект добавляется текстовый файл с содержимым памяти (более подробно об этом в [`Как добавить файл с содержимым памяти в проект`](../../Vivado%20Basics/How%20to%20add%20a%20mem-file.md)). Для того, чтобы отметить данный файл в качестве инициализирующего память, необходимо использовать одну из двух системных функций: `$readmemh` и `$readmemb`. `$readmemh` используется для файлов, описывающих содержимое памяти 16-ричными цифрами, в то время как `$readmemb` используется для файлов, описывающих содержимое памяти двоичными цифрами. Любую из этих системных функций необходимо поместить внутрь блока `initial`. У этих системных функций есть два обязательных аргумента:
|
||||
|
||||
- имя инициализирующего файла
|
||||
- имя инициализируемой памяти
|
||||
|
||||
и два опциональных:
|
||||
|
||||
- стартовый адрес, начиная с которого память будет проинициализирована данным файлом (по-умолчанию равен нулю)
|
||||
- конечный адрес, на котором инициализация закончится (даже если в файле были ещё какие-то данные).
|
||||
|
||||
Пример полного вызова одной из этих системных функций выглядит так:
|
||||
|
||||
`$readmemb("<data file name>",<memory name>,<start address>,<end address>);`
|
||||
|
||||
Однако на деле обычно используются только обязательные аргументы:
|
||||
|
||||
`$readmemb("<data file name>",<memory name>);`
|
||||
|
||||
Пример описанной выше памяти:
|
||||
|
||||
```SystemVerilog
|
||||
module rom16_8 (
|
||||
input logic [3:0] addr1, // первый 4-битный адресный вход
|
||||
input logic [3:0] addr2, // второй 4-битный адресный вход
|
||||
output logic [7:0] read_data1, // первый 8-битный выход считанных данных
|
||||
output logic [7:0] read_data2 // второй 8-битный выход считанных данных
|
||||
);
|
||||
|
||||
logic [7:0] ROM [0:15]; // создать память с 16-ю 8-битными ячейками
|
||||
|
||||
initial begin
|
||||
$readmemh("mem.txt", ROM); // поместить в память RAM содержимое
|
||||
end // файла mem.txt
|
||||
|
||||
|
||||
assign read_data1 = R0M[addr1]; // реализация первого порта на чтение
|
||||
assign read_data2 = R0M[addr2] // реализация второго порта на чтение
|
||||
|
||||
endmodule
|
||||
```
|
||||
|
||||
Содержимое файла `mem.txt`, к примеру может быть таким (каждая строка соответствует значению отдельной ячейки памяти, начиная со стартового адреса):
|
||||
|
||||
```hex
|
||||
FA
|
||||
E6
|
||||
0D
|
||||
15
|
||||
A7
|
||||
```
|
||||
|
||||
## Задание по реализации памяти
|
||||
|
||||
Необходимо описать на языке SystemVerilog следующие три вида памяти:
|
||||
|
||||
### 1. Память инструкций
|
||||
|
||||
У данной памяти будет два порта:
|
||||
|
||||
- 32-битный вход адреса
|
||||
- 32-битный выход данных (асинхронное чтение)
|
||||
|
||||
```SystemVerilog
|
||||
mоdulе instr_mеm(
|
||||
inрut logic [31:0] addr_i,
|
||||
оutрut logic [31:0] rеаd_dаtа_o
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
Из [теории про память](#теория-про-память) вы могли догадаться, что такой модуль описывает память ёмкостью `4 GiB`. Однако в реальности, наша память будет куда меньше (в ПЛИС попросту не хватит ресурсов на реализацию памяти подобного объёма).
|
||||
|
||||
На практике, внутри данного модуля вы должны будете реализовать память с 1024-мя 8-битными ячейками.
|
||||
|
||||
Как так вышло, что разрядность ячеек отличается от разрядности выходного сигнала?
|
||||
Дело в том, что по спецификации процессор RISC-V должен работать с памятью с побайтовой адресацией. Байтовая адресация означает, что процессор способен обращаться к отдельным байтам в памяти (за каждым байтом памяти закреплен свой индивидуальный адрес).
|
||||
|
||||
Именно поэтому, ячейки памяти должны быть восьмибитными.
|
||||
|
||||
Однако в то же время, инструкции процессора будут 32-битными (состоящими из 4 подряд идущих байт), поэтому выходной сигнал данной памяти будет 32-разрядным.
|
||||
Как сделать так, чтобы подавать на выход 4 подряд идущих байта? С помощью [конкатенации](../../Basic%20Verilog%20structures/Concatenation.md).
|
||||
|
||||
На вход реализуемого вами модуля приходит адрес младшего байта инструкции, которую вы должны вернуть (этот адрес всегда будет кратен четырем, поскольку в наших лабах инструкции всегда будут состоять из 4 байт).
|
||||
|
||||
На выход необходимо подать 32-разрядную склейку:
|
||||
|
||||

|
||||
|
||||
*Рисунок 3. Пример склейки для чтения*
|
||||
|
||||
Обращение в эту память по адресам, превышающим `1020` должны выдавать значение `32'd0`. Почему именно `1020`? Если обратиться по адресу `1021` (чего произойти не может, т.к. адрес всегда будет кратен четырем, но предположим что такое обращение все-таки произошло), то вы должны будете вернуть данные из ячеек памяти по адресам: `1021`, `1022`, `1023` и `1024`. При этом ячейки по адресу 1024 уже не будет (ведь 1024 ячейки расположатся по адресам `[0:1023]`).
|
||||
|
||||
Как реализовать подобный функционал? Разумеется, с помощью [мультиплексора](../../Basic%20Verilog%20structures/Multiplexors.md).
|
||||
|
||||
В данном случае, вы можете либо воспользоваться конструкцией `if-else` в блоке `always_comb`, либо **тернарным условным оператором** во время реализации **непрерывного присваивания**. В любом из случаев, вам нужно будет реализовать условие того, что пришедшее значение на шине адреса попадает в диапазон допустимых адресов. Если это так, вернуть склейку данных из памяти, начинающихся с этого адреса, в противном случае — вернуть 0.
|
||||
|
||||
### 2. Память данных
|
||||
|
||||
У данной памяти будет 6 портов:
|
||||
|
||||
- вход тактового синхроимпульса
|
||||
- вход запроса на работу с памятью
|
||||
- вход сигнала разрешения записи
|
||||
- 32-битный вход адреса
|
||||
- 32-битный вход данных записи
|
||||
- 32-битный выход данных синхронного чтения
|
||||
|
||||
```SystemVerilog
|
||||
mоdulе data_mеm(
|
||||
inрut logic clk_i,
|
||||
input logic mem_req_i,
|
||||
inрut logic write_enable_i,
|
||||
inрut logic [31:0] addr_i,
|
||||
inрut logic [31:0] write_data_i,
|
||||
оutрut logic [31:0] rеаd_dаtа_o
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
Идея этой памяти повторяет идею памяти инструкций: несмотря на разрядность адреса и данных, внутри будет память, состоящая из 4096-и 8-битных ячеек.
|
||||
|
||||
Однако в отличие от памяти инструкций, в память данных добавлено два управляющих сигнала (`mem_req_i`и `write_enable_i`). Сигнал `mem_req_i` является сигналом запроса на работу с памятью. Без этого сигнала память не должна выполнять операции чтения/записи (вне зависимости от сигнала `write_enable`, определяющего происходит сейчас запись или чтение). Как сделать так, чтобы не происходило чтение без запроса? Например возвращать на шину чтения специальное "магическое число".
|
||||
|
||||
- В случае `mem_req_i == 0` или `write_enable_i == 1` (т.е. когда не выполняется операция), на выходе `read_data_o` должно оказаться значение `32'hfa11_1eaf` (поскольку `1` схожа с латинским символом `l`, это выражение можно прочесть как `fall_leaf`).
|
||||
- В случае, если `mem_req_i == 1` и значение `addr_i` **попадает** в диапазон [0:4095], на выходе `read_data_o` должна оказаться склейка из 4 значений ячеек памяти, начиная ячейки, размещенной по пришедшему адресу (обратите внимание, что в отличие от памяти инструкций, мы **не уменьшаем** диапазон допустимых значений до `[0:4092]`, поскольку архитектура RISC-V допускает обращение в одну отдельную ячейку памяти).
|
||||
- В случае, если `mem_req_i == 1` и значение `addr_i` **не попадает** в диапазон [0:4095], на выходе `read_data_o` должно оказаться магическое число `32'hdead_beef`.
|
||||
|
||||
Зачем нужны эти магические числа `32'hfa11_1eaf` и `32'hdead_beef`? У этих чисел легко узнаваемая сигнатура, позволяющая обратить на них внимание. В случае, если при чтении из памяти в регистровый файл попадут эти значения, увидев их вы сможете почувствовать что "что-то не то", и проверить: а было ли в памяти по указанному адресу действительно такое значение (в отличие от значения `32'h0000_0000`, которое не вызовет у вас никаких вопросов). Вероятность того, что такие числа возникнут в естественном ходе работы программы достаточно мала, а значит скорее всего если вы встретите эти числа — это сигнал того, что что-то в вашем процессоре работает неправильно (например, произошло обращение за пределы памяти, или неправильно формируется сигнал `mem_req_i`).
|
||||
|
||||
Если `mem_req_i == 1` и `write_enable_i == 1` происходит запрос на запись в память. В этом случае, необходимо по положительному фронту `clk_i` записать в четыре подряд идущие ячейки, начиная с той, что расположена по адресу `addr_i` соответствующие байты с шины `write_data_i`. Во всех других случаях (любой из сигналов `mem_req_i`, `write_enable_i` равен нулю), запись в память не производится.
|
||||
|
||||

|
||||
|
||||
*Рисунок 4. Пример склейки для записи*
|
||||
|
||||
### 3. Регистровый файл
|
||||
|
||||
У данного модуля будет 8 портов:
|
||||
|
||||
- вход тактового синхроимпульса
|
||||
- вход сигнала разрешения записи
|
||||
- 5-битный вход первого адреса чтения
|
||||
- 5-битный вход второго адреса чтения
|
||||
- 5-битный вход адреса записи
|
||||
- 32-битный вход данных записи
|
||||
- 32-битный выход данных асинхронного чтения по первому адресу
|
||||
- 32-битный выход данных асинхронного чтения по второму адресу
|
||||
|
||||
```SystemVerilog
|
||||
mоdulе rf_r𝚒sсv(
|
||||
inрut logic сlk_i,
|
||||
inрut logic write_enable_i,
|
||||
|
||||
inрut logic [ 4:0] write_addr_i,
|
||||
inрut logic [ 4:0] read_addr1_i,
|
||||
inрut logic [ 4:0] read_addr2_i,
|
||||
|
||||
inрut logic [31:0] write_data_i,
|
||||
оutрut logic [31:0] read_data1_o,
|
||||
оutрut logic [31:0] read_data2_o
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
На языке SystemVerilog необходимо реализовать модуль регистрового файла (`rf_r𝚒sсv`) для процессора с архитектурой RISC-V, представляющего собой трехпортовую ОЗУ с двумя портами на чтение и одним портом на запись и состоящей из 32-х 32-битных регистров с именем `rf_mem`.
|
||||
|
||||
При этом по адресу `0` должно всегда считываться значение `0` вне зависимости от того, какое значение в этой ячейке памяти, и есть ли она вообще. Такая особенность обусловлена тем, что при выполнении операций очень часто используется ноль (сравнение с нулем, инициализация переменных нулевым значением, копирование значения одного регистра в другой посредством сложения с нулем и записи результата и т.п.).
|
||||
|
||||
Как и в случае реализации [памяти инструкций](#1-память-инструкций), вы можете решить эту задачу с помощью мультиплексора, управляющим сигналом которого является сигнал сравнения адреса на чтение с нулем.
|
||||
|
||||
Либо же можно проинициализировать нулевую ячейку памяти нулем с запретом записи в неё каких-либо значений. В этом случае в ячейке всегда будет ноль, а значит и считываться с нулевого адреса будет только он.
|
||||
|
||||
### 4. Проверка в ПЛИС
|
||||
|
||||
Последним этапом будет проверка работоспособности вашего регистрового файла в ПЛИС (файлы для прошивки как обычно расположены в папке [board files](board%20files)).
|
||||
|
||||
Разработанные блоки будут использованы при реализации процессора `CYBERcobra 3000 Pro 2.0` и последующих лабораторных для реализации системы с процессором с архитектурой RISC-V.
|
||||
|
||||
## Порядок выполнения работы
|
||||
|
||||
1. Внимательно ознакомьтесь с заданием. В случае возникновения вопросов, проконсультируйтесь с преподавателем.
|
||||
2. Реализуйте память инструкций. Для этого:
|
||||
1. В `Design Sources` проекта с предыдущих лаб, создайте `SystemVerilog`-файл `instr_mem.sv`.
|
||||
2. Опишите в нем модуль памяти инструкций с таким же именем и портами, как указано в задании.
|
||||
1. Сперва необходимо создать память (массив регистров). Как это сделать, сказано в разделе [описание памяти на языке SystemVerilog](#описание-памяти-на-языке-systemverilog). Разрядность ячеек памяти должна быть 8 бит, количество ячеек — 1024.
|
||||
2. Добавить в проект [`файл с содержимым памяти инструкций`](program.txt)([`как добавить файл, инициализирующий память`](../../Vivado%20Basics/How%20to%20add%20a%20mem-file.md)). Данный файл будет использоваться при вызове системной функции `$readmemh` в описании памяти инструкций.
|
||||
3. К созданной памяти необходимо подключить выход модуля `read_data_o`. При подключении должен быть использован вход модуля `addr_i` и оператор [конкатенации](../../Basic%20Verilog%20structures/Concatenation.md).
|
||||
4. При подключении выхода `read_data_o` помните, что чтение по адресам, превышающим `1020` должно возвращать `0`.
|
||||
3. После описания памяти инструкций, её необходимо проверить с помощью тестового окружения.
|
||||
1. Тестовое окружение находится [`здесь`](tb_instr_mem.sv).
|
||||
2. Для запуска симуляции воспользуйтесь [`этой инструкцией`](../../Vivado%20Basics/Run%20Simulation.md).
|
||||
3. Перед запуском симуляции убедитесь, что в качестве top-level модуля выбран корректный (`tb_instr_mem`).
|
||||
4. **Во время симуляции, вы должны прожать "Run All" и убедиться, что в логе есть сообщение о завершении теста!**
|
||||
3. Реализуйте память данных. Для этого:
|
||||
1. В `Design Sources` проекта создайте `SystemVerilog`-файл `data_mem.sv`.
|
||||
2. Опишите в нем модуль памяти данных с таким же именем и портами, как указано в задании.
|
||||
1. Описание модуля будет схожим с описанием модуля памяти инструкций, однако порт чтения в этот раз будет **синхронным** (запись в него будет происходить в блоке `always_ff`). Кроме того необходимо будет описать логику записи данных в память.
|
||||
2. Запись в ячейки памяти описывается подобно записи данных в [регистры](../../Basic%20Verilog%20structures/Registers.md), только при этом, происходит доступ к конкретной ячейке памяти с помощью входа `addr_i` (как осуществляется доступ к ячейкам памяти сказано в разделе [описание памяти на языке SystemVerilog](#описание-памяти-на-языке-systemverilog)).
|
||||
3. Необходимо помнить, что запись будет вестись в 4 ячейки памяти одновременно: ту, на которую указывает адрес и следующие за ней три ячейки.
|
||||
4. Обратите внимание что работа с памятью должна осуществляться только когда сигнал `mem_req_i == 1`, в противном случае запись не должна производиться, а на шину `read_data_o` должно возвращаться магическое число `32'hfall_leaf`.
|
||||
5. Как и в памяти инструкций, при чтении по адресам вне допустимого диапазона (только в этот раз старше адреса `1023`), на шине `read_data_o` должно выставляться значение `32'hdead_beaf`.
|
||||
3. После описания памяти данных, её необходимо проверить с помощью тестового окружения.
|
||||
1. Тестовое окружение находится [`здесь`](tb_data_mem.sv).
|
||||
2. Для запуска симуляции воспользуйтесь [`этой инструкцией`](../../Vivado%20Basics/Run%20Simulation.md).
|
||||
3. Перед запуском симуляции убедитесь, что в качестве top-level модуля выбран корректный (`tb_data_mem`).
|
||||
4. **Во время симуляции, вы должны прожать "Run All" и убедиться, что в логе есть сообщение о завершении теста!**
|
||||
4. Реализуйте регистровый файл. Для этого:
|
||||
1. В `Design Sources` проекта создайте `SystemVerilog`-файл `rf_riscv.sv`.
|
||||
2. Опишите в нем модуль регистрового файла с таким же именем и портами, как указано в задании.
|
||||
1. В отличии от памяти инструкций и данных, ячейки памяти регистрового файла должны быть 32-битными (а на 8-битными). Это означает, что реализация портов чтения и записи будет проще.
|
||||
2. Не забывайте, что у вас 2 порта на чтение и 1 порт на запись, при этом каждый порт не зависит от остальных (в модуле 3 независимых входа адреса).
|
||||
3. Чтение из нулевого регистра (чтение по адресу 0) всегда должно возвращать нулевое значение. Этого можно добиться двумя путями:
|
||||
1. Путем добавления мультиплексора перед выходным сигналом чтения (мультиплексор будет определять, пойдут ли на выход данные из ячейки регистрового файла, либо в случае если адрес равен нулю, на выход пойдет константа ноль).
|
||||
2. Путем инициализации нулевого регистра нулевым значением и запретом записи в этот регистр (при записи и проверки write_enable добавить дополнительную проверку на адрес).
|
||||
4. Каким образом будет реализована эта особенность регистрового файла не важно, выберите сами.
|
||||
3. После описания регистрового файла, его необходимо проверить с помощью [`тестового окружения`](../../Basic%20Verilog%20structures/Testbench.md).
|
||||
1. Тестовое окружение находится [`здесь`](tb_rf_riscv.sv).
|
||||
2. Для запуска симуляции воспользуйтесь [`этой инструкцией`](../../Vivado%20Basics/Run%20Simulation.md).
|
||||
3. Перед запуском симуляции убедитесь, что в качестве top-level модуля выбран корректный (`tb_rf_riscv`).
|
||||
4. **Во время симуляции, вы должны прожать "Run All" и убедиться, что в логе есть сообщение о завершении теста!**
|
||||
5. Добавьте в проект модуль верхнего уровня ([nexys_rf_riscv.sv](board%20files/nexys_rf_riscv.v)), соединяющий регистровый файл с периферией в ПЛИС. Описание модуля находится [здесь](board%20files)
|
||||
6. Подключите к проекту файл ограничений ([nexys_a7_100t.xdc](board%20files/nexys_a7_100t.xdc)), если тот еще не был подключен, либо замените его содержимое данными из файла к этой лабораторной работе.
|
||||
7. Проверьте работу регистрового файла в ПЛИС.
|
39
Labs/03. Register file and memory/board files/README.md
Normal file
39
Labs/03. Register file and memory/board files/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Проверка работы регистрового файла на ПЛИС
|
||||
|
||||
Если вы не понимаете, что лежит в этой папке, или если надо вспомнить, как прошить ПЛИС, можно воспользоваться [`этой инструкцией`](../../../Vivado%20Basics/Program%20nexys%20a7.md)
|
||||
|
||||
Для работы с регистровым файлом, необходимо выставлять сигналы адресов и данных.
|
||||
У платы недостаточно переключателей, для такого количества входов регистрового файла, поэтому подача сигналов на вход происходит в несколько этапов.
|
||||
Сперва можно ввести один из адресов регистрового файла (`A1`/`A2`/`A3`). Ввод осуществляется через `SW[4:0]`. Для того, чтобы выставить введенный адрес на какой-то из входов адреса, необходимо нажать одну из следующих кнопок:
|
||||
|
||||
1. `BTNL` чтобы выставить значение с `SW[4:0]` на `A1`
|
||||
2. `BTNC` чтобы выставить значение с `SW[4:0]` на `A2`
|
||||
3. `BTNR` чтобы выставить значение с `SW[4:0]` на `A3`
|
||||
|
||||
(расположение кнопок смотри на изображениях ниже).
|
||||
|
||||
После выставления адресов, переключатели можно использовать в качестве ввода первых шестнадцати бит данных. Для записи введенных данных по адресу `A3` используется кнопка `BTND`, для чтения данных по адресам `A1`, `A2` (с выводом результатов чтения на семисегментные индикаторы) используется кнопка `BTNU`.
|
||||
|
||||
Управление регистровым файлом.
|
||||
|
||||

|
||||
|
||||
Ниже показан пример последовательности действий для работы с платой.
|
||||
|
||||
Установка значения `5'b10101` на входы `A1`,`A2`,`A3`.
|
||||
|
||||

|
||||
|
||||
Запись информации по адресу А3.
|
||||
|
||||

|
||||
|
||||
Чтение по адресам А1 и А2.
|
||||
|
||||

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

|
||||
|
||||
Попробуйте записать информацию в нулевой регистр, затем по другим адресам, после чего считайте записанную информацию и убедитесь, что она соответствует той, которую вы записывали (с учетом особенностей регистрового файла RISC-V).
|
211
Labs/03. Register file and memory/board files/nexys_a7_100t.xdc
Normal file
211
Labs/03. Register file and memory/board files/nexys_a7_100t.xdc
Normal file
@@ -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
|
141
Labs/03. Register file and memory/board files/nexys_rf_riscv.sv
Normal file
141
Labs/03. Register file and memory/board files/nexys_rf_riscv.sv
Normal file
@@ -0,0 +1,141 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module nexys_rf_riscv(
|
||||
input CLK100,
|
||||
input resetn,
|
||||
input BTND, BTNU, BTNL, BTNR, BTNC,
|
||||
input [15:0] SW,
|
||||
output [15:0] LED,
|
||||
output CA, CB, CC, CD, CE, CF, CG, DP,
|
||||
output [7:0] AN,
|
||||
output LED16_B, LED16_G, LED16_R, LED17_B, LED17_G, LED17_R
|
||||
);
|
||||
|
||||
wire [31:0] WD3;
|
||||
wire WE;
|
||||
wire [31:0] RD1;
|
||||
wire [31:0] RD2;
|
||||
|
||||
localparam pwm = 1000;
|
||||
reg [9:0] counter;
|
||||
reg [3:0] semseg;
|
||||
reg [7:0] ANreg;
|
||||
reg CAr, CBr, CCr, CDr, CEr, CFr, CGr, DPr;
|
||||
reg [15:0] LEDr;
|
||||
|
||||
reg [4:0] a1;
|
||||
reg [4:0] a2;
|
||||
reg [4:0] a3;
|
||||
reg [31:0] rd1;
|
||||
reg [31:0] rd2;
|
||||
|
||||
rf_riscv DUT
|
||||
(
|
||||
.clk (CLK100),
|
||||
.A1 (a1),
|
||||
.A2 (a2),
|
||||
.A3 (a3),
|
||||
.WD3 (WD3),
|
||||
.WE (WE),
|
||||
|
||||
.RD1 (RD1),
|
||||
.RD2 (RD2)
|
||||
);
|
||||
|
||||
assign LED = {1'b0, a1, a2, a3};
|
||||
assign AN[7:0] = ANreg[7:0];
|
||||
assign {CA, CB, CC, CD, CE, CF, CG, DP} = {CAr, CBr, CCr, CDr, CEr, CFr, CGr, DPr};
|
||||
assign LED16_G = BTNC | BTNR;
|
||||
assign LED17_G = BTNL | BTNR;
|
||||
assign {LED16_R, LED17_R} = {2{BTND}};
|
||||
assign {LED16_B, LED17_B} = {2{BTNU}};
|
||||
|
||||
assign WD3 = 32'b0 | SW[15:0];
|
||||
assign WE = BTND;
|
||||
|
||||
|
||||
always @(posedge CLK100) begin
|
||||
if (!resetn) begin
|
||||
counter <= 'b0;
|
||||
ANreg[7:0] <= 8'b11111111;
|
||||
{CAr, CBr, CCr, CDr, CEr, CFr, CGr, DPr} <= 8'b11111111;
|
||||
{a1, a2, a3} <= 'b0;
|
||||
{rd1, rd2} <= 'b0;
|
||||
end
|
||||
else begin
|
||||
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
|
||||
a1 <= BTNL? SW[4:0]: a1;
|
||||
a2 <= BTNC? SW[4:0]: a2;
|
||||
a3 <= BTNR? SW[4:0]: a3;
|
||||
|
||||
rd1 <= BTNU? RD1: rd1;
|
||||
rd2 <= BTNU? RD2: rd2;
|
||||
|
||||
case (1'b0)
|
||||
ANreg[0]: begin
|
||||
semseg <= (rd2) % 5'h10;
|
||||
//DPr <= 1'b1;
|
||||
end
|
||||
ANreg[1]: begin
|
||||
semseg <= (rd2 / 'h10) % 5'h10;
|
||||
//DPr <= 1'b1;
|
||||
end
|
||||
ANreg[2]: begin
|
||||
semseg <= (rd2 / 'h100) % 5'h10;
|
||||
//DPr <= 1'b1;
|
||||
end
|
||||
ANreg[3]: begin
|
||||
semseg <= (rd2 / 'h1000) % 5'h10;
|
||||
//DPr <= 1'b1;
|
||||
end
|
||||
ANreg[4]: begin
|
||||
semseg <= (rd1) % 5'h10;
|
||||
//DPr <= 1'b1;
|
||||
end
|
||||
ANreg[5]: begin
|
||||
semseg <= (rd1 / 'h10) % 5'h10;
|
||||
//DPr <= 1'b1;
|
||||
end
|
||||
ANreg[6]: begin
|
||||
semseg <= (rd1 / 'h100) % 5'h10;
|
||||
//DPr <= 1'b1;
|
||||
end
|
||||
ANreg[7]: begin
|
||||
semseg <= (rd1 / 'h1000) % 5'h10;
|
||||
//DPr <= 1'b1;
|
||||
end
|
||||
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
|
1024
Labs/03. Register file and memory/program.txt
Normal file
1024
Labs/03. Register file and memory/program.txt
Normal file
File diff suppressed because it is too large
Load Diff
130
Labs/03. Register file and memory/tb_data_mem.sv
Normal file
130
Labs/03. Register file and memory/tb_data_mem.sv
Normal file
@@ -0,0 +1,130 @@
|
||||
`timescale 1ns / 1ps
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Company: MIET
|
||||
// Engineer: Nikita Bulavin
|
||||
//
|
||||
// Create Date:
|
||||
// Design Name:
|
||||
// Module Name: tb_data_mem
|
||||
// Project Name: RISCV_practicum
|
||||
// Target Devices: Nexys A7-100T
|
||||
// Tool Versions:
|
||||
// Description: tb for data memory
|
||||
//
|
||||
// Dependencies:
|
||||
//
|
||||
// Revision:
|
||||
// Revision 0.01 - File Created
|
||||
// Additional Comments:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
module tb_data_mem();
|
||||
|
||||
parameter ADDR_SIZE = 4096;
|
||||
parameter TIME_OPERATION = 50;
|
||||
|
||||
logic CLK;
|
||||
logic REQ;
|
||||
logic WE;
|
||||
logic [31:0] A;
|
||||
logic [31:0] WD;
|
||||
logic [31:0] RD;
|
||||
|
||||
data_mem DUT (
|
||||
.clk_i (CLK),
|
||||
.mem_req_i (REQ),
|
||||
.write_enable_i (WE ),
|
||||
.addr_i (A ),
|
||||
.write_data_i (WD),
|
||||
.read_data_o (RD)
|
||||
);
|
||||
|
||||
logic [31:0] RDa;
|
||||
|
||||
integer i, err_count = 0;
|
||||
|
||||
assign A = i;
|
||||
|
||||
parameter CLK_FREQ_MHz = 100;
|
||||
parameter CLK_SEMI_PERIOD= 1e3/CLK_FREQ_MHz/2;
|
||||
|
||||
initial CLK <= 0;
|
||||
always #CLK_SEMI_PERIOD CLK = ~CLK;
|
||||
|
||||
initial begin
|
||||
$timeformat(-9, 2, " ns", 3);
|
||||
$display( "\nStart test: \n\n==========================\nCLICK THE BUTTON 'Run All'\n==========================\n"); $stop();
|
||||
REQ = 1;
|
||||
WE = 0;
|
||||
i = 1; #10;
|
||||
if (RD !== 32'hx) begin
|
||||
$display("The data memory should not be initialized by the $readmemh function");
|
||||
err_count = err_count + 1;
|
||||
end
|
||||
for (i = 0; i < ADDR_SIZE; i = i + 4) begin
|
||||
@(posedge CLK);
|
||||
WE = 1;
|
||||
WD = $urandom;
|
||||
end
|
||||
for (i = 0; i < (ADDR_SIZE+1); i = i + 1) begin
|
||||
if (i != (ADDR_SIZE+1)) begin
|
||||
REQ = |($urandom %10);
|
||||
WE = 0;
|
||||
#TIME_OPERATION;
|
||||
RDa = RD;
|
||||
WD = $urandom;
|
||||
#TIME_OPERATION;
|
||||
WE = $urandom % 2;
|
||||
#TIME_OPERATION;
|
||||
if ((WE && REQ || !REQ) && RD !== 32'd4195425967) begin
|
||||
$display("When writing (write_enable_i = %h) read_data_o should be equal to fa11_1eaf, your data: %h_%h, time: %t", WE, RD[31:16],RD[15:0], $time);
|
||||
err_count = err_count + 1;
|
||||
end
|
||||
if ((!WE && REQ) && RD !== RDa) begin
|
||||
$display("When reading (write_enable_i = %h), the data %h is overwritten with data %h at address %h, time: %t", WE, RDa, RD, A, $time);
|
||||
err_count = err_count + 1;
|
||||
end
|
||||
end
|
||||
else begin
|
||||
REQ = 1;
|
||||
#TIME_OPERATION;
|
||||
if (RD !== 32'd3735928559) begin
|
||||
$display("When reading (write_enable_i = %h) at an address greater than 4095, it should return dead_beef yor data: %h_%h, time: %t", WE, RD[31:16],RD[15:0], $time);
|
||||
err_count = err_count + 1;
|
||||
end
|
||||
end
|
||||
#TIME_OPERATION;
|
||||
end
|
||||
#TIME_OPERATION;
|
||||
REQ = 1;
|
||||
WE = 1;
|
||||
#TIME_OPERATION;
|
||||
for (i = 0; i < 8; i = i + 4) begin
|
||||
WD = i? 32'hfecd_ba98: 32'h7654_3210;
|
||||
#TIME_OPERATION;
|
||||
end
|
||||
WE = 0;
|
||||
i = 2;
|
||||
#TIME_OPERATION;
|
||||
if (RD !== 32'hba98_7654) begin
|
||||
$display("data is being written to the cell incorrectly. RAM [0:7] must be 0x0123456789abcdef, time: %t", $time);
|
||||
err_count = err_count + 1;
|
||||
end
|
||||
@(posedge CLK)
|
||||
i = 0;
|
||||
@(negedge CLK);
|
||||
if (RD !== 32'hba98_7654) begin
|
||||
$display("reading from data memory must be synchronous, time: %t", $time);
|
||||
err_count = err_count + 1;
|
||||
end
|
||||
@(posedge CLK); #5;
|
||||
if (RD !== 32'h7654_3210) begin
|
||||
$display("synchronous data memory read error, time: %t", $time);
|
||||
err_count = err_count + 1;
|
||||
end
|
||||
$display("Number of errors: %d", err_count);
|
||||
if( !err_count ) $display("\ndata_mem SUCCESS!!!\n");
|
||||
$finish();
|
||||
end
|
||||
endmodule
|
8355
Labs/03. Register file and memory/tb_instr_mem.sv
Normal file
8355
Labs/03. Register file and memory/tb_instr_mem.sv
Normal file
File diff suppressed because it is too large
Load Diff
1212
Labs/03. Register file and memory/tb_rf_riscv.sv
Normal file
1212
Labs/03. Register file and memory/tb_rf_riscv.sv
Normal file
File diff suppressed because it is too large
Load Diff
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
|
267
Labs/05. Main decoder/README.md
Normal file
267
Labs/05. Main decoder/README.md
Normal file
@@ -0,0 +1,267 @@
|
||||
# Лабораторная работа 5 "Основной дешифратор команд"
|
||||
|
||||
Устройство управления – один из базовых блоков процессора, функцией которого является декодирование инструкций и выдача управляющих сигналов для всех блоков процессора.
|
||||
|
||||
## Цель
|
||||
|
||||
Описать на языке **SystemVerilog** блок основного дешифратора команд (модуль **Main Decoder**) для однотактного процессора с архитектурой **RISC-V**.
|
||||
|
||||
## Допуск к лабораторной работе
|
||||
|
||||
- Изучить форматы кодирования инструкций базового набора команд [`RV32I`](../../Other/rv32i.md)
|
||||
|
||||
## Ход работы
|
||||
|
||||
1. Изучить особенности архитектуры **RISC-V** ([#теория](#архитектура-risc-v-и-предлагаемая-микроархитектура))
|
||||
2. Изучить конструкции **SystemVerilog**, с помощью которых будет описан дешифратор ([#инструменты](#инструменты))
|
||||
3. Реализовать на языке **SystemVerilog** модуль основного дешифратора команд – **Main Decoder** ([#задание](#задание))
|
||||
4. Верифицировать разработанное устройство с помощью предлагаемого **testbench** (в том же [#задании](#задание))
|
||||
|
||||
## Архитектура RISC-V и предлагаемая микроархитектура
|
||||
|
||||
### Набор инструкций **RISC-V** и способы их кодирования
|
||||
|
||||
Все инструкции архитектуры **RISC-V** можно условно разделить на три категории:
|
||||
|
||||
- Вычислительные инструкции (операции выполняются на АЛУ)
|
||||
- Использующие в качестве операндов два регистра
|
||||
- Использующие в качестве операндов регистр и непосредственный операнд из инструкции (константу)
|
||||
- Инструкции для доступа к памяти
|
||||
- Загрузки из основной памяти в регистровый файл
|
||||
- Сохранения данных из регистрового файла в основную память
|
||||
- Инструкции управления программой (управляют тем, как изменится счетчик команд `PC`)
|
||||
- Условные переходы
|
||||
- Безусловные переходы
|
||||
|
||||
На рисунке ниже приводится фрагмент из [`оригинальной спецификации RISC-V`](https://github.com/riscv/riscv-isa-manual/releases/download/Ratified-IMAFDQC/riscv-spec-20191213.pdf). В верхней его части приводится 6 форматов кодирования инструкций: **R**, **I**, **S**, **B**, **U** и **J**, затем идут конкретные значения полей внутри инструкции. Под `rd` подразумевается 5-битный адрес регистра назначения (**r**egister **d**estination), `rs1` и `rs2` —5-битные адреса регистров источников (**r**egister **s**ource), `imm` — константа (immediate), расположение и порядок битов которой указывается в квадратных скобках. Обратите внимание, что в разных форматах кодирования константы имеют различную разрядность, а их биты упакованы по-разному. Для знаковых операций константу предварительно знаково расширяют до 32 бит. Для беззнаковых расширяют нулями до 32 бит.
|
||||
|
||||

|
||||
|
||||
*Таблица 1. Базовый набор инструкций из [спецификации RISC-V](https://github.com/riscv/riscv-isa-manual/releases/download/Ratified-IMAFDQC/riscv-spec-20191213.pdf) (стр. 130)*
|
||||
|
||||
| Кодирование | Описание |
|
||||
|-------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| R-тип | Арифметические и логические операции над двумя регистрами с записью результата в третий (регистр назначения может совпадать с одним из регистров-источников) |
|
||||
| I-тип | Инструкции с 12-битным непосредственным операндом |
|
||||
| S-тип | Инструкции записи в память (типа store) |
|
||||
| B-тип | Инструкции ветвления |
|
||||
| U-тип | Инструкции с 20-битным «длинным» непосредственным операндом, сдвинутым влево на 12 |
|
||||
| J-тип | Единственная инструкция jal, осуществляющая безусловный переход по адресу относительно текущего счетчика команд |
|
||||
|
||||
*Таблица 2. Описание типов форматов кодирования инструкций ISA RISC-V*
|
||||
|
||||
### Неподдерживаемые инструкции
|
||||
|
||||
В базовом наборе инструкций **RISC-V** к операциям `SYSTEM` относятся `ECALL` и `EBREAK`, к операциям `MISC-MEM` – операция `FENCE`. В реализуемом процессорном ядре эти инструкции не должны приводить ни к каким изменениям. Иначе говоря, они должны быть реализованы как инструкция `NOP` (**no operation**).
|
||||
Инструкция `FENCE` в **RISC-V** необходима при работе с несколькими аппаратными потоками, или хартами (hart – «hardware thread»). В **RISC-V** используется расслабленная модель памяти (**relaxed memory model**): потоки «видят» все инструкции чтения и записи, которые исполняются другими потоками, однако видимый порядок этих инструкций может отличаться от реального. Инструкция `FENCE`, использованная между двумя инструкциями чтения и/или записи гарантирует, что остальные потоки увидят первую инструкцию перед второй. Реализация `FENCE` является опциональной в **RISC-V** и в данном случае в ней нет необходимости, так как в системе не предполагается наличия нескольких аппаратных потоков.
|
||||
Инструкции `ECALL` и `EBREAK` должны вызывать исключение с последующим переходом в обработчик исключения (вызова операционной системы и перехода в привилегированный режим работы). Помимо этого, их вызов должен обновить содержимое некоторых управляющих регистров (**Control & Status Registers** – **CSR**). В рамках данного курса лабораторных работ эти инструкции не будут использоваться, а потому могут быть реализованы как `NOP`.
|
||||
|
||||
Список инструкций с указанием их типов и функциональным назначением:
|
||||
|
||||

|
||||
|
||||
*Таблица 3. Инструкции набора RV32I с приведением их типов, функционального описания и примеров использования.*
|
||||
|
||||
Обратите внимание на операции `slli`, `srli` и `srai` (операции сдвига на константную величину). У этих инструкций немного измененный формат кодирования **I\***. Формат кодирования **I** предоставляет 12-битную константу. Сдвиг 32-битного числа более, чем на 31 не имеет смысла. Для кодирования числа 31 требуется всего 5 бит. Выходит, что из 12 бит константы используется только 5 бит для операции сдвига, а оставшиеся 7 бит – не используются. А, главное (какое совпадение!), эти 7 бит находятся ровно в том же месте, где у других инструкций находится поле `func7`. Поэтому, чтобы у инструкций `slli`, `srli` и `srai` использующих формат **I** не пропадала эта часть поля, к ней относятся как к полю `func7`.
|
||||
|
||||
### Предлагаемая микроархитектура процессора RISC-V
|
||||
|
||||
Ниже приводится микроархитектура процессора RISC-V. Регистр `PC` (Program Counter – счетчик команд) подключен к адресному входу памяти инструкций. Считываемая инструкция декодируется основным дешифратором, после чего он выставляет управляющие сигналы для всех блоков процессора (мультиплексоры, АЛУ, интерфейс взаимодействия с памятью).
|
||||
|
||||
Приведенная архитектура не является заданием для текущей лабораторной работы, лишь отражает то, как в дальнейшем будет подключаться и использоваться реализуемый в данной лабораторной основной дешифратор.
|
||||
|
||||

|
||||
|
||||
Предложенная микроархитектура процессора `CYBERcobra 3000 Pro 2.0` из прошлой лабораторной имеет схожую структуру, с некоторыми изменениями.
|
||||
|
||||
В первую очередь изменились входы и выходы процессора:
|
||||
|
||||
- память инструкций вынесена наружу процессора, таким образом, у процессора появляются входы и выходы: `instr_addr_o` и `instr_i`;
|
||||
- помимо прочего, у модуля появились сигналы интерфейса памяти данных:
|
||||
- `data_addr_o` — адрес внешней памяти;
|
||||
- `mem_req_o` — запрос на обращение во внешнюю память;
|
||||
- `mem_size_o` — размер данных при обращении в память;
|
||||
- `mem_we_o` — сигнал разрешения записи во внешнюю память;
|
||||
- `mem_write_data_o` — данные для записи во внешнюю память;
|
||||
- `mem_read_data_i` — считанные из внешней памяти данные;
|
||||
- так же, у процессора появился вход `stall_i`, отключающий программный счетчик.
|
||||
|
||||
Так же добавились источники операндов АЛУ: программный счетчик, множество констант из инструкций и микроархитектурных констант. Выбор нужных операндов для АЛУ осуществляется с помощью двух мультиплексоров, управляемых сигналами декодера `src_a_op_sel_o` и `src_b_op_sel_o` (для краткости на схеме обозначены как `src_a_sel` и `src_b_sel`).
|
||||
|
||||
Изменилось и число источников записи в регистровый файл: их осталось два: результат операции на АЛУ и данные, считанные с внешней памяти. Выборка осуществляется сигналом декодера `wb_sel_o`.
|
||||
|
||||
### Интерфейс памяти
|
||||
|
||||
Интерфейс памяти использует несколько сигналов для взаимодействия с памятью: `mem_req_o` (этот выход должен быть выставлен в 1 каждый раз, когда необходимо обратиться к памяти – считать или записать), `mem_we_o` (выставляется в 1, если необходимо записать данные в память, и 0 – если считать из памяти) и `mem_size_o` (указывающий размер порции данных необходимых для передачи; возможные значения указаны в таблице ниже). Перечисленных сигналов достаточно для того, чтобы основная память понимала: обращаются ли к ней в данный момент, нужно ли записывать или считывать данные, и о какой порции данных идет речь.
|
||||
|
||||
|Название|Значение `mem_size_o`| Пояснение |
|
||||
|--------|---------------------|------------------------------|
|
||||
|LDST_B | 3'd0 |Знаковое 8-битное значение |
|
||||
|LDST_H | 3'd1 |Знаковое 16-битное значение |
|
||||
|LDST_W | 3'd2 |32-битное значение |
|
||||
|LDST_BU | 3'd4 |Беззнаковое 8-битное значение |
|
||||
|LDST_HU | 3'd5 |Беззнаковое 16-битное значение|
|
||||
|
||||
### Main Decoder — Основной дешифратор команд RISC-V
|
||||
|
||||
Как говорилось ранее, дешифратор инструкций в процессоре служит для преобразования инструкции в набор управляющих сигналов, необходимых для ее исполнения.
|
||||
|
||||
Пример: для выполнения инструкции загрузки слова из памяти данных в регистровый файл `lw`, дешифратор должен:
|
||||
|
||||
- направить в АЛУ два операнда (базовый адрес и смещение) вместе с кодом операции АЛУ (сложения) для вычисления адреса:
|
||||
- `a_sel_o = 2'd0`;
|
||||
- `b_sel_o = 3'd1`;
|
||||
- `alu_op_o= ALU_ADD`;
|
||||
- сформировать управляющие сигналы интерфейса памяти:
|
||||
- `mem_req_o = 1'b1`;
|
||||
- `mem_size_o= 3'd2`;
|
||||
- `mem_we_o = 1'b1`.
|
||||
|
||||
Несмотря на то, что для записи во внешнюю память ключевыми сигналами будут описанные выше, это не означает, что остальные выходные сигналы декодера могут быть абы какими.
|
||||
|
||||
Поскольку операция `lw` не является операцией перехода, сигналы `jal_o`, `jalr_o` и `branch_o` должны быть равны нулю (иначе процессор совершит переход, а инструкция `lw` этого не подразумевает). Точно так же, поскольку во время записи во внешнюю память, в регистровый файл не должно быть ничего записано, сигнал `gpr_we_o` так же должен быть равен нулю.
|
||||
|
||||
А вот сигнал `wb_sel` может принять любое значение (поскольку сигнал разрешения записи в регистровый файл равен нулю, не важно, каким будет источник данных для записи в регистровый файл, т.к. в него все равно ничего не будет записано).
|
||||
|
||||
Управляющие сигналы на выходе декодера зависят от трех полей инструкции: `opcode`, `func3` и `func7`. Обратите внимание, что расположение этих полей одинаково для всех типов инструкций. Это сделано для удобства декодирования. При этом для некоторых инструкций поля `func3` и `func7` могут отсутствовать.
|
||||
|
||||
|Название сигнала| Пояснение |
|
||||
|----------------|------------------------------------------------------------------------------------------------|
|
||||
|fetched_instr_i |Инструкция для декодирования, считанная из памяти инструкций |
|
||||
|a_sel_o |Управляющий сигнал мультиплексора для выбора первого операнда АЛУ |
|
||||
|b_sel_o |Управляющий сигнал мультиплексора для выбора второго операнда АЛУ |
|
||||
|alu_op_o |Операция АЛУ |
|
||||
|mem_req_o |Запрос на доступ к памяти (часть интерфейса памяти) |
|
||||
|mem_we_o |Сигнал разрешения записи в память, «write enable» (при равенстве нулю происходит чтение) |
|
||||
|mem_size_o |Управляющий сигнал для выбора размера слова при чтении-записи в память (часть интерфейса памяти)|
|
||||
|gpr_we_o |Сигнал разрешения записи в регистровый файл |
|
||||
|wb_sel_o |Управляющий сигнал мультиплексора для выбора данных, записываемых в регистровый файл |
|
||||
|illegal_instr_o |Сигнал о некорректной инструкции (на схеме не отмечен) |
|
||||
|branch_o |Сигнал об инструкции условного перехода |
|
||||
|jal_o |Сигнал об инструкции безусловного перехода jal |
|
||||
|jalr_o |Сигнал об инструкции безусловного перехода jalr |
|
||||
|
||||
Единственным входным сигналом этого модуля является `fetched_instr_i`.
|
||||
|
||||
В системе команд **RV32I** два младших бита поля opcode всегда равны `11`, таким образом декодер понимает, что будут исполняться именно 32-битные инструкции, а не 16-битные, например. **Main decoder** должен выдать единицу на выходе `illegal_instr_o` в случае:
|
||||
|
||||
- неравенства двух младших битов opcode значению `11`;
|
||||
- некорректного значения `func3` или `func7` для данной операции;
|
||||
- если значение `opcode` не совпадает ни с одним из известных и следовательно операция не определена.
|
||||
|
||||
При реализации декодера его удобнее описывать разбив все инструкции на однотипные группы, как это сделано ниже. Коды операций в таблице 5-битные потому, что 2 младших бита полноценного 7-битного кода операции должны отдельно проверяться и быть равны `11`
|
||||
|
||||
|Операция|Opcode| Описание операции | Краткая запись |
|
||||
|--------|------|----------------------------------------------------------------------------------------------|------------------------------------|
|
||||
|OP |01100 |Записать в `rd` результат вычисления АЛУ над `rs1` и `rs2` |`rd = alu_op(rs1, rs2)` |
|
||||
|OP_IMM |00100 |Записать в `rd` результат вычисления АЛУ над `rs1` и `imm` |`rd = alu_op(rs1, imm)` |
|
||||
|LUI |01101 |Записать в `rd` значение непосредственного операнда U-типа `imm_u` |`rd = imm << 12` |
|
||||
|LOAD |00000 |Записать в `rd` данные из памяти по адресу `rs1+imm` |`rd = Mem[rs1 + imm]` |
|
||||
|STORE |01000 |Записать в память по адресу `rs1+imm` данные из `rs2` |`Mem[rs1 + imm] = rs2` |
|
||||
|BRANCH |11000 |Увеличить счетчик команд на значение `imm`, если верен результат сравнения `rs1` и `rs2` |`if cmp_op(rs1, rs2) then PC += imm`|
|
||||
|JAL |11011 |Записать в `rd` следующий адрес счетчика команд, увеличить счетчик команд на значение `imm` |`rd = PC + 4; PC += imm` |
|
||||
|JALR |11001 |Записать в `rd` следующий адрес счетчика команд, в счетчик команд записать `rs1+imm` |`rd = PC + 4; PC = rs1+imm` |
|
||||
|AUIPC |00101 |Записать в `rd` результат сложения непосредственного операнда U-типа `imm_u` и счетчика команд|`rd = PC + (imm << 12)` |
|
||||
|MISC-MEM|00011 |Не производить операцию | `-` |
|
||||
|SYSTEM |11100 |Не производить операцию | `-` |
|
||||
|
||||
## Инструменты
|
||||
|
||||
В первую очередь язык описания аппаратуры **SystemVerilog** – это язык. С помощью этого языка человек объясняет либо синтезатору какое он хочет получить устройство, либо симулятору – как он хочет это устройство проверить. Синтезатор – это программа, которая создает из логических элементов цифровое устройство по описанию предоставляемому человеком. Синтезатору внутри **Vivado** нужно объяснить что ты от него хочешь. Например, чтобы спросить дорогу у испанца, придется делать это на испанском языке, иначе он ничем не сможет помочь. Если ты знаешь испанский, то это можно сделать еще и разными способами. В **SystemVerilog** точно также – одно и то же устройство можно описать разным кодом, но результат синтеза будет одним и тем же. Однако, часто два разных кода одинаковые по смыслу могут синтезироваться в разную аппаратуру, хотя функционально они будут идентичны, но могут отличаться, например, скоростью работы. Или одни и те же специальные языковые конструкции могут применяться для синтезирования разных цифровых элементов.
|
||||
|
||||
Основной дешифратор – это комбинационная схема, то есть, для каждой комбинации входных сигналов существует только одна комбинация выходных сигналов, потому что комбинационные схемы не содержат элементов памяти.
|
||||
|
||||
Можно по-разному описывать комбинационные схемы, например, через конструкцию `assign`. Для основного дешифратора отлично подойдет конструкция `case`, которая превратится не в мультиплексор, а в комбинационную схему с оптимальными параметрами критического пути. В доверилоговую эпоху разработчикам пришлось бы строить гигантские таблицы истинности и какие-нибудь [карты Карно](https://ru.wikipedia.org/wiki/Карта_Карно), искать оптимальные схемы реализации. Сегодня эту задачу решает синтезатор, по описанию устройства сам находит наиболее эффективное решение.
|
||||
|
||||
Разница с реализацией мультиплексора в том, что в этом случае справа от знака равно всегда стоит константа. Получается это такой способ описать таблицу истинности. В такой код легко вносить правки и искать интересующие фрагменты.
|
||||
|
||||
Рассмотрим пример ниже. Внутри конструкции `always_comb`, перед конструкцией `case` указываются значения по-умолчанию. Благодаря этому пропадает необходимость указывать все сигналы внутри каждого обработчика `case`, достаточно указать только те, что имеют значение отличное от значения по-умолчанию. Представленный пример реализует комбинационную схему, которая при `cucumber == 4'b1100` будет выставлять сигнал `c == 1'b0`, то есть отличное, от значения по-умолчанию. Сигнал `a` никак не меняется, поэтому он не указан в соответствующем обработчике. Если сигнал `size == 1'b0`, то `b` будет равен 1, а `d` равен 0. Если сигнал `size == 1'b1`, то наоборот – `b` будет равен 0, а `d` равен 1.
|
||||
|
||||
```SystemVerilog
|
||||
|
||||
|
||||
// ... какие-то еще дефайны
|
||||
|
||||
module tequila (
|
||||
input logic [3:0] cucumber;
|
||||
input logic size;
|
||||
output logic a, b, c, d;
|
||||
);
|
||||
parameter logic [3:0] PICKLE = 4'b1100;
|
||||
always_comb begin
|
||||
a <= 1'b0; // значения по-умолчанию
|
||||
b <= 1'b0;
|
||||
c <= 1'b1;
|
||||
d <= 1'b0;
|
||||
case(cucumber)
|
||||
// ... какие-то еще комбинации
|
||||
PICKLE: begin // если на cucumber значение PICKLE
|
||||
c <= 1'b0;
|
||||
case (size)
|
||||
1'b0: b <= 1'b1; // если на size значение 1'b0
|
||||
1'b1: d <= 1'b1; // если на size значение 1'b1
|
||||
endcase
|
||||
end
|
||||
// ... какие-то еще обработчики
|
||||
default: begin // так как описаны не все значения
|
||||
a <= 1'b0; // cucumber, то чтобы case не было
|
||||
b <= 1'b0; // защелки (latch) на выходе
|
||||
c <= 1'b1; // нужно обязательно добавлять
|
||||
d <= 1'b0; // default
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
endmodule
|
||||
```
|
||||
|
||||
## Задание
|
||||
|
||||
Необходимо реализовать на языке **SystemVerilog** модуль основного дешифратора команд однотактного процессора RISC-V в соответствии с предложенной микроархитектурой. Далее приводится прототип разрабатываемого модуля.
|
||||
|
||||
```SystemVerilog
|
||||
module decoder_riscv (
|
||||
input logic [31:0] fetched_instr_i,
|
||||
output logic [1:0] a_sel_o,
|
||||
output logic [2:0] b_sel_o,
|
||||
output logic [4:0] alu_op_o,
|
||||
output logic mem_req_o,
|
||||
output logic mem_we_o,
|
||||
output logic [2:0] mem_size_o,
|
||||
output logic gpr_we_o,
|
||||
output logic wb_sel_o, //write back selector
|
||||
output logic illegal_instr_o,
|
||||
output logic branch_o,
|
||||
output logic jal_o,
|
||||
output logic jalr_o
|
||||
);
|
||||
import riscv_pkg::*;
|
||||
|
||||
endmodule
|
||||
```
|
||||
|
||||
В зависимости от стиля оформления, модуль может занимать больше сотни строк кода, но это не делает его реализацию сложной. По сути, дешифратор — это просто большой `case` с описанием того, в каком случае, какие сигналы и чему должны быть равны. Работа требует внимательности, немного усидчивости и понимания выполняемых действий. С огромной вероятностью в коде будут ошибки и их нужно будет исправлять. Ошибки это нормально — все ошибаются, а исправление ошибок дает бесценный опыт разработки. Возможно реализация этого модуля в какой-то момент покажется рутинной, но поверь, по окончании следующей лабораторной работы удовольствие от результата покажет, что оно того стоило.
|
||||
|
||||
## Порядок выполнения задания
|
||||
|
||||
1. Внимательно ознакомьтесь с выходными сигналами декодера и тем, за что они отвечают, а так же типами команд. В случае возникновения вопросов, проконсультируйтесь с преподавателем.
|
||||
2. Реализуйте модуль `decoder_riscv`. Для этого:
|
||||
1. В `Design Sources` проекта с предыдущих лаб, создайте `SystemVerilog`-файл `decoder_riscv.sv`.
|
||||
2. Опишите в нем модуль основного дешифратора с таким же именем и портами, как указано в [задании](#задание).
|
||||
1. Для удобства дальнейшего описания модуля, рекомендуется сперва создать сигналы `opcode`, `func3`, `func7` и присвоить им соответствующие биты входного сигнала инструкции.
|
||||
2. При описании модуля вы можете воспользоваться параметрами, объявленными в файле **пакете** `riscv_pkg`, описанном в файле [riscv_pkg.sv](riscv_pkg.sv).
|
||||
3. Модуль может быть описан множеством способов: каждый выходной сигнал может быть описан через собственную комбинационную логику в отдельном блоке `case`, однако проще всего будет описать все сигналы через вложенные `case` внутри одного блока `always_comb`.
|
||||
4. Внутри блока `always_comb` до начала блока `case` можно указать базовые значения для всех выходных сигналов. Это не то же самое, что вариант `default` в блоке `case`. Здесь вы можете описать состояния, которые будут использованы чаще всего, и в этом случае, присваивание сигналу будет выполняться только в том месте, где появится инструкция, требующая значение этого сигнала, отличное от базового.
|
||||
5. Далее вы можете описать базовый блок `case`, где будет определен тип операции по ее коду.
|
||||
6. Определив тип операции, вы сможете определить какая конкретно операция по полям `func3` и `func7` (если данный тип имеет такие поля).
|
||||
7. Не забывайте, что в случае, если на каком-то из этапов (определения типа, или определения конкретной операции) вам приходит какое-то неправильное поле, необходимо выставить сигнал `illegal_instr_o`.
|
||||
8. В случае некорректной инструкции, вы должны гарантировать, что не произойдет условный/безусловный переход, а во внешнюю память и регистровый файл ничего не запишется. Не важно, что будет выполняться на АЛУ, не важно какие данные будут выбраны на мультиплексоре источника записи. Важно чтобы не произошел сам факт записи в любое из устройств (подумайте какие значения для каких сигналов необходимо для этого выставить).
|
||||
3. После описания модуля, его необходимо проверить с помощью тестового окружения.
|
||||
1. Тестовое окружение находится [`здесь`](tb_decoder_riscv.sv).
|
||||
2. Для запуска симуляции воспользуйтесь [`этой инструкцией`](../../Vivado%20Basics/Run%20Simulation.md).
|
||||
3. Перед запуском симуляции убедитесь, что выбран правильный модуль верхнего уровня.
|
||||
4. **Во время симуляции, вы должны прожать "Run All" и убедиться, что в логе есть сообщение о завершении теста!**
|
||||
5. Вполне возможно, что после первого запуска вы столкнетесь с сообщениями о множестве ошибок. Вам необходимо [исследовать](../../Vivado%20Basics/Debug%20manual.md) эти ошибки на временной диаграмме и исправить их в вашем модуле.
|
||||
4. Добавьте в проект модуль верхнего уровня ([nexys_decoder.v](board%20files/nexys_decoder.v)), соединяющий основной дешифратор с периферией в ПЛИС. Описание работы модуля находится [здесь](board%20files).
|
||||
5. Подключите к проекту файл ограничений ([nexys_a7_100t.xdc](board%20files/nexys_a7_100t.xdc)), если тот еще не был подключен, либо замените его содержимое данными из файла к этой лабораторной работе.
|
||||
6. Проверьте работу декодера в ПЛИС.
|
41
Labs/05. Main decoder/riscv_pkg.sv
Normal file
41
Labs/05. Main decoder/riscv_pkg.sv
Normal file
@@ -0,0 +1,41 @@
|
||||
package riscv_pkg;
|
||||
|
||||
import alu_opcodes_pkg::*;
|
||||
|
||||
// opcodes
|
||||
parameter LOAD_OPCODE = 5'b00_000;
|
||||
parameter MISC_MEM_OPCODE = 5'b00_011;
|
||||
parameter OP_IMM_OPCODE = 5'b00_100;
|
||||
parameter AUIPC_OPCODE = 5'b00_101;
|
||||
parameter STORE_OPCODE = 5'b01_000;
|
||||
parameter OP_OPCODE = 5'b01_100;
|
||||
parameter LUI_OPCODE = 5'b01_101;
|
||||
parameter BRANCH_OPCODE = 5'b11_000;
|
||||
parameter JALR_OPCODE = 5'b11_001;
|
||||
parameter JAL_OPCODE = 5'b11_011;
|
||||
parameter SYSTEM_OPCODE = 5'b11_100;
|
||||
|
||||
// dmem type load store
|
||||
parameter LDST_B = 3'b000;
|
||||
parameter LDST_H = 3'b001;
|
||||
parameter LDST_W = 3'b010;
|
||||
parameter LDST_BU = 3'b100;
|
||||
parameter LDST_HU = 3'b101;
|
||||
|
||||
// operand a selection
|
||||
parameter OP_A_RS1 = 2'b00;
|
||||
parameter OP_A_CURR_PC = 2'b01;
|
||||
parameter OP_A_ZERO = 2'b10;
|
||||
|
||||
// operand b selection
|
||||
parameter OP_B_RS2 = 3'b000;
|
||||
parameter OP_B_IMM_I = 3'b001;
|
||||
parameter OP_B_IMM_U = 3'b010;
|
||||
parameter OP_B_IMM_S = 3'b011;
|
||||
parameter OP_B_INCR = 3'b100;
|
||||
|
||||
// writeback source selection
|
||||
parameter WB_EX_RESULT = 1'b0;
|
||||
parameter WB_LSU_DATA = 1'b1;
|
||||
|
||||
endpackage
|
966
Labs/05. Main decoder/tb_decoder_riscv.sv
Normal file
966
Labs/05. Main decoder/tb_decoder_riscv.sv
Normal file
@@ -0,0 +1,966 @@
|
||||
`timescale 1ns / 1ps
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Company: MIET
|
||||
// Engineer: Nikita Bulavin
|
||||
//
|
||||
// Create Date:
|
||||
// Design Name:
|
||||
// Module Name: tb_decoder_riscv
|
||||
// Project Name: RISCV_practicum
|
||||
// Target Devices: Nexys A7-100T
|
||||
// Tool Versions:
|
||||
// Description: tb for decoder riscv
|
||||
//
|
||||
// Dependencies:
|
||||
//
|
||||
// Revision:
|
||||
// Revision 0.01 - File Created
|
||||
// Additional Comments:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import riscv_pkg::*;
|
||||
|
||||
module tb_decoder_riscv();
|
||||
|
||||
parameter delay = 4;
|
||||
parameter cycle = 200; // per one opcode
|
||||
|
||||
reg [31:0] instr;
|
||||
wire [1:0] a_sel;
|
||||
wire [2:0] b_sel;
|
||||
wire [ALU_OP_WIDTH-1:0] alu_op;
|
||||
wire mem_req;
|
||||
wire mem_we;
|
||||
wire [2:0] mem_size;
|
||||
wire gpr_we_a;
|
||||
wire wb_src_sel;
|
||||
wire illegal_instr;
|
||||
wire branch;
|
||||
wire jal;
|
||||
wire jalr;
|
||||
|
||||
reg [1:0] a_sel_miss;
|
||||
reg [2:0] b_sel_miss;
|
||||
reg [ALU_OP_WIDTH-1:0] alu_op_miss;
|
||||
reg mem_req_miss;
|
||||
reg mem_we_miss;
|
||||
reg [2:0] mem_size_miss;
|
||||
reg gpr_we_a_miss;
|
||||
reg wb_src_sel_miss;
|
||||
reg illegal_miss;
|
||||
reg branch_miss;
|
||||
reg jal_miss;
|
||||
reg jalr_miss;
|
||||
|
||||
string opcode_type;
|
||||
string instr_type;
|
||||
|
||||
decoder_riscv dut (
|
||||
.fetched_instr_i (instr),
|
||||
.a_sel_o (a_sel),
|
||||
.b_sel_o (b_sel),
|
||||
.alu_op_o (alu_op),
|
||||
.mem_req_o (mem_req),
|
||||
.mem_we_o (mem_we),
|
||||
.mem_size_o (mem_size),
|
||||
.gpr_we_a_o (gpr_we_a),
|
||||
.wb_src_sel_o (wb_src_sel),
|
||||
.illegal_instr_o (illegal_instr),
|
||||
.branch_o (branch),
|
||||
.jal_o (jal),
|
||||
.jalr_o (jalr)
|
||||
);
|
||||
|
||||
decoder_riscv_ref grm(.fetched_instr_i (instr));
|
||||
|
||||
wire [4:0] opcode;
|
||||
assign opcode = instr[6:2];
|
||||
|
||||
always @(*) begin
|
||||
case (opcode)
|
||||
LUI_OPCODE, AUIPC_OPCODE, JAL_OPCODE:
|
||||
instr_type = $sformatf("%020b %05b %07b ", instr[31:12], instr[11:7], instr[6:0]);
|
||||
JALR_OPCODE, LOAD_OPCODE, OP_IMM_OPCODE, SYSTEM_OPCODE:
|
||||
instr_type = $sformatf("%012b %05b %03b %05b %07b ", instr[31:20], instr[19:15], instr[14:12], instr[11:7], instr[6:0]);
|
||||
BRANCH_OPCODE, STORE_OPCODE, OP_OPCODE:
|
||||
instr_type = $sformatf("%07b %05b %05b %03b %05b %07b", instr[31:25], instr[24:20], instr[19:15], instr[14:12], instr[11:7], instr[6:0]);
|
||||
MISC_MEM_OPCODE:
|
||||
instr_type = $sformatf("%017b %03b %05b %07b ", instr[31:15], instr[14:12], instr[11:7], instr[6:0]);
|
||||
default:
|
||||
instr_type = $sformatf("%032b ", instr);
|
||||
endcase
|
||||
end
|
||||
|
||||
always @(*) begin
|
||||
a_sel_miss = 'b0;
|
||||
b_sel_miss = 'b0;
|
||||
alu_op_miss = 'b0;
|
||||
mem_req_miss = 'b0;
|
||||
mem_we_miss = 'b0;
|
||||
mem_size_miss = 'b0;
|
||||
gpr_we_a_miss = 'b0;
|
||||
wb_src_sel_miss = 'b0;
|
||||
illegal_miss = 'b0;
|
||||
branch_miss = 'b0;
|
||||
jal_miss = 'b0;
|
||||
jalr_miss = 'b0;
|
||||
case (opcode)
|
||||
LOAD_OPCODE, STORE_OPCODE:
|
||||
begin
|
||||
a_sel_miss = (grm.ex_op_a_sel_o !== a_sel) & !illegal_instr;
|
||||
b_sel_miss = (grm.ex_op_b_sel_o !== b_sel) & !illegal_instr;
|
||||
alu_op_miss = (grm.alu_op_o !== alu_op) & !illegal_instr;
|
||||
mem_req_miss = grm.mem_req_o !== mem_req;
|
||||
mem_we_miss = grm.mem_we_o !== mem_we;
|
||||
mem_size_miss = (grm.mem_size_o !== mem_size) & !illegal_instr;
|
||||
gpr_we_a_miss = grm.gpr_we_a_o !== gpr_we_a;
|
||||
wb_src_sel_miss = (grm.wb_src_sel_o !== wb_src_sel) & !illegal_instr;
|
||||
illegal_miss = grm.illegal_instr_o !== illegal_instr;
|
||||
branch_miss = grm.branch_o !== branch;
|
||||
jal_miss = grm.jal_o !== jal;
|
||||
jalr_miss = grm.jalr_o !== jalr;
|
||||
end
|
||||
|
||||
JAL_OPCODE, JALR_OPCODE,
|
||||
AUIPC_OPCODE,
|
||||
OP_IMM_OPCODE, OP_OPCODE:
|
||||
begin
|
||||
a_sel_miss = (grm.ex_op_a_sel_o !== a_sel) & !illegal_instr;
|
||||
b_sel_miss = (grm.ex_op_b_sel_o !== b_sel) & !illegal_instr;
|
||||
alu_op_miss = (grm.alu_op_o !== alu_op) & !illegal_instr;
|
||||
mem_req_miss = grm.mem_req_o !== mem_req;
|
||||
mem_we_miss = grm.mem_we_o !== mem_we;
|
||||
//mem_size_miss = (grm.mem_size_o !== mem_size) & !illegal_instr;
|
||||
gpr_we_a_miss = grm.gpr_we_a_o !== gpr_we_a;
|
||||
wb_src_sel_miss = (grm.wb_src_sel_o !== wb_src_sel) & !illegal_instr;
|
||||
illegal_miss = grm.illegal_instr_o !== illegal_instr;
|
||||
branch_miss = grm.branch_o !== branch;
|
||||
jal_miss = grm.jal_o !== jal;
|
||||
jalr_miss = grm.jalr_o !== jalr;
|
||||
end
|
||||
|
||||
BRANCH_OPCODE:
|
||||
begin
|
||||
a_sel_miss = (grm.ex_op_a_sel_o !== a_sel) & !illegal_instr;
|
||||
b_sel_miss = (grm.ex_op_b_sel_o !== b_sel) & !illegal_instr;
|
||||
alu_op_miss = (grm.alu_op_o !== alu_op) & !illegal_instr;
|
||||
mem_req_miss = grm.mem_req_o !== mem_req;
|
||||
mem_we_miss = grm.mem_we_o !== mem_we;
|
||||
//mem_size_miss = (grm.mem_size_o !== mem_size) & !illegal_instr;
|
||||
gpr_we_a_miss = grm.gpr_we_a_o !== gpr_we_a;
|
||||
//wb_src_sel_miss = (grm.wb_src_sel_o !== wb_src_sel) & !illegal_instr;
|
||||
illegal_miss = grm.illegal_instr_o !== illegal_instr;
|
||||
branch_miss = grm.branch_o !== branch;
|
||||
jal_miss = grm.jal_o !== jal;
|
||||
jalr_miss = grm.jalr_o !== jalr;
|
||||
end
|
||||
|
||||
LUI_OPCODE: begin
|
||||
a_sel_miss = (grm.ex_op_a_sel_o !== a_sel) & !illegal_instr;
|
||||
b_sel_miss = (grm.ex_op_b_sel_o !== b_sel) & !illegal_instr;
|
||||
alu_op_miss = ((alu_op !== ALU_ADD)&(alu_op !== ALU_XOR)&(alu_op !== ALU_OR)) & !illegal_instr;
|
||||
mem_req_miss = grm.mem_req_o !== mem_req;
|
||||
mem_we_miss = grm.mem_we_o !== mem_we;
|
||||
//mem_size_miss = (grm.mem_size_o !== mem_size) & !illegal_instr;
|
||||
gpr_we_a_miss = grm.gpr_we_a_o !== gpr_we_a;
|
||||
wb_src_sel_miss = (grm.wb_src_sel_o !== wb_src_sel) & !illegal_instr;
|
||||
illegal_miss = grm.illegal_instr_o !== illegal_instr;
|
||||
branch_miss = grm.branch_o !== branch;
|
||||
jal_miss = grm.jal_o !== jal;
|
||||
jalr_miss = grm.jalr_o !== jalr;
|
||||
end
|
||||
|
||||
default: //MISC_MEM_OPCODE, SYSTEM_OPCODE and other
|
||||
begin
|
||||
//a_sel_miss = grm.ex_op_a_sel_o !== a_sel;
|
||||
//b_sel_miss = grm.ex_op_b_sel_o !== b_sel;
|
||||
//alu_op_miss = grm.alu_op_o !== alu_op;
|
||||
mem_req_miss = grm.mem_req_o !== mem_req;
|
||||
mem_we_miss = grm.mem_we_o !== mem_we;
|
||||
//mem_size_miss = grm.mem_size_o !== mem_size;
|
||||
gpr_we_a_miss = grm.gpr_we_a_o !== gpr_we_a;
|
||||
//wb_src_sel_miss = grm.wb_src_sel_o !== wb_src_sel;
|
||||
illegal_miss = grm.illegal_instr_o !== illegal_instr;
|
||||
branch_miss = grm.branch_o !== branch;
|
||||
jal_miss = grm.jal_o !== jal;
|
||||
jalr_miss = grm.jalr_o !== jalr;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
reg [4:0] X;
|
||||
reg [$clog2(cycle+1)-1:0] V;
|
||||
integer error;
|
||||
|
||||
initial begin
|
||||
$timeformat(-9, 2, " ns", 3);
|
||||
error = 0;
|
||||
end
|
||||
|
||||
initial begin
|
||||
$display( "\nStart test: \n\n==========================\nCLICK THE BUTTON 'Run All'\n==========================\n"); $stop();
|
||||
|
||||
for (V=0; V<cycle/10; V=V+1) begin // illegal по 11 в конце opcode
|
||||
instr[1:0] = $random;
|
||||
instr[6:2] = {1'b0,V[1:0],2'b0};
|
||||
instr[31:7] = 'b0;
|
||||
#delay;
|
||||
end
|
||||
for (V=0; V<cycle; V=V+1) begin // illegal по OP_OPCODE funct7
|
||||
instr[11:0] = {5'b0,OP_OPCODE,2'b11};
|
||||
instr[14:12] = V;
|
||||
instr[24:15] = $random;
|
||||
instr[31:25] = 2**($random % 7);
|
||||
#delay;
|
||||
end
|
||||
for (V=0; V<cycle; V=V+1) begin // illegal по SYSTEM_OPCODE
|
||||
instr[6:0] = {SYSTEM_OPCODE,2'b11};
|
||||
instr[31:7] = 2**($random % 25);
|
||||
#delay;
|
||||
end
|
||||
|
||||
for (X=0; X<2**5-1; X=X+1) begin
|
||||
for (V=0; V<cycle; V=V+1) begin
|
||||
instr[1:0] = 2'b11;
|
||||
instr[6:2] = X;
|
||||
instr[31:7] = $random;
|
||||
#delay;
|
||||
end
|
||||
end
|
||||
for (V=0; V<cycle; V=V+1) begin
|
||||
instr = $random;
|
||||
#delay;
|
||||
end
|
||||
|
||||
if (|error)
|
||||
$display ("FAIL!\nThere are errors in the design, number of errors: %d", error);
|
||||
else
|
||||
$display ("\ndecoder SUCCESS!!!\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
always begin
|
||||
@(instr);
|
||||
#2;
|
||||
if (illegal_miss)begin
|
||||
$display("Output 'illegal_instr_o' is incorrect( %b instead of %b ), instruction: %s %s, time: %t",illegal_instr, grm.illegal_instr_o, instr_type, opcode_type, $time);
|
||||
error = error + 1'b1;
|
||||
end
|
||||
if (~illegal_miss) begin
|
||||
if (a_sel_miss)begin
|
||||
$display ("Output 'ex_op_a_sel_o ' is incorrect( %b instead of %b ), instruction: %s %s, time: %t",a_sel, grm.ex_op_a_sel_o, instr_type, opcode_type, $time);
|
||||
error = error + 1'b1;
|
||||
end
|
||||
if (b_sel_miss)begin
|
||||
$display ("Output 'ex_op_b_sel_o ' is incorrect( %b instead of %b ), instruction: %s %s, time: %t",b_sel, grm.ex_op_b_sel_o, instr_type, opcode_type, $time);
|
||||
error = error + 1'b1;
|
||||
end
|
||||
if (alu_op_miss)begin
|
||||
$display ("Output 'alu_op_o ' is incorrect(%b instead of %b), instruction: %s %s, time: %t",alu_op, grm.alu_op_o, instr_type, opcode_type, $time);
|
||||
error = error + 1'b1;
|
||||
end
|
||||
if (mem_we_miss)begin
|
||||
$display ("Output 'mem_we_o ' is incorrect( %b instead of %b ), instruction: %s %s, time: %t",mem_we, grm.mem_we_o, instr_type, opcode_type, $time);
|
||||
error = error + 1'b1;
|
||||
end
|
||||
if (mem_size_miss)begin
|
||||
$display ("Output 'mem_size_o ' is incorrect( %b instead of %b ), instruction: %s %s, time: %t",mem_size, grm.mem_size_o, instr_type, opcode_type, $time);
|
||||
error = error + 1'b1;
|
||||
end
|
||||
if (mem_req_miss)begin
|
||||
$display ("Output 'mem_req_o ' is incorrect( %b instead of %b ), instruction: %s %s, time: %t",mem_req, grm.mem_req_o, instr_type, opcode_type, $time);
|
||||
error = error + 1'b1;
|
||||
end
|
||||
if (wb_src_sel_miss)begin
|
||||
$display ("Output 'wb_src_sel_o ' is incorrect( %b instead of %b ), instruction: %s %s, time: %t",wb_src_sel, grm.wb_src_sel_o, instr_type, opcode_type, $time);
|
||||
error = error + 1'b1;
|
||||
end
|
||||
if (gpr_we_a_miss)begin
|
||||
$display ("Output 'gpr_we_a_o ' is incorrect( %b instead of %b ), instruction: %s %s, time: %t",gpr_we_a, grm.gpr_we_a_o, instr_type, opcode_type, $time);
|
||||
error = error + 1'b1;
|
||||
end
|
||||
if (branch_miss)begin
|
||||
$display ("Output 'branch_o ' is incorrect( %b instead of %b ), instruction: %s %s, time: %t",branch, grm.branch_o, instr_type, opcode_type, $time);
|
||||
error = error + 1'b1;
|
||||
end
|
||||
if (jal_miss)begin
|
||||
$display ("Output 'jal_o ' is incorrect( %b instead of %b ), instruction: %s %s, time: %t",jal, grm.jal_o, instr_type, opcode_type, $time);
|
||||
error = error + 1'b1;
|
||||
end
|
||||
if (jalr_miss)begin
|
||||
$display ("Output 'jalr_o ' is incorrect( %b instead of %b ), instruction: %s %s, time: %t",jalr, grm.jalr_o, instr_type, opcode_type, $time);
|
||||
error = error + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
if ((a_sel != OP_A_RS1) &
|
||||
(a_sel != OP_A_CURR_PC) &
|
||||
(a_sel != OP_A_ZERO)) begin
|
||||
$display ("Output 'ex_op_a_sel_o' must always have a legal value, instruction: %s %s, time: %t", instr_type, opcode_type, $time);
|
||||
error = error + 1;
|
||||
end
|
||||
if ((b_sel != OP_B_RS2) &
|
||||
(b_sel != OP_B_IMM_I) &
|
||||
(b_sel != OP_B_IMM_U) &
|
||||
(b_sel != OP_B_IMM_S) &
|
||||
(b_sel != OP_B_INCR)) begin
|
||||
$display ("Output 'ex_op_b_sel_o' must always have a legal value, instruction: %s %s, time: %t", instr_type, opcode_type, $time);
|
||||
error = error + 1;
|
||||
end
|
||||
if ((alu_op != ALU_ADD) & (alu_op != ALU_SUB) &
|
||||
(alu_op != ALU_XOR) & (alu_op != ALU_OR) &
|
||||
(alu_op != ALU_AND) & (alu_op != ALU_SRA) &
|
||||
(alu_op != ALU_SRL) & (alu_op != ALU_SLL) &
|
||||
(alu_op != ALU_LTS) & (alu_op != ALU_LTU) &
|
||||
(alu_op != ALU_GES) & (alu_op != ALU_GEU) &
|
||||
(alu_op != ALU_EQ) & (alu_op != ALU_NE) &
|
||||
(alu_op != ALU_SLTS) & (alu_op != ALU_SLTU)) begin
|
||||
$display ("Output 'alu_op_o' must always have a legal value, instruction: %s %s, time: %t", instr_type, opcode_type, $time);
|
||||
error = error + 1;
|
||||
end
|
||||
if ((mem_size != LDST_B) &
|
||||
(mem_size != LDST_H) &
|
||||
(mem_size != LDST_W) &
|
||||
(mem_size != LDST_BU) &
|
||||
(mem_size != LDST_HU)) begin
|
||||
$display ("Output 'mem_size_o' must always have a legal value, instruction: %s %s, time: %t", instr_type, opcode_type, $time);
|
||||
error = error + 1;
|
||||
end
|
||||
if ((wb_src_sel != WB_EX_RESULT) &
|
||||
(wb_src_sel != WB_LSU_DATA)) begin
|
||||
$display ("Output 'wb_src_sel_o' must always have a legal value, instruction: %s %s, time: %t", instr_type, opcode_type, $time);
|
||||
error = error + 1;
|
||||
end
|
||||
end
|
||||
|
||||
always begin
|
||||
@(instr);
|
||||
#1;
|
||||
if(&instr[1:0])
|
||||
case(opcode)
|
||||
LUI_OPCODE: begin
|
||||
opcode_type = "( LUI )";
|
||||
end
|
||||
AUIPC_OPCODE: begin
|
||||
opcode_type = "( AUIPC )";
|
||||
end
|
||||
JAL_OPCODE: begin
|
||||
opcode_type = "( JAL )";
|
||||
end
|
||||
JALR_OPCODE: begin
|
||||
opcode_type = instr[14:12]? "( illegal_JALR )": "( JALR )";
|
||||
end
|
||||
BRANCH_OPCODE: begin
|
||||
case(instr[14:12])
|
||||
3'b000: opcode_type = "( BEQ )";
|
||||
3'b001: opcode_type = "( BNE )";
|
||||
3'b100: opcode_type = "( BLT )";
|
||||
3'b101: opcode_type = "( BGE )";
|
||||
3'b110: opcode_type = "( BLTU )";
|
||||
3'b111: opcode_type = "( BGEU )";
|
||||
default: opcode_type= "(illegal_BRANCH)";
|
||||
endcase
|
||||
end
|
||||
LOAD_OPCODE: begin
|
||||
case(instr[14:12])
|
||||
3'b000: opcode_type = "( LB )";
|
||||
3'b001: opcode_type = "( LH )";
|
||||
3'b010: opcode_type = "( LW )";
|
||||
3'b100: opcode_type = "( LBU )";
|
||||
3'b101: opcode_type = "( LHU )";
|
||||
default: opcode_type= "( illegal_LOAD )";
|
||||
endcase
|
||||
end
|
||||
STORE_OPCODE: begin
|
||||
case(instr[14:12])
|
||||
3'b000: opcode_type = "( SB )";
|
||||
3'b001: opcode_type = "( SH )";
|
||||
3'b010: opcode_type = "( SW )";
|
||||
default: opcode_type = "(illegal_STORE)";
|
||||
endcase
|
||||
end
|
||||
OP_IMM_OPCODE: begin
|
||||
case({2'b0,instr[14:12]})
|
||||
ALU_ADD : opcode_type = "( ADDI )";
|
||||
ALU_XOR : opcode_type = "( XORI )";
|
||||
ALU_OR : opcode_type = "( ORI )";
|
||||
ALU_AND : opcode_type = "( ANDI )";
|
||||
ALU_SRL : opcode_type = {instr[31],instr[29:25]}? "(illegal_OP_IMM)": instr[30]? "( SRAI )": "( SRLI )";
|
||||
ALU_SLL : opcode_type = instr[31:25]? "(illegal_OP_IMM)": "( SLLI )";
|
||||
ALU_SLTS : opcode_type = "( SLTI )";
|
||||
ALU_SLTU : opcode_type = "( SLTIU )";
|
||||
default : opcode_type = "(illegal_OP_IMM)";
|
||||
endcase
|
||||
end
|
||||
OP_OPCODE: begin
|
||||
if(!instr[29:25])
|
||||
case({instr[31:30],instr[14:12]})
|
||||
ALU_ADD : opcode_type = "( ADD )";
|
||||
ALU_SUB : opcode_type = "( SUB )";
|
||||
ALU_XOR : opcode_type = "( XOR )";
|
||||
ALU_OR : opcode_type = "( OR )";
|
||||
ALU_AND : opcode_type = "( AND )";
|
||||
ALU_SRA : opcode_type = "( SRA )";
|
||||
ALU_SRL : opcode_type = "( SRL )";
|
||||
ALU_SLL : opcode_type = "( SLL )";
|
||||
ALU_SLTU : opcode_type = "( SLTU )";
|
||||
default : opcode_type = "( illegal_OP )";
|
||||
endcase
|
||||
else opcode_type = "( illegal_OP )";
|
||||
end
|
||||
MISC_MEM_OPCODE: begin
|
||||
opcode_type = instr[14:12]? "(illegal_FENCE )": "( FENCE )";
|
||||
end
|
||||
SYSTEM_OPCODE: begin
|
||||
opcode_type = {instr[31:21], instr[19:7]}? "(illegal_SYSTEM)": instr[20]? "( EBREAK )": "( ECALL )";
|
||||
end
|
||||
default: opcode_type = "(illegal_opcode)";
|
||||
endcase
|
||||
else opcode_type = "(illegal_opcode)";
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`timescale 1 ps / 1 ps
|
||||
|
||||
(* STRUCTURAL_NETLIST = "yes" *)
|
||||
module decoder_riscv_ref
|
||||
(fetched_instr_i,
|
||||
ex_op_a_sel_o,
|
||||
ex_op_b_sel_o,
|
||||
alu_op_o,
|
||||
mem_req_o,
|
||||
mem_we_o,
|
||||
mem_size_o,
|
||||
gpr_we_a_o,
|
||||
wb_src_sel_o,
|
||||
illegal_instr_o,
|
||||
branch_o,
|
||||
jal_o,
|
||||
jalr_o);
|
||||
input [31:0]fetched_instr_i;
|
||||
output [1:0]ex_op_a_sel_o;
|
||||
output [2:0]ex_op_b_sel_o;
|
||||
output [4:0]alu_op_o;
|
||||
output mem_req_o;
|
||||
output mem_we_o;
|
||||
output [2:0]mem_size_o;
|
||||
output gpr_we_a_o;
|
||||
output wb_src_sel_o;
|
||||
output illegal_instr_o;
|
||||
output branch_o;
|
||||
output jal_o;
|
||||
output jalr_o;
|
||||
|
||||
wire [3:0]\^alu_op_o ;
|
||||
wire \alu_op_o[1]_INST_0_i_1_n_0 ;
|
||||
wire \alu_op_o[3]_INST_0_i_1_n_0 ;
|
||||
wire \alu_op_o[3]_INST_0_i_2_n_0 ;
|
||||
wire \alu_op_o[3]_INST_0_i_3_n_0 ;
|
||||
wire \alu_op_o[3]_INST_0_i_4_n_0 ;
|
||||
wire branch_o;
|
||||
wire branch_o_INST_0_i_1_n_0;
|
||||
wire [1:0]ex_op_a_sel_o;
|
||||
wire \ex_op_a_sel_o[0]_INST_0_i_1_n_0 ;
|
||||
wire \ex_op_a_sel_o[0]_INST_0_i_2_n_0 ;
|
||||
wire \ex_op_a_sel_o[1]_INST_0_i_1_n_0 ;
|
||||
wire [2:0]ex_op_b_sel_o;
|
||||
wire \ex_op_b_sel_o[0]_INST_0_i_1_n_0 ;
|
||||
wire \ex_op_b_sel_o[0]_INST_0_i_2_n_0 ;
|
||||
wire \ex_op_b_sel_o[0]_INST_0_i_3_n_0 ;
|
||||
wire \ex_op_b_sel_o[0]_INST_0_i_4_n_0 ;
|
||||
wire \ex_op_b_sel_o[0]_INST_0_i_5_n_0 ;
|
||||
wire \ex_op_b_sel_o[0]_INST_0_i_6_n_0 ;
|
||||
wire \ex_op_b_sel_o[1]_INST_0_i_1_n_0 ;
|
||||
wire \ex_op_b_sel_o[1]_INST_0_i_2_n_0 ;
|
||||
wire \ex_op_b_sel_o[2]_INST_0_i_1_n_0 ;
|
||||
wire [31:0]fetched_instr_i;
|
||||
wire gpr_we_a_o;
|
||||
wire gpr_we_a_o_INST_0_i_1_n_0;
|
||||
wire illegal_instr_o;
|
||||
wire illegal_instr_o_INST_0_i_10_n_0;
|
||||
wire illegal_instr_o_INST_0_i_1_n_0;
|
||||
wire illegal_instr_o_INST_0_i_2_n_0;
|
||||
wire illegal_instr_o_INST_0_i_3_n_0;
|
||||
wire illegal_instr_o_INST_0_i_4_n_0;
|
||||
wire illegal_instr_o_INST_0_i_5_n_0;
|
||||
wire illegal_instr_o_INST_0_i_6_n_0;
|
||||
wire illegal_instr_o_INST_0_i_7_n_0;
|
||||
wire illegal_instr_o_INST_0_i_8_n_0;
|
||||
wire illegal_instr_o_INST_0_i_9_n_0;
|
||||
wire jal_o;
|
||||
wire jalr_o;
|
||||
wire mem_req_o;
|
||||
wire [2:0]mem_size_o;
|
||||
wire \mem_size_o[0]_INST_0_i_1_n_0 ;
|
||||
wire mem_we_o;
|
||||
wire wb_src_sel_o;
|
||||
|
||||
assign alu_op_o[4] = branch_o;
|
||||
assign alu_op_o[3:0] = \^alu_op_o [3:0];
|
||||
LUT6 #(
|
||||
.INIT(64'hA0E0A00000000000))
|
||||
\alu_op_o[0]_INST_0
|
||||
(.I0(\alu_op_o[1]_INST_0_i_1_n_0 ),
|
||||
.I1(\alu_op_o[3]_INST_0_i_3_n_0 ),
|
||||
.I2(fetched_instr_i[12]),
|
||||
.I3(fetched_instr_i[6]),
|
||||
.I4(fetched_instr_i[4]),
|
||||
.I5(branch_o_INST_0_i_1_n_0),
|
||||
.O(\^alu_op_o [0]));
|
||||
LUT6 #(
|
||||
.INIT(64'hA0E0A00000000000))
|
||||
\alu_op_o[1]_INST_0
|
||||
(.I0(\alu_op_o[1]_INST_0_i_1_n_0 ),
|
||||
.I1(\alu_op_o[3]_INST_0_i_3_n_0 ),
|
||||
.I2(fetched_instr_i[13]),
|
||||
.I3(fetched_instr_i[6]),
|
||||
.I4(fetched_instr_i[4]),
|
||||
.I5(branch_o_INST_0_i_1_n_0),
|
||||
.O(\^alu_op_o [1]));
|
||||
(* SOFT_HLUTNM = "soft_lutpair4" *)
|
||||
LUT4 #(
|
||||
.INIT(16'h00D0))
|
||||
\alu_op_o[1]_INST_0_i_1
|
||||
(.I0(fetched_instr_i[13]),
|
||||
.I1(fetched_instr_i[14]),
|
||||
.I2(fetched_instr_i[5]),
|
||||
.I3(fetched_instr_i[4]),
|
||||
.O(\alu_op_o[1]_INST_0_i_1_n_0 ));
|
||||
LUT6 #(
|
||||
.INIT(64'h6240000000000000))
|
||||
\alu_op_o[2]_INST_0
|
||||
(.I0(fetched_instr_i[6]),
|
||||
.I1(fetched_instr_i[4]),
|
||||
.I2(\alu_op_o[3]_INST_0_i_3_n_0 ),
|
||||
.I3(fetched_instr_i[5]),
|
||||
.I4(branch_o_INST_0_i_1_n_0),
|
||||
.I5(fetched_instr_i[14]),
|
||||
.O(\^alu_op_o [2]));
|
||||
LUT6 #(
|
||||
.INIT(64'hBAAAAAAAAAAAAAAA))
|
||||
\alu_op_o[3]_INST_0
|
||||
(.I0(branch_o),
|
||||
.I1(\alu_op_o[3]_INST_0_i_1_n_0 ),
|
||||
.I2(branch_o_INST_0_i_1_n_0),
|
||||
.I3(fetched_instr_i[30]),
|
||||
.I4(\alu_op_o[3]_INST_0_i_2_n_0 ),
|
||||
.I5(\alu_op_o[3]_INST_0_i_3_n_0 ),
|
||||
.O(\^alu_op_o [3]));
|
||||
LUT6 #(
|
||||
.INIT(64'hFB00FBFBFB00FBBB))
|
||||
\alu_op_o[3]_INST_0_i_1
|
||||
(.I0(\ex_op_b_sel_o[0]_INST_0_i_6_n_0 ),
|
||||
.I1(fetched_instr_i[5]),
|
||||
.I2(fetched_instr_i[30]),
|
||||
.I3(fetched_instr_i[13]),
|
||||
.I4(fetched_instr_i[12]),
|
||||
.I5(fetched_instr_i[14]),
|
||||
.O(\alu_op_o[3]_INST_0_i_1_n_0 ));
|
||||
(* SOFT_HLUTNM = "soft_lutpair7" *)
|
||||
LUT2 #(
|
||||
.INIT(4'h2))
|
||||
\alu_op_o[3]_INST_0_i_2
|
||||
(.I0(fetched_instr_i[4]),
|
||||
.I1(fetched_instr_i[6]),
|
||||
.O(\alu_op_o[3]_INST_0_i_2_n_0 ));
|
||||
(* SOFT_HLUTNM = "soft_lutpair0" *)
|
||||
LUT4 #(
|
||||
.INIT(16'h1F10))
|
||||
\alu_op_o[3]_INST_0_i_3
|
||||
(.I0(\ex_op_b_sel_o[0]_INST_0_i_6_n_0 ),
|
||||
.I1(\alu_op_o[3]_INST_0_i_4_n_0 ),
|
||||
.I2(fetched_instr_i[5]),
|
||||
.I3(\ex_op_b_sel_o[0]_INST_0_i_2_n_0 ),
|
||||
.O(\alu_op_o[3]_INST_0_i_3_n_0 ));
|
||||
(* SOFT_HLUTNM = "soft_lutpair3" *)
|
||||
LUT4 #(
|
||||
.INIT(16'h8AA8))
|
||||
\alu_op_o[3]_INST_0_i_4
|
||||
(.I0(fetched_instr_i[30]),
|
||||
.I1(fetched_instr_i[13]),
|
||||
.I2(fetched_instr_i[12]),
|
||||
.I3(fetched_instr_i[14]),
|
||||
.O(\alu_op_o[3]_INST_0_i_4_n_0 ));
|
||||
LUT6 #(
|
||||
.INIT(64'h008A000000000000))
|
||||
branch_o_INST_0
|
||||
(.I0(branch_o_INST_0_i_1_n_0),
|
||||
.I1(fetched_instr_i[14]),
|
||||
.I2(fetched_instr_i[13]),
|
||||
.I3(fetched_instr_i[4]),
|
||||
.I4(fetched_instr_i[5]),
|
||||
.I5(fetched_instr_i[6]),
|
||||
.O(branch_o));
|
||||
(* SOFT_HLUTNM = "soft_lutpair1" *)
|
||||
LUT4 #(
|
||||
.INIT(16'h0008))
|
||||
branch_o_INST_0_i_1
|
||||
(.I0(fetched_instr_i[1]),
|
||||
.I1(fetched_instr_i[0]),
|
||||
.I2(fetched_instr_i[3]),
|
||||
.I3(fetched_instr_i[2]),
|
||||
.O(branch_o_INST_0_i_1_n_0));
|
||||
LUT6 #(
|
||||
.INIT(64'h0031000000000300))
|
||||
\ex_op_a_sel_o[0]_INST_0
|
||||
(.I0(\ex_op_a_sel_o[0]_INST_0_i_1_n_0 ),
|
||||
.I1(\ex_op_a_sel_o[0]_INST_0_i_2_n_0 ),
|
||||
.I2(fetched_instr_i[3]),
|
||||
.I3(fetched_instr_i[4]),
|
||||
.I4(fetched_instr_i[6]),
|
||||
.I5(fetched_instr_i[5]),
|
||||
.O(ex_op_a_sel_o[0]));
|
||||
(* SOFT_HLUTNM = "soft_lutpair3" *)
|
||||
LUT3 #(
|
||||
.INIT(8'hFE))
|
||||
\ex_op_a_sel_o[0]_INST_0_i_1
|
||||
(.I0(fetched_instr_i[13]),
|
||||
.I1(fetched_instr_i[12]),
|
||||
.I2(fetched_instr_i[14]),
|
||||
.O(\ex_op_a_sel_o[0]_INST_0_i_1_n_0 ));
|
||||
(* SOFT_HLUTNM = "soft_lutpair6" *)
|
||||
LUT3 #(
|
||||
.INIT(8'h7F))
|
||||
\ex_op_a_sel_o[0]_INST_0_i_2
|
||||
(.I0(fetched_instr_i[2]),
|
||||
.I1(fetched_instr_i[1]),
|
||||
.I2(fetched_instr_i[0]),
|
||||
.O(\ex_op_a_sel_o[0]_INST_0_i_2_n_0 ));
|
||||
LUT6 #(
|
||||
.INIT(64'h0200000000000000))
|
||||
\ex_op_a_sel_o[1]_INST_0
|
||||
(.I0(\ex_op_a_sel_o[1]_INST_0_i_1_n_0 ),
|
||||
.I1(fetched_instr_i[6]),
|
||||
.I2(fetched_instr_i[3]),
|
||||
.I3(fetched_instr_i[2]),
|
||||
.I4(fetched_instr_i[4]),
|
||||
.I5(fetched_instr_i[5]),
|
||||
.O(ex_op_a_sel_o[1]));
|
||||
(* SOFT_HLUTNM = "soft_lutpair6" *)
|
||||
LUT2 #(
|
||||
.INIT(4'h8))
|
||||
\ex_op_a_sel_o[1]_INST_0_i_1
|
||||
(.I0(fetched_instr_i[0]),
|
||||
.I1(fetched_instr_i[1]),
|
||||
.O(\ex_op_a_sel_o[1]_INST_0_i_1_n_0 ));
|
||||
LUT6 #(
|
||||
.INIT(64'h0000000000002E22))
|
||||
\ex_op_b_sel_o[0]_INST_0
|
||||
(.I0(\ex_op_b_sel_o[0]_INST_0_i_1_n_0 ),
|
||||
.I1(fetched_instr_i[4]),
|
||||
.I2(fetched_instr_i[5]),
|
||||
.I3(\ex_op_b_sel_o[0]_INST_0_i_2_n_0 ),
|
||||
.I4(\ex_op_b_sel_o[0]_INST_0_i_3_n_0 ),
|
||||
.I5(fetched_instr_i[2]),
|
||||
.O(ex_op_b_sel_o[0]));
|
||||
(* SOFT_HLUTNM = "soft_lutpair2" *)
|
||||
LUT4 #(
|
||||
.INIT(16'h0377))
|
||||
\ex_op_b_sel_o[0]_INST_0_i_1
|
||||
(.I0(fetched_instr_i[12]),
|
||||
.I1(fetched_instr_i[13]),
|
||||
.I2(fetched_instr_i[5]),
|
||||
.I3(fetched_instr_i[14]),
|
||||
.O(\ex_op_b_sel_o[0]_INST_0_i_1_n_0 ));
|
||||
LUT6 #(
|
||||
.INIT(64'hFFFF0F4FFFFFFF4F))
|
||||
\ex_op_b_sel_o[0]_INST_0_i_2
|
||||
(.I0(\ex_op_b_sel_o[0]_INST_0_i_4_n_0 ),
|
||||
.I1(\ex_op_b_sel_o[0]_INST_0_i_5_n_0 ),
|
||||
.I2(fetched_instr_i[12]),
|
||||
.I3(fetched_instr_i[14]),
|
||||
.I4(fetched_instr_i[13]),
|
||||
.I5(\ex_op_b_sel_o[0]_INST_0_i_6_n_0 ),
|
||||
.O(\ex_op_b_sel_o[0]_INST_0_i_2_n_0 ));
|
||||
(* SOFT_HLUTNM = "soft_lutpair1" *)
|
||||
LUT4 #(
|
||||
.INIT(16'hFFF7))
|
||||
\ex_op_b_sel_o[0]_INST_0_i_3
|
||||
(.I0(fetched_instr_i[1]),
|
||||
.I1(fetched_instr_i[0]),
|
||||
.I2(fetched_instr_i[6]),
|
||||
.I3(fetched_instr_i[3]),
|
||||
.O(\ex_op_b_sel_o[0]_INST_0_i_3_n_0 ));
|
||||
LUT3 #(
|
||||
.INIT(8'hFE))
|
||||
\ex_op_b_sel_o[0]_INST_0_i_4
|
||||
(.I0(fetched_instr_i[30]),
|
||||
.I1(fetched_instr_i[31]),
|
||||
.I2(fetched_instr_i[29]),
|
||||
.O(\ex_op_b_sel_o[0]_INST_0_i_4_n_0 ));
|
||||
LUT4 #(
|
||||
.INIT(16'h0001))
|
||||
\ex_op_b_sel_o[0]_INST_0_i_5
|
||||
(.I0(fetched_instr_i[25]),
|
||||
.I1(fetched_instr_i[26]),
|
||||
.I2(fetched_instr_i[28]),
|
||||
.I3(fetched_instr_i[27]),
|
||||
.O(\ex_op_b_sel_o[0]_INST_0_i_5_n_0 ));
|
||||
LUT6 #(
|
||||
.INIT(64'hFFFFFFFFFFFFFFFE))
|
||||
\ex_op_b_sel_o[0]_INST_0_i_6
|
||||
(.I0(fetched_instr_i[26]),
|
||||
.I1(fetched_instr_i[25]),
|
||||
.I2(fetched_instr_i[27]),
|
||||
.I3(fetched_instr_i[28]),
|
||||
.I4(fetched_instr_i[29]),
|
||||
.I5(fetched_instr_i[31]),
|
||||
.O(\ex_op_b_sel_o[0]_INST_0_i_6_n_0 ));
|
||||
LUT6 #(
|
||||
.INIT(64'hAAAEAEAEAAAAAAAA))
|
||||
\ex_op_b_sel_o[1]_INST_0
|
||||
(.I0(\ex_op_b_sel_o[1]_INST_0_i_1_n_0 ),
|
||||
.I1(fetched_instr_i[5]),
|
||||
.I2(fetched_instr_i[14]),
|
||||
.I3(fetched_instr_i[13]),
|
||||
.I4(fetched_instr_i[12]),
|
||||
.I5(\ex_op_b_sel_o[1]_INST_0_i_2_n_0 ),
|
||||
.O(ex_op_b_sel_o[1]));
|
||||
LUT6 #(
|
||||
.INIT(64'h0008000000000000))
|
||||
\ex_op_b_sel_o[1]_INST_0_i_1
|
||||
(.I0(fetched_instr_i[4]),
|
||||
.I1(fetched_instr_i[2]),
|
||||
.I2(fetched_instr_i[3]),
|
||||
.I3(fetched_instr_i[6]),
|
||||
.I4(fetched_instr_i[0]),
|
||||
.I5(fetched_instr_i[1]),
|
||||
.O(\ex_op_b_sel_o[1]_INST_0_i_1_n_0 ));
|
||||
LUT6 #(
|
||||
.INIT(64'h0000000000001000))
|
||||
\ex_op_b_sel_o[1]_INST_0_i_2
|
||||
(.I0(fetched_instr_i[2]),
|
||||
.I1(fetched_instr_i[3]),
|
||||
.I2(fetched_instr_i[0]),
|
||||
.I3(fetched_instr_i[1]),
|
||||
.I4(fetched_instr_i[6]),
|
||||
.I5(fetched_instr_i[4]),
|
||||
.O(\ex_op_b_sel_o[1]_INST_0_i_2_n_0 ));
|
||||
LUT6 #(
|
||||
.INIT(64'hB000000000000000))
|
||||
\ex_op_b_sel_o[2]_INST_0
|
||||
(.I0(fetched_instr_i[3]),
|
||||
.I1(\ex_op_a_sel_o[0]_INST_0_i_1_n_0 ),
|
||||
.I2(\ex_op_b_sel_o[2]_INST_0_i_1_n_0 ),
|
||||
.I3(fetched_instr_i[2]),
|
||||
.I4(fetched_instr_i[1]),
|
||||
.I5(fetched_instr_i[0]),
|
||||
.O(ex_op_b_sel_o[2]));
|
||||
(* SOFT_HLUTNM = "soft_lutpair4" *)
|
||||
LUT3 #(
|
||||
.INIT(8'h40))
|
||||
\ex_op_b_sel_o[2]_INST_0_i_1
|
||||
(.I0(fetched_instr_i[4]),
|
||||
.I1(fetched_instr_i[5]),
|
||||
.I2(fetched_instr_i[6]),
|
||||
.O(\ex_op_b_sel_o[2]_INST_0_i_1_n_0 ));
|
||||
LUT6 #(
|
||||
.INIT(64'hBBBBAAABBABAAAAB))
|
||||
gpr_we_a_o_INST_0
|
||||
(.I0(ex_op_b_sel_o[2]),
|
||||
.I1(\ex_op_b_sel_o[0]_INST_0_i_3_n_0 ),
|
||||
.I2(fetched_instr_i[2]),
|
||||
.I3(gpr_we_a_o_INST_0_i_1_n_0),
|
||||
.I4(fetched_instr_i[4]),
|
||||
.I5(\alu_op_o[3]_INST_0_i_3_n_0 ),
|
||||
.O(gpr_we_a_o));
|
||||
(* SOFT_HLUTNM = "soft_lutpair2" *)
|
||||
LUT4 #(
|
||||
.INIT(16'hFEAA))
|
||||
gpr_we_a_o_INST_0_i_1
|
||||
(.I0(fetched_instr_i[5]),
|
||||
.I1(fetched_instr_i[14]),
|
||||
.I2(fetched_instr_i[12]),
|
||||
.I3(fetched_instr_i[13]),
|
||||
.O(gpr_we_a_o_INST_0_i_1_n_0));
|
||||
LUT6 #(
|
||||
.INIT(64'hF1F1F0F0F1F1F0FF))
|
||||
illegal_instr_o_INST_0
|
||||
(.I0(illegal_instr_o_INST_0_i_1_n_0),
|
||||
.I1(fetched_instr_i[6]),
|
||||
.I2(illegal_instr_o_INST_0_i_2_n_0),
|
||||
.I3(fetched_instr_i[3]),
|
||||
.I4(fetched_instr_i[4]),
|
||||
.I5(illegal_instr_o_INST_0_i_3_n_0),
|
||||
.O(illegal_instr_o));
|
||||
(* SOFT_HLUTNM = "soft_lutpair0" *)
|
||||
LUT5 #(
|
||||
.INIT(32'hAEAEAEFE))
|
||||
illegal_instr_o_INST_0_i_1
|
||||
(.I0(fetched_instr_i[2]),
|
||||
.I1(\ex_op_b_sel_o[0]_INST_0_i_2_n_0 ),
|
||||
.I2(fetched_instr_i[5]),
|
||||
.I3(\alu_op_o[3]_INST_0_i_4_n_0 ),
|
||||
.I4(\ex_op_b_sel_o[0]_INST_0_i_6_n_0 ),
|
||||
.O(illegal_instr_o_INST_0_i_1_n_0));
|
||||
LUT6 #(
|
||||
.INIT(64'hFFFFFFFFFFFFFFFE))
|
||||
illegal_instr_o_INST_0_i_10
|
||||
(.I0(fetched_instr_i[14]),
|
||||
.I1(fetched_instr_i[12]),
|
||||
.I2(fetched_instr_i[13]),
|
||||
.I3(fetched_instr_i[9]),
|
||||
.I4(fetched_instr_i[7]),
|
||||
.I5(fetched_instr_i[30]),
|
||||
.O(illegal_instr_o_INST_0_i_10_n_0));
|
||||
LUT6 #(
|
||||
.INIT(64'hEEEEEEEEFFFFFFFE))
|
||||
illegal_instr_o_INST_0_i_2
|
||||
(.I0(illegal_instr_o_INST_0_i_4_n_0),
|
||||
.I1(illegal_instr_o_INST_0_i_5_n_0),
|
||||
.I2(\ex_op_b_sel_o[0]_INST_0_i_6_n_0 ),
|
||||
.I3(illegal_instr_o_INST_0_i_6_n_0),
|
||||
.I4(illegal_instr_o_INST_0_i_7_n_0),
|
||||
.I5(illegal_instr_o_INST_0_i_8_n_0),
|
||||
.O(illegal_instr_o_INST_0_i_2_n_0));
|
||||
LUT6 #(
|
||||
.INIT(64'hC080C091C1D1C1D1))
|
||||
illegal_instr_o_INST_0_i_3
|
||||
(.I0(fetched_instr_i[2]),
|
||||
.I1(fetched_instr_i[6]),
|
||||
.I2(fetched_instr_i[5]),
|
||||
.I3(fetched_instr_i[14]),
|
||||
.I4(fetched_instr_i[12]),
|
||||
.I5(fetched_instr_i[13]),
|
||||
.O(illegal_instr_o_INST_0_i_3_n_0));
|
||||
LUT6 #(
|
||||
.INIT(64'h30FFF0B0FFFFFFFF))
|
||||
illegal_instr_o_INST_0_i_4
|
||||
(.I0(\ex_op_a_sel_o[0]_INST_0_i_1_n_0 ),
|
||||
.I1(fetched_instr_i[2]),
|
||||
.I2(fetched_instr_i[3]),
|
||||
.I3(fetched_instr_i[5]),
|
||||
.I4(fetched_instr_i[6]),
|
||||
.I5(\ex_op_a_sel_o[1]_INST_0_i_1_n_0 ),
|
||||
.O(illegal_instr_o_INST_0_i_4_n_0));
|
||||
(* SOFT_HLUTNM = "soft_lutpair5" *)
|
||||
LUT4 #(
|
||||
.INIT(16'h4000))
|
||||
illegal_instr_o_INST_0_i_5
|
||||
(.I0(fetched_instr_i[3]),
|
||||
.I1(fetched_instr_i[2]),
|
||||
.I2(\ex_op_a_sel_o[0]_INST_0_i_1_n_0 ),
|
||||
.I3(fetched_instr_i[6]),
|
||||
.O(illegal_instr_o_INST_0_i_5_n_0));
|
||||
LUT6 #(
|
||||
.INIT(64'hFFFFFFFFFFFFFFFE))
|
||||
illegal_instr_o_INST_0_i_6
|
||||
(.I0(fetched_instr_i[10]),
|
||||
.I1(fetched_instr_i[21]),
|
||||
.I2(fetched_instr_i[23]),
|
||||
.I3(fetched_instr_i[16]),
|
||||
.I4(fetched_instr_i[19]),
|
||||
.I5(fetched_instr_i[11]),
|
||||
.O(illegal_instr_o_INST_0_i_6_n_0));
|
||||
LUT6 #(
|
||||
.INIT(64'hFFFFFFFFFFFFFFFE))
|
||||
illegal_instr_o_INST_0_i_7
|
||||
(.I0(illegal_instr_o_INST_0_i_9_n_0),
|
||||
.I1(fetched_instr_i[17]),
|
||||
.I2(fetched_instr_i[18]),
|
||||
.I3(fetched_instr_i[8]),
|
||||
.I4(fetched_instr_i[24]),
|
||||
.I5(illegal_instr_o_INST_0_i_10_n_0),
|
||||
.O(illegal_instr_o_INST_0_i_7_n_0));
|
||||
(* SOFT_HLUTNM = "soft_lutpair5" *)
|
||||
LUT3 #(
|
||||
.INIT(8'h1F))
|
||||
illegal_instr_o_INST_0_i_8
|
||||
(.I0(fetched_instr_i[6]),
|
||||
.I1(fetched_instr_i[3]),
|
||||
.I2(fetched_instr_i[4]),
|
||||
.O(illegal_instr_o_INST_0_i_8_n_0));
|
||||
LUT4 #(
|
||||
.INIT(16'hFFFE))
|
||||
illegal_instr_o_INST_0_i_9
|
||||
(.I0(fetched_instr_i[3]),
|
||||
.I1(fetched_instr_i[2]),
|
||||
.I2(fetched_instr_i[15]),
|
||||
.I3(fetched_instr_i[22]),
|
||||
.O(illegal_instr_o_INST_0_i_9_n_0));
|
||||
LUT5 #(
|
||||
.INIT(32'h80000000))
|
||||
jal_o_INST_0
|
||||
(.I0(fetched_instr_i[3]),
|
||||
.I1(\ex_op_b_sel_o[2]_INST_0_i_1_n_0 ),
|
||||
.I2(fetched_instr_i[2]),
|
||||
.I3(fetched_instr_i[1]),
|
||||
.I4(fetched_instr_i[0]),
|
||||
.O(jal_o));
|
||||
LUT6 #(
|
||||
.INIT(64'h0000000008000000))
|
||||
jalr_o_INST_0
|
||||
(.I0(fetched_instr_i[1]),
|
||||
.I1(fetched_instr_i[0]),
|
||||
.I2(fetched_instr_i[3]),
|
||||
.I3(\ex_op_b_sel_o[2]_INST_0_i_1_n_0 ),
|
||||
.I4(fetched_instr_i[2]),
|
||||
.I5(\ex_op_a_sel_o[0]_INST_0_i_1_n_0 ),
|
||||
.O(jalr_o));
|
||||
LUT5 #(
|
||||
.INIT(32'h002A222A))
|
||||
mem_req_o_INST_0
|
||||
(.I0(\ex_op_b_sel_o[1]_INST_0_i_2_n_0 ),
|
||||
.I1(fetched_instr_i[14]),
|
||||
.I2(fetched_instr_i[5]),
|
||||
.I3(fetched_instr_i[13]),
|
||||
.I4(fetched_instr_i[12]),
|
||||
.O(mem_req_o));
|
||||
LUT6 #(
|
||||
.INIT(64'h0000400040004000))
|
||||
\mem_size_o[0]_INST_0
|
||||
(.I0(fetched_instr_i[13]),
|
||||
.I1(fetched_instr_i[12]),
|
||||
.I2(branch_o_INST_0_i_1_n_0),
|
||||
.I3(\mem_size_o[0]_INST_0_i_1_n_0 ),
|
||||
.I4(fetched_instr_i[14]),
|
||||
.I5(fetched_instr_i[5]),
|
||||
.O(mem_size_o[0]));
|
||||
(* SOFT_HLUTNM = "soft_lutpair7" *)
|
||||
LUT2 #(
|
||||
.INIT(4'h1))
|
||||
\mem_size_o[0]_INST_0_i_1
|
||||
(.I0(fetched_instr_i[4]),
|
||||
.I1(fetched_instr_i[6]),
|
||||
.O(\mem_size_o[0]_INST_0_i_1_n_0 ));
|
||||
LUT6 #(
|
||||
.INIT(64'h0000000000100000))
|
||||
\mem_size_o[1]_INST_0
|
||||
(.I0(fetched_instr_i[4]),
|
||||
.I1(fetched_instr_i[6]),
|
||||
.I2(branch_o_INST_0_i_1_n_0),
|
||||
.I3(fetched_instr_i[12]),
|
||||
.I4(fetched_instr_i[13]),
|
||||
.I5(fetched_instr_i[14]),
|
||||
.O(mem_size_o[1]));
|
||||
LUT6 #(
|
||||
.INIT(64'h0000000000100000))
|
||||
\mem_size_o[2]_INST_0
|
||||
(.I0(fetched_instr_i[4]),
|
||||
.I1(fetched_instr_i[6]),
|
||||
.I2(branch_o_INST_0_i_1_n_0),
|
||||
.I3(fetched_instr_i[13]),
|
||||
.I4(fetched_instr_i[14]),
|
||||
.I5(fetched_instr_i[5]),
|
||||
.O(mem_size_o[2]));
|
||||
LUT5 #(
|
||||
.INIT(32'h002A0000))
|
||||
mem_we_o_INST_0
|
||||
(.I0(\ex_op_b_sel_o[1]_INST_0_i_2_n_0 ),
|
||||
.I1(fetched_instr_i[12]),
|
||||
.I2(fetched_instr_i[13]),
|
||||
.I3(fetched_instr_i[14]),
|
||||
.I4(fetched_instr_i[5]),
|
||||
.O(mem_we_o));
|
||||
LUT5 #(
|
||||
.INIT(32'h0000222A))
|
||||
wb_src_sel_o_INST_0
|
||||
(.I0(\ex_op_b_sel_o[1]_INST_0_i_2_n_0 ),
|
||||
.I1(fetched_instr_i[13]),
|
||||
.I2(fetched_instr_i[12]),
|
||||
.I3(fetched_instr_i[14]),
|
||||
.I4(fetched_instr_i[5]),
|
||||
.O(wb_src_sel_o));
|
||||
endmodule
|
151
Labs/06. Datapath/README.md
Normal file
151
Labs/06. Datapath/README.md
Normal file
@@ -0,0 +1,151 @@
|
||||
# Лабораторная работа 6 "Тракт данных"
|
||||
|
||||
Микроархитектуру можно разделить на две части: тракт данных и устройство управления. По тракту данных перемещаются данные (из памяти инструкций, регистрового файла, АЛУ, памяти данных, мультиплексоров), а устройство управления (основной дешифратор команд) получает текущую инструкцию из тракта и в ответ говорит ему как именно выполнить эту инструкцию, то есть управляет тем, как эти данные будут через тракт данных проходить.
|
||||
|
||||
## Цель
|
||||
|
||||
Описать на языке **SystemVerilog** процессор с архитектурой **RISC-V**, реализовав его тракт данных, использую ранее разработанные блоки, и подключив к нему устройство управления. В рамках этого трека лабораторных работ требуется реализовать только поддержку обработки слов (то есть БЕЗ инструкций связанных с байтами и полусловами: `lh`, `lhu`, `lb`, `lbu`, `sh`, `sb`).
|
||||
|
||||
## Ход работы
|
||||
|
||||
1. Изучить микроархитектурную реализацию однотактного процессора RISC-V (без поддержки команд загрузки/сохранения байт/полуслов)
|
||||
2. Реализовать тракт данных с подключенным к нему устройством управления([#задание](#задание))
|
||||
3. Подготовить программу по индивидуальному заданию и загрузить ее в память инструкций
|
||||
4. Сравнить результат работы процессора на модели в **Vivado** и в симуляторе программы ассемблера
|
||||
|
||||
## Микроархитектура RISC-V
|
||||
|
||||
### riscv_core
|
||||
|
||||
Рассмотрим микроархитектуру процессорного ядра `riscv_core`.
|
||||
|
||||

|
||||
|
||||
В отличие от реализованного ранее процессора с архитектурой Cybercobra, в данном модуле отсутствует память (она подключается извне, а значит у этого модуля должны быть сигналы интерфейса памяти).
|
||||
|
||||
Кроме того, в данной микроархитектуре используется пять различных видов констант (соответствующих определенным типам инструкций).
|
||||
|
||||
Константы `I`,`U`,`S` используются для вычисления адресов и значений. Поэтому все эти константы должны быть подключены к АЛУ. А значит теперь, для выбора значения для операндов требуются мультиплексоры, определяющие что именно будет подаваться на АЛУ.
|
||||
|
||||
Обратите внимание на константу `imm_U`. В отличие от всех остальных констант, она не знакорасширяется, вместо этого к ней приклеивается справа 12 нулевых бит.
|
||||
|
||||
Программный счетчик (`PC`) теперь также изменяется более сложным образом. Поскольку появился еще один вид безусловного перехода (`jalr`), программный счетчик может не просто увеличиться на значение константы из инструкции, но и получить совершенно новое значение в виде суммы константы и значения из регистрового файла (см. на самый левый мультиплексор схемы).
|
||||
|
||||
Поскольку обращение во внешнюю память требует времени, необходимо останавливать программный счетчик, чтобы до конца обращения в память не начались исполняться последующие инструкции. Для этого у программного счетчика появился управляющий сигнал `stall_i`. Программный счетчик может меняться только когда этот сигнал равен нулю (иными словами, инверсия этого сигнала является сигналом `enable` для регистра `PC`).
|
||||
|
||||
### riscv_unit
|
||||
|
||||
После реализации процессорного ядра, к нему необходимо подключить память. Это происходит в модуле `riscv_unit`.
|
||||
|
||||

|
||||
|
||||
Обратите внимание на регистр `stall`. Этот регистр и будет управлять разрешением на запись в программный счетчик `PC`. Поскольку мы используем блочную память, расположенную прямо в ПЛИС, доступ к ней осуществляется за 1 такт, а значит, что при обращении в память, нам необходимо "отключить" программный счетчик ровно на 1 такт. Если бы использовалась действительно "внешняя" память (например чип DDR3), то вместо этого регистра появилась бы другая логика, выставляющая на вход ядра `stall_i` единицу пока идет обращение в память.
|
||||
|
||||
## Задание
|
||||
|
||||
Реализовать ядро процессора `riscv_core` архитектуры RISC-V по предложенной микроархитектуре. Подключить к нему память инструкций и память данных в модуле `riscv_unit`. Проверить работу процессора с помощью программы, написанной на ассемблере RISC-V по индивидуальному заданию, которое использовалось для написания программы для процессора архитектуры CYBERcobra.
|
||||
|
||||
### Как инициализировать память инструкций новой программой
|
||||
|
||||
Поскольку теперь ваш процессор почти полностью соответствует спецификации RISC-V, вы можете пользоваться существующими компиляторами, а значит, теперь для написании программы можно воспользоваться языком ассемблера RISC-V (помните, что пока вы не поддерживаете инструкции `lh`, `lhu`, `lb`, `lbu`, `sh`, `sb`).
|
||||
|
||||
Обычно ассемблеры выдают код собранной программы в виде шестнадцатеричных строк. При записи программы в файл инициализации, вы должны убрать префикс `0x`, если таковой имеется, поскольку системная функция инициализации памяти `$readmemh` и так уже настроена читать в шестнадцатеричном формате.
|
||||
|
||||
Кроме того, поскольку каждая ячейка памяти занимает 8 бит, необходимо разбить строки инструкции на отдельные байты. Однако после того как вы это сделаете, нарушится порядок байт. Микроархитектурная реализация процессора построена с использованием порядка байт под названием **Little endian**. Это означает, что старший байт инструкции должен располагаться по старшему адресу, младший байт инструкции — по младшему (привязка к **Little endian** вытекает из двух модулей: памяти инструкций и декодера инструкций). Проблема заключается в том, что функция `$readmemh` загружает байты, начиная с младших адресов.
|
||||
|
||||
Предположим, мы описываем содержимое памяти инструкций и у нас есть очередная инструкция `0xDEADBEEF` (`jal`). Если она должна быть размещена в памяти, начиная с адреса `4`, то байт `EF` должен находиться по 4-ому адресу, байт `BE` — по пятому и т.п. Допустим, мы разделили байты инструкций символами переноса строк (и что строки в файле нумеруются с нуля). Тогда соответствие между строкой, байтом инструкции и адресом в памяти, где этот байт должен быть расположен будет следующим:
|
||||
|
||||

|
||||
|
||||
Если после разделения инструкции переносами, мы не изменим порядок байт в файле, при считывании файла САПР будет инициализировать память наоборот: ячейка с младшим адресом будет проинициализирована строкой с младшим номером. Если оставить все как есть, процессор считает из памяти инструкцию `0xEFBEADDE` (вместо jal получаем нелегальную инструкцию, т.к. младшие 2 бита не равны 1).
|
||||
|
||||
Чтобы данные легли в память в нужном порядке, **необходимо изменить порядок следования байт в текстовом файле**. Современные текстовые редакторы поддерживают режим множественных курсоров, что позволяет довольно быстро выполнить данную процедуру.
|
||||
|
||||
<details>
|
||||
<summary> Пример такого редактирования </summary>
|
||||
В VSCode дополнительные курсоры создаются либо через `alt+ЛКМ`, либо через `alt+ctrl+UP`, `alt+ctrl+DOWN`. Vivado так же поддерживает множественные курсоры (проведя мышью с зажатой ЛКМ вдоль нужных строк при зажатой клавише `Ctrl`).
|
||||
|
||||

|
||||
|
||||
</details>
|
||||
|
||||
Напишем простую программу, которая использует все типы инструкций для проверки нашего процессора. Сначала напишем программу на ассемблере:
|
||||
|
||||
```assembly
|
||||
00: addi x1, x0, 0x75С
|
||||
04: addi x2, x0, 0x8A7
|
||||
08: add x3, x1, x2
|
||||
0C: and x4, x1, x2
|
||||
10: sub x5, x4, x3
|
||||
14: mul x6, x3, x4 // неподдерживаемая инструкция
|
||||
18: jal x15, jlr
|
||||
1C: jalr x15, 0x0(x6)
|
||||
20: slli x7, x5, 31
|
||||
24: srai x8, x7, 1
|
||||
28: srli x9, x8, 29
|
||||
2C: sw x1, 0x0(x5)
|
||||
30: lw x10, 0x0(x5)
|
||||
34: lui x11, 0xFFF80
|
||||
38: auipc x12, 0x00004
|
||||
3C: bne x3, x4, 0x04
|
||||
40: jal x13, 0x00004
|
||||
44: jalr x14, 0x0(x13)
|
||||
48: jalr x15, 0x4(x15)
|
||||
```
|
||||
|
||||
Теперь в соответствии с кодировкой инструкций переведем программу в машинные коды:
|
||||
|
||||
```text
|
||||
00: 011101011100 00000 000 00001 0010011
|
||||
04: 100010100111 00000 000 00010 0010011
|
||||
08: 0000000 00001 00010 000 00011 0110011
|
||||
0C: 0000000 00001 00010 111 00100 0110011
|
||||
10: 0100000 00011 00100 000 00101 0110011
|
||||
14: 0000001 00011 00100 000 00110 0110011
|
||||
18: 00000011000000000000 01111 1101111
|
||||
1C: 000000000000 00110 000 01111 1100111
|
||||
20: 0000000 11111 00101 001 00111 0010011
|
||||
24: 0100000 00001 00111 101 01000 0010011
|
||||
28: 0000000 11101 01000 101 01001 0010011
|
||||
2C: 0000000 00001 00101 010 00000 0100011
|
||||
30: 000000000000 00101 010 01010 0000011
|
||||
34: 11111111111110000000 01011 0110111
|
||||
38: 00000000000000000100 01100 0010111
|
||||
3C: 0000000 00011 00100 001 00100 1100011
|
||||
40: 00000000010000000000 01101 1101111
|
||||
44: 000000000000 01101 000 01110 1100111
|
||||
48: 000000000100 01111 000 01111 1100111
|
||||
```
|
||||
|
||||
Данная программа, представленная в шестнадцатиричном формате находится в файле [program.txt](program.txt).
|
||||
|
||||
## Порядок выполнения задания
|
||||
|
||||
1. Внимательно ознакомьтесь микроархитектурной реализацией. В случае возникновения вопросов, проконсультируйтесь с преподавателем.
|
||||
2. Реализуйте модуль `riscv_core`. Для этого:
|
||||
1. В `Design Sources` проекта с предыдущих лаб, создайте `SystemSystemVerilog`-файл `riscv_core.sv`.
|
||||
2. Опишите в нем модуль процессор `riscv_core` с таким же именем и портами, как указано в [задании](#задание).
|
||||
1. Процесс реализации модуля очень похож на процесс описания модуля cybercobra, однако теперь появляется:
|
||||
1. декодер
|
||||
2. дополнительные мультиплексоры и знакорасширители.
|
||||
3. Создайте в проекте новый `SystemSystemVerilog`-файл `riscv_unit.sv` и опишите в нем модуль `riscv_unit`, объединяющий ядро процессора (`riscv_core`) с памятями инструкция и данных.
|
||||
3. После описания модуля, его необходимо проверить с помощью тестового окружения.
|
||||
1. Тестовое окружение находится [`здесь`](tb_riscv_unit.sv).
|
||||
2. Программа, которой необходимо проинициализировать память инструкций находится [`здесь`](program.txt).
|
||||
3. Для запуска симуляции воспользуйтесь [`этой инструкцией`](../../Vivado%20Basics/Run%20Simulation.md).
|
||||
4. Перед запуском симуляции убедитесь, что выбран правильный модуль верхнего уровня.
|
||||
5. **Во время симуляции убедитесь, что в логе есть сообщение о завершении теста!**
|
||||
6. Вполне возможно, что после первого запуска вы столкнетесь с сообщениями о множестве ошибок. Вам необходимо [исследовать](../../Vivado%20Basics/Debug%20manual.md) эти ошибки на временной диаграмме и исправить их в вашем модуле.
|
||||
4. Добавьте в проект модуль верхнего уровня ([nexys_riscv_unit.sv](board%20files/nexys_riscv_unit.sv)), соединяющий основной ваш процессор с периферией в ПЛИС. Описание работы модуля находится [здесь](board%20files).
|
||||
5. Подключите к проекту файл ограничений ([nexys_a7_100t.xdc](board%20files/nexys_a7_100t.xdc)), если тот еще не был подключен, либо замените его содержимое данными из файла к этой лабораторной работе.
|
||||
6. Проверьте работу процессора в ПЛИС.
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Прочти меня, когда выполнишь.</summary>
|
||||
Поздравляю, ты сделал(а) свой первый взрослый процессор! Теперь ты можешь говорить:
|
||||
|
||||
>Я способен(на) на всё! Я сам(а) полностью, с нуля, сделал(а) процессор с архитектурой RISC-V! Что? Не знаешь, что такое архитектура? Пф, щегол! Подрастешь – узнаешь
|
||||
|
||||
</details>
|
5
Labs/06. Datapath/board files/README.md
Normal file
5
Labs/06. Datapath/board files/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Проверка работы riscv_unit на ПЛИС
|
||||
|
||||
Если вы не понимаете, что лежит в этой папке, или если надо вспомнить, как прошить ПЛИС, можно воспользоваться [`этой инструкцией`](../../../Vivado%20Basics/Program%20nexys%20a7.md)
|
||||
|
||||
Файл [`nexys_riscv_unit.v`](nexys_riscv_unit.v), который нужно запускать с [`демонстрационным файлом инструкций`](../data_path.txt), является демонстрацией работы вашего ядра, каждое нажатие на BTND формирует тактовый импульс, впоследствии пошагово переходя по инструкциям, которые в свою очередь отображаются на семисегментных индикаторах.
|
211
Labs/06. Datapath/board files/nexys_a7_100t.xdc
Normal file
211
Labs/06. Datapath/board files/nexys_a7_100t.xdc
Normal file
@@ -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
|
148
Labs/06. Datapath/board files/nexys_riscv_unit.sv
Normal file
148
Labs/06. Datapath/board files/nexys_riscv_unit.sv
Normal file
@@ -0,0 +1,148 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module nexys_riscv_unit(
|
||||
input CLK100,
|
||||
input resetn,
|
||||
input BTND,
|
||||
output CA, CB, CC, CD, CE, CF, CG,
|
||||
output [7:0] AN
|
||||
);
|
||||
|
||||
riscv_unit unit(
|
||||
.clk_i(btn),
|
||||
.rst_i(!resetn)
|
||||
);
|
||||
|
||||
wire [31:0] instr_addr;
|
||||
wire [31:0] instr;
|
||||
reg btn;
|
||||
|
||||
assign instr_addr = unit.instr_addr;
|
||||
assign instr = unit.instr;
|
||||
|
||||
localparam pwm = 1000;
|
||||
reg [9:0] counter;
|
||||
reg [7:0] semseg;
|
||||
reg [7:0] ANreg;
|
||||
reg CAr, CBr, CCr, CDr, CEr, CFr, CGr;
|
||||
|
||||
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
|
||||
if(|(~ANreg[5:4])) begin
|
||||
case (1'b0)
|
||||
ANreg[4]: semseg <= instr_addr[3:0];
|
||||
ANreg[5]: semseg <= instr_addr[7:4];
|
||||
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 else begin
|
||||
case (1'b0)
|
||||
ANreg[7]: begin
|
||||
{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0011000;
|
||||
end
|
||||
ANreg[6]: begin
|
||||
{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0110001;
|
||||
end
|
||||
ANreg[3]: begin
|
||||
case(instr[6:2])
|
||||
5'b01101:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1111111; //LUI -
|
||||
5'b00101:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0001000; //AUIP A
|
||||
5'b11011:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1111111; //JAL -
|
||||
5'b11001:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1000111; //JALR J
|
||||
5'b11000:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1100000; //brch b
|
||||
5'b00000:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1110001; //LOAd L
|
||||
5'b01000:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0100100; //STOr S
|
||||
5'b00100:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0000001; //OPIM O
|
||||
5'b01100:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1111111; //OP -
|
||||
default: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1111001; //ILLE I
|
||||
endcase
|
||||
end
|
||||
ANreg[2]: begin
|
||||
case(instr[6:2])
|
||||
5'b01101:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1110001; //LUI L
|
||||
5'b00101:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1000001; //AUIP U
|
||||
5'b11011:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1000111; //JAL J
|
||||
5'b11001:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0001000; //JALR A
|
||||
5'b11000:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1111010; //brch r
|
||||
5'b00000:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0000001; //LOAd O
|
||||
5'b01000:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1110000; //StOr t
|
||||
5'b00100:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0011000; //OPIM P
|
||||
5'b01100:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1111111; //OP -
|
||||
default: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1110001; //ILLE L
|
||||
endcase
|
||||
end
|
||||
ANreg[1]: begin
|
||||
case(instr[6:2])
|
||||
5'b01101:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1000001; //LUI U
|
||||
5'b00101:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1111001; //AUIP I
|
||||
5'b11011:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0001000; //JAL A
|
||||
5'b11001:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1110001; //JALR L
|
||||
5'b11000:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1110010; //brch c
|
||||
5'b00000:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0001000; //LOAd A
|
||||
5'b01000:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0000001; //STOr O
|
||||
5'b00100:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1111001; //OPIM I
|
||||
5'b01100:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0000001; //OP O
|
||||
default: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1110001; //ILLE L
|
||||
endcase
|
||||
end
|
||||
ANreg[0]: begin
|
||||
case(instr[6:2])
|
||||
5'b01101:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1111001; //LUI I
|
||||
5'b00101:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0011000; //AUIP P
|
||||
5'b11011:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1110001; //JAL L
|
||||
5'b11001:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1111010; //JALr r
|
||||
5'b11000:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1101000; //brch h
|
||||
5'b00000:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1000010; //LOAd d
|
||||
5'b01000:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b1111010; //STOr r
|
||||
5'b00100:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0101010; //OPIM M
|
||||
5'b01100:{CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0011000; //OP P
|
||||
default: {CAr, CBr, CCr, CDr, CEr, CFr, CGr} <= 7'b0110000; //ILLE E
|
||||
endcase
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
endmodule
|
76
Labs/06. Datapath/program.txt
Normal file
76
Labs/06. Datapath/program.txt
Normal file
@@ -0,0 +1,76 @@
|
||||
93
|
||||
00
|
||||
c0
|
||||
75
|
||||
13
|
||||
01
|
||||
70
|
||||
8a
|
||||
b3
|
||||
01
|
||||
11
|
||||
00
|
||||
33
|
||||
72
|
||||
11
|
||||
00
|
||||
b3
|
||||
02
|
||||
32
|
||||
40
|
||||
33
|
||||
83
|
||||
41
|
||||
02
|
||||
ef
|
||||
07
|
||||
00
|
||||
03
|
||||
e7
|
||||
07
|
||||
03
|
||||
00
|
||||
93
|
||||
93
|
||||
f2
|
||||
01
|
||||
13
|
||||
d4
|
||||
13
|
||||
40
|
||||
93
|
||||
54
|
||||
d4
|
||||
01
|
||||
23
|
||||
a0
|
||||
12
|
||||
00
|
||||
03
|
||||
a5
|
||||
02
|
||||
00
|
||||
b7
|
||||
05
|
||||
f8
|
||||
ff
|
||||
17
|
||||
46
|
||||
00
|
||||
00
|
||||
63
|
||||
92
|
||||
41
|
||||
00
|
||||
ef
|
||||
06
|
||||
40
|
||||
00
|
||||
67
|
||||
87
|
||||
06
|
||||
00
|
||||
e7
|
||||
87
|
||||
47
|
||||
00
|
51
Labs/06. Datapath/tb_riscv_unit.sv
Normal file
51
Labs/06. Datapath/tb_riscv_unit.sv
Normal file
@@ -0,0 +1,51 @@
|
||||
`timescale 1ns / 1ps
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Company: MIET
|
||||
// Engineer: Nikita Bulavin
|
||||
//
|
||||
// Create Date:
|
||||
// Design Name:
|
||||
// Module Name: tb_riscv_unit
|
||||
// Project Name: RISCV_practicum
|
||||
// Target Devices: Nexys A7-100T
|
||||
// Tool Versions:
|
||||
// Description: tb for datapath
|
||||
//
|
||||
// Dependencies:
|
||||
//
|
||||
// Revision:
|
||||
// Revision 0.01 - File Created
|
||||
// Additional Comments:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
module tb_riscv_unit();
|
||||
|
||||
reg clk;
|
||||
reg rst;
|
||||
|
||||
riscv_unit unit(
|
||||
.clk_i(clk),
|
||||
.rst_i(rst)
|
||||
);
|
||||
|
||||
initial clk = 0;
|
||||
always #10 clk = ~clk;
|
||||
initial begin
|
||||
$display( "\nStart test: \n\n==========================\nCLICK THE BUTTON 'Run All'\n==========================\n"); $stop();
|
||||
rst = 1;
|
||||
#20;
|
||||
rst = 0;
|
||||
#500;
|
||||
$display("\n The test is over \n See the internal signals of the module on the waveform \n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
stall: assert property (
|
||||
@(posedge clk)
|
||||
disable iff ( rst )
|
||||
(top.mem_req) |-> (top.stall) |-> ##1 (!top.stall & top.mem_req)
|
||||
|
||||
)else $error("\n================================================\nThe realisation of the STALL signal is INCORRECT\n================================================\n");
|
||||
|
||||
endmodule
|
25
Labs/07. Load-store unit/README.md
Normal file
25
Labs/07. Load-store unit/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Лабораторная работа 5 "Блок загрузки и сохранения"
|
||||
|
||||
## Цель
|
||||
|
||||
--
|
||||
|
||||
## Ход работы
|
||||
|
||||
--
|
||||
|
||||
## Теория
|
||||
|
||||
==
|
||||
|
||||
## Практика
|
||||
|
||||
--
|
||||
|
||||
## Задание
|
||||
|
||||
--
|
||||
|
||||
### Порядок выполнения задания
|
||||
|
||||
--
|
65
Labs/07. Load-store unit/miriscv_ram.sv
Normal file
65
Labs/07. Load-store unit/miriscv_ram.sv
Normal file
@@ -0,0 +1,65 @@
|
||||
module miriscv_ram
|
||||
#(
|
||||
parameter RAM_SIZE = 256, // bytes
|
||||
parameter RAM_INIT_FILE = ""
|
||||
)
|
||||
(
|
||||
// clock, reset
|
||||
input clk_i,
|
||||
input rst_n_i,
|
||||
|
||||
// instruction memory interface
|
||||
output logic [31:0] instr_rdata_o,
|
||||
input [31:0] instr_addr_i,
|
||||
|
||||
// data memory interface
|
||||
output logic [31:0] data_rdata_o,
|
||||
input data_req_i,
|
||||
input data_we_i,
|
||||
input [3:0] data_be_i,
|
||||
input [31:0] data_addr_i,
|
||||
input [31:0] data_wdata_i
|
||||
);
|
||||
|
||||
reg [31:0] mem [0:RAM_SIZE/4-1];
|
||||
reg [31:0] data_int;
|
||||
|
||||
//Init RAM
|
||||
integer ram_index;
|
||||
|
||||
initial begin
|
||||
if(RAM_INIT_FILE != "")
|
||||
$readmemh(RAM_INIT_FILE, mem);
|
||||
else
|
||||
for (ram_index = 0; ram_index < RAM_SIZE/4-1; ram_index = ram_index + 1)
|
||||
mem[ram_index] = {32{1'b0}};
|
||||
end
|
||||
|
||||
|
||||
//Instruction port
|
||||
assign instr_rdata_o = mem[(instr_addr_i % RAM_SIZE) / 4];
|
||||
|
||||
always@(posedge clk_i) begin
|
||||
if(!rst_n_i) begin
|
||||
data_rdata_o <= 32'b0;
|
||||
end
|
||||
else if(data_req_i) begin
|
||||
data_rdata_o <= mem[(data_addr_i % RAM_SIZE) / 4];
|
||||
|
||||
if(data_we_i && data_be_i[0])
|
||||
mem [data_addr_i[31:2]] [7:0] <= data_wdata_i[7:0];
|
||||
|
||||
if(data_we_i && data_be_i[1])
|
||||
mem [data_addr_i[31:2]] [15:8] <= data_wdata_i[15:8];
|
||||
|
||||
if(data_we_i && data_be_i[2])
|
||||
mem [data_addr_i[31:2]] [23:16] <= data_wdata_i[23:16];
|
||||
|
||||
if(data_we_i && data_be_i[3])
|
||||
mem [data_addr_i[31:2]] [31:24] <= data_wdata_i[31:24];
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
endmodule
|
74
Labs/07. Load-store unit/miriscv_top.sv
Normal file
74
Labs/07. Load-store unit/miriscv_top.sv
Normal file
@@ -0,0 +1,74 @@
|
||||
module miriscv_top
|
||||
#(
|
||||
parameter RAM_SIZE = 256, // bytes
|
||||
parameter RAM_INIT_FILE = ""
|
||||
)
|
||||
(
|
||||
// clock, reset
|
||||
input clk_i,
|
||||
input rst_n_i
|
||||
);
|
||||
|
||||
logic [31:0] instr_rdata_core;
|
||||
logic [31:0] instr_addr_core;
|
||||
|
||||
logic [31:0] data_rdata_core;
|
||||
logic data_req_core;
|
||||
logic data_we_core;
|
||||
logic [3:0] data_be_core;
|
||||
logic [31:0] data_addr_core;
|
||||
logic [31:0] data_wdata_core;
|
||||
|
||||
logic [31:0] data_rdata_ram;
|
||||
logic data_req_ram;
|
||||
logic data_we_ram;
|
||||
logic [3:0] data_be_ram;
|
||||
logic [31:0] data_addr_ram;
|
||||
logic [31:0] data_wdata_ram;
|
||||
|
||||
logic data_mem_valid;
|
||||
assign data_mem_valid = (data_addr_core >= RAM_SIZE) ? 1'b0 : 1'b1;
|
||||
|
||||
assign data_rdata_core = (data_mem_valid) ? data_rdata_ram : 1'b0;
|
||||
assign data_req_ram = (data_mem_valid) ? data_req_core : 1'b0;
|
||||
assign data_we_ram = data_we_core;
|
||||
assign data_be_ram = data_be_core;
|
||||
assign data_addr_ram = data_addr_core;
|
||||
assign data_wdata_ram = data_wdata_core;
|
||||
|
||||
miriscv_core core (
|
||||
.clk_i ( clk_i ),
|
||||
.arstn_i ( rst_n_i ),
|
||||
|
||||
.instr_rdata_i ( instr_rdata_core ),
|
||||
.instr_addr_o ( instr_addr_core ),
|
||||
|
||||
.data_rdata_i ( data_rdata_core ),
|
||||
.data_req_o ( data_req_core ),
|
||||
.data_we_o ( data_we_core ),
|
||||
.data_be_o ( data_be_core ),
|
||||
.data_addr_o ( data_addr_core ),
|
||||
.data_wdata_o ( data_wdata_core )
|
||||
);
|
||||
|
||||
miriscv_ram
|
||||
#(
|
||||
.RAM_SIZE (RAM_SIZE),
|
||||
.RAM_INIT_FILE (RAM_INIT_FILE)
|
||||
) ram (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_n_i ( rst_n_i ),
|
||||
|
||||
.instr_rdata_o ( instr_rdata_core ),
|
||||
.instr_addr_i ( instr_addr_core ),
|
||||
|
||||
.data_rdata_o ( data_rdata_ram ),
|
||||
.data_req_i ( data_req_ram ),
|
||||
.data_we_i ( data_we_ram ),
|
||||
.data_be_i ( data_be_ram ),
|
||||
.data_addr_i ( data_addr_ram ),
|
||||
.data_wdata_i ( data_wdata_ram )
|
||||
);
|
||||
|
||||
|
||||
endmodule
|
33
Labs/07. Load-store unit/tb_miriscv_top.v
Normal file
33
Labs/07. Load-store unit/tb_miriscv_top.v
Normal file
@@ -0,0 +1,33 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module tb_miriscv_top();
|
||||
|
||||
parameter HF_CYCLE = 2.5; // 200 MHz clock
|
||||
parameter RST_WAIT = 10; // 10 ns reset
|
||||
parameter RAM_SIZE = 512; // in 32-bit words
|
||||
|
||||
// clock, reset
|
||||
reg clk;
|
||||
reg rst_n;
|
||||
|
||||
miriscv_top #(
|
||||
.RAM_SIZE ( RAM_SIZE ),
|
||||
.RAM_INIT_FILE ( "program_sort.dat" )
|
||||
) dut (
|
||||
.clk_i ( clk ),
|
||||
.rst_n_i ( rst_n )
|
||||
);
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst_n = 1'b0;
|
||||
#RST_WAIT;
|
||||
rst_n = 1'b1;
|
||||
end
|
||||
|
||||
always begin
|
||||
#HF_CYCLE;
|
||||
clk = ~clk;
|
||||
end
|
||||
|
||||
endmodule
|
BIN
Labs/07. Load-store unit/ЛР5. Блок загрузки-сохранения.pdf
Normal file
BIN
Labs/07. Load-store unit/ЛР5. Блок загрузки-сохранения.pdf
Normal file
Binary file not shown.
25
Labs/08. Interrupt subsystem/README.md
Normal file
25
Labs/08. Interrupt subsystem/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Лабораторная работа 6 "Подсистема прерывания"
|
||||
|
||||
## Цель
|
||||
|
||||
--
|
||||
|
||||
## Ход работы
|
||||
|
||||
--
|
||||
|
||||
## Теория
|
||||
|
||||
==
|
||||
|
||||
## Практика
|
||||
|
||||
--
|
||||
|
||||
## Задание
|
||||
|
||||
--
|
||||
|
||||
### Порядок выполнения задания
|
||||
|
||||
--
|
BIN
Labs/08. Interrupt subsystem/ЛР6. Подсистема прерывания.pdf
Normal file
BIN
Labs/08. Interrupt subsystem/ЛР6. Подсистема прерывания.pdf
Normal file
Binary file not shown.
95
Labs/09. Peripheral units/PS2Receiver.v
Normal file
95
Labs/09. Peripheral units/PS2Receiver.v
Normal file
@@ -0,0 +1,95 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module PS2Receiver(
|
||||
input clk,
|
||||
input kclk,
|
||||
input kdata,
|
||||
output reg [15:0] keycodeout,
|
||||
output keycode_valid
|
||||
);
|
||||
|
||||
reg flag;
|
||||
reg [3:0] flag_shift;
|
||||
wire kclkf, kdataf;
|
||||
reg [3:0]cnt;
|
||||
|
||||
assign keycode_valid = flag_shift[0] && !flag_shift[2];
|
||||
|
||||
initial begin //for tb
|
||||
cnt = 0;
|
||||
keycodeout = 0;
|
||||
flag_shift = 0;
|
||||
flag = 0;
|
||||
end
|
||||
|
||||
debouncer debounce(
|
||||
.clk(clk),
|
||||
.I0(kclk),
|
||||
.I1(kdata),
|
||||
.O0(kclkf),
|
||||
.O1(kdataf)
|
||||
);
|
||||
always@(posedge clk) begin
|
||||
flag_shift <= (flag_shift << 1) + flag;
|
||||
end
|
||||
|
||||
always@(negedge(kclkf))begin
|
||||
case(cnt)
|
||||
0:if(keycodeout != 16'hE000)keycodeout <= 0;//Start bit
|
||||
1:keycodeout[0]<=kdataf;
|
||||
2:keycodeout[1]<=kdataf;
|
||||
3:keycodeout[2]<=kdataf;
|
||||
4:keycodeout[3]<=kdataf;
|
||||
5:keycodeout[4]<=kdataf;
|
||||
6:keycodeout[5]<=kdataf;
|
||||
7:keycodeout[6]<=kdataf;
|
||||
8:keycodeout[7]<=kdataf;
|
||||
//TODO ADD PARITY CHECK
|
||||
9:begin
|
||||
flag<=1'b1;
|
||||
if(keycodeout[7:0] == 8'hE0) begin
|
||||
keycodeout <= {keycodeout[7:0], 8'd0};
|
||||
end
|
||||
end
|
||||
10:flag<=1'b0;
|
||||
default: cnt <= 0;
|
||||
endcase
|
||||
if(cnt<=9) cnt<=cnt+1;
|
||||
else if(cnt==10) cnt<=0;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
module debouncer(
|
||||
input clk,
|
||||
input I0,
|
||||
input I1,
|
||||
output reg O0,
|
||||
output reg O1
|
||||
);
|
||||
|
||||
reg [4:0]cnt0, cnt1;
|
||||
reg Iv0=0,Iv1=0;
|
||||
reg out0, out1;
|
||||
|
||||
always@(posedge(clk))begin
|
||||
if (I0==Iv0)begin
|
||||
if (cnt0==19)O0<=I0;
|
||||
else cnt0<=cnt0+1;
|
||||
end
|
||||
else begin
|
||||
cnt0<="00000";
|
||||
Iv0<=I0;
|
||||
end
|
||||
if (I1==Iv1)begin
|
||||
if (cnt1==19)O1<=I1;
|
||||
else cnt1<=cnt1+1;
|
||||
end
|
||||
else begin
|
||||
cnt1<="00000";
|
||||
Iv1<=I1;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
570
Labs/09. Peripheral units/README.md
Normal file
570
Labs/09. Peripheral units/README.md
Normal file
@@ -0,0 +1,570 @@
|
||||
# Лабораторная работа 7 "Периферийные устройства"
|
||||
|
||||
На прошлой лабораторной работе вы реализовали свой собственный RISC-V процессор. Однако пока что он находится "в вакууме" и никак не связан с внешним миром. Для исправления этого недостатка вами будет реализована системная шина, через которую к процессору смогут подключаться различные периферийные устройства.
|
||||
|
||||
## Цель
|
||||
|
||||
Интегрировать периферийные устройства в процессорную систему.
|
||||
|
||||
---
|
||||
|
||||
## Допуск к лабораторной работе
|
||||
|
||||
Для успешного выполнения лабораторной работы, вам необходимо:
|
||||
|
||||
* ознакомиться с [примером описания модуля-контроллера](../../Basic%20Verilog%20structures/Controllers.md);
|
||||
* ознакомиться с [описанием](#описание-контроллеров-периферийных-устройств) контроллеров периферийных устройств.
|
||||
|
||||
## Ход работы
|
||||
|
||||
1. Изучить теорию об адресном пространстве
|
||||
2. Получить индивидуальный вариант со своим набором периферийных устройств
|
||||
3. Интегрировать контроллеры периферийных устройств в адресное пространство вашей системы
|
||||
4. Собрать финальную схему вашей системы
|
||||
5. Проверить работу системы в ПЛИС с помощью демонстрационного ПО, загружаемого в память инструкций
|
||||
|
||||
---
|
||||
|
||||
## Теория
|
||||
|
||||
Помимо процессора и памяти, третьим ключевым элементом вычислительной системы является система ввода/вывода, обеспечивающая обмен информации между ядром вычислительной машины и периферийными устройствами.
|
||||
|
||||
Любое периферийное устройство, со стороны вычислительной машины, представляется набором ячеек памяти (регистров). С помощью чтения и записи этих регистров происходит обмен информации с периферийным устройством, и управление им. Например, датчик температуры может быть реализован самыми разными способами, но для процессора он в любом случае ячейка памяти, из которой он считывает число – температуру.
|
||||
|
||||
Система ввода/вывода может быть организована одним из двух способов: с **выделенным адресным пространством** устройств ввода/вывода, или с **совместным адресным пространством**. В первом случае система ввода/вывода имеет отдельную шину для подключения к процессору (и отдельные инструкции для обращения к периферии), во втором – шина для памяти и системы ввода/вывода общая (а обращение к периферии осуществляется теми же инструкциями, что и обращение к памяти).
|
||||
|
||||
### Адресное пространство
|
||||
|
||||
Архитектура RISC-V подразумевает использование совместного адресного пространства, это значит, что в лабораторной работе будет использована единая шина для подключения памяти и регистров управления периферийными устройствами. При обращении по одному диапазону адресов процессор будет
|
||||
попадать в память, при обращении по другим – взаимодействовать с регистрами управления/статуса периферийного устройства. Например, можно разделить 32-битное адресное пространство на 256 частей, отдав старшие 8 бит адреса под указание конкретного периферийного устройства. Тогда каждое из периферийных устройств получит 24-битное адресное пространство (16 MiB). Допустим, мы распределили эти части адресного пространства в следующем порядке (от младшего диапазона адресов к старшему):
|
||||
|
||||
0. Переключатели
|
||||
1. Светодиоды
|
||||
2. Клавиатура PS/2
|
||||
3. Семисегментные индикаторы
|
||||
4. UART-приемник
|
||||
5. UART-передатчик
|
||||
|
||||
В таком случае, если мы захотим обратиться в четвертый регистр семисегментных индикаторов, мы должны будем использовать адрес `0x03000004`. Старшие 8 бит (`0x03`) определяют выбранное периферийное устройство, оставшиеся 24 бита определяют конкретный адрес в адресном пространстве этого устройства.
|
||||
|
||||
На рисунке ниже представлен способ подключения процессора к памяти инструкций и данных, а так же 255 периферийным устройствам.
|
||||
|
||||

|
||||
|
||||
### Активация выбранного устройства
|
||||
|
||||
В зависимости от интерфейса используемой шины, периферийные устройства либо знают какой диапазон адресов им выделен (например, в интерфейсе I²C), либо нет (интерфейс APB). В первом случае, устройство понимает что к нему обратились непосредственно по адресу в данном обращении, во втором случае — по специальному сигналу.
|
||||
|
||||
На приведенной выше схеме используется второй вариант — устройство понимает, что к нему обратились по специальному сигналу `req_i`. Данный сигнал формируется из двух частей: сигнала `req` исходящего из процессорного ядра (сигнал о том, обращение в память вообще происходит) и специального сигнала-селектора исходящего из 256-разрядной шины. Формирование значения на этой шине происходит с помощью [унитарного](https://ru.wikipedia.org/wiki/Унитарный_код) ([one-hot](https://en.wikipedia.org/wiki/One-hot)) кодирования. Процесс кодирования достаточно прост. В любой момент времени на выходной шине должен быть **ровно один** бит, равный единице. Индекс этого бита совпадает со значением старших восьми бит адреса. Поскольку для восьмибитного значения существует 256 комбинаций значений, именно такая разрядность будет на выходе кодировщика. Это означает, что в данной системе можно связать процессор с 256 устройствами (одним из которых будет память данных).
|
||||
|
||||
Реализация такого кодирования предельно проста:
|
||||
|
||||
* Нулевой сигнал этой шины будет равен единице только если `data_addr_o[31:24] = 8'd0`.
|
||||
* Первый бит этой шины будет равен единице только если `data_addr_o[31:24] = 8'd1`.
|
||||
* ...
|
||||
* Двести пятьдесят пятый бит шины будет равен единице только если `data_addr_o[31:24] = 8'd255`.
|
||||
|
||||
Для реализации такого кодирования достаточно выполнить сдвиг влево `255'd1` на значение `data_addr_o[31:24]`.
|
||||
|
||||
### Дополнительные правки модуля riscv_unit
|
||||
|
||||
Ранее, для того чтобы ваши модули могли работать в ПЛИС, вам предоставлялся специальный модуль верхнего уровня, который выполнял всю работу по связи с периферией через входы и выходы ПЛИС. Поскольку в текущей лабораторной вы завершаете свою процессорную систему, она сама должна оказаться модулем верхнего уровня, а значит здесь вы должны и выполнить все подключение к периферии.
|
||||
|
||||
Для этого необходимо добавить в модуль `riscv_unit` дополнительные входы и выходы, которые подключены посредством файла ограничений ([nexys_a7_100t.xdc](nexys_a7_100t.xdc)) к входам и выходам ПЛИС.
|
||||
|
||||
```SystemVerilog
|
||||
module riscv_unit(
|
||||
input logic clk_i,
|
||||
input logic resetn_i,
|
||||
|
||||
// Входы и выходы периферии
|
||||
input logic [15:0] sw_i, // Переключатели
|
||||
output logic [15:0] led_o, // Светодиоды
|
||||
input logic kclk_i, // Тактирующий сигнал клавиатуры
|
||||
input logic kdata_i, // Сигнал данных клавиатуры
|
||||
output logic [ 6:0] hex_led_o, // Вывод семисегментных индикаторов
|
||||
output logic [ 7:0] hex_sel_o, // Селектор семисегментных индикаторов
|
||||
input logic rx_i, // Линия приема по UART
|
||||
output logic tx_o // Линия передачи по UART
|
||||
);
|
||||
//...
|
||||
endmodule
|
||||
```
|
||||
|
||||
Эти входы порты подключены к одноименным портам ваших контроллеров периферии (речь идет только о реализуемых вами контроллерах, остальные порты должны остаться неподключенными).
|
||||
|
||||
Обратите внимание на то, что изменился сигнал сброса (`resetn_i`). Буква `n` на конце означает, что сброс работает по уровню `0` (когда сигнал равен нулю — это сброс, когда единице — не сброс).
|
||||
|
||||
Помимо прочего, необходимо подключить к вашему модулю `блок делителя частоты`. Поскольку в данном курсе лабораторных работ вы выполняли реализацию однотактного процессора, инструкция должна пройти через все ваши блоки за один такт. Из-за этого критический путь вашей схемы не позволит использовать тактовый сигнал частотой в `100 МГц`, от которого работает отладочный стенд. Поэтому, необходимо создать отдельный сигнал с пониженной тактовой частотой, от которого будет работать ваша схема.
|
||||
|
||||
Для этого необходимо:
|
||||
|
||||
1. Подключить файл [`sys_clk_rst_gen.v`](sys_clk_rst_gen.v) в ваш проект.
|
||||
2. Подключить этот модуль внутри `riscv_unit` следующим образом:
|
||||
|
||||
```SystemVerilog
|
||||
logic sysclk, rst;
|
||||
sys_clk_rst_gen divider(.ex_clk_i(clk_i),.ex_areset_n_i(resetn_i),.div_i(10),.sys_clk_o(sysclk), .sys_reset_o(rst));
|
||||
```
|
||||
|
||||
3. После вставки данных строк в начало описания модуля `riscv_unit` вы получите тактовый сигнал `sysclk` с частотой в 10 МГц и сигнал сброса `rst` с активным уровнем `1` (как и в предыдущих лабораторных). Все ваши внутренние модули (`riscv_core`, `data_mem` и `контроллеры периферии`) должны работать от тактового сигнала `sysclk`. На модули, имеющие входной сигнал сброса (`rst_i`) необходимо подать ваш сигнал `rst`.
|
||||
|
||||
---
|
||||
|
||||
## Задание
|
||||
|
||||
В рамках данной лабораторной работы необходимо реализовать модули-контроллеры двух периферийных устройств, реализующих управление в соответствии с приведенной ниже картой памяти и встроить их в процессорную систему, используя используя схему приведенную выше. На карте приведено шесть периферийных устройств, вам необходимо взять только два из них в зависимости от вашего варианта индивидуального задания (ИЗ) по следующему правилу:
|
||||
|
||||
1. Те кому достались варианты ИЗ 1-5 должны реализовать контроллеры переключателей и светодиодов.
|
||||
2. Те кому достались варианты ИЗ 6-15 должны реализовать контроллеры клавиатуры PS/2 и семисегментных индикаторов.
|
||||
3. Те кому достались варианты BP 16-29 должны реализовать контроллеры приемника и передатчика UART.
|
||||
|
||||

|
||||
|
||||
Работа с картой осуществляется следующим образом. Под названием каждого периферийного устройства указана старшая часть адреса (чему должны быть равны старшие 8 бит адреса, чтобы было сформировано обращение к данному периферийному устройству). Например, для переключателей это значение равно `0x01`, для светодиодов `0x02` и т.п.
|
||||
В самом левом столбце указаны используемые/неиспользуемые адреса в адресном пространстве данного периферийного устройства. Например для переключателей есть только один используемый адрес: `0x000000`. Его функциональное назначение и разрешения на доступ указаны в столбце соответствующего периферийного устройства. Возвращаясь к адресу `0x000000` для переключателей мы видим следующее:
|
||||
|
||||
* (R) означает что разрешен доступ только на чтение (операция записи по этому адресу должна игнорироваться вашим контроллером).
|
||||
* "Выставленное на переключателях значение" означает ровно то что и означает. Если процессор выполняет операцию чтения по адресу 0x01000000 (`0x01` (старшая часть адреса переключателей) + `0x000000` (младшая часть адреса для получения выставленного на переключателях значения) ), то контроллер должен выставить на выходной сигнал RD значение на переключателях (о том как получить это значение будет рассказано чуть позже).
|
||||
|
||||
Рассмотрим еще один пример. При обращении по адресу `0x02000024` (`0x02` (старшая часть адреса контроллера светодиодов) + `0x000024` (младшая часть адреса для доступа на запись к регистру сброса) ) должна произойти запись в регистр сброса, который должен сбросить значения в регистре управления зажигаемых светодиодов и регистре управления режимом "моргания" светодиодов (подробнее о том как должны работать эти регистры будет ниже).
|
||||
|
||||
Таким образом, каждый контроллер периферийного устройства должен выполнять две вещи:
|
||||
|
||||
1. При получении сигнала `req_i`, записать в регистр или вернуть значение из регистра, ассоциированного с переданным адресом (адрес передается с обнуленной старшей частью). Если регистра, ассоциированного с таким адресом нет (например для переключателей не ассоциировано ни одного адреса кроме `0x000000`), игнорировать эту операцию.
|
||||
2. Выполнять управление периферийным устройством с помощью управляющих регистров.
|
||||
|
||||
Подробное описание периферийных устройств их управления и назначение управляющих регистров будет дано после порядка выполнения задания.
|
||||
|
||||
---
|
||||
|
||||
## Порядок выполнения задания
|
||||
|
||||
1. Внимательно ознакомьтесь с [примером описания модуля контроллера](../../Basic%20Verilog%20structures/Controllers.md).
|
||||
2. Внимательно ознакомьтесь со спецификацией контроллеров периферии своего варианта. В случае возникновения вопросов, проконсультируйтесь с преподавателем.
|
||||
3. Реализуйте модули контроллеров периферии. Имена модулей и их порты будут указаны в [описании контроллеров](#описание-контроллеров-периферийных-устройств). Пример разработки контроллера приведен [здесь](../../Basic%20Verilog%20structures/Controllers.md).
|
||||
4. Обновите модуль `riscv_unit` в соответствии с разделом ["Дополнительные правки модуля riscv_unit"](#дополнительные-правки-модуля-riscv_unit).
|
||||
1. Подключите в проект файл `sys_clk_rst_gen.sv`.
|
||||
2. Добавьте в модуль `riscv_unit` входы и выходы периферии.
|
||||
3. Подключите к модулю `riscv_unit` модуль `sys_clk_rst_gen` скопировав приведенный фрагмент кода.
|
||||
4. Замените подключение тактового сигнала исходных подмодулей `riscv_unit` на появившийся сигнал `sysclk`. Убедитесь, что на модули имеющие сигнал сброса приходит сигнал `rst`.
|
||||
5. Интегрируйте модули контроллеров периферии в процессорную систему по приведенной схеме руководствуясь старшими адресами контроллеров, представленными на карте памяти. Это означает, что если вы реализуете контроллер светодиодов, на его входов `req_i` должна подаваться единица в случае если `mem_req_o == 1` и старшие 8 бит адреса равны `0x02`.
|
||||
1. При интеграции вы должны подключить только модули-контроллеры вашего варианта. Контроллеры периферии других вариантов подключать не надо.
|
||||
2. При этом во время интеграции, вы должны использовать старшую часть адреса, представленную в карте памяти для формирования сигнала `req_i` для ваших модулей-контроллеров.
|
||||
3. Даже если вы не используете какие-то входные/выходные сигналы в модуле `riscv_unit` (например по варианту вам не достался контроллер клавиатуры и поэтому вы не используете сигналы `kclk_i` и `kdata_i`), вы все равно должны их описать во входах и выходах модуля `riscv_unit`.
|
||||
6. Проинициализируйте память инструкций с помощью предоставленной для каждой пары контроллеров программы.
|
||||
7. Подключите к проекту файл ограничений ([nexys_a7_100t.xdc](nexys_a7_100t.xdc)), если тот еще не был подключен, либо замените его содержимое данными из файла к этой лабораторной работе.
|
||||
8. Проверьте работу вашей процессорной системы с помощью отладочного стенда с ПЛИС и (при соответствующем варианте) клавиатуры/рабочего компьютера.
|
||||
1. Обратите внимание, что в данной лабораторной уже не будет модуля верхнего уровня `nexys_...`, так как ваш модуль процессорной системы уже полностью самостоятелен и взаимодействует непосредственно с ножками ПЛИС через модули, управляемые контроллерами периферии.
|
||||
|
||||
---
|
||||
|
||||
## Описание контроллеров периферийных устройств
|
||||
|
||||
Для того, чтобы избежать избыточности в контексте описания контроллеров периферийных устройств будет использоваться два термина:
|
||||
|
||||
1. Под "**запросом на запись** по адресу `0xАДРЕС`" будет пониматься совокупность следующих условий:
|
||||
1. Происходит восходящий фронт `clk_i`.
|
||||
2. На входе `req_i` выставлено значение `1`.
|
||||
3. На входе `write_enable_i` выставлено значение `1`.
|
||||
4. На входе `addr_i` выставлено значение `0xАДРЕС`
|
||||
2. Под "**запросом на чтение** по адресу `0xАДРЕС`" будет пониматься совокупность следующих условий:
|
||||
1. На входе `req_i` выставлено значение `1`.
|
||||
2. На входе `write_enable_i` выставлено значение `0`.
|
||||
3. На входе `addr_i` выставлено значение `0xАДРЕС`
|
||||
|
||||
Обратите внимание на то, что **запрос на чтение** должен обрабатываться **синхронно** (выходные данные должны выдаваться по положительному фронту `clk_i`).
|
||||
|
||||
При описании поддерживаемых режимов доступа по данному адресу используется интуитивно понятное обозначение:
|
||||
|
||||
* R — доступ **только на чтение**;
|
||||
* W — доступ **только на запись**;
|
||||
* RW — доступ на **чтение и запись**.
|
||||
|
||||
В случае отсутствия **запроса на чтения**, на выход `read_data_o` должно подаваться значение `32'hfa11_1eaf`. Это никак не повлияет на работу процессора, но будет удобно в процессе отладки на временной диаграмме (тоже самое было сделано в процессе разработки памяти данных).
|
||||
|
||||
Если пришел **запрос на запись** или **чтение**, это еще не значит, что контроллер должен его выполнить. В случае, если запрос происходит по адресу, не поддерживающему этот запрос (например **запрос на запись** по адресу поддерживающему только чтение или наоборот), данный запрос должен игнорироваться, а на выходе `read_data_o` должно появиться значение `32'hdead_beef`.
|
||||
|
||||
К примеру, в случае запроса на чтение по адресу `0x0100004` (четвертый байт в адресном пространстве периферийного устройства "переключатели"), на выходе `read_data_o` должно оказаться значение `32'hdead_beef`. В случае отсутствия запроса на чтение (`req_i == 0` или `write_enable_i == 1`), на выходе `read_data_o` контроллера переключателей должно оказаться значение `32'hfa11_1eaf`.
|
||||
|
||||
В случае осуществления записи по принятому запросу, необходимо записать данные с сигнала `write_data_i` в регистр, ассоциированный с адресом `addr_i` (если разрядность регистра меньше разрядности сигнала `write_data_i`, старшие биты записываемых данных отбрасываются).
|
||||
|
||||
В случае осуществления чтения по принятому запросу, необходимо по положительному фронту `clk_i` выставить данные с сигнала, ассоциированного с адресом `addr_i` на выходной сигнал `read_data_o` (если разрядность сигнала меньше разрядности выходного сигнала `read_data_o`, возвращаемые данные должны дополниться нулями в старших битах).
|
||||
|
||||
### Переключатели
|
||||
|
||||
Переключатели являются простейшим устройством ввода на отладочном стенде `Nexys A7`. Соответственно и контроллер, осуществляющий доступ процессора к ним так же будет очень простым. Рассмотрим прототип модуля, который вам необходимо реализовать:
|
||||
|
||||
```SystemVerilog
|
||||
module sw_sb_ctrl(
|
||||
/*
|
||||
Часть интерфейса модуля, отвечающая за подключение к системной шине
|
||||
*/
|
||||
input logic clk_i,
|
||||
input logic rst_i
|
||||
input logic req_i,
|
||||
input logic write_enable_i,
|
||||
input logic [31:0] addr_i,
|
||||
input logic [31:0] write_data_i, // не используется, добавлен для
|
||||
// совместимости с системной шиной
|
||||
output logic [31:0] read_data_o,
|
||||
|
||||
/*
|
||||
Часть интерфейса модуля, отвечающая за подключение к периферии
|
||||
*/
|
||||
input logic [15:0] sw_i
|
||||
);
|
||||
|
||||
endmodule
|
||||
```
|
||||
|
||||
По сути, логика работы контроллера сводится к тому, выдавать на шину `read_data_o` данные со входа `sw_i` каждый раз, когда приходит **запрос на чтение** по нулевому адресу. Поскольку разрядность `sw_i` в два раза меньше разрядности выхода `read_data_o` его старшие биты необходимо дополнить нулями.
|
||||
|
||||
Адресное пространство контроллера:
|
||||
|
||||
|Адрес|Режим доступа| Функциональное назначение |
|
||||
|-----|-------------|-------------------------------------------------|
|
||||
|0x00 | R | Чтение значения, выставленного на переключателях|
|
||||
|
||||
### Светодиоды
|
||||
|
||||
Как и переключатели, светодиоды являются простейшим устройством вывода. Поэтому, чтобы задание было интересней, для их управления был добавлен регистр, управляющий режимом вывода данных на светодиоды.
|
||||
Рассмотрим прототип модуля, который вам необходимо реализовать:
|
||||
|
||||
```SystemVerilog
|
||||
module led_sb_ctrl(
|
||||
/*
|
||||
Часть интерфейса модуля, отвечающая за подключение к системной шине
|
||||
*/
|
||||
input logic clk_i,
|
||||
input logic rst_i
|
||||
input logic req_i,
|
||||
input logic write_enable_i,
|
||||
input logic [31:0] addr_i,
|
||||
input logic [31:0] write_data_i,
|
||||
output logic [31:0] read_data_o,
|
||||
|
||||
/*
|
||||
Часть интерфейса модуля, отвечающая за подключение к периферии
|
||||
*/
|
||||
output logic [15:0] led_o
|
||||
);
|
||||
|
||||
logic [15:0] led_val;
|
||||
logic led_mode;
|
||||
|
||||
endmodule
|
||||
```
|
||||
|
||||
Данный модуль должен выводить на выходной сигнал `led_o` данные с регистра `led_val`. Запись и чтение регистра `led_val` осуществляется по адресу `0x00`. Запись любого значения, превышающего `2¹⁶-1` должна игнорироваться.
|
||||
|
||||
Регистр `led_mode` отвечает за режим вывода данных на светодиоды. Когда этот регистр равен единице, светодиоды должны "моргать" выводимым значением. Под морганием подразумевается вывод значения из регистра `led_val` на выход `led_o` на одну секунду (загорится часть светодиодов, соответствующие которым биты шины `led_o` равны единице), после чего на одну секунду выход `led_o` необходимо подать нули. Запись и чтение регистра `led_mode` осуществляется по адресу `0x04`. Запись любого значения, отличного от `0` и `1` должна игнорироваться.
|
||||
|
||||
Отсчет времени можно реализовать простейшим счетчиком, каждый такт увеличивающимся на 1 и сбрасывающимся по достижении определенного значения, чтобы продолжить считать с нуля. Зная тактовую частоту, нетрудно определить до скольки должен считать счетчик. При тактовой частоте в 10 МГц происходит 10 миллионов тактов в секунду. Это означает, что при такой тактовой частоте через секунду счетчик будет равен `10⁷-1` (счет идет с нуля).
|
||||
|
||||
Обратите внимание на то, что адрес `0x24` является адресом сброса. В случае записи по этому адресу единицы вы должны сбросить регистры `led_val`, `led_mode` и все вспомогательные регистры, которые вы создали. Для реализации сброса вы можете как создать отдельный регистр `led_rst`, в который будет происходить запись, а сам сброс будет происходить по появлению единицы в этом регистре (в этом случае необходимо не забыть сбрасывать и этот регистр), так и создать обычный провод, формирующий единицу в случае выполнения всех указанных условий (условий запроса на запись, адреса сброса и значения записываемых данных равному единице).
|
||||
|
||||
Адресное пространство контроллера:
|
||||
|
||||
|Адрес|Режим доступа|Допустимые значения| Функциональное назначение |
|
||||
|-----|-------------|-------------------|-----------------------------------------------------------------------------------|
|
||||
|0x00 | RW | [0:65535] | Чтение и запись в регистр `led_val` отвечающий за вывод данных на светодиоды |
|
||||
|0x04 | RW | [0:1] | Чтение и запись в регистр `led_mode`, отвечающий за режим "моргания" светодиодами |
|
||||
|0x24 | W | 1 | Запись сигнала сброса |
|
||||
|
||||
### Клавиатура PS/2
|
||||
|
||||
Клавиатура [PS/2](https://ru.wikipedia.org/wiki/PS/2_(порт)) осуществляет передачу [скан-кодов](https://ru.wikipedia.org/wiki/Скан-код), нажатых на этой клавиатуре клавиш.
|
||||
|
||||
В рамках данной лабораторной работы вам будет предоставлен модуль, осуществляющий прием данных с клавиатуры. Вам нужно написать лишь модуль, осуществляющий контроль предоставленным модулем. У предоставленного модуля будет следующий прототип:
|
||||
|
||||
```SystemVerilog
|
||||
module PS2Receiver(
|
||||
input clk_i, // Сигнал тактирования процессора и вашего модуля-контроллера
|
||||
input kclk_i, // Тактовый сигнал, приходящий с клавиатуры
|
||||
input kdata_i, // Сигнал данных, приходящий с клавиатуры
|
||||
output [15:0] keycode_o, // Сигнал полученного с клавиатуры скан-кода клавиши
|
||||
output keycode_valid_o // Сигнал готовности данных на выходе keycodeout
|
||||
);
|
||||
endmodule
|
||||
```
|
||||
|
||||
Вам необходимо реализовать модуль-контроллер со следующим прототипом:
|
||||
|
||||
```SystemVerilog
|
||||
module ps2_sb_ctrl(
|
||||
/*
|
||||
Часть интерфейса модуля, отвечающая за подключение к системной шине
|
||||
*/
|
||||
input logic clk_i,
|
||||
input logic rst_i,
|
||||
input logic [31:0] addr_i,
|
||||
input logic req_i,
|
||||
input logic [31:0] write_data_i,
|
||||
input logic write_enable_i,
|
||||
output logic [31:0] read_data_o,
|
||||
|
||||
/*
|
||||
Часть интерфейса модуля, отвечающая за подключение к модулю,
|
||||
осуществляющему прием данных с клавиатуры
|
||||
*/
|
||||
input logic kclk_i,
|
||||
input logic kdata_i
|
||||
);
|
||||
|
||||
logic [7:0] scan_code;
|
||||
logic scan_code_is_unread;
|
||||
|
||||
endmodule
|
||||
```
|
||||
|
||||
В первую очередь, вы должны инстанциировать модуль `PS2Receiver` внутри вашего модуля-контроллера, соединив соответствующие входы. Для подключения к выходам необходимо создать дополнительные провода.
|
||||
|
||||
По каждому восходящему фронту сигнала `clk_i` вы должны проверять выход `keycode_valid_o` и если тот равен единице, записать значение с выхода `keycode_o` в регистр `scan_code`. При этом значение регистра `scan_code_is_unread` необходимо выставить в единицу.
|
||||
|
||||
В случае, если произошел запрос на чтение по адресу `0x00`, необходимо выставить на выход `read_data_o` значение регистра `scan_code` (дополнив старшие биты нулями), при этом значение регистра `scan_code_is_unread` необходимо обнулить.
|
||||
|
||||
В случае запроса на чтение по адресу `0x04` необходимо вернуть значение регистра `scan_code_is_unread`.
|
||||
|
||||
В случае запроса на запись по адресу `0x24` со значением `1`, необходимо осуществить сброс регистров `scan_code` и `scan_code_is_unread` в `0`.
|
||||
|
||||
Адресное пространство контроллера:
|
||||
|
||||
|Адрес|Режим доступа|Допустимые значения| Функциональное назначение |
|
||||
|-----|-------------|-------------------|-------------------------------------------------------------------------------------------------------------------|
|
||||
|0x00 | R | [0:255] | Чтение из регистра `scan_code`, хранящего скан-код нажатой клавиши |
|
||||
|0x04 | R | [0:1] | Чтение из регистра `scan_code_is_unread`, сообщающего о том, что есть непрочитанные данные в регистре `scan_code` |
|
||||
|0x24 | W | 1 | Запись сигнала сброса |
|
||||
|
||||
### Семисегментные индикаторы
|
||||
|
||||
Семисегментные индикаторы позволяют выводить арабские цифры и первые шесть букв латинского алфавита, тем самым позволяя отображать шестнадцатиричные цифры. На отладочном стенде `Nexys A7` размещено восемь семисегментных индикаторов. Для вывода цифр на эти индикаторы, вам будет предоставлен модуль `hex_digits`, вам нужно лишь написать модуль, осуществляющий контроль над ним. Прототип модуля `hex_digits` следующий:
|
||||
|
||||
```SystemVerilog
|
||||
module hex_digits(
|
||||
input logic clk_i,
|
||||
input logic rst_i,
|
||||
input logic [3:0] hex0_i, // Цифра, выводимой на нулевой (самый правый) индикатор
|
||||
input logic [3:0] hex1_i, // Цифра, выводимая на первый индикатор
|
||||
input logic [3:0] hex2_i, // Цифра, выводимая на второй индикатор
|
||||
input logic [3:0] hex3_i, // Цифра, выводимая на третий индикатор
|
||||
input logic [3:0] hex4_i, // Цифра, выводимая на четвертый индикатор
|
||||
input logic [3:0] hex5_i, // Цифра, выводимая на пятый индикатор
|
||||
input logic [3:0] hex6_i, // Цифра, выводимая на шестой индикатор
|
||||
input logic [3:0] hex7_i, // Цифра, выводимая на седьмой индикатор
|
||||
input logic [7:0] bitmask_i, // Битовая маска для включения/отключения
|
||||
// отдельных индикаторов
|
||||
|
||||
output logic [6:0] hex_led_o // Сигнал, контролирующий каждый отдельный
|
||||
// светодиод индикатора
|
||||
output logic [7:0] hex_sel_o // Сигнал, указывающий на какой индикатор
|
||||
// выставляется hex_led
|
||||
);
|
||||
endmodule
|
||||
```
|
||||
|
||||
Для того, чтобы вывести шестнадцатеричную цифру на любой из индикаторов, необходимо выставить двоичное представление этой цифры на соответствующий вход `hex0-hex7`.
|
||||
|
||||
За включение/отключение индикаторов отвечает входной сигнал `bitmask_i`, состоящий из 8 бит, каждый из которых включает/отключает соответствующий индикатор. Например, при `bitmask_i == 8'b0000_0101`, включены будут нулевой и второй индикаторы, остальные будут погашены.
|
||||
|
||||
Выходные сигналы `hex_led` и `hex_sel` необходимо просто подключить к соответствующим выходным сигналам модуля-контроллера. Они пойдут на выходы ПЛИС, соединенные с семисегментными индикаторами.
|
||||
|
||||
Для управления данным модулем, необходимо написать модуль-контроллер со следующим прототипом:
|
||||
|
||||
```SystemVerilog
|
||||
module hex_sb_ctrl(
|
||||
/*
|
||||
Часть интерфейса модуля, отвечающая за подключение к системной шине
|
||||
*/
|
||||
input logic clk_i,
|
||||
input logic [31:0] addr_i,
|
||||
input logic req_i,
|
||||
input logic [31:0] write_data_i,
|
||||
input logic write_enable_i,
|
||||
output logic [31:0] read_data_o,
|
||||
|
||||
/*
|
||||
Часть интерфейса модуля, отвечающая за подключение к модулю,
|
||||
осуществляющему вывод цифр на семисегментные индикаторы
|
||||
*/
|
||||
output logic [6:0] hex_led,
|
||||
output logic [7:0] hex_sel
|
||||
);
|
||||
|
||||
logic [3:0] hex0, hex1, hex2, hex3, hex4, hex5, hex6, hex7;
|
||||
logic [7:0] bitmask;
|
||||
endmodule
|
||||
```
|
||||
|
||||
Регистры `hex0-hex7` отвечают за вывод цифры на соответствующий семисегментный индикатор. Регистр `bitmask` отвечает за включение/отключение семисегментных индикаторов. Когда в регистре `bitmask` бит, индекс которого совпадает с номером индикатора равен единице — тот включен и выводит число, совпадающее со значением в соответствующем регистре `hex0-hex7`. Когда бит равен нулю — этот индикатор гаснет.
|
||||
|
||||
Доступ на чтение/запись регистров `hex0-hex7` осуществляется по адресам `0x00-0x1c` (см. таблицу адресного пространства).
|
||||
|
||||
Доступ на чтение/запись регистра `bitmask` осуществляется по адресу `0x20`.
|
||||
|
||||
При запросе на запись единицы по адресу `0x24` необходимо выполнить сброс всех регистров. При этом регистр `bitmask` должен сброситься в значение `0xFF`.
|
||||
|
||||
Адресное пространство контроллера:
|
||||
|
||||
|Адрес|Режим доступа|Допустимые значения| Функциональное назначение |
|
||||
|-----|-------------|-------------------|---------------------------------------------------------|
|
||||
|0x00 | RW | [0:31] | Регистр, хранящий значение, выводимое на hex0 |
|
||||
|0x04 | RW | [0:31] | Регистр, хранящий значение, выводимое на hex1 |
|
||||
|0x08 | RW | [0:31] | Регистр, хранящий значение, выводимое на hex2 |
|
||||
|0x0C | RW | [0:31] | Регистр, хранящий значение, выводимое на hex3 |
|
||||
|0x10 | RW | [0:31] | Регистр, хранящий значение, выводимое на hex4 |
|
||||
|0x14 | RW | [0:31] | Регистр, хранящий значение, выводимое на hex5 |
|
||||
|0x18 | RW | [0:31] | Регистр, хранящий значение, выводимое на hex6 |
|
||||
|0x1C | RW | [0:31] | Регистр, хранящий значение, выводимое на hex7 |
|
||||
|0x20 | RW | [0:255] | Регистр, управляющий включением/отключением индикаторов |
|
||||
|0x24 | W | 1 | Запись сигнала сброса |
|
||||
|
||||
### UART
|
||||
|
||||
[UART](https://ru.wikipedia.org/wiki/Универсальный_асинхронный_приёмопередатчик) — это последовательный интерфейс, использующий для приема и передачи данных по одной независимой линии с поддержкой контроля целостности данных.
|
||||
|
||||
Для того, чтобы передача данных была успешно осуществлена, приемник и передатчик на обоих концах одного провода должны договориться о параметрах передачи:
|
||||
|
||||
* её скорости (бодрейт);
|
||||
* контроля целостности данных (использование бита четности/нечетности/отсутствие контроля);
|
||||
* длины стопового бита.
|
||||
|
||||
Вам будут предоставлены модули, осуществляющие прием и передачу данных по этому интерфейсу, от вас лишь требуется написать модули, осуществляющие управление предоставленными модулями.
|
||||
|
||||
```SystemVerilog
|
||||
module uart_rx (
|
||||
input logic clk_i, // Тактирующий сигнал
|
||||
input logic rst_i, // Сигнал сброса
|
||||
input logic rx_i, // Сигнал линии, подключенной к выводу ПЛИС,
|
||||
// по которой будут приниматься данные
|
||||
output logic busy_o, // Сигнал о том, что модуль занят приемом данных
|
||||
input logic [15:0] baudrate_i, // Настройка скорости передачи данных
|
||||
input logic parity_en_i,// Настройка контроля целостности через бит четности
|
||||
input logic stopbit_i, // Настройка длины стопового бита
|
||||
output logic [7:0] rx_data_o, // Принятые данные
|
||||
output logic rx_valid_o // Сигнал о том, что прием данных завершен
|
||||
|
||||
);
|
||||
endmodule
|
||||
```
|
||||
|
||||
```SystemVerilog
|
||||
module uart_tx (
|
||||
input logic clk_i, // Тактирующий сигнал
|
||||
input logic rst_i, // Сигнал сброса
|
||||
output logic tx_o, // Сигнал линии, подключенной к выводу ПЛИС,
|
||||
// по которой будут отправляться данные
|
||||
output logic busy_o, // Сигнал о том, что модуль занят передачей данных
|
||||
input logic [15:0] baudrate_i, // Настройка скорости передачи данных
|
||||
input logic parity_en_i,// Настройка контроля целостности через бит четности
|
||||
input logic stopbit_i, // Настройка длины стопового бита
|
||||
input logic [7:0] tx_data_i, // Отправляемые данные
|
||||
input logic tx_valid_i // Сигнал о старте передачи данных
|
||||
);
|
||||
endmodule
|
||||
```
|
||||
|
||||
Для управления этими модулями вам необходимо написать два модуля-контроллера со следующими прототипами
|
||||
|
||||
```SystemVerilog
|
||||
module uart_rx_sb_ctrl(
|
||||
/*
|
||||
Часть интерфейса модуля, отвечающая за подключение к системной шине
|
||||
*/
|
||||
input logic clk_i,
|
||||
input logic rst_i
|
||||
input logic [31:0] addr_i,
|
||||
input logic req_i,
|
||||
input logic [31:0] write_data_i,
|
||||
input logic write_enable_i,
|
||||
output logic [31:0] read_data_o,
|
||||
|
||||
/*
|
||||
Часть интерфейса модуля, отвечающая за подключение передающему,
|
||||
входные данные по UART
|
||||
*/
|
||||
input logic rx_i
|
||||
);
|
||||
|
||||
logic busy;
|
||||
logic [15:0] baudrate;
|
||||
logic parity_en;
|
||||
logic stopbit;
|
||||
logic data;
|
||||
logic valid;
|
||||
|
||||
endmodule
|
||||
```
|
||||
|
||||
```SystemVerilog
|
||||
module uart_tx_sb_ctrl(
|
||||
/*
|
||||
Часть интерфейса модуля, отвечающая за подключение к системной шине
|
||||
*/
|
||||
input logic clk_i,
|
||||
input logic rst_i
|
||||
input logic [31:0] addr_i,
|
||||
input logic req_i,
|
||||
input logic [31:0] write_data_i,
|
||||
input logic write_enable_i,
|
||||
output logic [31:0] read_data_o,
|
||||
|
||||
/*
|
||||
Часть интерфейса модуля, отвечающая за подключение передающему,
|
||||
выходные данные по UART
|
||||
*/
|
||||
output logic tx_o
|
||||
);
|
||||
|
||||
logic busy;
|
||||
logic [15:0] baudrate;
|
||||
logic parity_en;
|
||||
logic stopbit;
|
||||
|
||||
endmodule
|
||||
```
|
||||
|
||||
У обоих предоставленных модулей схожий прототип, различия заключаются лишь в направлениях некоторых сигналов:
|
||||
|
||||
Управление сигналами этого модуля достаточно просто.
|
||||
|
||||
Сигналы `clk_i` и `rx_i`/`tx_i` подключаются напрямую к соответствующим сигналам модуля-контроллера.
|
||||
|
||||
Сигнал `rst_i` модулей `uart_rx` / `uart_tx` должен быть равен единице при запросе на запись единицы по адресу `0x24`, а так же в случае когда сигнал `rst_i` модуля-контроллера равен единице.
|
||||
|
||||
Выходной сигнал `busy_o` на каждом такте `clk_i` должен записываться в регистр `busy`, доступ на чтение к которому осуществляется по адресу `0x08`.
|
||||
|
||||
Значение входных сигналов `baudrate_i`, `parity_en_i`, `stopbit_i` берутся из соответствующих регистров, доступ на запись к которым осуществляется по адресам `0x0C`, `0x10`, `0x14` соответственно, но только в моменты, когда выходной сигнал `busy_o` равен нулю. Иными словами, изменение настроек передачи возможно только в моменты, когда передача не происходит. Доступ на чтение этих регистров может осуществляться в любой момент времени.
|
||||
|
||||
В регистр `data` модуля `uart_rx_sb_ctrl` записывается значение одноименного выхода модуля `uart_rx` в моменты положительного фронта `clk_i`, когда сигнал `rx_valid_o` равен единице. Доступ на чтение этого регистра осуществляется по адресу `0x00`.
|
||||
|
||||
В регистр `valid` модуля `uart_rx_sb_ctrl` записывается единица по положительному фронту clk_i, когда выход `rx_valid_o` равен единице. Данный регистр сбрасывается в ноль при выполнении запроса на чтение по адресу `0x00`. Сам регистр доступен для чтения по адресу `0x04`.
|
||||
|
||||
На вход `tx_data_i` модуля `uart_tx` подаются данные из регистра `data` модуля `uart_tx_sb_ctrl`. Доступ на запись в этот регистр происходит по адресу `0x00` в моменты положительного фронта `clk_i`, когда сигнал `busy_o` равен нулю. Доступ на чтение этого регистра может осуществляться в любой момент времени.
|
||||
|
||||
На вход `tx_valid_i` модуля `uart_tx` подается единица в момент выполнения запроса на запись по адресу `0x00` (при сигнале `busy` равном нулю). В остальное время на вход этого сигнала подается `0`.
|
||||
|
||||
В случае запроса на запись единицы по адресу `0x24` (адресу сброса), все регистры модуля-контроллера должны сброситься. При этом регистр `baudrate` должен принять значение `9600`, регистр `parity` должен принять значение `1`, регистр, `stopbit` должен принять значение `1`. Остальные регистры должны принять значение `0`.
|
||||
|
||||
Адресное пространство контроллера `uart_rx_sb_ctrl`:
|
||||
|
||||
|Адрес|Режим доступа|Допустимые значения| Функциональное назначение |
|
||||
|-----|-------------|-------------------|---------------------------------------------------------------------------------------------------------|
|
||||
|0x00 | R | [0:255] | Чтение из регистра `data`, хранящего значение принятых данных |
|
||||
|0x04 | R | [0:1] | Чтение из регистра `valid`, сообщающего о том, что есть непрочитанные данные в регистре `data` |
|
||||
|0x08 | R | [0:1] | Чтение из регистра `busy`, сообщающего о том, что модуль находится в процессе приема данных |
|
||||
|0x0C | RW | [0:65535] | Чтение/запись регистра `baudrate`, отвечающего за скорость передачи данных |
|
||||
|0x10 | RW | [0:1] | Чтение/запись регистра `parity`, отвечающего за включение отключение проверки данных через бит четности |
|
||||
|0x14 | RW | [0:1] | Чтение/запись регистра `stopbit`, отвечающего за длину стопового бита |
|
||||
|0x24 | W | 1 | Запись сигнала сброса |
|
||||
|
||||
Адресное пространство контроллера `uart_tx_sb_ctrl`:
|
||||
|
||||
|Адрес|Режим доступа|Допустимые значения| Функциональное назначение |
|
||||
|-----|-------------|-------------------|---------------------------------------------------------------------------------------------------------|
|
||||
|0x00 | RW | [0:255] | Чтение и запись регистра `data`, хранящего значение отправляемых данных |
|
||||
|0x08 | R | [0:1] | Чтение из регистра `busy`, сообщающего о том, что модуль находится в процессе передачи данных |
|
||||
|0x0C | RW | [0:65535] | Чтение/запись регистра `baudrate`, отвечающего за скорость передачи данных |
|
||||
|0x10 | RW | [0:1] | Чтение/запись регистра `parity`, отвечающего за включение отключение проверки данных через бит четности |
|
||||
|0x14 | RW | [0:1] | Чтение/запись регистра `stopbit`, отвечающего за длину стопового бита |
|
||||
|0x24 | W | 1 | Запись сигнала сброса |
|
78
Labs/09. Peripheral units/hex_digits.v
Normal file
78
Labs/09. Peripheral units/hex_digits.v
Normal file
@@ -0,0 +1,78 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module hex_digits(
|
||||
input clk_i, rst_i,
|
||||
input [4:0] hex0, // Входной сигнал со значением цифры, выводимой на нулевой (самый правый) индикатор
|
||||
input [4:0] hex1, // Входной сигнал со значением цифры, выводимой на первый индикатор
|
||||
input [4:0] hex2, // Входной сигнал со значением цифры, выводимой на второй индикатор
|
||||
input [4:0] hex3, // Входной сигнал со значением цифры, выводимой на третий индикатор
|
||||
input [4:0] hex4, // Входной сигнал со значением цифры, выводимой на четвертый индикатор
|
||||
input [4:0] hex5, // Входной сигнал со значением цифры, выводимой на пятый индикатор
|
||||
input [4:0] hex6, // Входной сигнал со значением цифры, выводимой на шестой индикатор
|
||||
input [4:0] hex7, // Входной сигнал со значением цифры, выводимой на седьмой индикатор
|
||||
|
||||
output [6:0] hex_led, // Выходной сигнал, контролирующий каждый отдельный светодиод индикатора
|
||||
output [7:0] hex_sel // Выходной сигнал, указывающий на какой индикатор выставляется hex_led
|
||||
);
|
||||
`define pwm 1000 //шим сегментов
|
||||
|
||||
reg [9:0] counter;
|
||||
reg [4:0] semseg;
|
||||
reg [7:0] ANreg;
|
||||
reg [6:0] hex_ledr;
|
||||
|
||||
assign hex_sel = ANreg;
|
||||
assign hex_led = hex_ledr;
|
||||
|
||||
always @(posedge clk_i) begin
|
||||
if (rst_i) begin
|
||||
counter <= 'b0;
|
||||
ANreg[7:0] <= 8'b11111111;
|
||||
hex_ledr <= 7'b1111111;
|
||||
end
|
||||
else begin
|
||||
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 <= hex0;
|
||||
ANreg[1]: semseg <= hex1;
|
||||
ANreg[2]: semseg <= hex2;
|
||||
ANreg[3]: semseg <= hex3;
|
||||
ANreg[4]: semseg <= hex4;
|
||||
ANreg[5]: semseg <= hex5;
|
||||
ANreg[6]: semseg <= hex6;
|
||||
ANreg[7]: semseg <= hex7;
|
||||
endcase
|
||||
case (semseg)
|
||||
5'h10: hex_ledr <= 7'b0000001;
|
||||
5'h11: hex_ledr <= 7'b1001111;
|
||||
5'h12: hex_ledr <= 7'b0010010;
|
||||
5'h13: hex_ledr <= 7'b0000110;
|
||||
5'h14: hex_ledr <= 7'b1001100;
|
||||
5'h15: hex_ledr <= 7'b0100100;
|
||||
5'h16: hex_ledr <= 7'b0100000;
|
||||
5'h17: hex_ledr <= 7'b0001111;
|
||||
5'h18: hex_ledr <= 7'b0000000;
|
||||
5'h19: hex_ledr <= 7'b0000100;
|
||||
5'h1A: hex_ledr <= 7'b0001000;
|
||||
5'h1B: hex_ledr <= 7'b1100000;
|
||||
5'h1C: hex_ledr <= 7'b0110001;
|
||||
5'h1D: hex_ledr <= 7'b1000010;
|
||||
5'h1E: hex_ledr <= 7'b0110000;
|
||||
5'h1F: hex_ledr <= 7'b0111000;
|
||||
default: hex_ledr <= 7'b1111111;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
211
Labs/09. Peripheral units/nexys_a7_100t.xdc
Normal file
211
Labs/09. Peripheral units/nexys_a7_100t.xdc
Normal file
@@ -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 clk_i]
|
||||
create_clock -period 10.000 -name sys_clk_pin -waveform {0.000 5.000} -add [get_ports clk_i]
|
||||
create_generated_clock -name sys_clk10_pin -source [get_ports clk_i] -divide_by 10 [get_pins div/clkbuf/O]
|
||||
|
||||
#Switches
|
||||
set_property -dict {PACKAGE_PIN J15 IOSTANDARD LVCMOS33} [get_ports {sw_i[0]}]
|
||||
set_property -dict {PACKAGE_PIN L16 IOSTANDARD LVCMOS33} [get_ports {sw_i[1]}]
|
||||
set_property -dict {PACKAGE_PIN M13 IOSTANDARD LVCMOS33} [get_ports {sw_i[2]}]
|
||||
set_property -dict {PACKAGE_PIN R15 IOSTANDARD LVCMOS33} [get_ports {sw_i[3]}]
|
||||
set_property -dict {PACKAGE_PIN R17 IOSTANDARD LVCMOS33} [get_ports {sw_i[4]}]
|
||||
set_property -dict {PACKAGE_PIN T18 IOSTANDARD LVCMOS33} [get_ports {sw_i[5]}]
|
||||
set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports {sw_i[6]}]
|
||||
set_property -dict {PACKAGE_PIN R13 IOSTANDARD LVCMOS33} [get_ports {sw_i[7]}]
|
||||
set_property -dict {PACKAGE_PIN T8 IOSTANDARD LVCMOS18} [get_ports {sw_i[8]}]
|
||||
set_property -dict {PACKAGE_PIN U8 IOSTANDARD LVCMOS18} [get_ports {sw_i[9]}]
|
||||
set_property -dict {PACKAGE_PIN R16 IOSTANDARD LVCMOS33} [get_ports {sw_i[10]}]
|
||||
set_property -dict {PACKAGE_PIN T13 IOSTANDARD LVCMOS33} [get_ports {sw_i[11]}]
|
||||
set_property -dict {PACKAGE_PIN H6 IOSTANDARD LVCMOS33} [get_ports {sw_i[12]}]
|
||||
set_property -dict {PACKAGE_PIN U12 IOSTANDARD LVCMOS33} [get_ports {sw_i[13]}]
|
||||
set_property -dict {PACKAGE_PIN U11 IOSTANDARD LVCMOS33} [get_ports {sw_i[14]}]
|
||||
set_property -dict {PACKAGE_PIN V10 IOSTANDARD LVCMOS33} [get_ports {sw_i[15]}]
|
||||
|
||||
## LEDs
|
||||
set_property -dict {PACKAGE_PIN H17 IOSTANDARD LVCMOS33} [get_ports {led_o[0]}]
|
||||
set_property -dict {PACKAGE_PIN K15 IOSTANDARD LVCMOS33} [get_ports {led_o[1]}]
|
||||
set_property -dict {PACKAGE_PIN J13 IOSTANDARD LVCMOS33} [get_ports {led_o[2]}]
|
||||
set_property -dict {PACKAGE_PIN N14 IOSTANDARD LVCMOS33} [get_ports {led_o[3]}]
|
||||
set_property -dict {PACKAGE_PIN R18 IOSTANDARD LVCMOS33} [get_ports {led_o[4]}]
|
||||
set_property -dict {PACKAGE_PIN V17 IOSTANDARD LVCMOS33} [get_ports {led_o[5]}]
|
||||
set_property -dict {PACKAGE_PIN U17 IOSTANDARD LVCMOS33} [get_ports {led_o[6]}]
|
||||
set_property -dict {PACKAGE_PIN U16 IOSTANDARD LVCMOS33} [get_ports {led_o[7]}]
|
||||
set_property -dict {PACKAGE_PIN V16 IOSTANDARD LVCMOS33} [get_ports {led_o[8]}]
|
||||
set_property -dict {PACKAGE_PIN T15 IOSTANDARD LVCMOS33} [get_ports {led_o[9]}]
|
||||
set_property -dict {PACKAGE_PIN U14 IOSTANDARD LVCMOS33} [get_ports {led_o[10]}]
|
||||
set_property -dict {PACKAGE_PIN T16 IOSTANDARD LVCMOS33} [get_ports {led_o[11]}]
|
||||
set_property -dict {PACKAGE_PIN V15 IOSTANDARD LVCMOS33} [get_ports {led_o[12]}]
|
||||
set_property -dict {PACKAGE_PIN V14 IOSTANDARD LVCMOS33} [get_ports {led_o[13]}]
|
||||
set_property -dict {PACKAGE_PIN V12 IOSTANDARD LVCMOS33} [get_ports {led_o[14]}]
|
||||
set_property -dict {PACKAGE_PIN V11 IOSTANDARD LVCMOS33} [get_ports {led_o[15]}]
|
||||
|
||||
### RGB LEDs
|
||||
#set_property -dict {PACKAGE_PIN R12 IOSTANDARD LVCMOS33} [get_ports LED16_B]
|
||||
#set_property -dict {PACKAGE_PIN M16 IOSTANDARD LVCMOS33} [get_ports LED16_G]
|
||||
#set_property -dict {PACKAGE_PIN N15 IOSTANDARD LVCMOS33} [get_ports LED16_R]
|
||||
#set_property -dict {PACKAGE_PIN G14 IOSTANDARD LVCMOS33} [get_ports LED17_B]
|
||||
#set_property -dict {PACKAGE_PIN R11 IOSTANDARD LVCMOS33} [get_ports LED17_G]
|
||||
#set_property -dict {PACKAGE_PIN N16 IOSTANDARD LVCMOS33} [get_ports LED17_R]
|
||||
|
||||
##7 segment display
|
||||
set_property -dict {PACKAGE_PIN T10 IOSTANDARD LVCMOS33} [get_ports hex_led[6]]
|
||||
set_property -dict {PACKAGE_PIN R10 IOSTANDARD LVCMOS33} [get_ports hex_led[5]]
|
||||
set_property -dict {PACKAGE_PIN K16 IOSTANDARD LVCMOS33} [get_ports hex_led[4]]
|
||||
set_property -dict {PACKAGE_PIN K13 IOSTANDARD LVCMOS33} [get_ports hex_led[3]]
|
||||
set_property -dict {PACKAGE_PIN P15 IOSTANDARD LVCMOS33} [get_ports hex_led[2]]
|
||||
set_property -dict {PACKAGE_PIN T11 IOSTANDARD LVCMOS33} [get_ports hex_led[1]]
|
||||
set_property -dict {PACKAGE_PIN L18 IOSTANDARD LVCMOS33} [get_ports hex_led[0]]
|
||||
#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 {hex_sel[0]}]
|
||||
set_property -dict {PACKAGE_PIN J18 IOSTANDARD LVCMOS33} [get_ports {hex_sel[1]}]
|
||||
set_property -dict {PACKAGE_PIN T9 IOSTANDARD LVCMOS33} [get_ports {hex_sel[2]}]
|
||||
set_property -dict {PACKAGE_PIN J14 IOSTANDARD LVCMOS33} [get_ports {hex_sel[3]}]
|
||||
set_property -dict {PACKAGE_PIN P14 IOSTANDARD LVCMOS33} [get_ports {hex_sel[4]}]
|
||||
set_property -dict {PACKAGE_PIN T14 IOSTANDARD LVCMOS33} [get_ports {hex_sel[5]}]
|
||||
set_property -dict {PACKAGE_PIN K2 IOSTANDARD LVCMOS33} [get_ports {hex_sel[6]}]
|
||||
set_property -dict {PACKAGE_PIN U13 IOSTANDARD LVCMOS33} [get_ports {hex_sel[7]}]
|
||||
|
||||
##Buttons
|
||||
set_property -dict {PACKAGE_PIN C12 IOSTANDARD LVCMOS33} [get_ports 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 { rx_i }]; #IO_L7P_T1_AD6P_35 Sch=uart_txd_in
|
||||
set_property -dict { PACKAGE_PIN D4 IOSTANDARD LVCMOS33 } [get_ports { tx_o }]; #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 kclk]
|
||||
set_property -dict {PACKAGE_PIN B2 IOSTANDARD LVCMOS33} [get_ports kdata]
|
||||
|
||||
##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
|
112
Labs/09. Peripheral units/ps2_hex.txt
Normal file
112
Labs/09. Peripheral units/ps2_hex.txt
Normal file
@@ -0,0 +1,112 @@
|
||||
93 00 10 00
|
||||
13 01 d0 02
|
||||
93 01 00 08
|
||||
37 e2 00 00
|
||||
13 02 b2 06
|
||||
b7 e2 00 00
|
||||
93 82 42 07
|
||||
93 04 f0 0f
|
||||
13 03 00 08
|
||||
13 04 50 05
|
||||
13 0a e0 04
|
||||
93 05 60 01
|
||||
13 06 e0 01
|
||||
93 06 60 02
|
||||
13 07 50 02
|
||||
93 07 e0 02
|
||||
13 08 60 03
|
||||
93 08 d0 03
|
||||
13 09 e0 03
|
||||
93 09 60 04
|
||||
13 05 50 04
|
||||
93 03 00 04
|
||||
93 0a 00 02
|
||||
13 0b 00 01
|
||||
93 0c 80 00
|
||||
13 0d 40 00
|
||||
93 0d 20 00
|
||||
37 0e 00 03
|
||||
13 0e 0e 00
|
||||
b7 0e 00 04
|
||||
93 8e 0e 00
|
||||
23 22 1e 02
|
||||
23 a2 1e 02
|
||||
83 2b 4e 00
|
||||
e3 8e 0b fe
|
||||
03 2c 0e 00
|
||||
63 00 2c 04
|
||||
63 04 4c 04
|
||||
63 08 5c 04
|
||||
63 0c 8c 04
|
||||
63 00 4c 07
|
||||
63 06 ac 06
|
||||
63 08 bc 06
|
||||
63 0a cc 06
|
||||
63 0c dc 06
|
||||
63 0e ec 06
|
||||
63 00 fc 08
|
||||
63 02 0c 09
|
||||
63 04 1c 09
|
||||
63 06 2c 09
|
||||
63 08 3c 09
|
||||
6f ff 9f fb
|
||||
23 22 1e 02
|
||||
23 a2 1e 02
|
||||
6f ff 9f f2
|
||||
e3 84 61 fa
|
||||
b3 91 11 00
|
||||
6f ff 1f fa
|
||||
e3 8e 11 f8
|
||||
b3 d1 11 00
|
||||
6f ff 5f f9
|
||||
b3 e4 91 00
|
||||
23 a0 9e 02
|
||||
6f ff 9f f8
|
||||
b3 e4 91 00
|
||||
b3 84 34 40
|
||||
23 a0 9e 02
|
||||
6f ff 9f f7
|
||||
b3 0f 00 00
|
||||
6f 0f c0 04
|
||||
b3 0f 10 00
|
||||
6f 0f 40 04
|
||||
93 0f 20 00
|
||||
6f 0f c0 03
|
||||
93 0f 30 00
|
||||
6f 0f 40 03
|
||||
93 0f 40 00
|
||||
6f 0f c0 02
|
||||
93 0f 50 00
|
||||
6f 0f 40 02
|
||||
93 0f 60 00
|
||||
6f 0f c0 01
|
||||
93 0f 70 00
|
||||
6f 0f 40 01
|
||||
93 0f 80 00
|
||||
6f 0f c0 00
|
||||
93 0f 90 00
|
||||
6f 0f 40 00
|
||||
63 96 61 00
|
||||
23 ae fe 01
|
||||
6f ff df f1
|
||||
63 96 71 00
|
||||
23 ac fe 01
|
||||
6f ff 1f f1
|
||||
63 96 51 01
|
||||
23 aa fe 01
|
||||
6f ff 5f f0
|
||||
63 96 61 01
|
||||
23 a8 fe 01
|
||||
6f ff 9f ef
|
||||
63 96 91 01
|
||||
23 a6 fe 01
|
||||
6f ff df ee
|
||||
63 96 a1 01
|
||||
23 a4 fe 01
|
||||
6f ff 1f ee
|
||||
63 96 b1 01
|
||||
23 a2 fe 01
|
||||
6f ff 5f ed
|
||||
e3 98 11 ec
|
||||
23 a0 fe 01
|
||||
6f ff 9f ec
|
21
Labs/09. Peripheral units/sw_led.txt
Normal file
21
Labs/09. Peripheral units/sw_led.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
93 04 10 00
|
||||
13 09 a0 0a
|
||||
93 02 a0 0a
|
||||
13 19 89 00
|
||||
33 09 59 00
|
||||
93 09 50 55
|
||||
93 02 50 00
|
||||
93 99 49 00
|
||||
b3 89 59 00
|
||||
37 0e 00 01
|
||||
b7 0e 00 02
|
||||
03 2a 0e 00
|
||||
63 0a 2a 01
|
||||
63 0c 3a 01
|
||||
23 a0 4e 01
|
||||
23 a2 0e 00
|
||||
ef f2 df fe
|
||||
23 a2 9e 02
|
||||
ef f2 5f fe
|
||||
23 a2 9e 00
|
||||
ef f2 df fd
|
54
Labs/09. Peripheral units/sys_clk_rst_gen.v
Normal file
54
Labs/09. Peripheral units/sys_clk_rst_gen.v
Normal file
@@ -0,0 +1,54 @@
|
||||
module sys_clk_rst_gen#(
|
||||
parameter DIV_WIDTH = 4
|
||||
)(
|
||||
input ex_clk_i,
|
||||
input ex_areset_n_i,
|
||||
input [DIV_WIDTH-1:0] div_i,
|
||||
|
||||
output sys_clk_o,
|
||||
output sys_reset_o
|
||||
);
|
||||
|
||||
reg [1:0] ex_arstn_buf;
|
||||
reg [1:0] sys_rstn_buf;
|
||||
|
||||
wire ex_arstn_buffered;
|
||||
assign ex_arstn_buffered = ex_arstn_buf[1];
|
||||
assign sys_reset_o = !sys_rstn_buf[1];
|
||||
|
||||
always @(posedge ex_clk_i or negedge ex_areset_n_i) begin
|
||||
if(!ex_areset_n_i) begin
|
||||
ex_arstn_buf <= 2'b0;
|
||||
end
|
||||
else begin
|
||||
ex_arstn_buf <= {ex_arstn_buf[0], 1'b1};
|
||||
end
|
||||
end
|
||||
|
||||
reg [DIV_WIDTH-1:0] cnt;
|
||||
reg clk_div;
|
||||
always@( posedge ex_clk_i or negedge ex_arstn_buffered ) begin
|
||||
if ( ~ex_arstn_buffered ) begin
|
||||
cnt <= 0;
|
||||
clk_div <= 0;
|
||||
end else if ( cnt == 0 ) begin
|
||||
cnt <= div_i;
|
||||
clk_div <= !clk_div;
|
||||
end else begin
|
||||
cnt <= cnt - 1;
|
||||
end
|
||||
end
|
||||
|
||||
BUFG clkbuf (.O(sys_clk_o),.I(clk_div));
|
||||
|
||||
always @(posedge sys_clk_o or negedge ex_arstn_buffered) begin
|
||||
if(!ex_arstn_buffered) begin
|
||||
sys_rstn_buf <= 2'b0;
|
||||
end
|
||||
else begin
|
||||
sys_rstn_buf <= {sys_rstn_buf[0], 1'b1};
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
endmodule
|
108
Labs/09. Peripheral units/testbench.sv
Normal file
108
Labs/09. Peripheral units/testbench.sv
Normal file
@@ -0,0 +1,108 @@
|
||||
`timescale 1ns / 1ps
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Company: MIET
|
||||
// Engineer: Nikita Bulavin
|
||||
//
|
||||
// Create Date:
|
||||
// Design Name:
|
||||
// Module Name: tb_riscv_unit
|
||||
// Project Name: RISCV_practicum
|
||||
// Target Devices: Nexys A7-100T
|
||||
// Tool Versions:
|
||||
// Description: tb for peripheral units
|
||||
//
|
||||
// Dependencies:
|
||||
//
|
||||
// Revision:
|
||||
// Revision 0.01 - File Created
|
||||
// Additional Comments:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
module tb_riscv_unit();
|
||||
|
||||
localparam variant = 'd1; //1-SW_LED; 2-PS2_HEXLED; 3-UART;
|
||||
localparam button = 8'h16; //keyboard key code
|
||||
|
||||
logic clk;
|
||||
logic ps2_clk;
|
||||
logic ps2_dat;
|
||||
logic resetn;
|
||||
logic [15:0] sw_i;
|
||||
logic [15:0] led_o;
|
||||
logic parity;
|
||||
logic starter;
|
||||
|
||||
logic [ 6:0] hex_led_o;
|
||||
logic [ 7:0] hex_sel_o;
|
||||
logic rx_i;
|
||||
logic tx_o;
|
||||
|
||||
initial begin clk = 0; ps2_clk = 0; end
|
||||
|
||||
always #50 clk = ~clk;
|
||||
always #50000 if (variant == 2) if(starter || (cntr > 0)) ps2_clk = ~ps2_clk; else ps2_clk = 1;
|
||||
|
||||
logic [11:0] data;
|
||||
|
||||
initial begin
|
||||
resetn = 1;
|
||||
repeat(2)@(posedge clk);
|
||||
resetn = 0;
|
||||
repeat(2) @(posedge clk);
|
||||
resetn = 1;
|
||||
end
|
||||
|
||||
riscv_unit dut(
|
||||
.clk_i (clk ),
|
||||
.resetn_i (resetn ),
|
||||
.sw_i (sw_i ),
|
||||
.led_o (led_o ),
|
||||
.kclk_i (ps2_clk ),
|
||||
.kdata_i (ps2_dat ),
|
||||
.hex_led_o(hex_led_o),
|
||||
.hex_sel_o(hex_sel_o),
|
||||
.rx_i (rx_i ),
|
||||
.tx_o (tx_o )
|
||||
);
|
||||
|
||||
logic [3:0] cntr;
|
||||
|
||||
always @(negedge ps2_clk) begin
|
||||
if(starter || (cntr > 0))
|
||||
if(cntr == 10)
|
||||
cntr <= 0;
|
||||
else
|
||||
cntr <= cntr + 1;
|
||||
end
|
||||
assign ps2_dat = cntr>0? data[cntr-1] : 1;
|
||||
|
||||
initial begin
|
||||
sw_i = 16'h0;
|
||||
cntr = 0;
|
||||
starter = 0;
|
||||
parity = !(^button);
|
||||
data = 0;
|
||||
case (variant)
|
||||
1: begin
|
||||
#10000;
|
||||
sw_i = 16'h01AA;
|
||||
#20000;
|
||||
sw_i = 16'hFF00;
|
||||
end
|
||||
2: begin
|
||||
data = {2'b11, parity, button, 1'b0};
|
||||
#100000;
|
||||
starter = 1;
|
||||
@(posedge ps2_clk)
|
||||
starter = 0;
|
||||
end
|
||||
3: begin
|
||||
|
||||
end
|
||||
endcase
|
||||
|
||||
end
|
||||
|
||||
|
||||
endmodule
|
393
Labs/10. Programming/README.md
Normal file
393
Labs/10. Programming/README.md
Normal file
@@ -0,0 +1,393 @@
|
||||
# Лабораторная работа 8 "Высокоуровневое программирование"
|
||||
|
||||
## Цель
|
||||
|
||||
В соответствии с индивидуальным заданием, написать программу на языке программирования высокого уровня C, скомпилировать в машинные коды и запустить на ранее разработанном процессоре RISC-V.
|
||||
|
||||
## Ход работы
|
||||
|
||||
1. Изучить теорию:
|
||||
1. [Соглашение о вызовах](#соглашение-о-вызовах)
|
||||
2. [Скрипт для компоновки](#скрипт-для-компоновки-linker_scriptld)
|
||||
3. [Файл первичных команд](#файл-первичных-команд-при-загрузке-startups)
|
||||
2. Подготовить набор инструментов для кросс-компиляции.
|
||||
3. Изучить порядок компиляции и команды, её осуществляющую.
|
||||
4. Написать и скомпилировать собственную программу.
|
||||
5. Проверить исполнение программы вашим процессором в ПЛИС.
|
||||
|
||||
## Теория
|
||||
|
||||
В рамках данной лабораторной работы вы напишите полноценную программу, которая будет запущена на вашем процессоре. В процессе компиляции, вам потребуются файлы, лежащие в этой папке.
|
||||
|
||||
> — Но зачем мне эти файлы? Мы ведь уже делали задания по программированию на предыдущих лабораторных работах и нам не были нужны никакие дополнительные файлы.
|
||||
|
||||
Дело в том, что ранее вы писали небольшие программки на ассемблере. Однако, язык ассемблера архитектуры RISC-V, так же как и любой другой RISC архитектуры, недружелюбен к программисту, поскольку изначально создавался с прицелом на то, что будут созданы компиляторы и программы будут писаться на более удобных для человека языках высокого уровня. Ранее вы писали простенькие программы, которые можно было реализовать на ассемблере, теперь же вам будет предложено написать полноценную программу на языке Си.
|
||||
|
||||
> — Но разве в процессе компиляции исходного кода на языке Си мы не получаем программу, написанную на языке ассемблера? Получится ведь тот же код, что мы могли написать и сами.
|
||||
|
||||
Штука в том, что ассемблерный код который писали ранее вы отличается от ассемблерного кода, генерируемого компилятором. Код, написанный вами обладал, скажем так... более тонким микро-контролем хода программы. Когда вы писали программу, вы знали какой у вас размер памяти, где в памяти расположены инструкции, а где данные (ну, при написании программ вы почти не пользовались памятью данных, а когда пользовались — просто лупили по случайным адресам и все получалось). Вы пользовались всеми регистрами регистрового файла по своему усмотрению, без ограничений. Однако, представьте на секунду, что вы пишете проект на ассемблере вместе с коллегой: вы пишите одни функции, а он другие. Как в таком случае вы будете пользоваться регистрами регистрового файла? Поделите его напополам и будете пользоваться каждый своей половиной? Но что будет, если к проекту присоединится еще один коллега — придется делить регистровый файл уже на три части? Так от него уже ничего не останется. Для разрешения таких ситуаций было разработано [соглашение о вызовах](#соглашение-о-вызовах) (calling convention).
|
||||
|
||||
Таким образом, генерируя ассемблерный код, компилятор не может так же, как это делали вы, использовать все ресурсы без каких-либо ограничений — он должен следовать ограничениям, накладываемым на него соглашением о вызовах, а так же ограничениям, связанным с тем, что он ничего не знает о памяти устройства, в котором будет исполняться программа — а потому он не может работать с памятью абы как. Работая с памятью, компилятор следует некоторым правилам, благодаря которым после компиляции компоновщик сможет собрать программу под ваше устройство с помощью специального скрипта.
|
||||
|
||||
### Соглашение о вызовах
|
||||
|
||||
Соглашение о вызовах [устанавливает](https://github.com/riscv-non-isa/riscv-elf-psabi-doc/releases/download/v1.0/riscv-abi.pdf) порядок вызова функций: где размещаются аргументы при вызове функций, где находятся указатель на стек и адрес возврата и т.п.
|
||||
|
||||
Кроме того, соглашение делит регистры регистрового файла на две группы: оберегаемые и необерегаемые регистры. При работе с оберегаемыми регистрами, функция должна гарантировать, что перед возвратом в этих регистрах останется тоже самое значение, что было при вызове функции. То есть, если функция собирается записать что-то в оберегаемый регистр, она должна сохранить перед этим его значение на стек, а затем, перед возвратом, вернуть это значение со стека обратно в этот же регистр. Простая аналогия — в маленькой квартире двое делят один рабочий стол по времени. Каждый использует стол по полной, но после себя он должен оставить половину стола соседа (оберегаемые регистры) в том же виде, в котором ее получил, а со своей (необерегаемые регистры) делает что хочет. Кстати, вещи соседа, чтоб не потерять, убирают на стопку рядом (в основную память). С необерегаемыми регистрами функция может работать как ей угодно — не существует никаких гарантий, которые вызванная функция должна исполнить. При этом, если функция вызывает другую функцию, она точно так же не получает никаких гарантий, что вызванная функция оставит значения необерегаемых регистров без изменений, поэтому если там хранятся значения, которые потребуются по окончанию выполнения вызываемой функции, эти значения необходимо сохранить на стек.
|
||||
|
||||
В таблице ниже приведено разделение регистров на оберегаемые (в правом столбце записано `Callee`, т.е. за их сохранение отвечает вызванная функция) и необерегаемые (`Caller` — за сохранение отвечает вызывающая функция). Кроме того, есть три регистра, для которых правый столбец не имеет значения: нулевой регистр (поскольку его невозможно изменить) и указатели на поток и глобальную область памяти. По соглашению о вызовах, эти регистры нельзя использовать для вычислений функций, они изменяются только по заранее оговоренным ситуациям.
|
||||
|
||||
В столбце `ABI name` записывается синоним имени регистра, связанный с его функциональным назначением (см. описание регистра). Часто ассемблеры одинаково воспринимают обе формы написания имени регистров.
|
||||
|
||||
|Register|ABI Name| Description | Saver |
|
||||
|--------|--------|---------------------------------|-------|
|
||||
| x0 | zero |Hard-wired zero | — |
|
||||
| x1 | ra |Return address |Caller |
|
||||
| x2 | sp |Stack pointer |Callee |
|
||||
| x3 | gp |Global pointer | — |
|
||||
| x4 | tp |Thread pointer | — |
|
||||
| x5–7 | t0–2 |Temporaries |Caller |
|
||||
| x8 | s0/fp |Saved register/frame pointer |Callee |
|
||||
| x9 | s1 |Saved register |Callee |
|
||||
| x10–11 | a0–1 |Function arguments/return values |Caller |
|
||||
| x12–17 | a2–7 |Function arguments |Caller |
|
||||
| x18–27 | s2–11 |Saved registers |Callee |
|
||||
| x28–31 | t3–6 |Temporaries |Caller |
|
||||
|
||||
Не смотря на то, что указатель на стек помечен как Callee-saved регистр, это не означает, вызываемая функция может записать в него что заблагорассудится, предварительно сохранив его значение на стек. Ведь как вы вернете значение указателя на стек со стека, если в регистре указателя на стек лежит что-то не то? Запись `Callee` означает, что к моменту возврата из вызываемой функции, значение Callee-saved регистров должно быть ровно таким же, каким было в момент вызова функций. Для s0-s11 регистров это осуществляется путем сохранения их значений на стек. При этом, перед каждым сохранением на стек, изменяется значение указателя на стек таким образом, чтобы он указывал на сохраняемое значение (обычно он декрементируется). Затем, перед возвратом из функций все сохраненные на стек значения восстанавливаются, попутно изменяя значение указателя на стек противоположным образом (инкрементируют его). Таким образом, не смотря на то, что значение указателя на стек менялось в процессе работы вызываемой функции, к моменту выхода из нее, его значение в итоге останется тем же.
|
||||
|
||||
### Скрипт для компоновки (linker_script.ld)
|
||||
|
||||
Скрипт для компоновки описывает то, как в вашей памяти будут храниться данные. Вы уже могли слышать о том, что исполняемый файл содержит секции `.text` и `.data` — инструкций и данных соответственно. Линковщик ничего не знает о том, какая у вас структура памяти: принстонская у вас архитектура или гарвардская, по каким адресам у вас должны храниться инструкции, а по каким данные. У вас может быть несколько типов памятей, под особые секции — и обо всем этом компоновщику можно сообщить в скрипте для компоновки.
|
||||
|
||||
В самом простом виде скрипт компоновки состоит из одного раздела: раздела секций, в котором вы и описываете какие части программы куда и в каком порядке необходимо разместить.
|
||||
|
||||
Для удобства этого описания существует вспомогательная переменная: счетчик адресов. В начале скрипта этот счетчик равен нулю. Размещая очередную секцию, этот счетчик увеличивается на размер этой секции. Допустим, у нас есть два файла `fourier.o` и `main.o`, в каждом из которых есть секции `.text` и `.data`. Мы хотим разместить их в памяти следующим образом: сперва разместить секции `.text` обоих файлов, а затем секции `.data`.
|
||||
|
||||
В итоге по нулевом адресу будет размещена секция `.text` файла `fourier.o`. Она будет размещена именно там, поскольку счетчик адресов в начале скрипта равен нулю, а очередная секция размещается по адресу, куда указывает счетчик адресов. После этого, счетчик адресов будет увеличен на размер этой секции и секция `.text` файла `main.o` будет размещена сразу же за секцией `.text` файла `fourier.o`. После этого счетчик адресов будет увеличен на размер этой секции. То же самое произойдет и при размещении оставшихся секций.
|
||||
|
||||
Кроме того, вы в любой момент можете изменить значение счетчика адресов. Например, у вас две раздельные памяти: память инструкций объемом 512 байт и память данных объемом 1024 байта. Эти памяти находятся в одном адресном пространстве. Диапазон адресов памяти инструкций: `[0:511]`, диапазон памяти данных: `[512:1535]`. При этом общий объем секций `.text` составляет 416 байт. В этом случае, вы можете сперва разместить секции `.text` так же, как было описано в предыдущем примере, а затем, выставив значение на счетчике адресов равное `512`, описываете размещение секций данных. Тогда, между секциями будет появится разрыв в 96 байт. А данные окажутся в диапазоне адресов, выделенном для памяти данных.
|
||||
|
||||
Помимо прочего, в скрипте компоновщика необходимо прописать где будет находиться стек, и какое будет значение у указателя на глобальную область памяти.
|
||||
|
||||
Все это с подробными комментариями описано в файле `linker_script.ld`.
|
||||
|
||||
```ld
|
||||
OUTPUT_FORMAT("elf32-littleriscv")
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
PROVIDE( _start = 0x00000000 );
|
||||
PROVIDE( _memory_size = 1024); /* 1024 байта */
|
||||
|
||||
.text : {*(.boot) *(.text*)}
|
||||
/*
|
||||
В скриптах линковщика есть внутренняя переменная, записываемая как '.'
|
||||
Эта переменная называется счетчиком адресов. Она хранит текущий адрес в
|
||||
памяти.
|
||||
В начале файла она инициализируется нулем. Добавляя новые секции, эта
|
||||
переменная будет увеличиваться на размер каждой новой секции.
|
||||
Если при размещении секций не указывается никакой адрес, они будут размещены
|
||||
по текущему значению счетчика адресов.
|
||||
Этой переменной можно присваивать значения, после этого, она будет
|
||||
увеличиваться с этого значения.
|
||||
Подробнее:
|
||||
https://home.cs.colorado.edu/~main/cs1300/doc/gnu/ld_3.html#IDX338
|
||||
*/
|
||||
. = ALIGN(4);
|
||||
.data : {*(.data*)}
|
||||
/*
|
||||
Значение, присвоенное глобальному указателю (GP) выходит за границы RAM,
|
||||
однако (для архитектуры RISC-V) общепринято присваивать GP значение равное
|
||||
началу секции данных, смещенное на 2048 байт вперед.
|
||||
Благодаря относительной адресации со смещением по 12-битному значению, можно
|
||||
адресоваться на начало секции данных, а так же по всему адресному
|
||||
пространству вплоть до 4096 байт от начала секции данных, что сокращает
|
||||
объем требуемых для адресации инструкций (практически не используются
|
||||
операции LUI, поскольку GP уже хранит базовый адрес и нужно только смещение).
|
||||
Подробнее:
|
||||
https://groups.google.com/a/groups.riscv.org/g/sw-dev/c/60IdaZj27dY/m/s1eJMlrUAQAJ
|
||||
*/
|
||||
_gbl_ptr = . + 0x800 ;
|
||||
. = ALIGN(4);
|
||||
/*
|
||||
BSS (block started by symbol, неофициально его расшифровывают как
|
||||
better save space) — это сегмент, в котором размещаются неинициализированные
|
||||
статические переменные. В стандарте Си сказано, что такие переменные
|
||||
инициализируются нулем (или NULL для указателей). Когда вы создаете
|
||||
статический массив — он должен быть размещен в исполняемом файле.
|
||||
Без bss-секции, этот массив должен был бы занимать такой же объем
|
||||
исполняемого файла, какого объема он сам. Массив на 1000 байт занял бы
|
||||
1000 байт в секции .data.
|
||||
Благодаря секции bss, начальные значения массива не задаются, вместо этого
|
||||
здесь только записываются названия переменных и их адреса.
|
||||
Однако на этапе загрузки исполняемого файла теперь необходимо принудительно
|
||||
занулить участок памяти, занимаемый bss-секцией, поскольку статические
|
||||
переменные должны быть проинициализированы нулем.
|
||||
Таким образом, bss-секция значительным образом сокращает объем исполняемого
|
||||
файла (в случае использования неинициализированных статических массивов)
|
||||
ценой увеличения времени загрузки этого файла.
|
||||
Для того, чтобы занулить bss-секцию, в скрипте заводятся две переменные,
|
||||
указывающие на начало и конец bss-секции посредством счетчика адресов.
|
||||
Подробнее:
|
||||
https://en.wikipedia.org/wiki/.bss
|
||||
*/
|
||||
_bss_start = .;
|
||||
.bss : {*(.bss*)}
|
||||
_bss_end = .;
|
||||
|
||||
|
||||
/*=================================
|
||||
Секция аллоцированных данных завершена, остаток свободной памяти отводится
|
||||
под программный стек и (возможно) кучу. В соглашении о
|
||||
вызовах архитектуры RISC-V сказано, что стек растет снизу вверх, поэтому
|
||||
наша цель разместить его в самых последних адресах памяти.
|
||||
Однако перед этим, мы должны убедиться, что под программный стек останется
|
||||
хотя бы 256 байт (ничем не обоснованное число, взятое с потолка).
|
||||
Поскольку указатель стека (SP) должен быть выровнен до 16 байт, мы
|
||||
обеспечиваем себе максимум 16 вложенных вызовов.
|
||||
Подробнее:
|
||||
https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf
|
||||
=================================
|
||||
*/
|
||||
|
||||
/* Мы хотим гарантировать, что под стек останется как минимум 256 байт */
|
||||
ASSERT(. < (_memory_size - 256),
|
||||
"Program size is too big")
|
||||
|
||||
/* Перемещаем счетчик адресов в конец памяти (чтобы после мы могли
|
||||
использовать его в вызове ALIGN) */
|
||||
. = _memory_size;
|
||||
|
||||
/*
|
||||
Размещаем указатель программного стека так близко к концу памяти,
|
||||
насколько это можно с учетом требования о выравнивании адреса
|
||||
стека до 16 байт.
|
||||
*/
|
||||
_stack_ptr = ALIGN(16) <= _memory_size ?
|
||||
ALIGN(16) : ALIGN(16) - 16;
|
||||
ASSERT(_stack_ptr <= _memory_size, "SP exceed memory size")
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Файл первичных команд при загрузке (startup.S)
|
||||
|
||||
В стартап-файле хранятся инструкции, которые обязательно необходимо выполнить перед началом исполнения любой программы. Это инициализация регистров указателей на стек и глобальную область данных, регистра, хранящего адрес вектора прерываний и т.п.
|
||||
|
||||
По завершению инициализации, стартап-файл выполняет процедуру передаче управления точке входа в запускаемую программу.
|
||||
|
||||
```asm
|
||||
.section .boot
|
||||
|
||||
.global _start
|
||||
_start:
|
||||
la gp, _gbl_ptr # Инициализация глобального указателя
|
||||
la sp, _stack_ptr # Инициализация указателя на стек
|
||||
|
||||
# Инициализация (зануление) сегмента bss
|
||||
la t0, _bss_start
|
||||
la t1, _bss_end
|
||||
_bss_init_loop:
|
||||
beq t0, t1, _main_call
|
||||
sw zero, 0(t0)
|
||||
addi t0, t0, 4
|
||||
j _bss_init_loop
|
||||
|
||||
# Вызов функции main
|
||||
_main_call:
|
||||
li a0, 0 # Передача аргументов argc и argv в main. Формально, argc должен
|
||||
li a1, 0 # быть больше нуля, а argv должен указывать на массив строк,
|
||||
# нулевой элемент которого является именем исполняемого файла,
|
||||
# Но для простоты реализации оба аргумента всего лишь обнулены.
|
||||
# Это сделано для детерминированного поведения программы в случае,
|
||||
# если будет пытаться использовать эти аргументы.
|
||||
call main
|
||||
# Зацикливание после выхода из функции main
|
||||
_endless_loop:
|
||||
j _endless_loop
|
||||
```
|
||||
|
||||
## Практика
|
||||
|
||||
Для того, чтобы запустить симуляцию исполнения программы на вашем процессоре, сперва эту программу необходимо скомпилировать и преобразовать в текстовый файл, которым САПР сможет проинициализировать память процессора. Для компиляции программы, вам потребуется особый компилятор, называемый "кросскомпилятор". Он позволяет компилировать исходный код под архитектуру компьютера, отличную от компьютера, на котором ведется компиляция. В нашем случае, вы будете собирать код под архитектуру RISC-V на компьютере с архитектурой `x86_64`.
|
||||
|
||||
Компилятор, который подойдет для данной задачи (для запуска в операционной системе Windows) вы можете скачать [отсюда](https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/latest/download/xpack-riscv-none-elf-gcc-12.2.0-3-win32-x64.zip).
|
||||
|
||||
Вам потребуется скомпилировать файлы с исходным кодом в объектные. Это можно сделать следующей командой:
|
||||
|
||||
```text
|
||||
<исполняемый файл компилятора> -с <флаги компиляции> <входной файл с исходным кодом> -o <выходной объектный файл>
|
||||
```
|
||||
|
||||
Вам потребуются следующие флаги компиляции:
|
||||
|
||||
* `-march=rv32i` — указание разрядности и набора расширений в архитектуре, под которую идет компиляция
|
||||
* `-mabi=ilp32` — указание двоичного интерфейса приложений
|
||||
|
||||
С учетом названия исполняемого файла скачанного вами компилятора (при условии, что папку из архива вы переименовали в `riscv_cc` и скопировали в корень диска `C:`, а команду запускаете из терминала `git bash`), командой для компиляции файла [`startup.S`](startup.S) может быть:
|
||||
|
||||
```bash
|
||||
/c/riscv_cc/bin/riscv-none-elf-gcc -c -march=rv32i -mabi=ilp32 startup.S -o startup.o
|
||||
```
|
||||
|
||||
Далее необходимо выполнить компоновку объектных файлов. Это можно выполнить командной следующего формата:
|
||||
|
||||
```text
|
||||
<исполняемый файл компилятора> <флаги компоновки> <входные объектные файлы> -o <выходной объектный файл>
|
||||
```
|
||||
|
||||
Исполняемый файл компилятора тот же самый, флаги компоновки будут следующие:
|
||||
|
||||
* `-march=rv32i -mabi=ilp32` — те же самые флаги, что были при компиляции (нам все еще нужно указывать архитектуру, иначе компоновщик может скомпоновать объектные файлы со стандартными библиотеками от другой архитектуры)
|
||||
* `-Wl,--gc-sections` — указать компоновщику удалять неиспользуемые секции (сокращает объем итогового файла)
|
||||
* `-nostartfiles` — указать компоновщику не использовать стартап-файлы стандартных библиотек (сокращает объем файла)
|
||||
* `-T $(LINK_SCRIPT)` — передать компоновщику скрипт компоновки
|
||||
|
||||
Пример команды компоновки:
|
||||
|
||||
```bash
|
||||
/c/riscv_cc/bin/riscv-none-elf-gcc -march=rv32i -mabi=ilp32 -Wl,--gc-sections -nostartfiles -T linker_script.ld startup.o main.o -o result.elf
|
||||
```
|
||||
|
||||
В результате компоновки вы получите исполняемый файл формата `elf` ([Executable and Linkable Format](https://ru.wikipedia.org/wiki/Executable_and_Linkable_Format)). Это двоичный файл, однако это не просто набор двоичных инструкций и данных, которые будут загружены в память процессора. Данный файл содержит заголовки и специальную информацию, которая поможет загрузчику разместить этот файл в памяти компьютера. Поскольку роль загрузчика будете выполнять вы и САПР, на котором будет вестись моделирование, эти данные вам не понадобятся, поэтому вам потребуется экспортировать из данного файла только двоичные инструкции и данные, отбросив всю остальную информацию. Полученный файл уже можно будет использовать в функции `$readmemh`.
|
||||
|
||||
Для экспорта используйте команду:
|
||||
|
||||
```bash
|
||||
/c/riscv_cc/bin/riscv-none-elf-objcopy -O verilog result.elf init.mem
|
||||
```
|
||||
|
||||
ключ `-O verilog` говорит о том, что файл надо сохранить в формате, который сможет воспринять команда `$readmemh`.
|
||||
Если память инструкций и данных у вас разделены, можно экспортировать отдельные секции в разные файлы:
|
||||
|
||||
```bash
|
||||
/c/riscv_cc/bin/riscv-none-elf-objcopy -O verilog -j .text result.elf init_instr.mem
|
||||
/c/riscv_cc/bin/riscv-none-elf-objcopy -O verilog -j .data -j .bss result.elf init_data.mem
|
||||
```
|
||||
|
||||
В процессе отладки лабораторной работы потребуется много раз смотреть на программный счетчик и текущую инструкцию. Довольно тяжело декодировать инструкцию самостоятельно, чтобы понять что сейчас выполняется. Для облегчения задачи можно дизасемблировать скомпилированный файл. Полученный файл на языке ассемблера будет хранить адреса инструкций, а так же их двоичное и ассемблерное представление.
|
||||
|
||||
Пример дизасемблированного файла:
|
||||
|
||||
```asm
|
||||
Disassembly of section .text:
|
||||
|
||||
00000000 <_start>:
|
||||
0: 00001197 auipc gp,0x1
|
||||
4: adc18193 addi gp,gp,-1316 # adc <_gbl_ptr>
|
||||
8: 76000113 li sp,1888
|
||||
c: 2dc00293 li t0,732
|
||||
10: 2dc00313 li t1,732
|
||||
|
||||
00000014 <_bss_init_loop>:
|
||||
14: 00628863 beq t0,t1,24 <_irq_config>
|
||||
18: 0002a023 sw zero,0(t0)
|
||||
1c: 00428293 addi t0,t0,4
|
||||
...
|
||||
|
||||
00000164 <bubble_sort>:
|
||||
164: fd010113 addi sp,sp,-48
|
||||
168: 02112623 sw ra,44(sp)
|
||||
16c: 02812423 sw s0,40(sp)
|
||||
170: 03010413 addi s0,sp,48
|
||||
174: fca42e23 sw a0,-36(s0)
|
||||
178: fcb42c23 sw a1,-40(s0)
|
||||
17c: fe042623 sw zero,-20(s0)
|
||||
180: 09c0006f j 21c <bubble_sort+0xb8>
|
||||
...
|
||||
|
||||
00000244 <main>:
|
||||
244: ff010113 addi sp,sp,-16
|
||||
248: 00112623 sw ra,12(sp)
|
||||
24c: 00812423 sw s0,8(sp)
|
||||
250: 01010413 addi s0,sp,16
|
||||
254: 00a00593 li a1,10
|
||||
258: 2b400513 li a0,692
|
||||
25c: f09ff0ef jal ra,164 <bubble_sort>
|
||||
260: 2b400793 li a5,692
|
||||
...
|
||||
|
||||
Disassembly of section .data:
|
||||
|
||||
000002b4 <array_to_sort>:
|
||||
2b4: 00000003 lb zero,0(zero) # 0 <_start>
|
||||
2b8: 0005 c.nop 1
|
||||
2ba: 0000 unimp
|
||||
2bc: 0010 0x10
|
||||
2be: 0000 unimp
|
||||
...
|
||||
```
|
||||
|
||||
Числа в самом левом столбце, увеличивающиеся на 4 — это адреса в памяти. Отлаживая программу на временной диаграмме вы можете ориентироваться на эти числа, как на значения PC.
|
||||
|
||||
Следующая за адресом строка, записанная в шестнадцатеричном виде — это та инструкция (или данные), которая размещена по этому адресу. С помощью этого столбца вы можете проверить, что стянутая инструкция на временной диаграмме (сигнал `instr`) корректна.
|
||||
|
||||
В правом столбце находится ассемблерный (человекочитаемый) аналог инструкции из предыдущего столбца. Например, инструкция `00001197` — это операция `auipc gp,0x1`, где `gp` — это синоним (ABI name) регистра `x3` (см. раздел [Соглашение о вызовах](#соглашение-о-вызовах)).
|
||||
|
||||
Обратите внимание на последнюю часть листинга: дизасм секции `.data`. В этой секции адреса могут увеличиваться на любое число, шестнадцатеричные данные могут быть любого размера, а на ассемблерные инструкции в правом столбце и вовсе не надо обращать внимание.
|
||||
|
||||
Дело в том, что дизасемблер пытается декодировать вообще все двоичные данные, которые видит: не делая различий инструкции это или нет. В итоге, если у него получается как-то декодировать байты из секции данных (которые могут быть абсолютно любыми) — он это сделает. Причем получившиеся инструкции могут быть из совершенно не поддерживаемых текущим файлом расширений: сжатыми (по два байта вместо четырех), инструкциями операций над числами с плавающей точкой, атомарными и т.п.
|
||||
|
||||
Это не значит, что секция данных в дизасме бесполезна — в приведенном выше листинге вы можете понять, что первыми элементами массива `array_to_sort` являются числа `3`, `5`, `10`, а так же то, по каким адресам они лежат (`0x2b4`, `0x2b8`, `0x2bc`, если непонятно почему первое число записано в одну 4-байтовую строку, а два других разделены на две двубайтовые — попробуйте перечитать предыдущий абзац). Просто разбирая дизасемблерный файл, обращайте внимание на то, какую именно секцию вы сейчас читаете.
|
||||
|
||||
Для того, чтобы произвести дизасемблирование, необходимо выполнить следующую команду:
|
||||
|
||||
```text
|
||||
<исполняемый файл дизасемблера> -D (либо -d) <входной исполняемый файл> > <выходной файл на языке ассемблер>
|
||||
```
|
||||
|
||||
Для нашего примера, командной будет
|
||||
|
||||
```bash
|
||||
/c/riscv_cc/bin/riscv-none-elf-objdump -D result.elf > disasmed_result.S
|
||||
```
|
||||
|
||||
Опция `-D` говорит что дизасемблировать необходимо вообще все секции. Опция `-d` говорит дизасемблировать только исполняемые секции (секции с инструкциями). Таким образом, выполнив дизасемблирование с опцией `-d` мы избавимся от проблем с непонятными инструкциями, в которые декодировались данные из секции `.data`, однако в этом случае, мы не сможем проверить адреса и значения, которые хранятся в этих секциях.
|
||||
|
||||
---
|
||||
|
||||
## Задание
|
||||
|
||||
Вам необходимо написать программу для вашего индивидуального задания на языке C или C++ (в зависимости от выбранного языка необходимо использовать соответствующий компилятор: gcc для C, g++ для C++).
|
||||
|
||||
Доступ к контрольным и статусным регистрам осуществляется через разыменование указателей. Скорее всего, вам будет удобно объявить дефайны с базовыми адресами периферийного устройства и смещения до конкретного регистра в контроллере этого устройства.
|
||||
|
||||
При написании программы, помните что в C++ сильно ограничена арифметика указателей, поэтому при присваивании указателю целочисленного значения адреса, необходимо использовать оператор `reinterpret_cast`.
|
||||
|
||||
Пример доступа к регистрам устройства:
|
||||
|
||||
```C++
|
||||
#define SUPER_COLLIDER_BASE_ADDR 0x05000000
|
||||
#define SUPER_COLLIDER_IS_READY_OFFSET 0x00000000
|
||||
#define SUPER_COLLIDER_START_OFFSET 0x00000004
|
||||
|
||||
void main(int argc, char** argv)
|
||||
{
|
||||
uint32_t* is_ready_ptr = reinterpret_cast<uint32_t*>(SUPER_COLLIDER_BASE_ADDR + SUPER_COLLIDER_IS_READY_OFFSET);
|
||||
uint32_t* start_ptr = reinterpret_cast<uint32_t*>(SUPER_COLLIDER_BASE_ADDR + SUPER_COLLIDER_START_OFFSET);
|
||||
while(1){ // В бесконечном цикле
|
||||
while (*is_ready_ptr) // Ждем пока супер-коллайдер не сообщит о готовности
|
||||
// путем выставления 1 в статусном регистре по нулевому адресу
|
||||
*start_ptr = 1; // После чего начинаем испытания путем записи единицы
|
||||
// в контрольный регистр по адресу 4.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Порядок выполнения задания
|
||||
|
||||
1. Написать программу для своего индивидуального задания на языке C или C++.
|
||||
2. [Скомпилировать](#практика) программу и [стартап-файл](startup.S) в объектные файлы.
|
||||
3. Скомпоновать объектные файлы исполняемый файл, передав компоновщику соответствующий [скрипт](linker_script.ld).
|
||||
4. Экспортировать из объектного файла секции `.text` и `.data` в текстовые файлы `program.txt`, `data.txt`. Если вы не создавали инициализированных статических массивов, то файл `data.txt` может быть оказаться пустым.
|
||||
1. Если файл `data.txt` не пустой, необходимо добавить инициализацию памяти данных этим файлом добавлением системной функции `$readmemh` как это было сделано для памяти инструкций.
|
||||
5. Добавить получившиеся текстовые файлы в проект Vivado.
|
||||
6. Запустить моделирование исполнения программы вашим процессором. Для отладки во время моделирования будет удобно использовать дизасемблерный файл, ориентируясь на сигналы адреса и данных шины инструкций.
|
||||
7. Проверить исполнение программы процессором в ПЛИС.
|
||||
|
||||
---
|
97
Labs/10. Programming/linker_script.ld
Normal file
97
Labs/10. Programming/linker_script.ld
Normal file
@@ -0,0 +1,97 @@
|
||||
OUTPUT_FORMAT("elf32-littleriscv")
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
PROVIDE( _start = 0x00000000 );
|
||||
PROVIDE( _memory_size = 1024); /* 1024 байта */
|
||||
|
||||
.text : {*(.boot) *(.text*)}
|
||||
/*
|
||||
В скриптах линковщика есть внутренняя переменная, записываемая как '.'
|
||||
Эта переменная называется счетчиком адресов. Она хранит текущий адрес в
|
||||
памяти.
|
||||
В начале файла она инициализируется нулем. Добавляя новые секции, эта
|
||||
переменная будет увеличиваться на размер каждой новой секции.
|
||||
Если при размещении секций не указывается никакой адрес, они будут размещены
|
||||
по текущему значению счетчика адресов.
|
||||
Этой переменной можно присваивать значения, после этого, она будет
|
||||
увеличиваться с этого значения.
|
||||
Подробнее:
|
||||
https://home.cs.colorado.edu/~main/cs1300/doc/gnu/ld_3.html#IDX338
|
||||
*/
|
||||
. = ALIGN(4);
|
||||
.data : {*(.data*)}
|
||||
/*
|
||||
Значение, присвоенное глобальному указателю (GP) выходит за границы RAM,
|
||||
однако (для архитектуры RISC-V) общепринято присваивать GP значение равное
|
||||
началу секции данных, смещенное на 2048 байт вперед.
|
||||
Благодаря относительной адресации со смещением в 12 бит, можно адресоваться
|
||||
на начало секции данных, а так же по всему адресному пространству вплоть до
|
||||
4096 байт от начала секции данных, что сокращает объем требуемых для
|
||||
адресации инструкций (практически не используются операции LUI, поскольку GP
|
||||
уже хранит базовый адрес и нужно только смещение).
|
||||
Подробнее:
|
||||
https://groups.google.com/a/groups.riscv.org/g/sw-dev/c/60IdaZj27dY/m/s1eJMlrUAQAJ
|
||||
*/
|
||||
_gbl_ptr = . + 0x800 ;
|
||||
. = ALIGN(4);
|
||||
/*
|
||||
BSS (block started by symbol, неофициально его расшифровывают как
|
||||
better save space) — это сегмент, в котором размещаются неинициализированные
|
||||
статические переменные. В стандарте Си сказано, что такие переменные
|
||||
инициализируются нулем (или NULL для указателей). Когда вы создаете
|
||||
статический массив — он должен быть размещен в исполняемом файле.
|
||||
Без bss-секции, этот массив должен был бы занимать такой же объем
|
||||
исполняемого файла, какого объема он сам. Массив на 1000 байт занял бы
|
||||
1000 байт в секции .data.
|
||||
Благодаря секции bss, начальные значения массива не задаются, вместо этого
|
||||
здесь только записываются названия переменных и их адреса.
|
||||
Однако на этапе загрузки исполняемого файла теперь необходимо принудительно
|
||||
занулить участок памяти, занимаемый bss-секцией, поскольку статические
|
||||
переменные должны быть проинициализированы нулем.
|
||||
Таким образом, bss-секция значительным образом сокращает объем исполняемого
|
||||
файла (в случае использования неинициализированных статических массивов)
|
||||
ценой увеличения времени загрузки этого файла.
|
||||
Для того, чтобы занулить bss-секцию, в скрипте заводятся две переменные,
|
||||
указывающие на начало и конец bss-секции посредством счетчика адресов.
|
||||
Подробнее:
|
||||
https://en.wikipedia.org/wiki/.bss
|
||||
*/
|
||||
_bss_start = .;
|
||||
.bss : {*(.bss*)}
|
||||
_bss_end = .;
|
||||
|
||||
|
||||
/*=================================
|
||||
Секция аллоцированных данных завершена, остаток свободной памяти отводится
|
||||
под программный стек и (возможно) кучу. В соглашении о
|
||||
вызовах архитектуры RISC-V сказано, что стек растет снизу вверх, поэтому
|
||||
наша цель разместить его в самых последних адресах памяти.
|
||||
Однако перед этим, мы должны убедиться, что под программный стек останется
|
||||
хотя бы 256 байт (ничем не обоснованное число, взятое с потолка).
|
||||
Поскольку указатель стека (SP) должен быть выровнен до 16 байт, мы
|
||||
обеспечиваем себе максимум 16 вложенных вызовов.
|
||||
Подробнее:
|
||||
https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf
|
||||
=================================
|
||||
*/
|
||||
|
||||
/* Мы хотим гарантировать, что под стек останется как минимум 256 байт */
|
||||
ASSERT(. < (_memory_size - 256),
|
||||
"Program size is too big")
|
||||
|
||||
/* Перемещаем счетчик адресов в конец памяти (чтобы после мы могли
|
||||
использовать его в вызове ALIGN) */
|
||||
. = _memory_size;
|
||||
|
||||
/*
|
||||
Размещаем указатель программного стека так близко к концу памяти,
|
||||
насколько это можно с учетом требования о выравнивании адреса
|
||||
стека до 16 байт.
|
||||
*/
|
||||
_stack_ptr = ALIGN(16) <= _memory_size ?
|
||||
ALIGN(16) : ALIGN(16) - 16;
|
||||
ASSERT(_stack_ptr <= _memory_size, "SP exceed memory size")
|
||||
|
||||
}
|
28
Labs/10. Programming/startup.S
Normal file
28
Labs/10. Programming/startup.S
Normal file
@@ -0,0 +1,28 @@
|
||||
.section .boot
|
||||
|
||||
.global _start
|
||||
_start:
|
||||
la gp, _gbl_ptr # Инициализация глобального указателя
|
||||
la sp, _stack_ptr # Инициализация указателя на стек
|
||||
|
||||
# Инициализация (зануление) сегмента bss
|
||||
la t0, _bss_start
|
||||
la t1, _bss_end
|
||||
_bss_init_loop:
|
||||
beq t0, t1, _main_call
|
||||
sw zero, 0(t0)
|
||||
addi t0, t0, 4
|
||||
j _bss_init_loop
|
||||
|
||||
# Вызов функции main
|
||||
_main_call:
|
||||
li a0, 0 # Передача аргументов argc и argv в main. Формально, argc должен
|
||||
li a1, 0 # быть больше нуля, а argv должен указывать на массив строк,
|
||||
# нулевой элемент которого является именем исполняемого файла,
|
||||
# Но для простоты реализации оба аргумента всего лишь обнулены.
|
||||
# Это сделано для детерминированного поведения программы в случае,
|
||||
# если будет пытаться использовать эти аргументы.
|
||||
call main
|
||||
# Зацикливание после выхода из функции main
|
||||
_endless_loop:
|
||||
j _endless_loop
|
140
Labs/README.md
Normal file
140
Labs/README.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# Курс лабораторных работ
|
||||
|
||||
## Содержание
|
||||
- [Курс лабораторных работ](#курс-лабораторных-работ)
|
||||
- [Содержание](#содержание)
|
||||
- [Полезное](#полезное)
|
||||
- [Порядок выполнения лабораторных работ для групп](#порядок-выполнения-лабораторных-работ-для-групп)
|
||||
- [ИБ, ИКТ, КТ, РТ](#иб-икт-кт-рт)
|
||||
- [ПИН, ПМ](#пин-пм)
|
||||
- [ИВТ](#ивт)
|
||||
- [Обзор лабораторных работ](#обзор-лабораторных-работ)
|
||||
- [1. Сумматор. Verilog HDL (Adder)](#1-сумматор-verilog-hdl-adder)
|
||||
- [2. Арифметико-логическое устройство (ALU)](#2-арифметико-логическое-устройство-alu)
|
||||
- [3. Регистровый файл и память (RF)](#3-регистровый-файл-и-память-rf)
|
||||
- [4. Простейшее программируемое устройство (PPD)](#4-простейшее-программируемое-устройство-ppd)
|
||||
- [5. Основной дешифратор команд (MD)](#5-основной-дешифратор-команд-md)
|
||||
- [6. Тракт данных (DP)](#6-тракт-данных-dp)
|
||||
- [7. Блог загрузки и сохранения данных (LSU)](#7-блог-загрузки-и-сохранения-данных-lsu)
|
||||
- [8. Подсистема прерывания (IC)](#8-подсистема-прерывания-ic)
|
||||
- [9. Периферийные устройства (PU)](#9-периферийные-устройства-pu)
|
||||
- [10. Программирование на языке высокого уровня](#10-программирование-на-языке-высокого-уровня)
|
||||
|
||||
## Полезное
|
||||
|
||||
- [Как установить Vivado](../Vivado%20Basics/Install%20Vivado.md)
|
||||
- [Создание базового проекта с прошивкой ПЛИС в Vivado](../Vivado%20Basics/Vivado%20trainer.md)
|
||||
- [Что такое язык описания аппаратуры HDL](../Introduction/What%20is%20HDL.md)
|
||||
- [Как работает ПЛИС](../Introduction/How%20FPGA%20works.md)
|
||||
- [Синтакс языка Verilog](../Basic%20Verilog%20structures/Verilog%20syntax.md)
|
||||
- [Базовые конструкции Verilog](../Basic%20Verilog%20structures/)
|
||||
- [Тестовое окружение](../Basic%20Verilog%20structures/Testbench.md)
|
||||
|
||||
## Порядок выполнения лабораторных работ для групп
|
||||
|
||||
### ИБ, ИКТ, КТ, РТ
|
||||
|
||||
1. Сумматор ([01. Adder](01.%20Adder))
|
||||
2. АЛУ ([02. Arithmetic-logic unit](02.%20Arithmetic-logic%20unit))
|
||||
3. Регистровый файл и внешняя память ([03. Register file and memory](03.%20Register%20file%20and%20memory))
|
||||
4. Простейшее программируемое устройство ([04. Primitive programmable device](04.%20Primitive%20programmable%20device))
|
||||
|
||||
### ПИН, ПМ
|
||||
|
||||
1. Сумматор ([01. Adder](01.%20Adder))
|
||||
2. АЛУ ([02. Arithmetic-logic unit](02.%20Arithmetic-logic%20unit))
|
||||
3. Регистровый файл и внешняя память ([03. Register file and memory](03.%20Register%20file%20and%20memory))
|
||||
4. Простейшее программируемое устройство ([04. Primitive programmable device](04.%20Primitive%20programmable%20device))
|
||||
5. Основной дешифратор ([05. Main decoder](05.%20Main%20decoder))
|
||||
6. Тракт данных ([06. Datapath](06.%20Datapath))
|
||||
7. Периферийные устройства ([09. Peripheral units](09.%20Peripheral%20units))
|
||||
8. Программирование ([10. Programming](10.%20Programming))
|
||||
|
||||
### ИВТ
|
||||
|
||||
1. АЛУ ([02. Arithmetic-logic unit](02.%20Arithmetic-logic%20unit))
|
||||
2.
|
||||
1. Память ([03. Register file and memory](03.%20Register%20file%20and%20memory)),
|
||||
2. Простейшее программируемое устройство ([04. Primitive programmable device](04.%20Primitive%20programmable%20device))
|
||||
3. Основной дешифратор ([05. Main decoder](05.%20Main%20decoder))
|
||||
4. Тракт данных ([06. Datapath](06.%20Datapath))
|
||||
5. Модуль загрузки и сохранения (07. Load-store unit)
|
||||
6. Контроллер прерываний ()
|
||||
7. Периферийные устройства ([09. Peripheral units](09.%20Peripheral%20units))
|
||||
8. Программирование ([10. Programming](10.%20Programming))
|
||||
|
||||
## Обзор лабораторных работ
|
||||
|
||||

|
||||
|
||||
Курс *Архитектур процессорных систем* включает в себя цикл из 10 лабораторных работ, в течение которых используя язык описания аппаратуры **Verilog HDL** на основе **FPGA** (ПЛИС, программируемая логическая интегральная схема), с нуля, последовательно, создается система, под управлением процессора с архитектурой **RISC-V**, управляющего периферийными устройствами и программируемого на языке высокого уровня **C++**.
|
||||
|
||||
Создаваемая система на ПЛИС состоит из: процессора, памяти, контроллера прерываний и контроллеров периферийных устройств.
|
||||
|
||||

|
||||
|
||||
Выполнение лабораторных работ это последовательный процесс в результате которого будет освоен ряд различных инструментов и средств. В общих словах это:
|
||||
|
||||
**Verilog HDL** - язык описания аппаратуры, благодаря которому схемы не рисуются, а описываются с помощью текста (кода).
|
||||
|
||||
**Testbench** - тестовые окружения, которые представляют собой несинтезируемые (то есть не существующие в реальном физическом мире) блоки, созданные на языке Verilog HDL для автоматического тестирования разрабатываемых устройств и проверки их корректной работоспособности.
|
||||
|
||||
**FPGA** - программируемая логическая интегральная схема (ПЛИС), изменяя внутреннюю конфигурацию которой можно создать любые цифровые устройства (в рамках предоставляемых ресурсов).
|
||||
|
||||
**Vivado** - система автоматизированного проектирования, которая превращает Verilog-код в конфигурацию и прошивает ей ПЛИС на отладочной плате.
|
||||
|
||||
**Архитектура RISC-V** - открытая и свободная система команд и процессорная архитектура на основе концепции RISC для микропроцессоров и микроконтроллеров.
|
||||
|
||||
**Язык ассемблера RISC-V** - список основных команд и особенности их использования и написания программ.
|
||||
|
||||
**Ассемблер RISC-V** - программа, которая превращает код, написанный на языке ассемблера RISC-V в машинные инструкции для процессора с архитектурой RISC-V.
|
||||
|
||||
Далее приводится краткое описание и цель каждой отдельной лабораторной работы.
|
||||
|
||||
## 1. Сумматор. Verilog HDL (Adder)
|
||||
|
||||

|
||||
На первой лабораторной работе изучаются базовые конструкции языка описания аппаратуры Verilog HDL, с помощью которого разрабатывается цифровой сумматор из примитивных логических вентилей, который, в последствии, конфигурируется в ПЛИС и его работа проверяется на отладочном стенде.
|
||||
|
||||
## 2. Арифметико-логическое устройство (ALU)
|
||||
|
||||

|
||||
На второй лабораторной изучаются новые конструкции языка Verilog HDL, на основе которых разрабатывается блок арифметико-логического устройства (АЛУ). АЛУ - это устройство, на входы которого подаются операнды, над которыми нужно выполнить некоторую операцию (сложение, вычитание и тому подобное) и код операции, которую нужно выполнить, а на выходе появляется результат этой операции. Проще говоря АЛУ - это "калькулятор" процессора.
|
||||
|
||||
Для проверки правильной работоспособности АЛУ в конце лабораторной работы, на языке Verilog HDL, пишется testbench (тестовое окружение), которое автоматически проверяет корректность его реализации.
|
||||
|
||||
## 3. Регистровый файл и память (RF)
|
||||
|
||||

|
||||
На третьей лабораторной разрабатываются элементы памяти для будущего процессора: память команд, память данных и регистровый файл. В памяти команд будет храниться программа, которую будет выполнять процессор. В памяти данных хранятся данные, которые будут обрабатываться процессором. Регистровый файл - это маленькая память, тоже с данными, которые могут быть поданы непосредственно на АЛУ. Особенность RISC-архитектур в том, что данные перед обработкой необходимо перенести из памяти данных в регистровый файл, только после этого к ним можно применять различные операции.
|
||||
|
||||
## 4. Простейшее программируемое устройство (PPD)
|
||||
|
||||

|
||||
Эта работа – небольшое отступление от реализации процессора с архитектурой RISC-V и нужна для более глубокого понимания принципов работы и организации программируемых устройств. В рамках четвертой лабораторной работы из реализованных блоков собирается примитивное программируемое устройство, для которого пишется программа в машинных кодах.
|
||||
|
||||
## 5. Основной дешифратор команд (MD)
|
||||
|
||||

|
||||
Пятая лабораторная посвящена разработке устройства управления – основному дешифратору команд. Функция основного дешифратора - получать на вход коды выполняемых операций и преобразовывать их в управляющие сигналы для всех блоков процессора (АЛУ, память, регистровый файл, мультиплексоры). Работа требует внимательности в реализации, а ее результат проверяется заранее подготовленными автоматическими тестами.
|
||||
|
||||
## 6. Тракт данных (DP)
|
||||
|
||||

|
||||
Разработанные блоки объединяются, образуя тракт данных, управляемый основным дешифратором команд. Результатом шестой лабораторной работы является однотактный процессор, с архитектурой RISC-V, поддерживающий стандартный набор целочисленных инструкций RV32I. В качестве проверки на процессоре запускаются программы, заранее написанные на языке ассемблера RISC-V. Сравнивается результат полученный на симуляторе и на разработанном процессоре.
|
||||
|
||||
## 7. Блог загрузки и сохранения данных (LSU)
|
||||

|
||||
В современных компьютерах память является отдельным от процессора устройством. В пятой работе память программ и память данных выносится за пределы процессора, объединяются в одну общую память и подключается через общую шину. В процессоре для этого появляется блок Load/Store Unit.
|
||||
|
||||
## 8. Подсистема прерывания (IC)
|
||||

|
||||
Одной из основных функций процессоров является возможность реагировать на внешние события (дернуть мышку, нажать кнопку и т.п.), автоматически запуская, при их возникновении, соответствующие программы. В шестой работе создается и подключается подсистема прерывания, к которой относятся контроллер прерываний с циклическим опросом и блок регистров статуса и управления.
|
||||
|
||||
## 9. Периферийные устройства (PU)
|
||||

|
||||
На седьмой работе создаются и подключаются к общей шине и подсистеме прерывания контроллеры периферийных устройств, такие как контроллер клавиатуры и VGA-контроллер.
|
||||
|
||||
## 10. Программирование на языке высокого уровня
|
||||

|
||||
В рамках восьмой работы настраивается компилятор GCC для RISC-V и для разработанной системы пишется программное обеспечение на языке программирования C++.
|
Reference in New Issue
Block a user