mirror of
https://github.com/MPSU/APS.git
synced 2025-09-15 09:10:10 +00:00
WIP: APS cumulative update (#98)
* WIP: APS cumulative update * Update How FPGA works.md * Перенос раздела "Последовательностная логика" в отдельный док * Исправление картинки * Исправление оформления индексов * Переработка раздела Vivado Basics * Добавление картинки в руководство по созданию проекта * Исправление ссылок в анализе rtl * Обновление изображения в sequential logic * Исправление ссылок в bug hunting * Исправление ссылок * Рефактор руководства по прошивке ПЛИС * Mass update * Update fig_10 * Restore fig_02
This commit is contained in:
committed by
GitHub
parent
78bb01ef95
commit
a28002e681
@@ -1,4 +1,4 @@
|
||||
# Лабораторная работа 7 "Тракт данных"
|
||||
# Лабораторная работа №7 "Тракт данных"
|
||||
|
||||
Микроархитектуру можно разделить на две части: тракт данных и устройство управления. По тракту данных перемещаются данные (из памяти инструкций, регистрового файла, АЛУ, памяти данных, мультиплексоров), а устройство управления (в нашем случае — декодер инструкций) получает текущую инструкцию из тракта и в ответ говорит ему как именно её выполнить, то есть управляет тем, как эти данные будут через проходить тракт данных.
|
||||
|
||||
@@ -15,13 +15,12 @@
|
||||
|
||||
## Микроархитектура RISC-V
|
||||
|
||||
### riscv_core
|
||||
### processor_core
|
||||
|
||||
Рассмотрим микроархитектуру процессорного ядра `riscv_core`. Данный модуль обладает следующим прототипом и микроархитектурой:
|
||||
|
||||
```SystemVerilog
|
||||
module riscv_core (
|
||||
Рассмотрим микроархитектуру процессорного ядра `processor_core`. Данный модуль обладает следующим прототипом и микроархитектурой:
|
||||
|
||||
```Verilog
|
||||
module processor_core (
|
||||
input logic clk_i,
|
||||
input logic rst_i,
|
||||
|
||||
@@ -67,16 +66,16 @@ _Рисунок 1. Микроархитектура ядра процессор
|
||||
|
||||
Константы `B` и `J` используются для условного и безусловного перехода (в киберкобре для этого использовалась одна константа `offset`).
|
||||
|
||||
Программный счетчик (`PC`) теперь также изменяется более сложным образом. Поскольку появился еще один вид безусловного перехода (`jalr`), программный счетчик может не просто увеличиться на значение константы из инструкции, но и получить совершенно новое значение в виде суммы константы и значения из регистрового файла (см. на самый левый мультиплексор схемы). Обратите внимание, что младший бит этой суммы должен быть обнулен — таково требование спецификации.
|
||||
Программный счётчик (`PC`) теперь также изменяется более сложным образом. Поскольку появился еще один вид безусловного перехода (`jalr`), программный счётчик может не просто увеличиться на значение константы из инструкции, но и получить совершенно новое значение в виде суммы константы и значения из регистрового файла (см. на самый левый мультиплексор _рис. 1_). Обратите внимание, что младший бит этой суммы должен быть обнулен — таково требование спецификации [[1](https://github.com/riscv/riscv-isa-manual/releases/download/20240411/unpriv-isa-asciidoc.pdf), стр. 28].
|
||||
|
||||
Поскольку обращение во внешнюю память требует времени, необходимо приостанавливать программный счетчик, чтобы до конца обращения в память не начались исполняться последующие инструкции. Для этого у программного счетчика появился управляющий сигнал `stall_i`. Программный счетчик может меняться только когда этот сигнал равен нулю (иными словами, инверсия этого сигнала является сигналом `enable` для регистра `PC`).
|
||||
Поскольку обращение во внешнюю память требует времени, необходимо приостанавливать программный счётчик, чтобы до конца обращения в память не начались исполняться последующие инструкции. Для этого у программного счётчика появился управляющий сигнал `stall_i`. Программный счётчик может меняться только когда этот сигнал равен нулю (иными словами, инверсия этого сигнала является сигналом `enable` для регистра `PC`).
|
||||
|
||||
### riscv_unit
|
||||
### processor_system
|
||||
|
||||
После реализации процессорного ядра, к нему необходимо подключить память. Это происходит в модуле `riscv_unit`.
|
||||
После реализации процессорного ядра, к нему необходимо подключить память. Это происходит в модуле `processor_system`.
|
||||
|
||||
```SystemVerilog
|
||||
module riscv_unit(
|
||||
```Verilog
|
||||
module processor_system(
|
||||
input logic clk_i,
|
||||
input logic rst_i
|
||||
);
|
||||
@@ -86,39 +85,14 @@ endmodule
|
||||
|
||||

|
||||
|
||||
_Рисунок 2. Микроархитектура процессора._
|
||||
_Рисунок 2. Микроархитектура процессорной системы._
|
||||
|
||||
Обратите внимание на регистр `stall`. Этот регистр и будет управлять разрешением на запись в программный счетчик `PC`. Поскольку мы используем блочную память, расположенную прямо в ПЛИС, доступ к ней осуществляется за 1 такт, а значит, что при обращении в память, нам необходимо "отключить" программный счетчик ровно на 1 такт. Если бы использовалась действительно "внешняя" память (например чип DDR3), то вместо этого регистра появилась бы другая логика, выставляющая на вход ядра `stall_i` единицу пока идет обращение в память.
|
||||
Обратите внимание на регистр `stall`. Этот регистр и будет управлять разрешением на запись в программный счётчик `PC`. Поскольку мы используем блочную память, расположенную прямо в ПЛИС, доступ к ней осуществляется за 1 такт, а значит, что при обращении в память, нам необходимо "отключить" программный счётчик ровно на 1 такт. Если бы использовалась действительно "внешняя" память (например чип DDR3), то вместо этого регистра появилась бы другая логика, выставляющая на вход ядра `stall_i` единицу пока идет обращение в память.
|
||||
|
||||
## Задание
|
||||
|
||||
Реализовать ядро процессора `riscv_core` архитектуры RISC-V по предложенной микроархитектуре. Подключить к нему память инструкций и память данных в модуле `riscv_unit`. Проверить работу процессора с помощью программы, написанной на ассемблере RISC-V по индивидуальному заданию, которое использовалось для написания программы для процессора архитектуры CYBERcobra.
|
||||
Реализовать ядро процессора `processor_core` архитектуры RISC-V по предложенной микроархитектуре. Подключить к нему память инструкций и память данных в модуле `processor_system`. Проверить работу процессора с помощью программы, написанной на ассемблере 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
|
||||
@@ -191,36 +165,24 @@ _Листинг 2. Программа из Листинга 1, представ
|
||||
|
||||
## Порядок выполнения задания
|
||||
|
||||
1. Внимательно ознакомьтесь микроархитектурной реализацией. В случае возникновения вопросов, проконсультируйтесь с преподавателем.
|
||||
2. Реализуйте модуль `riscv_core`. Для этого:
|
||||
1. В `Design Sources` проекта с предыдущих лаб, создайте `SystemVerilog`-файл `riscv_core.sv`.
|
||||
2. Опишите в нем модуль процессор `riscv_core` с таким же именем и портами, как указано в [задании](#задание).
|
||||
1. Процесс реализации модуля очень похож на процесс описания модуля cybercobra, однако теперь появляется:
|
||||
1. декодер
|
||||
2. дополнительные мультиплексоры и знакорасширители.
|
||||
2. Сперва рекомендуется создать все провода, которые будут подключены к входам и выходам каждого модуля на схеме.
|
||||
3. Затем необходимо создать экземпляры модулей.
|
||||
4. Также необходимо создать 32-разрядные константы I, U, S, B и J-типа и программный счетчик.
|
||||
5. После необходимо описать логику, управляющую созданными в п. 2.1 проводами.
|
||||
6. В конце останется описать логику работы программного счетчика.
|
||||
3. Создайте в проекте новый `SystemVerilog`-файл `riscv_unit.sv` и опишите в нем модуль `riscv_unit`, объединяющий ядро процессора (`riscv_core`) с памятями инструкция и данных.
|
||||
1. **При создании объекта модуля `riscv_core` в модуле `riscv_unit` вы должны использовать имя сущности `core` (т.е. создать объект в виде: `riscv_core core(...`)**.
|
||||
3. После описания модуля его необходимо проверить с помощью тестового окружения.
|
||||
1. Тестовое окружение находится [`здесь`](tb_riscv_unit.sv).
|
||||
2. Программа, которой необходимо проинициализировать память инструкций находится в файле [`program.mem`](program.mem).
|
||||
3. Для запуска симуляции воспользуйтесь [`этой инструкцией`](../../Vivado%20Basics/Run%20Simulation.md).
|
||||
4. Перед запуском симуляции убедитесь, что выбран правильный модуль верхнего уровня.
|
||||
5. **По завершению симуляции убедитесь, что в логе есть сообщение о завершении теста!**
|
||||
6. Как и в случае с проверкой процессора архитектуры CYBERcobra, вам не будет сказано пройден тест или нет. Вы должны сами, такт за тактом проверить что процессор правильно выполняет описанные в _Листинге 1_ инструкции. Для этого, необходимо сперва самостоятельно рассчитать что именно должна сделать данная инструкция, а потом проверить что процессор сделал именно это.
|
||||
7. Вполне возможно, что после первого запуска вы столкнетесь с сообщениями о множестве ошибок. Вам необходимо [исследовать](../../Vivado%20Basics/Debug%20manual.md) эти ошибки на временной диаграмме и исправить их в вашем модуле.
|
||||
4. Проверьте работоспособность вашей цифровой схемы в ПЛИС. Для этого:
|
||||
1. Добавьте файлы из папки [`board files`](https://github.com/MPSU/APS/tree/master/Labs/07.%20Datapath/board%20files) в проект.
|
||||
1. Файл [nexys_riscv_unit.sv](https://github.com/MPSU/APS/tree/master/Labs/07.%20Datapath/board%20files/nexys_riscv_unit.sv) необходимо добавить в `Design Sources` проекта.
|
||||
2. Файл [nexys_a7_100t.xdc](https://github.com/MPSU/APS/tree/master/Labs/07.%20Datapath/board%20files/nexys_a7_100t.xdc) необходимо добавить в `Constraints` проекта. В случае, если вы уже добавляли одноименный файл в рамках предыдущих лабораторных работ, его содержимое необходимо заменить содержимым нового файла.
|
||||
2. Выберите `nexys_riscv_unit` в качестве модуля верхнего уровня (`top-level`).
|
||||
3. Выполните генерацию битстрима и сконфигурируйте ПЛИС. Для этого воспользуйтесь [следующей инструкцией](../../Vivado%20Basics/How%20to%20program%20an%20fpga%20board.md).
|
||||
4. Описание логики работы модуля верхнего уровня и связи периферии ПЛИС с реализованным модулем находится в папке [`board files`](https://github.com/MPSU/APS/tree/master/Labs/07.%20Datapath/board%20files).
|
||||
5. После всех проверок вы можете загрузить ваше индивидуальное задание, написанное на языке ассемблера RISC-V чтобы проверить, что ваш процессор вычисляет корректный результат.
|
||||
1. Внимательно ознакомьтесь микроархитектурной реализацией процессорного ядра. В случае возникновения вопросов, проконсультируйтесь с преподавателем.
|
||||
2. Замените файл `program.mem` в `Design Sources` проекта новым файлом [program.mem](program.mem), приложенном в данной лабораторной работе. Данный файл содержит программу из _листинга 1_.
|
||||
3. Опишите модуль процессорного ядра с таким же именем и портами, как указано в задании.
|
||||
1. Процесс реализации модуля очень похож на процесс описания модуля cybercobra, однако теперь появляется:
|
||||
1. декодер
|
||||
2. дополнительные мультиплексоры и знакорасширители.
|
||||
2. Сперва рекомендуется создать все провода, которые будут подключены к входам и выходам каждого модуля на схеме.
|
||||
3. Затем необходимо создать экземпляры модулей.
|
||||
4. Также необходимо создать 32-разрядные константы I, U, S, B и J-типа и программный счётчик.
|
||||
5. После необходимо описать логику, управляющую созданными в п. 3.2 проводами.
|
||||
6. В конце останется описать логику работы программного счётчика.
|
||||
4. Опишите модуль процессорной системы, объединяющий ядро процессора (`processor_core`) с памятями инструкция и данных.
|
||||
1. Опишите модуль с таким же именем и портами, как указано в задании.
|
||||
2. **При создании объекта модуля `processor_core` в модуле `processor_system` вы должны использовать имя сущности `core` (т.е. создать объект в виде: `processor_core core(...`)**.
|
||||
5. Проверьте модуль с помощью верификационного окружения, представленного в файле [`lab_07.tb_processor_system.sv`](lab_07.tb_processor_system.sv).
|
||||
1. Перед запуском симуляции убедитесь, что выбран правильный модуль верхнего уровня в `Simulation Sources`.
|
||||
2. Как и в случае с проверкой процессора архитектуры CYBERcobra, вам не будет сказано пройден тест или нет. Вам необходимо самостоятельно, такт за тактом проверить что процессор правильно выполняет описанные в _Листинге 1_ инструкции (см. порядок выполнения задания ЛР№4). Для этого, необходимо сперва самостоятельно рассчитать что именно должна сделать данная инструкция, а потом проверить что процессор сделал именно это.
|
||||
6. Проверьте работоспособность вашей цифровой схемы в ПЛИС.
|
||||
|
||||
---
|
||||
|
||||
@@ -231,3 +193,7 @@ _Листинг 2. Программа из Листинга 1, представ
|
||||
>Я способен(на) на всё! Я сам(а) полностью, с нуля, сделал(а) процессор с архитектурой RISC-V! Что? Не знаешь, что такое архитектура? Пф, щегол! Подрастешь – узнаешь
|
||||
|
||||
</details>
|
||||
|
||||
## Список источников
|
||||
|
||||
1. [The RISC-V Instruction Set Manual Volume I: Unprivileged ISA](https://github.com/riscv/riscv-isa-manual/releases/download/20240411/unpriv-isa-asciidoc.pdf).
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
После того, как вы проверили на моделировании дизайн, вам необходимо проверить его работу на прототипе в ПЛИС.
|
||||
|
||||
Инструкция по реализации прототипа описана [здесь](../../../Vivado%20Basics/How%20to%20program%20an%20fpga%20board.md).
|
||||
Инструкция по реализации прототипа описана [здесь](../../../Vivado%20Basics/07.%20Program%20and%20debug.md).
|
||||
|
||||
На _рис. 1_ представлена схема прототипа в ПЛИС.
|
||||
|
||||
|
@@ -8,12 +8,12 @@
|
||||
See https://github.com/MPSU/APS/blob/master/LICENSE file for licensing details.
|
||||
* ------------------------------------------------------------------------------
|
||||
*/
|
||||
module tb_riscv_unit();
|
||||
module lab_07_tb_processor_system();
|
||||
|
||||
reg clk;
|
||||
reg rst;
|
||||
|
||||
riscv_unit unit(
|
||||
processor_system system(
|
||||
.clk_i(clk),
|
||||
.rst_i(rst)
|
||||
);
|
||||
@@ -21,7 +21,7 @@ module tb_riscv_unit();
|
||||
initial clk = 0;
|
||||
always #10 clk = ~clk;
|
||||
initial begin
|
||||
$display( "\nStart test: \n\n==========================\nCLICK THE BUTTON 'Run All'\n==========================\n"); $stop();
|
||||
$display( "\nTest has been started.");
|
||||
rst = 1;
|
||||
#40;
|
||||
rst = 0;
|
||||
@@ -31,12 +31,12 @@ module tb_riscv_unit();
|
||||
end
|
||||
|
||||
stall_seq: assert property (
|
||||
@(posedge unit.core.clk_i) disable iff ( unit.core.rst_i )
|
||||
unit.core.mem_req_o |-> (unit.core.stall_i || $past(unit.core.stall_i))
|
||||
@(posedge system.core.clk_i) disable iff ( system.core.rst_i )
|
||||
system.core.mem_req_o |-> (system.core.stall_i || $past(system.core.stall_i))
|
||||
)else $error("\nincorrect implementation of stall signal\n");
|
||||
|
||||
stall_seq_fall: assert property (
|
||||
@(posedge unit.core.clk_i) disable iff ( unit.core.rst_i )
|
||||
(unit.core.stall_i) |=> !unit.core.stall_i
|
||||
@(posedge system.core.clk_i) disable iff ( system.core.rst_i )
|
||||
(system.core.stall_i) |=> !system.core.stall_i
|
||||
)else $error("\nstall must fall exact one cycle after rising\n");
|
||||
endmodule
|
Reference in New Issue
Block a user