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:
Andrei Solodovnikov
2024-09-02 10:20:08 +03:00
committed by GitHub
parent 78bb01ef95
commit a28002e681
195 changed files with 3640 additions and 2664 deletions

View File

@@ -1,6 +1,6 @@
# Лабораторная работа 8 "Блок загрузки и сохранения"
# Лабораторная работа 8 "Блок загрузки и сохранения"
Итогом шестой лабораторной работы стал практически завершенный процессор архитектуры RISC-V. Особенностью той реализации процессора было отсутствие поддержки инструкций `LB`, `LBU`, `SB`, `LH`, `LHU`, `SH`. Тому было две причины:
Итогом шестой лабораторной работы стал практически завершенный процессор архитектуры RISC-V. Особенностью реализации процессора было отсутствие поддержки инструкций `LB`, `LBU`, `SB`, `LH`, `LHU`, `SH`. Тому было две причины:
- подключенный к памяти данных сигнал `byte_enable_i` был аппаратно зафиксирован на значении `4'b1111`, но на самом деле этим сигналом должен кто-то управлять;
- необходимо подготовить считанные из памяти полуслова / байты для записи в регистровый файл.
@@ -21,7 +21,7 @@
- Интерфейс процессора и блока загрузки/сохранения
- Интерфейс блока загрузки/сохранения и памяти
Реализовать и проверить модуль `riscv_lsu`.
Реализовать и проверить модуль `lsu`.
---
@@ -39,7 +39,7 @@ _Рисунок 1. Место LSU в микроархитектуре RISC-пр
Инструкции `LOAD` и `STORE` в **RV32I** поддерживают обмен 8-битными, 16-битными или 32-битными значениями, однако в самом процессоре происходит работа только с 32-битными числами, поэтому загружаемые из памяти байты и послуслова необходимо предварительно расширить до 32-битного значения. Расширять значения можно либо знаковым битом, либо нулями — в зависимости от того как должно быть интерпретировано загружаемое число: как знаковое или беззнаковое. Во время записи данных в память, они не расширяются, поскольку в отличие от регистрового файла, основная память имеет возможность обновлять отдельные байты. Таким образом, различать знаковые и беззнаковые числа необходимо только на этапе загрузки, но не сохранения.
Для выбора разрядности и формата представления числа, на вход **LSU** подается сигнал `core_size_i`, принимающий следующие значения (для удобства использования, данные значения определены в виде параметров в пакете `decoder_pkg`):
Для выбора разрядности и формата представления числа, на вход **LSU** подаётся сигнал `core_size_i`, принимающий следующие значения (для удобства использования, данные значения определены в виде параметров в пакете `decoder_pkg`):
| Параметр |Значение| Пояснение |
|----------|--------|-------------------------------|
@@ -51,7 +51,7 @@ _Рисунок 1. Место LSU в микроархитектуре RISC-пр
Для операций типа `STORE` формат представления чисел не важен, для них `core_size_i` сможет принимать значение только от 0 до 2.
Выходной сигнал `core_stall_o` нужен для приостановки программного счетчика. Ранее логика этого сигнала временно находилась в модуле `riscv_unit` — теперь она займет свое законное место в модуле **LSU**.
Выходной сигнал `core_stall_o` нужен для приостановки программного счётчика. Ранее логика этого сигнала временно находилась в модуле `processor_system` — теперь она займёт своё законное место в модуле **LSU**.
### Интерфейс блока загрузки/сохранения и памяти
@@ -59,11 +59,11 @@ _Рисунок 1. Место LSU в микроархитектуре RISC-пр
После получения запроса на чтение/запись из ядра **LSU** перенаправляет запрос в память данных, взаимодействие осуществляется следующими сигналами:
- сигнал `mem_req_o` сообщает памяти о наличии запроса в память (напрямую подключен к `core_req_i`);
- сигнал `mem_we_o` сообщает памяти о типе этого запроса (напрямую подключен к `core_we_i`):
- сигнал `mem_req_o` сообщает памяти о наличии запроса в память (напрямую подключён к `core_req_i`);
- сигнал `mem_we_o` сообщает памяти о типе этого запроса (напрямую подключён к `core_we_i`):
- `mem_we_o` равен 1, если отправлен запрос на запись,
- `mem_we_o` равен 0, если отправлен запрос на чтение;
- сигнал `mem_wd_o` содержит данные на запись в память. В зависимости от размера записи, данные этого сигнала будут отличаться от пришедшего сигнала `core_wd_i` и будут является результатом определенных преобразований.
- сигнал `mem_wd_o` содержит данные на запись в память. В зависимости от размера записи, данные этого сигнала будут отличаться от пришедшего сигнала `core_wd_i` и будут является результатом определённых преобразований.
- сигнал `mem_rd_i` содержит считанные из памяти данные. Перед тем, как вернуть считанные данные ядру через выходной сигнал `core_rd_o`, эти данные будет необходимо подготовить.
- сигнал `mem_ready_i` сообщает о готовности памяти завершить транзакцию на текущем такте. Этот сигнал используется для управления выходным сигналом `core_stall_o`.
@@ -98,7 +98,7 @@ _Рисунок 1. Место LSU в микроархитектуре RISC-пр
> Познай как описать выходные сигналы модуля — и ты познаешь как описать сам модуль. ©Джейсон Стейтем
Реализация любого модуля сводится к реализации логики, управляющей каждым отдельным выходным сигналом посредством входных сигналов. Разберем принцип работы каждого выходного сигнала:
Реализация любого модуля сводится к реализации логики, управляющей каждым отдельным выходным сигналом посредством входных сигналов. Разберём принцип работы каждого выходного сигнала:
### mem_req_o, mem_we_o, mem_addr_o
@@ -112,7 +112,7 @@ _Рисунок 1. Место LSU в микроархитектуре RISC-пр
Данный сигнал принимает ненулевые значения только по запросу на запись (`core_req_i == 1`, `core_we_i == 1`), во время которого происходит мультиплексирование сигнала `core_size_i`. Если `core_size_i` соответствует инструкции записи байта (`LDST_B`, 3'd0), то в сигнале `mem_be_o` бит с индексом равным значению двух младших бит адреса `core_addr_i` должен быть равен единице.
Допустим, пришел запрос на запись байта по адресу 18:
Допустим, пришёл запрос на запись байта по адресу 18:
- `core_req_i == 1`,
- `core_we_i == 1`,
@@ -121,9 +121,9 @@ _Рисунок 1. Место LSU в микроархитектуре RISC-пр
В данном случае, необходимо выставить единицу во втором (считая с нуля) бите сигнала `mem_be_o` (поскольку значение двух младших бит `core_addr_i` равно двум): `mem_be_o == 4'b0100`.
Если пришел запрос на запись полуслова (`core_size_i == LDST_H`), то в сигнале `mem_be_o` необходимо выставить в единицу либо два старших, либо два младших бита (в зависимости от `core_addr_i[1]`: если `core_addr_i[1] == 1`, то в двух старших битах, если `core_addr_i[1] == 0`, то в двух младших).
Если пришёл запрос на запись полуслова (`core_size_i == LDST_H`), то в сигнале `mem_be_o` необходимо выставить в единицу либо два старших, либо два младших бита (в зависимости от `core_addr_i[1]`: если `core_addr_i[1] == 1`, то в двух старших битах, если `core_addr_i[1] == 0`, то в двух младших).
Если пришел запрос на запись слова (`core_size_i == LDST_W`), то в сигнале `mem_be_o` необходимо выставить в единицу все биты.
Если пришёл запрос на запись слова (`core_size_i == LDST_W`), то в сигнале `mem_be_o` необходимо выставить в единицу все биты.
![../../.pic/Labs/lab_08_lsu/fig_02.wavedrom.svg](../../.pic/Labs/lab_08_lsu/fig_02.wavedrom.svg)
@@ -178,9 +178,9 @@ _Рисунок 4. Временна́я диаграмма запросов на
### core_stall_o
Сигнал `core_stall_o` запрещает менять значение программного счетчика на время обращения в память. Этот сигнал должен:
Сигнал `core_stall_o` запрещает менять значение программного счётчика на время обращения в память. Этот сигнал должен:
- стать равным единице в тот же такт, когда пришел сигнал `core_req_i`
- стать равным единице в тот же такт, когда пришёл сигнал `core_req_i`
- удерживать это значение до тех пор, пока не придет сигнал `mem_ready_i`, но не менее 1 такта (т.е. даже если сигнал `mem_ready_i` будет равен единице, `core_req_i` должен подняться хотя бы на 1 такт).
Для реализации подобного функционала вам потребуется вспомогательный регистр `stall_reg`, каждый такт записывающий значение выхода `core_stall_o` и таблица истинности для этого выхода, представленная на _рис. 5_.
@@ -195,8 +195,8 @@ _Рисунок 5. Таблица истинности выхода `core_stall_
Реализовать блок загрузки и сохранения со следующим прототипом:
```SystemVerilog
module riscv_lsu(
```Verilog
module lsu(
input logic clk_i,
input logic rst_i,
@@ -223,21 +223,17 @@ module riscv_lsu(
![../../.pic/Labs/lab_08_lsu/fig_05.drawio.svg](../../.pic/Labs/lab_08_lsu/fig_06.drawio.svg)
_Рисунок 6. Структурная схема модуля `riscv_lsu`._
_Рисунок 6. Функциональная схема модуля `lsu`._
---
### Порядок выполнения задания
1. Внимательно ознакомьтесь с описанием функционального поведения выходов **LSU**. В случае возникновения вопросов, проконсультируйтесь с преподавателем.
2. Реализуйте модуль `riscv_lsu`. Для этого:
1. В `Design Sources` проекта с предыдущих лаб, создайте `SystemVerilog`-файл `riscv_lsu.sv`.
2. Опишите в нем модуль `riscv_lsu` с таким же именем и портами, как указано в [задании](#задание).
1. При описании обратите внимание на то, что большая часть модуля является чисто комбинационной. В этом плане реализация модуля будет частично похожа на реализацию декодера.
1. При описании мультиплексоров, управляемых сигналом core_size_i посредством конструкции `case`, не забывайте описать блок `default`, иначе вы получите защелку!
2. Однако помимо комбинационной части, в модуле будет присутствовать и один регистр.
3. После описания модуля его необходимо проверить с помощью тестового окружения.
1. Тестовое окружение находится [здесь](tb_lsu.sv).
2. Для запуска симуляции воспользуйтесь [`этой инструкцией`](../../Vivado%20Basics/Run%20Simulation.md).
3. Перед запуском симуляции убедитесь, что в качестве top-level модуля выбран корректный (`tb_lsu`).
4. **По завершению симуляции убедитесь, что в логе есть сообщение о завершении теста!**
2. Опишите модуль загрузки и сохранения с таким же именем и портами, как указано в задании
1. При описании обратите внимание на то, что большая часть модуля является чисто комбинационной. В этом плане реализация модуля будет частично похожа на реализацию декодера.
1. При описании мультиплексоров, управляемых сигналом core_size_i посредством конструкции `case`, не забывайте описать блок `default`, иначе вы получите защелку!
2. Однако помимо комбинационной части, в модуле будет присутствовать и один регистр.
3. Проверьте модуль с помощью верификационного окружения, представленного в файле [`lab_08.tb_lsu.sv`](lab_08.tb_lsu.sv). В случае, если в TCL-консоли появились сообщения об ошибках, вам необходимо [найти](../../Vivado%20Basics/05.%20Bug%20hunting.md) и исправить их.
1. Перед запуском моделирования, убедитесь, что у вас выбран корректный модуль верхнего уровня в `Simulation Sources`.
4. Данная лабораторная работа не предполагает проверки в ПЛИС.