English version draft

Assisted-by: Claude:claude-4.6-sonnet
This commit is contained in:
Andrei Solodovnikov
2026-04-12 13:53:25 +03:00
parent 63260f434e
commit f3fcd27387
74 changed files with 5133 additions and 5875 deletions

View File

@@ -1,23 +1,23 @@
# Лабораторная работа №7 "Тракт данных"
# Lab 7. Datapath
Микроархитектуру можно разделить на две части: тракт данных и устройство управления. По тракту данных перемещаются данные (из памяти инструкций, регистрового файла, АЛУ, памяти данных, мультиплексоров), а устройство управления (в нашем случае — декодер инструкций) получает текущую инструкцию из тракта и в ответ говорит ему как именно её выполнить, то есть управляет тем, как эти данные будут проходить через тракт данных.
The microarchitecture can be divided into two parts: the datapath and the control unit. Data flows through the datapath (from instruction memory, the register file, the ALU, data memory, and multiplexers), while the control unit (in our case — the instruction decoder) receives the current instruction from the datapath and tells it exactly how to execute it, i.e., controls how data passes through the datapath.
## Цель
## Objective
Описать на языке **SystemVerilog** процессор с архитектурой **RISC-V**, реализовав его тракт данных, используя разработанные ранее блоки, и подключив к нему устройство управления. Итогом текущей лабораторной работы станет процессор RISC-V, который пока что сможет работать с памятью данных лишь посредством 32-битных слов (то есть БЕЗ инструкций, связанных с байтами и полусловами: `lh`, `lhu`, `lb`, `lbu`, `sh`, `sb`).
Describe a **RISC-V** processor in **SystemVerilog** by implementing its datapath using previously developed blocks and connecting the control unit to it. The result of this lab will be a RISC-V processor that, for now, can only interact with data memory using 32-bit words (i.e., WITHOUT byte- and halfword-related instructions: `lh`, `lhu`, `lb`, `lbu`, `sh`, `sb`).
## Ход работы
## Workflow
1. Изучить микроархитектурную реализацию однотактного процессора RISC-V (без поддержки команд загрузки/сохранения байт/полуслов)
2. Реализовать тракт данных с подключенным к нему устройством управления([#задание](#Задание))
3. Подготовить программу по индивидуальному заданию и загрузить ее в память инструкций
4. Сравнить результат работы процессора на модели в **Vivado** и в симуляторе программы ассемблера
1. Study the microarchitectural implementation of the single-cycle RISC-V processor (without support for byte/halfword load/store instructions)
2. Implement the datapath with the control unit connected to it ([#assignment](#Assignment))
3. Prepare a program for the individual task and load it into instruction memory
4. Compare the processor's behavior in the **Vivado** model against the assembly program simulator
## Микроархитектура процессорной системы
## Processor System Microarchitecture
### processor_core
Рассмотрим микроархитектуру процессорного ядра `processor_core`. Микроархитектура данного модуля представлена на _рис. 1_.
Let us examine the microarchitecture of the `processor_core` module. The microarchitecture of this module is shown in _Fig. 1_.
```Verilog
module processor_core (
@@ -41,39 +41,39 @@ endmodule
![../../.pic/Labs/lab_07_dp/fig_01.drawio.svg](../../.pic/Labs/lab_07_dp/fig_01.drawio.svg)
_Рисунок 1. Микроархитектура ядра процессора RISC-V._
_Figure 1. RISC-V processor core microarchitecture._
Предложенная микроархитектура имеет схожую структуру c процессором `CYBERcobra` из [ЛР№4](../04.%20Primitive%20programmable%20device/), с некоторыми изменениями.
The proposed microarchitecture shares a similar structure with the `CYBERcobra` processor from [Lab 4](../04.%20Primitive%20programmable%20device/), with some modifications.
В первую очередь изменились входы и выходы процессора:
First, the processor's inputs and outputs have changed:
- память инструкций вынесена наружу процессора, таким образом, у процессора появляются входы и выходы: `instr_addr_o` и `instr_i`;
- помимо прочего, у модуля появились сигналы интерфейса памяти данных, реализованной в [ЛР№6](../06.%20Main%20memory/):
- `mem_addr_o`адрес внешней памяти;
- `mem_req_o`запрос на обращение во внешнюю память;
- `mem_size_o`размер данных при обращении в память;
- `mem_we_o`сигнал разрешения записи во внешнюю память;
- `mem_wd_o`данные для записи во внешнюю память;
- `mem_rd_i`считанные из внешней памяти данные;
Эти сигналы используются при выполнении инструкций загрузки (сохранения) информации из (в) памяти данных.
- ещё у процессора появился вход `stall_i`, приостанавливающий обновление программного счётчика.
- The instruction memory has been moved outside the processor, so the processor now has the `instr_addr_o` and `instr_i` ports;
- Additionally, the module now has data memory interface signals, as implemented in [Lab 6](../06.%20Main%20memory/):
- `mem_addr_o`external memory address;
- `mem_req_o`request to access external memory;
- `mem_size_o`data size for memory access;
- `mem_we_o`write enable signal for external memory;
- `mem_wd_o`data to write to external memory;
- `mem_rd_i`data read from external memory;
These signals are used when executing load/store instructions for data memory.
- The processor also has a new `stall_i` input that pauses the program counter update.
Кроме того, в данной микроархитектуре используется пять различных видов констант (соответствующих определенным типам инструкций).
Furthermore, this microarchitecture uses five different types of immediate constants (corresponding to specific instruction types).
Константы `I`, `U`, `S` используются для вычисления адресов и значений. Поэтому все эти константы должны быть подключены к АЛУ. А значит теперь, для выбора значения для операндов требуются мультиплексоры, определяющие, что именно будет подаваться на АЛУ.
The `I`, `U`, and `S` immediates are used to compute addresses and values. Therefore, all of these constants must be connected to the ALU. This means multiplexers are now required to select what is fed into the ALU operands.
> [!IMPORTANT]
> Обратите внимание на константу `imm_U`. В отличие от всех остальных констант, она не знакорасширяется, вместо этого к ней "приклеивается" справа 12 нулевых бит.
> Note the `imm_U` constant. Unlike all other constants, it is not sign-extended; instead, 12 zero bits are appended to its right.
Константы `B` и `J` используются для условного и безусловного перехода (в киберкобре для этого использовалась одна константа `offset`).
The `B` and `J` immediates are used for conditional and unconditional branches (in CYBERcobra, a single `offset` constant was used for this purpose).
Программный счётчик (`PC`) теперь также изменяется более сложным образом. Поскольку появился ещё один вид безусловного перехода (`jalr`), программный счётчик может не просто увеличиться на значение константы из инструкции, но и получить совершенно новое значение в виде суммы константы и значения из регистрового файла (см. на самый левый мультиплексор _рис. 1_). Обратите внимание, что младший бит этой суммы должен быть обнулен — таково требование спецификации [[1](https://github.com/riscv/riscv-isa-manual/releases/download/20240411/unpriv-isa-asciidoc.pdf), стр. 28].
The program counter (`PC`) now also updates in a more complex way. Since a new type of unconditional branch (`jalr`) has been added, the program counter can not only be incremented by a constant from the instruction, but can also receive an entirely new value as the sum of a constant and a value from the register file (see the leftmost multiplexer in _Fig. 1_). Note that the least significant bit of this sum must be cleared — this is a requirement of the specification [[1](https://github.com/riscv/riscv-isa-manual/releases/download/20240411/unpriv-isa-asciidoc.pdf), p. 28].
Поскольку обращение во внешнюю память требует времени, необходимо приостанавливать программный счётчик, чтобы до конца обращения в память не начали исполняться последующие инструкции. Для этого у программного счётчика появился управляющий сигнал `stall_i`. Программный счётчик может меняться только когда этот сигнал равен нулю (иными словами, инверсия этого сигнала является сигналом `enable` для регистра `PC`).
Since accessing external memory takes time, the program counter must be stalled to prevent subsequent instructions from executing before the memory access completes. For this purpose, a `stall_i` control signal has been added to the program counter. The program counter can only update when this signal is zero (in other words, the inverse of this signal serves as the `enable` signal for the `PC` register).
### processor_system
После реализации процессорного ядра к нему необходимо подключить память. Это происходит в модуле `processor_system`.
After implementing the processor core, memory must be connected to it. This is done in the `processor_system` module.
```Verilog
module processor_system(
@@ -86,18 +86,18 @@ endmodule
![../../.pic/Labs/lab_07_dp/fig_02.drawio.svg](../../.pic/Labs/lab_07_dp/fig_02.drawio.svg)
_Рисунок 2. Микроархитектура процессорной системы._
_Figure 2. Processor system microarchitecture._
Обратите внимание на регистр `stall` (_рисунок 2_). Этот регистр и будет управлять разрешением на запись в программный счётчик. Поскольку мы используем блочную память, расположенную прямо в ПЛИС, доступ к ней осуществляется за 1 такт, а значит, что при обращении в память, нам необходимо "отключить" программный счётчик ровно на 1 такт. Если бы использовалась действительно "внешняя" память (например память типа DDR3), то вместо этого регистра появилась бы другая логика, выставляющая на вход ядра `stall_i` единицу пока идет обращение в память.
Pay attention to the `stall` register (_Figure 2_). This register controls the write enable for the program counter. Since we are using block RAM located directly inside the FPGA, access to it takes 1 clock cycle, which means that when a memory access occurs, the program counter must be "disabled" for exactly 1 clock cycle. If truly "external" memory were used (e.g., DDR3), this register would be replaced by different logic that asserts the `stall_i` input of the core while a memory access is in progress.
## Задание
## Assignment
1. Реализовать ядро процессора RISC-V по предложенной микроархитектуре (`processor_core`).
2. Подключить к нему память инструкций и память данных в модуле `processor_system`.
3. Проверить работу системы, с помощью тестовой программы из _листинга 1_.
4. Написать собственную программу на ассемблере RISC-V по индивидуальному заданию, которое было выбрано в ЛР№4, и проверить выполнение программы на вашей процессорной системе.
1. Implement the RISC-V processor core according to the proposed microarchitecture (`processor_core`).
2. Connect instruction memory and data memory to it in the `processor_system` module.
3. Verify the system using the test program from _Listing 1_.
4. Write your own RISC-V assembly program for the individual task chosen in Lab 4, and verify its execution on your processor system.
Напишем простую программу, которая использует все типы инструкций для проверки нашего процессора. Сначала напишем программу на ассемблере:
Let us write a simple program that uses all instruction types to test our processor. First, let us write the program in assembly:
```assembly
00: addi x1, x0, 0x75С
@@ -105,8 +105,8 @@ _Рисунок 2. Микроархитектура процессорной с
08: add x3, x1, x2
0C: and x4, x1, x2
10: sub x5, x4, x3
14: mul x6, x3, x4 // неподдерживаемая инструкция
18: jal x15, 0x00050 // прыжок на адрес 0x68
14: mul x6, x3, x4 // unsupported instruction
18: jal x15, 0x00050 // jump to address 0x68
1C: jalr x15, 0x0(x6)
20: slli x7, x5, 31
24: srai x8, x7, 1
@@ -122,16 +122,16 @@ _Рисунок 2. Микроархитектура процессорной с
4С: lhu x14, 0x0(x4)
50: lbu x15, 0x0(x4)
54: auipc x16, 0x00004
58: bne x3, x4, 0x08 // перескок через
5С: // нелегальную нулевую инструкцию
58: bne x3, x4, 0x08 // skip over
5С: // the illegal zero instruction
60: jal x17, 0x00004
64: jalr x14, 0x0(x17)
68: jalr x18, 0x4(x15)
```
_Листинг 1. Пример программы на ассемблере._
_Listing 1. Example assembly program._
Теперь в соответствии с кодировкой инструкций переведем программу в машинные коды:
Now, following the instruction encoding, let us convert the program to machine code:
```text
00: 011101011100 00000 000 00001 0010011 (0x75C00093)
@@ -163,43 +163,43 @@ _Листинг 1. Пример программы на ассемблере._
68: 000000000100 01111 000 10010 1100111 (0x00478967)
```
_Листинг 2. Программа из Листинга 1, представленная в машинных кодах._
_Listing 2. The program from Listing 1 represented in machine code._
Данная программа, представленная в шестнадцатеричном формате находится в файле [program.mem](program.mem).
This program in hexadecimal format is stored in the file [program.mem](program.mem).
## Порядок выполнения задания
## Steps
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. После проверки работы процессора программой из _листинга 1_, вам необходимо написать собственную программу по варианту индивидуального задания, полученного в ЛР№4.
1. Для того, чтобы перевести код из ассемблера RISC-V в двоичный код, вы можете воспользоваться онлайн-компилятором, например: https://venus.kvakil.me. Напишите код во вкладке "Editor", затем, перейдя на вкладку "Simulator", нажмите кнопку "Dump". Двоичный код будет скопирован в буфер обмена в том формате, который подходит для инициализации памяти в Vivado, замените этим кодом содержимое файла program.mem в вашем проекте. С помощью кнопок "Step" и "Run" вы можете отладить вашу программу в онлайн-симуляторе, прежде чем перейти к запуску этой программы на собственной процессорной системе. В ЛР№14 вам будет рассказано, как скомпилировать программу самостоятельно, без помощи сторонних сервисов.
7. Проверьте работоспособность вашей цифровой схемы в ПЛИС.
1. Carefully study the microarchitectural implementation of the processor core. If you have any questions, consult your instructor.
2. Replace the `program.mem` file in the project's `Design Sources` with the new [program.mem](program.mem) file provided with this lab. This file contains the program from _Listing 1_.
3. Describe the processor core module with the same name and ports as specified in the assignment.
1. The process of implementing the module is similar to describing the CYBERcobra module, but now the following are added:
1. the decoder
2. additional multiplexers and sign-extension units.
2. It is recommended to first create all the wires that will be connected to the inputs and outputs of each module in the schematic.
3. Then create the module instances.
4. Also create the 32-bit I-, U-, S-, B-, and J-type immediate constants and the program counter.
5. Next, describe the logic that drives the wires created in step 3.2.
6. Finally, describe the program counter logic.
4. Describe the processor system module that combines the processor core (`processor_core`) with instruction memory and data memory.
1. Describe the module with the same name and ports as specified in the assignment.
2. **When instantiating the `processor_core` module inside `processor_system`, you must use the instance name `core` (i.e., instantiate it as: `processor_core core(...`)**.
5. Verify the module using the testbench provided in the file [`lab_07.tb_processor_system.sv`](lab_07.tb_processor_system.sv).
1. Before running the simulation, make sure the correct top-level module is selected in `Simulation Sources`.
2. As with the CYBERcobra processor verification, you will not be told whether the test passed or not. You must manually verify, cycle by cycle, that the processor correctly executes the instructions described in _Listing 1_ (refer to the step-by-step instructions of Lab 4). To do this, first calculate what a given instruction should do, then verify that the processor did exactly that.
6. After verifying the processor with the program from _Listing 1_, write your own program for the individual task variant received in Lab 4.
1. To convert RISC-V assembly code to binary, you can use an online compiler such as: https://venus.kvakil.me. Write the code in the "Editor" tab, then switch to the "Simulator" tab and click the "Dump" button. The binary code will be copied to the clipboard in a format suitable for memory initialization in Vivado; replace the contents of the program.mem file in your project with this code. Using the "Step" and "Run" buttons, you can debug your program in the online simulator before running it on your own processor system. In Lab 14, you will learn how to compile a program on your own without third-party services.
7. Verify the functionality of your digital circuit on the FPGA.
---
<details>
<summary>Прочти меня, когда выполнишь.</summary>
Поздравляю, ты сделал(а) свой первый взрослый процессор! Теперь ты можешь говорить:
<summary>Read me when you're done.</summary>
Congratulations, you've built your first grown-up processor! Now you can say:
>Я способен(на) на всё! Я сам(а) полностью, с нуля, сделал(а) процессор с архитектурой RISC-V! Что? Не знаешь, что такое архитектура? Пф, щегол! Подрастешь узнаешь
>I can do anything! I built a complete RISC-V processor from scratch, all by myself! What? You don't know what an architecture is? Pfft, rookie! You'll find out when you grow up.
</details>
## Список источников
## References
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).

View File

@@ -1,46 +1,46 @@
# Проверка работы processor_system на ПЛИС
# Testing processor_system on FPGA
После того, как вы проверили на моделировании дизайн, вам необходимо проверить его работу на прототипе в ПЛИС.
After verifying the design through simulation, you need to test it on an FPGA prototype.
Инструкция по реализации прототипа описана [здесь](../../../Vivado%20Basics/07.%20Program%20and%20debug.md).
Instructions for implementing the prototype are described [here](../../../Vivado%20Basics/07.%20Program%20and%20debug.md).
На _рис. 1_ представлена схема прототипа в ПЛИС.
_Fig. 1_ shows the structure of the FPGA prototype.
![../../../.pic/Labs/board%20files/nexys_processor_system_structure.drawio.svg](../../../.pic/Labs/board%20files/nexys_processor_system_structure.drawio.svg)
_Рисунок 1. Структурная схема модуля `nexys_processor_system`._
_Figure 1. Block diagram of the `nexys_processor_system` module._
Прототип позволяет потактово исполнять программу, прошитую в память инструкций. Также прототип отображает операцию исполняемую в данный момент.
The prototype allows step-by-step execution of the program loaded into instruction memory. It also displays the operation currently being executed.
> [!NOTE]
> Объект модуля `processor_core` в модуле `processor_system` **должен** называться `core`. Т.е. строка создания сущности модуля должна выглядеть следующим образом: `processor_core core(...)`.
> The instance of the `processor_core` module inside the `processor_system` module **must** be named `core`. That is, the instantiation line must look as follows: `processor_core core(...)`.
## Описание используемой периферии
## Description of the peripherals used
- ### Кнопки
- ### Buttons
- `BTND`при нажатии создает тактовый импульс, поступающий на порт тактирования `clk_i` модуля дизайна. Стоит помнить то, что инструкции, работающие с внешней памятью, требуют несколько тактов для своего выполнения.
- `CPU_RESET`соединен со входом `rst_i` модуля дизайна. Поскольку в модуле `processor_system` используется синхронный сброс (то есть сигнал сброса учитывается только во время восходящего фронта тактового сигнала), то для сброса модуля `processor_system` и вложенных в него модулей необходимо при зажатой кнопке сброса еще нажать кнопку тактирования.
- `BTND`when pressed, generates a clock pulse delivered to the `clk_i` port of the design module. Note that instructions that access external memory require several clock cycles to complete.
- `CPU_RESET`connected to the `rst_i` input of the design module. Since `processor_system` uses synchronous reset (i.e., the reset signal is only sampled on the rising edge of the clock), resetting `processor_system` and its submodules requires holding the reset button while also pressing the clock button.
- ### Семисегментные индикаторы
- ### Seven-segment displays
Семисегментные индикаторы разбиты на 2 блока (см. _рис. 1_):
The seven-segment displays are divided into 2 groups (see _Fig. 1_):
- `PC`отображают в виде шестнадцатеричного числа младшие 16 бит программного счетчика, которые вычисляются на основе выхода `instr_addr_o` модуля процессорного ядра.
- `operation`отображают [операцию](#Операции-отображаемые-прототипом), исполняемую процессором на текущем такте.
- `PC`displays the lower 16 bits of the program counter as a hexadecimal number, derived from the `instr_addr_o` output of the processor core module.
- `operation`displays the [operation](#Operations-displayed-by-the-prototype) currently being executed by the processor.
## Операции, отображаемые прототипом
## Operations displayed by the prototype
Прототип определяет тип операции по младшим 7 битам инструкции.
The prototype determines the operation type from the lower 7 bits of the instruction.
Если тип операции является легальным в рамках процессорного устройства, реализуемого на лабораторных работах, то отображается соответствующий опкод. Опкоды описаны в [decoder_pkg.sv](../../05.%20Main%20decoder/decoder_pkg.sv). Если определенный прототипом тип операции является нелегальным, то на семисегментных индикаторах отображается `ILL` (от **ill**egal).
If the operation type is legal within the processor implemented in the lab assignments, the corresponding opcode is displayed. Opcodes are defined in [decoder_pkg.sv](../../05.%20Main%20decoder/decoder_pkg.sv). If the operation type determined by the prototype is illegal, the seven-segment displays show `ILL` (from **ill**egal).
Соответствие операции ее отображению на семисегментных индикаторах представлено на _рис. 2_:
The mapping between operations and their representation on the seven-segment displays is shown in _Fig. 2_:
!['../../../.pic/Labs/board%20files/nexys_processor_system_operations.drawio.svg'](../../../.pic/Labs/board%20files/nexys_processor_system_operations.drawio.svg)
_Рисунок 2. Соответствие операции ее отображению на семисегментных индикаторах._
_Figure 2. Mapping between operations and their representation on the seven-segment displays._
## Демонстрационная программа
## Demo program
В качестве демонстрационной программы, предлагается использовать [program.mem](../program.mem). Описание ее работы можно прочитать в разделе [#задание](../#Задание).
The recommended demo program is [program.mem](../program.mem). A description of its operation can be found in the [#task](../#Task) section.