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,170 +1,170 @@
# Лабораторная работа №5 "Декодер инструкций"
# Lab 5. Instruction Decoder
Устройство управления (УУ) один из базовых блоков процессора, функцией которого является декодирование инструкций и выдача управляющих сигналов для всех блоков процессора. Роль УУ в данном курсе (с некоторыми оговорками) будет играть декодер инструкций.
The control unit (CU) is one of the core blocks of a processor, responsible for decoding instructions and generating control signals for all processor blocks. In this course, the role of the CU (with some caveats) is played by the instruction decoder.
## Цель
## Objective
Описать на языке **SystemVerilog** блок декодера инструкций для однотактного процессора с архитектурой **RISC-V**.
Describe an instruction decoder block for a single-cycle **RISC-V** processor in **SystemVerilog**.
## Материалы для подготовки к лабораторной работе
## Preparation Materials
- [Форматы кодирования инструкций базового набора команд `RV32I`](../../Other/rv32i.md).
- [Теорию по регистрам контроля и статуса](../../Other/CSR.md).
- [Различия между блокирующими и неблокирующими присваиваниями](../../Basic%20Verilog%20structures/Assignments.md).
- [Instruction encoding formats of the `RV32I` base instruction set](../../Other/rv32i.md).
- [Theory on control and status registers](../../Other/CSR.md).
- [Differences between blocking and non-blocking assignments](../../Basic%20Verilog%20structures/Assignments.md).
## Ход работы
## Workflow
1. Изучить микроархитектуру реализуемого процессорного ядра.
1. Разобраться с логикой формирования управляющих сигналов для всех типов инструкций.
2. Изучить [описание сигналов декодера инструкций](#Описание-сигналов-декодера-инструкций).
3. Изучить [набор поддерживаемых инструкций **RISC-V** и способы их кодирования](#Набор-поддерживаемых-инструкций-risc-v-и-способы-их-кодирования)
4. Изучить конструкции **SystemVerilog**, с помощью которых будет описан декодер ([#инструменты](#Инструменты))
5. Реализовать на языке **SystemVerilog** декодер инструкций ([#задание](#Задание))
6. Проверить с помощью верификационного окружения корректность его работы.
1. Study the microarchitecture of the processor core being implemented.
1. Understand the logic for generating control signals for all instruction types.
2. Study the [instruction decoder signal descriptions](#Instruction-Decoder-Signal-Descriptions).
3. Study the [supported **RISC-V** instruction set and encoding schemes](#Supported-RISC-V-Instruction-Set-and-Encoding-Schemes).
4. Study the **SystemVerilog** constructs to be used for describing the decoder ([#tools](#Tools)).
5. Implement the instruction decoder in **SystemVerilog** ([#task](#Task)).
6. Verify correct operation using the verification environment.
## Предлагаемая микроархитектура процессора RISC-V
## Proposed RISC-V Processor Microarchitecture
На _рис. 1_ приводится микроархитектура реализуемого ядра процессора RISC-V.
_Fig. 1_ shows the microarchitecture of the RISC-V processor core being implemented.
**Приведенная архитектура не является заданием для текущей лабораторной работы, лишь отражает то, как в дальнейшем будет подключаться и использоваться реализуемый в данной лабораторной работе декодер.**
**The architecture shown is not the assignment for this lab — it merely illustrates how the instruction decoder implemented here will be connected and used later.**
![../../.pic/Labs/lab_10_irq/fig_03.drawio.svg](../../.pic/Labs/lab_10_irq/fig_03.drawio.svg)
_Рисунок 1. Микроархитектура будущего процессорного ядра._
_Figure 1. Microarchitecture of the future processor core._
Предложенная микроархитектура похожа на микроархитектуру процессора `CYBERcobra` из ЛР№4, но с некоторыми изменениями.
The proposed microarchitecture is similar to the `CYBERcobra` processor microarchitecture from Lab 4, but with several changes.
В первую очередь изменились входы и выходы процессора:
First, the processor's inputs and outputs have changed:
- память инструкций вынесена наружу, таким образом, у процессора появляются входы и выходы: `instr_addr_o` и `instr_i`;
- также у процессора появились сигналы интерфейса памяти данных:
- `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;
- The processor also has data memory interface signals:
- `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.
- The processor also has a `stall_i` input that halts the program counter update.
Кроме того, появилось два новых модуля: **Interrupt Controller** и **Control Status Registers**. Эти модули будут обеспечивать поддержку прерываний в процессорной системе.
Additionally, two new modules have appeared: **Interrupt Controller** and **Control Status Registers**. These modules will provide interrupt support in the processor system.
Так же добавились источники операндов АЛУ: программный счетчик, множество констант из инструкций и микроархитектурных констант — а значит необходимо мультиплексировать эти сигналы.
ALU operand sources have also been added: the program counter, various constants from instructions, and microarchitectural constants — requiring these signals to be multiplexed.
Изменились и источники записи в регистровый файл, теперь это:
The write-back sources to the register file have also changed, and now include:
- результат операции на АЛУ;
- данные, считанные с внешней памяти;
- данные из модуля регистров контроля и статуса.
- the result of an ALU operation;
- data read from external memory;
- data from the control and status registers module.
Для того, чтобы управлять усложнившимся набором мультиплексоров, интерфейсом памяти данных и появившимися модулями нужно специальное устройство — Устройство управления (УУ). В данной микроархитектуре логика устройства управления не вынесена в отдельный модуль, лишь выделена на схеме синим цветом. По большей части, в предложенной микроархитектуре роль устройства управления выполняет декодер инструкций.
To control the expanded set of multiplexers, the data memory interface, and the new modules, a dedicated unit is needed — the Control Unit (CU). In this microarchitecture, the control unit logic is not separated into its own module; it is only highlighted in blue on the diagram. For the most part, the instruction decoder serves as the control unit in the proposed microarchitecture.
## Описание сигналов декодера инструкций
## Instruction Decoder Signal Descriptions
Список портов декодера инструкций и их назначение представлен в аблице 1_.
The list of instruction decoder ports and their descriptions is given in _Table 1_.
|Название сигнала| Пояснение |
|----------------|------------------------------------------------------------------------------------------------|
|fetched_instr_i |Инструкция, подлежащая декодированию |
|a_sel_o |Управляющий сигнал мультиплексора для выбора первого операнда АЛУ |
|b_sel_o |Управляющий сигнал мультиплексора для выбора второго операнда АЛУ |
|alu_op_o |Операция АЛУ |
|csr_op_o |Операция модуля CSR |
|csr_we_o |Разрешение на запись в CSR |
|mem_req_o |Запрос на доступ к памяти (часть интерфейса памяти) |
|mem_we_o |Сигнал разрешения записи в память, «write enable» (при равенстве нулю происходит чтение) |
|mem_size_o |Управляющий сигнал для выбора размера слова при чтении-записи в память (часть интерфейса памяти)|
|wb_sel_o |Управляющий сигнал мультиплексора для выбора данных, записываемых в регистровый файл |
|gpr_we_o |Сигнал разрешения записи в регистровый файл |
|branch_o |Сигнал об инструкции условного перехода |
|jal_o |Сигнал об инструкции безусловного перехода jal |
|jalr_o |Сигнал об инструкции безусловного перехода jalr |
|mret_o |Сигнал об инструкции возврата из прерывания/исключения mret |
|illegal_instr_o |Сигнал о некорректной инструкции |
| Signal Name | Description |
|------------------|----------------------------------------------------------------------------------------------------|
| fetched_instr_i | Instruction to be decoded |
| a_sel_o | Multiplexer control signal for selecting the first ALU operand |
| b_sel_o | Multiplexer control signal for selecting the second ALU operand |
| alu_op_o | ALU operation |
| csr_op_o | CSR module operation |
| csr_we_o | CSR write enable |
| mem_req_o | Memory access request (part of the memory interface) |
| mem_we_o | Memory write enable signal (when zero, a read is performed) |
| mem_size_o | Control signal for selecting data transfer size during memory read/write (part of memory interface)|
| wb_sel_o | Multiplexer control signal for selecting data to write back to the register file |
| gpr_we_o | Register file write enable signal |
| branch_o | Signal indicating a conditional branch instruction |
| jal_o | Signal indicating an unconditional jump instruction `jal` |
| jalr_o | Signal indicating an unconditional jump instruction `jalr` |
| mret_o | Signal indicating a return-from-trap instruction `mret` |
| illegal_instr_o | Signal indicating an illegal instruction |
_Таблица 1. Описание портов декодера инструкций._
_Table 1. Instruction decoder port descriptions._
У данного модуля будет лишь один вход: `fetched_instr_i` — декодируемая в данный момент инструкция. Все остальные сигналы — это выходы модуля, которые можно сгруппировать по нескольким классам.
This module has only one input: `fetched_instr_i` — the instruction currently being decoded. All other signals are module outputs, which can be grouped into several classes.
### Сигналы кода операции
### Operation Code Signals
В данный класс будут входить сигналы, которые сообщают отдельному функциональному блоку о том, какую из операций он должен выполнить. Таких блока два: **АЛУ** и модуль **регистров контроля и статуса**. АЛУ может выполнять одну из 16 операций, представленных в ЛР№2, для выбора которой и нужен подобный сигнал. Вы ещё не знакомы с появившимся в микроархитектуре модулем регистров контроля и статуса, однако на текущий момент нужно лишь понимать, что он тоже может выполнять одну из нескольких операций и что для этого ему нужен специальный сигнал.
This class includes signals that tell an individual functional block which operation to perform. There are two such blocks: the **ALU** and the **Control and Status Registers** module. The ALU can perform one of 16 operations (introduced in Lab 2), and this signal is used to select which one. You are not yet familiar with the CSR module that appeared in the microarchitecture, but for now it is enough to understand that it can also perform one of several operations and requires a dedicated signal for that purpose.
Таким образом, в класс сигналов кода операции входят:
The operation code signal class therefore includes:
- `alu_op_o`,
- `csr_op_o`.
Для удобства использования, возможные значения этих сигналов определены в виде параметров в пакетах `alu_opcodes_pkg` и `csr_pkg` соответственно.
For convenience, the possible values of these signals are defined as parameters in the `alu_opcodes_pkg` and `csr_pkg` packages respectively.
### Управляющие сигналы мультиплексоров стадии выполнения и записи результата
### Execute-Stage and Write-Back Multiplexer Control Signals
В данный класс входят сигналы, управляющие мультиплексорами, размещенными в правой части схемы:
This class includes signals controlling the multiplexers located on the right side of the diagram:
- `a_sel_o`,
- `b_sel_o`,
- `wb_sel_o`.
Сигналы `a_sel_o` и `b_sel_o` определяют откуда пойдут данные на операнды АЛУ `a_i`, `b_i` соответственно. К примеру, если мы хотим, чтобы оба операнда брались из регистрового файла, нам необходимо подать значение `0` на входы управляющих сигналов обоих мультиплексоров.
The `a_sel_o` and `b_sel_o` signals determine the source of data for ALU operands `a_i` and `b_i` respectively. For example, if both operands should come from the register file, the value `0` must be applied to both multiplexer control inputs.
Сигнал `wb_sel_o` определяет источник данных для записи в регистровый файл: это либо результат операции на АЛУ, считанные данные из памяти данных, либо же данные, полученные из модуля регистров контроля и статуса.
The `wb_sel_o` signal determines the write-back data source for the register file: either the result of an ALU operation, data read from data memory, or data from the control and status registers module.
### Интерфейс памяти
### Memory Interface
Память данных используется для хранения и доступа к информации, необходимой для выполнения программы. Несмотря на то что такая память, как и регистровый файл используются для хранения данных, назначение этих модулей различно: регистровый файл используется для хранения данных, работа над которыми осуществляется здесь и сейчас (в пределах нескольких инструкций процессора), в то время как память данных хранит всю остальную информацию, которая не может уместиться в регистровый файл в виду ограниченности его размера.
Data memory is used to store and access information required for program execution. Although data memory and the register file both store data, their roles are different: the register file stores data that is being actively processed (within a few instructions), while data memory holds all other information that does not fit in the register file due to its limited size.
Для взаимодействия с подсистемой памяти данных декодер инструкций будет использовать следующие сигналы:
The instruction decoder will use the following signals to interact with the data memory subsystem:
- `mem_req_o`этот сигнал должен быть выставлен в 1 каждый раз, когда необходимо обратиться к памяти (считать или записать данные);
- `mem_we_o` этот сигнал должен быть выставлен в 1, если необходимо записать данные в память, (0 при чтении);
- `mem_size_o`этот сигнал указывает размер порции данных для передачи (возможные значения этого сигнала указаны в _Таблице 2_). Для удобства использования, данные значения определены в виде параметров в пакете `decoder_pkg`.
- `mem_req_o`this signal must be asserted to 1 whenever a memory access is required (read or write);
- `mem_we_o`this signal must be asserted to 1 when writing to memory (0 for reads);
- `mem_size_o` this signal specifies the data transfer size (possible values are listed in _Table 2_). For convenience, these values are defined as parameters in the `decoder_pkg` package.
|Параметр|Значение `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-битное значение|
| Parameter | `mem_size_o` Value | Description |
|-----------|--------------------|-------------------------------|
| LDST_B | 3'd0 | Signed 8-bit value |
| LDST_H | 3'd1 | Signed 16-bit value |
| LDST_W | 3'd2 | 32-bit value |
| LDST_BU | 3'd4 | Unsigned 8-bit value |
| LDST_HU | 3'd5 | Unsigned 16-bit value |
_Таблица 2. Значения сигнала `mem_size_o` при передаче различных порций данных._
_Table 2. Values of the `mem_size_o` signal for different data transfer sizes._
Перечисленных сигналов достаточно для того, чтобы основная память понимала: обращаются ли к ней в данный момент, нужно ли записывать или считывать данные, и о какой порции данных идет речь.
These signals are sufficient for the main memory to understand whether it is being accessed at a given moment, whether a read or write is required, and what size of data is being transferred.
### Сигналы разрешения записи
### Write Enable Signals
В данную категорию входят два однобитных сигнала:
This category includes two single-bit signals:
- `gpr_we_o`сигнал разрешения записи в регистровый файл (General Purpose Registers, GPR);
- `csr_we_o`сигнал разрешения записи в модуле регистров контроля и статуса.
- `gpr_we_o`register file write enable signal (General Purpose Registers, GPR);
- `csr_we_o`write enable signal for the control and status registers module.
### Сигналы управления программным счетчиком
### Program Counter Control Signals
В данную категорию входят однобитные сигналы, которые оповещают о том, что выполняется инструкция, связанная с изменением значения программного счетчика:
This category includes single-bit signals that indicate an instruction related to a program counter update is being executed:
- `branch_o`сигнал об инструкции условного перехода;
- `jal_o`сигнал об инструкции безусловного перехода `jal`;
- `jalr_o`сигнал об инструкции безусловного перехода `jalr`;
- `mret_o`сигнал об инструкции возврата из прерывания/исключения `mret`.
- `branch_o`signal indicating a conditional branch instruction;
- `jal_o`signal indicating an unconditional jump instruction `jal`;
- `jalr_o`signal indicating an unconditional jump instruction `jalr`;
- `mret_o`signal indicating a return-from-trap instruction `mret`.
### Сигнал нелегальной инструкции
### Illegal Instruction Signal
Сигнал, который должен принять значение `1`, в случае если пришла инструкция, которая не входит в список поддерживаемых процессором.
A signal that must be set to `1` when an instruction arrives that is not in the processor's supported instruction list.
Это не единственное, что должен сделать декодер в подобной ситуации. Давайте разберем подробнее, что должно происходить по приходу нелегальной инструкции.
This is not the only action the decoder must take in such a situation. Let us examine in detail what should happen when an illegal instruction arrives.
## Обработка нелегальной инструкции
## Illegal Instruction Handling
Существует множество причин, почему процессору может прийти на исполнение неподдерживаемая инструкция, в том числе:
There are many reasons why an unsupported instruction might reach the processor for execution, including:
- ошибка компиляции: либо баг в самом компиляторе, либо компиляция с неверными параметрами;
- ошибка в аппаратуре (например сбой в работе памяти);
- намеренная вставка неподдерживаемой инструкции (например для эксплуатации какой-нибудь уязвимости);
- инструкция, которая на самом деле поддерживается процессором, но требует большего уровня привилегий и потому не может быть выполнена.
- a compilation error: either a bug in the compiler itself or compilation with incorrect parameters;
- a hardware fault (e.g., a memory malfunction);
- a deliberately inserted unsupported instruction (e.g., to exploit a vulnerability);
- an instruction that is actually supported by the processor but requires a higher privilege level and therefore cannot be executed.
В случае появления инструкции, которая не поддерживается процессором, устройство управления должно обеспечить стабильность системы. В самом простом случае, такую инструкцию необходимо пропустить, сохранив так называемое **архитектурное состояние** процессора — т.е. сохранив значение всех элементов, характеризующих состояние системы в текущий момент. К таким элементам относятся: содержимое регистрового файла, основой памяти, содержимое регистров контроля и статуса и т.п. Значение программного счетчика также входит в архитектурное состояние процессора, однако в контексте пропуска инструкции с сохранением архитектурного состояния, его значение нужно изменить, иначе система оказалась бы в бесконечном цикле (неизменный счетчик бы указывал на ту же самую инструкцию, которая не должна менять архитектурного состояния).
When an unsupported instruction arrives, the control unit must ensure system stability. In the simplest case, the instruction must be skipped while preserving the processor's so-called **architectural state** — that is, the values of all elements that characterize the current system state. These elements include the register file contents, main memory contents, CSR contents, etc. The program counter value is also part of the architectural state; however, in the context of skipping an instruction while preserving the architectural state, its value must be changed — otherwise the system would be stuck in an infinite loop (an unchanged counter would keep pointing to the same instruction that is not supposed to modify the architectural state).
Иными словами, в случае появления нелегальной инструкции, устройство управления (роль которого в нашей системе по большей части играет декодер) должно проследить за тем, чтобы в системе не изменилось ничего кроме программного счетчика. К сигналам, влияющим на изменение архитектурного состояния, относятся:
In other words, when an illegal instruction arrives, the control unit (whose role is largely played by the decoder in our system) must ensure that nothing in the system changes except the program counter. The signals that affect the architectural state are:
- `mem_req_o`,
- `mem_we_o`,
@@ -173,180 +173,180 @@ _Таблица 2. Значения сигнала `mem_size_o` при пере
- `branch_o`,
- `jal_o`,
- `jalr_o`,
- `mret_o`,
- `mret_o`.
то есть, должны быть запрещены все запросы на запись, обращения в память и любые "прыжки" программного счетчика.
That is, all write requests, memory accesses, and any program counter "jumps" must be disabled.
Давайте теперь разберемся с тем, какие именно инструкции должен будет поддерживать наш процессор.
Let us now determine exactly which instructions our processor must support.
### Набор поддерживаемых инструкций **RISC-V** и способы их кодирования
### Supported RISC-V Instruction Set and Encoding Schemes
Все инструкции архитектуры **RISC-V** можно условно разделить на три категории.
All **RISC-V** architecture instructions can be broadly divided into three categories.
- Вычислительные инструкции (операции выполняются на АЛУ, с записью результата в регистровый файл). В основном это инструкции:
- использующие в качестве операндов два регистра;
- использующие в качестве операндов регистр и непосредственный операнд из инструкции (константу).
- Инструкции для доступа к памяти:
- загрузки из основной памяти в регистровый файл;
- сохранения данных из регистрового файла в основную память;
- Инструкции управления:
- Условные переходы
- Безусловные переходы
- Системные инструкции
- обращение к регистрам контроля и статуса;
- системные вызовы и возврат из обработчика прерываний
- Computational instructions (operations performed on the ALU with the result written to the register file). These are primarily instructions:
- using two registers as operands;
- using a register and an immediate operand from the instruction (a constant).
- Memory access instructions:
- loading from main memory into the register file;
- storing data from the register file to main memory.
- Control instructions:
- Conditional branches
- Unconditional jumps
- System instructions
- access to control and status registers;
- system calls and return from interrupt handler.
В _Таблице 3_ приводится фрагмент из `спецификации RISC-V`. В верхней её части приводится 6 форматов кодирования инструкций: **R**, **I**, **S**, **B**, **U** и **J** (описание типов представлено в аблице 4_). Затем список всех инструкций с конкретными значениями полей, соответствующих формату кодирования инструкции данного типа.
_Table 3_ shows a fragment from the `RISC-V specification`. The upper portion lists 6 instruction encoding formats: **R**, **I**, **S**, **B**, **U**, and **J** (format descriptions are given in _Table 4_). Below that is a list of all instructions with specific field values corresponding to the encoding format of each instruction type.
Под `rd` подразумевается 5-битный адрес регистра назначения (**r**egister **d**estination), `rs1` и `rs2` — 5-битные адреса регистров источников (**r**egister **s**ource), `imm` — непосредственный (immediate, задающийся прямиком в инструкции) операнд (константа), расположение и порядок битов которого указывается в квадратных скобках. Обратите внимание, что в разных форматах кодирования константы имеют различную разрядность, а их биты расположены по-разному. Непосредственные операнды всех типов интерпретируются как знаковые и требуют знакового расширения [[1](https://github.com/riscv/riscv-isa-manual/releases/download/20240411/unpriv-isa-asciidoc.pdf), стр. 23]. Исключение составляют 5-битные константы CSR-инструкций.
`rd` denotes the 5-bit destination register address (**r**egister **d**estination), `rs1` and `rs2` are 5-bit source register addresses (**r**egister **s**ource), `imm` is an immediate operand (a constant encoded directly in the instruction), with bit positions and ordering indicated in square brackets. Note that immediate operands have different widths in different encoding formats, and their bits are arranged differently. Immediate operands of all types are interpreted as signed values and require sign extension [[1](https://github.com/riscv/riscv-isa-manual/releases/download/20240411/unpriv-isa-asciidoc.pdf), p. 23]. The exception is 5-bit immediate operands used in CSR instructions.
![../../.pic/Labs/lab_05_decoder/rv32i_BIS.png](../../.pic/Labs/lab_05_decoder/rv32i_BIS.png)
_Таблица 3. Базовый набор инструкций из спецификации RISC-V[[1, стр.554]](https://github.com/riscv/riscv-isa-manual/releases/download/20240411/unpriv-isa-asciidoc.pdf), Стандартное расширение Zicsr[[1, стр.556]](https://github.com/riscv/riscv-isa-manual/releases/download/20240411/unpriv-isa-asciidoc.pdf), а также привилегированная инструкция mret[[2, стр.51]](https://github.com/riscv/riscv-isa-manual/releases/download/20240411/priv-isa-asciidoc.pdf)._
_Table 3. Base instruction set from the RISC-V specification [[1, p. 554]](https://github.com/riscv/riscv-isa-manual/releases/download/20240411/unpriv-isa-asciidoc.pdf), the standard Zicsr extension [[1, p. 556]](https://github.com/riscv/riscv-isa-manual/releases/download/20240411/unpriv-isa-asciidoc.pdf), and the privileged instruction mret [[2, p. 51]](https://github.com/riscv/riscv-isa-manual/releases/download/20240411/priv-isa-asciidoc.pdf)._
| Кодирование | Описание |
|-------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
| R-тип | Арифметические и логические операции над двумя регистрами с записью результата в третий (регистр назначения может совпадать с одним из регистров-источников) |
| I-тип | Инструкции с 12-битным непосредственным операндом |
| S-тип | Инструкции записи в память (инструкции store) |
| B-тип | Инструкции ветвления |
| U-тип | Инструкции с 20-битным «длинным» непосредственным операндом, сдвинутым влево на 12 |
| J-тип | Единственная инструкция jal, осуществляющая безусловный переход по адресу относительно текущего счетчика команд |
| Encoding | Description |
|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
| R-type | Arithmetic and logical operations on two registers, with the result written to a third (the destination register may be the same as one of the source registers) |
| I-type | Instructions with a 12-bit immediate operand |
| S-type | Store instructions (write to memory) |
| B-type | Branch instructions |
| U-type | Instructions with a 20-bit "long" immediate operand, shifted left by 12 |
| J-type | The single `jal` instruction, performing an unconditional jump relative to the current program counter |
_Таблица 4. Описание типов форматов кодирования инструкций ISA RISC-V._
_Table 4. RISC-V ISA instruction encoding format descriptions._
### Декодирование инструкций RISC-V
### RISC-V Instruction Decoding
Как уже описывалось в дополнительных материалах, декодирование инструкции начинается с поля `opcode` (**op**eration **code**, опкод). По этому полю определяется группа инструкций одного типа. Далее (для большинства типов кодирования) инструкция доопределяется через поля `func3` и `func7` (при наличии). Обратите внимание, что расположение этих полей одинаково для всех типов инструкций (см. верхнюю часть аблицы 3_).
As described in the preparation materials, instruction decoding begins with the `opcode` field (**op**eration **code**). This field identifies a group of instructions of the same type. The instruction is then further specified (for most encoding types) through the `func3` and `func7` fields (when present). Note that the positions of these fields are the same across all instruction types (see the upper portion of _Table 3_).
Поля `rs1`/`rs2`/`imm` и `rd` декодеру не нужны и используются напрямую для адресации регистров / задания констант.
The `rs1`/`rs2`/`imm` and `rd` fields are not needed by the decoder and are used directly for register addressing and constant specification.
Существуют особые инструкции, не имеющие никаких переменных полей (к примеру инструкция ECALL в аблице 3_). Такие инструкции необходимо проверять целиком (нужно убедиться, что инструкция совпадает вплоть до бита).
Some instructions have no variable fields (for example, the ECALL instruction in _Table 3_). Such instructions must be checked in their entirety (the full 32-bit value must match).
В _Таблице 5_ представлены все опкоды реализуемых нами инструкций. Представленные в ней коды операций 5-битные потому, что 2 младших бита полноценного 7-битного кода операции в реализуемых нами инструкциях должны всегда быть равны `11`. Если это не так, то вся инструкция уже запрещенная и не нуждается в дальнейшем декодировании.
_Table 5_ lists all opcodes of the instructions we are implementing. The opcodes shown are 5 bits because the 2 least significant bits of the full 7-bit opcode must always be `11` for all instructions we are implementing. If this is not the case, the entire instruction is already illegal and requires no further decoding.
Для удобства, значения кодов операций определены в виде параметров в пакете `decoder_pkg`.
For convenience, the opcode values are defined as parameters in the `decoder_pkg` package.
|Параметр|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 |Записать в `rd` значение `csr`. Обновить значение `csr` с помощью `rs1`. (либо инструкция `mret`/`ecall`/`ebreak`)|`csr = csr_op(rs1); rd = csr` |
| Parameter | Opcode | Operation Group Description | Short Notation |
|-----------|--------|--------------------------------------------------------------------------------------------------------------------|--------------------------------------|
| OP | 01100 | Write the ALU result of `rs1` and `rs2` to `rd` | `rd = alu_op(rs1, rs2)` |
| OP_IMM | 00100 | Write the ALU result of `rs1` and `imm` to `rd` | `rd = alu_op(rs1, imm)` |
| LUI | 01101 | Write the U-type immediate operand `imm_u` to `rd` | `rd = imm << 12` |
| LOAD | 00000 | Write memory data at address `rs1+imm` to `rd` | `rd = Mem[rs1 + imm]` |
| STORE | 01000 | Write `rs2` data to memory at address `rs1+imm` | `Mem[rs1 + imm] = rs2` |
| BRANCH | 11000 | Increment the program counter by `imm` if the comparison of `rs1` and `rs2` is true | `if cmp_op(rs1, rs2) then PC += imm` |
| JAL | 11011 | Write the next program counter value to `rd`, increment the program counter by `imm` | `rd = PC + 4; PC += imm` |
| JALR | 11001 | Write the next program counter value to `rd`, set the program counter to `rs1+imm` | `rd = PC + 4; PC = rs1+imm` |
| AUIPC | 00101 | Write the sum of the U-type immediate operand `imm_u` and the program counter to `rd` | `rd = PC + (imm << 12)` |
| MISC-MEM | 00011 | Perform no operation | `-` |
| SYSTEM | 11100 | Write the `csr` value to `rd`. Update `csr` using `rs1`. (or execute `mret`/`ecall`/`ebreak`) | `csr = csr_op(rs1); rd = csr` |
_Таблица 5. Описание кодов операций._
_Table 5. Opcode descriptions._
#### SYSTEM-инструкции
#### SYSTEM Instructions
SYSTEM-инструкции используются для доступа к системным функциям и могут требовать привилегированный доступ. Данные инструкции могут быть разделены на два класса:
SYSTEM instructions are used to access system functions and may require privileged access. These instructions can be divided into two classes:
- Обращение к регистрам статуса и контроля (**Control and Status Registers**, **CSR**)
- Все остальные инструкции (возможно из набора привилегированных инструкций)
- Access to **Control and Status Registers** (**CSR**)
- All other instructions (possibly from the privileged instruction set)
Для того, чтобы в будущем процессор поддерживал прерывания, нам требуется декодировать инструкции обоих классов.
To support interrupts in the future, we need to decode instructions of both classes.
Обращение к регистрам контроля и статуса осуществляется шестью инструкциями стандартного расширения `Zicsr`. Каждая из этих инструкций (если у нее легальные поля) осуществляет запись в CSR и регистровый файл (блоки `Control Status Registers` и `Register File` на _рис. 1_ соответственно).
Access to the control and status registers is performed using the six instructions of the standard `Zicsr` extension. Each of these instructions (if its fields are legal) performs a write to the CSR and to the register file (the `Control Status Registers` and `Register File` blocks in _Fig. 1_ respectively).
Кроме того, для возврата управления основному потоку инструкций, нужна дополнительная `SYSTEM`-инструкция привилегированного набора команд `MRET`.
Additionally, to return control to the main instruction stream, an additional `SYSTEM` instruction from the privileged instruction set is needed: `MRET`.
Перечисленные выше инструкции являются "дополнительными" — их добавили сверх стандартного набора инструкций, чтобы обеспечить требуемый нашей системе функционал. Однако осталось ещё две SYSTEM-инструкции, которые мы должны уметь декодировать, поскольку они есть в стандартном наборе инструкций.
The instructions listed above are "extensions" — they were added on top of the standard instruction set to provide the functionality required by our system. However, there are two more SYSTEM instructions that we must be able to decode, since they are part of the standard instruction set.
Инструкции `ECALL` и `EBREAK` вызывают исключение. Подробнее исключения и прерывания будут разобраны в ЛР№10, пока что надо знать лишь то, что в нашей процессорной системе все исключения будут реализованы через выставление 1 на сигнале `illegal_instr_o`.
The `ECALL` and `EBREAK` instructions trigger an exception. Exceptions and interrupts will be covered in detail in Lab 10; for now, it is enough to know that in our processor system all exceptions will be implemented by asserting 1 on the `illegal_instr_o` signal.
#### MISC-MEM инструкции
#### MISC-MEM Instructions
В базовом наборе инструкций **RISC-V** к `MISC-MEM`-операциям относятся инструкции `FENCE`, `FENCE.TSO`, `PAUSE` (которые объединены в таблице 5 в одну инструкцию `FENCE`). В реализуемом процессорном ядре эта инструкция не должна приводить к изменениям. Инструкция `FENCE` в **RISC-V** необходима при работе с несколькими аппаратными потоками, или "хартами" (hart «**har**dware **t**hread»). Она помогает согласовать доступ к данным между ними. В **RISC-V** используется "расслабленная модель" памяти (**relaxed memory model**): которая позволяет потокам видеть операции других потоков, но не обязательно в том порядке, в каком они были записаны в коде программы. Инструкция `FENCE`, использованная между двумя инструкциями чтения и/или записи гарантирует, что остальные потоки увидят первую инструкцию перед второй. Реализация `FENCE` является опциональной в **RISC-V** и в данном случае в ней нет необходимости, так как в системе не предполагается наличия нескольких аппаратных потоков. Данная инструкция должна быть реализована как `NOP` (**n**o **op**eration).
In the **RISC-V** base instruction set, the `MISC-MEM` operations include `FENCE`, `FENCE.TSO`, and `PAUSE` (combined into a single `FENCE` instruction in Table 5). In the processor core being implemented, this instruction must not cause any state changes. The `FENCE` instruction in **RISC-V** is needed when working with multiple hardware threads, or "harts" (hart — "**har**dware **t**hread"). It helps synchronize data access between them. **RISC-V** uses a **relaxed memory model**, which allows threads to observe operations of other threads, but not necessarily in the order they appear in program code. A `FENCE` instruction placed between two read and/or write instructions guarantees that other threads will see the first instruction before the second. `FENCE` implementation is optional in **RISC-V**, and in our case it is not necessary since the system does not involve multiple hardware threads. This instruction must be implemented as a `NOP` (**n**o **op**eration).
В аблице 6_ представлены инструкции из таблицы 3 с приведением их типов, значениями полей `opcode`, `func3`, `func7`, функциональным описанием и примерами использования.
_Table 6_ shows the instructions from Table 3 with their types, `opcode`, `func3`, `func7` field values, functional descriptions, and usage examples.
![../../.pic/Labs/lab_05_decoder/rv32i_summary.png](../../.pic/Labs/lab_05_decoder/rv32i_summary.png)
_Таблица 6. Расширенное описание инструкций RV32IZicsr._
_Table 6. Extended description of RV32IZicsr instructions._
> [!IMPORTANT]
> Обратите внимание на операции `slli`, `srli` и `srai` (операции сдвига на константную величину). У этих инструкций немного измененный формат кодирования **I\***. Формат кодирования **I** предоставляет 12-битную константу. Сдвиг 32-битного числа более, чем на 31 не имеет смысла. Для кодирования числа 31 требуется всего 5 бит. Выходит, что из 12 бит константы для операции сдвига используется только 5 бит (которые обозначены в виде поля `shamt`, сокращение от **sh**ift **am**oun**t** — "сколько раз сдвигать"), а оставшиеся 7 бит – не используются. А, главное (какое совпадение!), эти 7 бит находятся ровно в том же месте, где у других инструкций находится поле `func7`. Поэтому, чтобы у инструкций `slli`, `srli` и `srai` использующих формат **I** не пропадала эта часть поля, к ней относятся как к полю `func7`. Именно поэтому в ЛР№2 мы использовали только 5 младших бит операнда `B` — чтобы отбросить ту часть константы, которая используется в качестве `func7` на операциях битового сдвига.
> Note the `slli`, `srli`, and `srai` instructions (constant-shift operations). These instructions use a slightly modified **I\*** encoding format. The **I** encoding format provides a 12-bit immediate. Shifting a 32-bit number by more than 31 makes no sense. Only 5 bits are needed to encode the value 31. Therefore, only 5 bits of the 12-bit immediate are used for the shift amount (denoted as the `shamt` field, short for **sh**ift **am**oun**t**), while the remaining 7 bits are unused. Crucially (what a coincidence!), these 7 bits occupy exactly the same position as the `func7` field in other instructions. Therefore, to avoid wasting this portion of the field in `slli`, `srli`, and `srai` — which use the **I** format — it is treated as the `func7` field. This is precisely why in Lab 2 we used only the 5 least significant bits of operand `B` — to discard the portion of the immediate that serves as `func7` in shift operations.
> [!IMPORTANT]
> Также обратите внимание на инструкции `ecall`, `ebreak` и `mret`. Все эти инструкции I-типа имеют поле func3, равное нулю. С точки зрения декодирования инструкции I-типа, это одна и та же инструкция с разными значениями поля `imm`. Однако конкретно в данном случае (SYSTEM_OPCODE и `func3 == 0`) эти инструкции должны рассматриваться как совокупность всех 32-бит сразу (см. _таблицу 3_).
> Also note the `ecall`, `ebreak`, and `mret` instructions. All of these are I-type instructions with a `func3` field equal to zero. From a decoding standpoint, they appear to be the same instruction type with different `imm` values. However, in this specific case (SYSTEM_OPCODE and `func3 == 0`), these instructions must be treated as a full 32-bit match (see _Table 3_).
### Формирование управляющих сигналов
### Control Signal Generation
Как говорилось ранее, декодер инструкций в процессоре служит для преобразования инструкции в набор управляющих сигналов, необходимых для ее исполнения. Таким образом, для каждой инструкции из аблицы 3_ декодер должен поставить в соответствие конкретное значение для каждого из выходов, перечисленных в аблице 1_.
As discussed earlier, the instruction decoder in a processor is responsible for converting an instruction into the set of control signals required to execute it. For each instruction in _Table 3_, the decoder must assign a specific value to each of the outputs listed in _Table 1_.
Пример: для выполнения инструкции записи 32-бит данных из регистрового файла во внешнюю память (инструкции `sw`), дешифратор должен направить в АЛУ два операнда (базовый адрес и смещение) вместе с кодом операции АЛУ (сложения) для вычисления адреса записи. Базовый адрес берется из регистрового файла, а смещение является непосредственным операндом инструкции S-типа. Таким образом для вычисления адреса записи декодер должен выставить следующие значения на выходах:
Example: to execute the instruction that writes 32-bit data from the register file to external memory (the `sw` instruction), the decoder must direct two operands (a base address and an offset) to the ALU along with the ALU operation code (addition) to compute the write address. The base address comes from the register file, and the offset is the immediate operand of the S-type instruction. To compute the write address, the decoder must assert the following output values:
- `a_sel_o = 2'd0`,
- `b_sel_o = 3'd3`,
- `alu_op_o= ALU_ADD`.
- `alu_op_o = ALU_ADD`.
(см. _рисунок 1_).
(see _Figure 1_).
Кроме того, для самой операции записи в основную память, декодер должен сформировать управляющие сигналы интерфейса памяти (запрос на обращение в память, размер передаваемых данных и сигнал разрешения записи):
Additionally, for the memory write operation itself, the decoder must generate the memory interface control signals (memory access request, data transfer size, and write enable):
- `mem_req_o = 1'b1`,
- `mem_size_o = LDST_W` (см. аблицу 2_),
- `mem_size_o = LDST_W` (see _Table 2_),
- `mem_we_o = 1'b1`.
Несмотря на то, что для записи во внешнюю память ключевыми сигналами будут описанные выше, это не означает, что остальные выходные сигналы дешифратора команд могут быть абы какими.
Although the signals described above are the key ones for a memory write operation, this does not mean that the remaining decoder output signals can take arbitrary values.
Поскольку операция `sw` не является операцией перехода, сигналы `jal_o`, `jalr_o` и `branch_o` и `mret_o` должны быть равны нулю (иначе процессор совершит переход, а инструкция `sw` этого не подразумевает). Точно так же, поскольку во время записи во внешнюю память, в регистровый файл и регистры контроля и статуса ничего не должно быть записано, сигналы `gpr_we_o` и `csr_we_o` также должны быть равны нулю.
Since the `sw` operation is not a jump instruction, the `jal_o`, `jalr_o`, `branch_o`, and `mret_o` signals must be zero (otherwise the processor would jump, which the `sw` instruction does not imply). Similarly, since nothing should be written to the register file or CSRs during a memory write, `gpr_we_o` and `csr_we_o` must also be zero.
Иными словами, крайне важно следить за выходными сигналами, влияющими на изменение архитектурного состояния процессора, не затрагиваемые инструкцией в явном виде.
In other words, it is critically important to track all output signals that affect the processor's architectural state and that are not explicitly involved in the current instruction.
А вот сигнал `wb_sel` может принять любое значение (поскольку сигнал разрешения записи в регистровый файл равен нулю, не важно, каким будет источник данных для записи в регистровый файл, т.к. в него все равно ничего не будет записано).
The `wb_sel` signal, however, can take any value (since the register file write enable is zero, it does not matter what the write-back source is — nothing will be written to the register file regardless).
Разумеется, описывая модуль декодера инструкций, было бы нерационально прописывать для каждой из 47 инструкций значение 14 выходов модуля, особенно учитывая, что многие выходные сигналы будут иметь одно и то же значение для всех инструкций одного опкода, поэтому удобнее всего будет описывать их, сгруппировав по кодам операций.
Of course, when implementing the instruction decoder module, it would be impractical to specify values for all 14 outputs for each of the 47 instructions, especially since many outputs have the same value for all instructions sharing the same opcode. It is most convenient to describe them grouped by opcode.
В аблице 7_ определен список выходных сигналов декодера инструкций и групп инструкций, при которых эти выходы могут принимать ненулевое значение.
_Table 7_ lists the instruction decoder output signals and the instruction groups for which these outputs can take a non-zero value.
|Название сигнала| Пояснение | На каких опкодах может принять ненулевое значение (см. таблицу 6)|
|----------------|------------------------------------------------------------------------------------------------|------------------------------------------------------------------|
|a_sel_o |Управляющий сигнал мультиплексора для выбора первого операнда АЛУ | На всех кроме MISC_MEM и SYSTEM |
|b_sel_o |Управляющий сигнал мультиплексора для выбора второго операнда АЛУ | На всех кроме MISC_MEM и SYSTEM |
|alu_op_o |Операция АЛУ | На всех кроме MISC_MEM и SYSTEM |
|csr_op_o |Операция модуля CSR | Только на SYSTEM |
|csr_we_o |Разрешение на запись в CSR | Только на SYSTEM |
|mem_req_o |Запрос на доступ к памяти (часть интерфейса памяти) | На LOAD и STORE |
|mem_we_o |Сигнал разрешения записи в память, «write enable» (при равенстве нулю происходит чтение) | Только на STORE |
|mem_size_o |Управляющий сигнал для выбора размера слова при чтении-записи в память (часть интерфейса памяти)| На LOAD и STORE |
|gpr_we_o |Сигнал разрешения записи в регистровый файл | На всех кроме STORE, BRANCH, MISC_MEM |
|wb_sel_o |Управляющий сигнал мультиплексора для выбора данных, записываемых в регистровый файл | На всех кроме STORE, BRANCH, MISC_MEM |
|illegal_instr_o |Сигнал о некорректной инструкции | На всех кроме JAL, LUI, AUIPC |
|branch_o |Сигнал об инструкции условного перехода | Только на BRANCH |
|jal_o |Сигнал об инструкции безусловного перехода jal | Только на JAL |
|jalr_o |Сигнал об инструкции безусловного перехода jalr | Только на JALR |
|mret_o |Сигнал об инструкции возврата из прерывания/исключения mret | Только на SYSTEM |
| Signal Name | Description | Opcodes for Which a Non-Zero Value Is Possible (see Table 6) |
|-----------------|----------------------------------------------------------------------------------------------------|--------------------------------------------------------------|
| a_sel_o | Multiplexer control signal for selecting the first ALU operand | All except MISC_MEM and SYSTEM |
| b_sel_o | Multiplexer control signal for selecting the second ALU operand | All except MISC_MEM and SYSTEM |
| alu_op_o | ALU operation | All except MISC_MEM and SYSTEM |
| csr_op_o | CSR module operation | SYSTEM only |
| csr_we_o | CSR write enable | SYSTEM only |
| mem_req_o | Memory access request (part of the memory interface) | LOAD and STORE |
| mem_we_o | Memory write enable signal (when zero, a read is performed) | STORE only |
| mem_size_o | Control signal for selecting data transfer size (part of memory interface) | LOAD and STORE |
| gpr_we_o | Register file write enable signal | All except STORE, BRANCH, MISC_MEM |
| wb_sel_o | Multiplexer control signal for selecting write-back data | All except STORE, BRANCH, MISC_MEM |
| illegal_instr_o | Signal indicating an illegal instruction | All except JAL, LUI, AUIPC |
| branch_o | Signal indicating a conditional branch instruction | BRANCH only |
| jal_o | Signal indicating an unconditional jump instruction jal | JAL only |
| jalr_o | Signal indicating an unconditional jump instruction jalr | JALR only |
| mret_o | Signal indicating a return-from-trap instruction mret | SYSTEM only |
_Таблица 7. Описание портов дешифратора команд._
_Table 7. Instruction decoder port descriptions._
Дешифратор должен выдать единицу на выходе `illegal_instr_o` в случае:
The decoder must assert `illegal_instr_o` in the following cases:
- неравенства двух младших битов opcode значению `11`;
- если значение поля `opcode` не совпадает ни с одним из известных и следовательно операция не определена.
- некорректного значения полей `func3` или `func7` для данного опкода.
- the two least significant bits of the opcode are not equal to `11`;
- the `opcode` field value does not match any known opcode, and therefore the operation is undefined;
- the `func3` or `func7` fields contain invalid values for the given opcode.
Кроме того, поскольку представленная на _рис. 1_ микроархитектура поддерживает только одно исключение (исключение через сигнал `illegal_instr_o`), этот сигнал должен быть равен единице и в случае:
Furthermore, since the microarchitecture shown in _Fig. 1_ supports only one exception (via the `illegal_instr_o` signal), this signal must also be asserted in the case of:
- если это инструкция `ECALL` / `EBREAK`.
- an `ECALL` / `EBREAK` instruction.
## Инструменты
## Tools
**SystemVerilog** – это язык описания аппаратуры. С помощью этого языка человек объясняет либо синтезатору какое он хочет получить устройство, либо симулятору как он хочет это устройство проверить. Синтезатор – это программа, которая создает из логических элементов цифровое устройство по описанию, предоставляемому человеком. Синтезатору внутри **Vivado** нужно объяснить, что от него нужно. Например, чтобы спросить дорогу у испанца, придется делать это на испанском языке, иначе он ничем не сможет помочь. А если вы хорошо знаете испанский, то скорее всего сможете это сделать еще и разными способами. В **SystemVerilog** точно также одно и то же устройство можно описать разным кодом, но результат синтеза будет одним и тем же. Однако, часто два разных кода, одинаковых по смыслу, могут синтезироваться в разную аппаратуру, хотя функционально они будут идентичны, но могут отличаться, например, скоростью работы. Или одни и те же специальные языковые конструкции могут применяться для синтезирования разных цифровых элементов.
**SystemVerilog** is a hardware description language. Using this language, a designer either tells a synthesizer what hardware to produce, or tells a simulator how to verify that hardware. A synthesizer is a program that builds a digital device from logic elements based on the description provided. The synthesizer inside **Vivado** needs to be told what is expected of it. For instance, to ask directions from a Spanish speaker you need to speak Spanish — otherwise they cannot help. And if you speak Spanish well, you can likely phrase the same question in multiple ways. **SystemVerilog** works the same way — the same device can be described by different code, but the synthesis result may be identical. However, two semantically equivalent descriptions can sometimes synthesize into different hardware that is functionally identical yet may differ in performance, for example. Similarly, the same language constructs can be used to synthesize different digital elements.
Декодер – комбинационная схема. Это значит, что каждый раз подавая на вход одни и те же значения, вы будете получать на выходе один и тот же результат.
The decoder is a combinational circuit. This means that the same input values will always produce the same output values.
Можно по-разному описывать комбинационные схемы. Например — через оператор непрерывного присваивания `assign`. Для описания декодера отлично подойдет конструкция `case`, которая превратится не в мультиплексор, а в комбинационную схему с оптимальными параметрами критического пути. В доверилоговую эпоху разработчикам пришлось бы строить гигантские таблицы истинности и [карты Карно](https://ru.wikipedia.org/wiki/Карта_Карно), искать оптимальные схемы реализации. Сегодня эту задачу решает синтезатор, по описанию устройства он сам находит наиболее эффективное решение.
Combinational circuits can be described in various ways — for example, using the continuous assignment operator `assign`. The `case` construct works well for describing a decoder; it synthesizes not into a multiplexer but into a combinational circuit with optimal critical-path characteristics. In the pre-HDL era, designers had to construct enormous truth tables and [Karnaugh maps](https://en.wikipedia.org/wiki/Karnaugh_map) to find optimal implementations. Today, the synthesizer handles this task automatically — it finds the most efficient solution from the device description.
Разница с реализацией мультиплексора в том, что в этом случае справа от знака равно всегда стоит константа. Получается это такой способ описать таблицу истинности. В такой код легко вносить правки и искать интересующие фрагменты.
The difference from a multiplexer implementation is that here the right-hand side of every assignment is always a constant. This effectively becomes a way to describe a truth table. Such code is easy to edit and navigate.
Рассмотрим _листинг 1_. Внутри конструкции `always_comb`, перед конструкцией `case` указываются значения по умолчанию. Благодаря этому пропадает необходимость указывать все сигналы внутри каждого обработчика `case`, достаточно указать только те, что имеют значение отличное от значения по умолчанию. Представленный пример реализует комбинационную схему, которая при `control_signal== 4'b1100` будет выставлять сигнал `c = 1'b0`, то есть отличное, от значения по умолчанию. Сигнал `a` никак не меняется, поэтому он не указан в соответствующем обработчике. Если `sub_signal == 1'b0`, то `b` будет равен 1, а `d` равен 0. Если `sub_signal == 1'b1`, то наоборот `b` будет равен 0, а `d` равен 1.
Consider _Listing 1_. Inside the `always_comb` block, default values are specified before the `case` construct. This eliminates the need to specify all signals inside every `case` handler — only those that differ from the default value need to be listed. The example implements a combinational circuit that, when `control_signal == 4'b1100`, asserts `c = 1'b0` (a value different from its default). Signal `a` is not modified, so it does not appear in the corresponding handler. If `sub_signal == 1'b0`, then `b` equals 1 and `d` equals 0. If `sub_signal == 1'b1`, the reverse is true — `b` equals 0 and `d` equals 1.
```Verilog
module example (
@@ -356,25 +356,25 @@ module example (
);
parameter logic [3:0] SOME_PARAM = 4'b1100;
always_comb begin
a = 1'b0; // значения по умолчанию
b = 1'b0; // обратите внимание, что в блоке
c = 1'b1; // always_comb используется оператор
d = 1'b0; // блокирующего присваивания
a = 1'b0; // default values
b = 1'b0; // note that blocking assignment is used
c = 1'b1; // inside the always_comb block
d = 1'b0;
case(control_signal)
// ... какие-то еще комбинации
SOME_PARAM: begin // если на control_signal значение SOME_PARAM
// ... some other combinations
SOME_PARAM: begin // if control_signal equals SOME_PARAM
c = 1'b0;
case (sub_signal)
1'b0: b = 1'b1; // если на sub_signal значение 1'b0
1'b1: d = 1'b1; // если на sub_signal значение 1'b1
1'b0: b = 1'b1; // if sub_signal is 1'b0
1'b1: d = 1'b1; // if sub_signal is 1'b1
endcase
end
// ... какие-то еще обработчики
default: begin // так как описаны не все значения
a = 1'b0; // control_signal, то чтобы результатом
b = 1'b0; // case не было защелки (latch),
c = 1'b1; // на выходе нужно обязательно добавлять
d = 1'b0; // default
// ... some other handlers
default: begin // since not all values of
a = 1'b0; // control_signal are listed, a default
b = 1'b0; // is required to prevent the case
c = 1'b1; // construct from inferring a latch
d = 1'b0;
end
endcase
end
@@ -382,15 +382,15 @@ module example (
endmodule
```
_Листинг 1. Пример описания декодера._
_Listing 1. Example decoder description._
Имейте в виду, что значения по умолчанию, описанные в начале блока `always_comb`, можно использовать таким образом только при помощи **блокирующих присваиваний** (которые [следует](../../Basic%20Verilog%20structures/Assignments.md) использовать только в комбинационных блоках).
Keep in mind that the default values specified at the beginning of the `always_comb` block can only be used this way with **blocking assignments** (which [should be](../../Basic%20Verilog%20structures/Assignments.md) used exclusively in combinational blocks).
Кроме того, использование вложенных блоков `case` обосновано только в ситуации создания блока декодера (т.е. в случаях, когда справа от всех присваиваний будут использованы константы, а не другие сигналы). В случае описания мультиплексора, вложенные блоки `case` могут быть синтезированы в каскад мультиплексоров, что негативно скажется на временных характеристиках схемы.
Furthermore, the use of nested `case` blocks is only justified when implementing a decoder block (i.e., when all right-hand side values in assignments are constants, not other signals). When describing a multiplexer, nested `case` blocks may synthesize into a cascade of multiplexers, which will negatively affect the timing characteristics of the circuit.
## Задание
## Assignment
Необходимо реализовать на языке **SystemVerilog** модуль декодера инструкций однотактного процессора RISC-V в соответствии с предложенной микроархитектурой. В _листинге 2_ приводится прототип разрабатываемого модуля.
Implement the instruction decoder module for a single-cycle RISC-V processor in **SystemVerilog**, following the proposed microarchitecture. The module prototype is given in _Listing 2_.
```Verilog
module decoder (
@@ -416,27 +416,27 @@ module decoder (
endmodule
```
_Листинг 2. Прототип декодера инструкций._
_Listing 2. Instruction decoder prototype._
В зависимости от стиля оформления, модуль может занимать больше сотни строк кода, но это не делает его реализацию сложной. По сути, декодер — это просто большой блок `case` с описанием того, в каком случае, какие сигналы и чему должны быть равны. Работа требует внимательности, немного усидчивости и понимания выполняемых действий. С огромной вероятностью в коде будут ошибки и их нужно будет исправлять. Ошибки — это нормально (не ошибается тот, кто ничего не делает), а исправление ошибок дает бесценный опыт разработки. Возможно, реализация этого модуля в какой-то момент покажется рутинной, но по окончании лабораторной работы №7 удовольствие от результата покажет, что оно того стоило.
Depending on coding style, the module may span over a hundred lines of code, but this does not make it difficult to implement. At its core, the decoder is simply a large `case` block describing which signals take which values under which conditions. The work requires attention, some patience, and a clear understanding of what is being done. There will almost certainly be bugs that need to be fixed. Bugs are normal — those who do nothing make no mistakes — and fixing them provides invaluable development experience. The implementation may feel tedious at times, but by the end of Lab 7, the satisfaction from the result will show that it was worth it.
## Порядок выполнения задания
## Steps
1. Внимательно ознакомьтесь с выходными сигналами декодера инструкций и тем, как они управляют функциональными блоками процессорного ядра, представленного на _рис. 1_, а также типами команд. В случае возникновения вопросов, проконсультируйтесь с преподавателем.
2. Добавьте в `Design Sources` проекта файл [`alu_opcodes_pkg.sv`](alu_opcodes_pkg.sv) (если тот ещё не был добавлен в ходе выполнения ЛР№2), а также файлы [`csr_pkg.sv`](csr_pkg.sv) и [`decoder_pkg.sv`](decoder_pkg.sv). Эти файлы содержат параметры, которые будет удобно использовать при описании декодера.
3. Опишите модуль декодера инструкций с таким же именем и портами, как указано в задании.
1. Для удобства дальнейшего описания модуля рекомендуется сперва создать сигналы `opcode`, `func3`, `func7` и присвоить им соответствующие биты входного сигнала инструкции.
2. Модуль может быть описан множеством способов: каждый выходной сигнал может быть описан через собственную комбинационную логику в отдельном блоке `case`, однако проще всего будет описать все сигналы через вложенные `case` внутри одного блока `always_comb`.
3. Внутри блока `always_comb` до начала блока `case` можно указать базовые значения для всех выходных сигналов. Это не то же самое, что вариант `default` в блоке `case`. Здесь вы можете описать состояния, которые будут использованы чаще всего, и в этом случае, присваивание сигналу будет выполняться только в том месте, где появится инструкция, требующая значение этого сигнала, отличное от базового.
4. Далее вы можете описать базовый блок `case`, где будет определен тип операции по ее коду.
5. Определив тип, вы сможете понять, какая именно вам пришла операция по полям `func3` и `func7` (если данный тип имеет такие поля).
6. Не забывайте, что в случае, если на каком-то из этапов (определения типа или определения конкретной операции) вам приходит непредусмотренное ISA значение какого-либо поля, необходимо выставить сигнал `illegal_instr_o`.
7. В случае некорректной инструкции, вы должны гарантировать, что не произойдет условный/безусловный переход, а во внешнюю память, регистровый файл, а также регистры контроля и статуса ничего не запишется. Неважно, что будет выполняться на АЛУ и какие данные будут выбраны на мультиплексоре источника записи. Важно, чтобы не произошел сам факт записи в любое из устройств (подумайте, какие значения для каких сигналов необходимо для этого выставить).
4. Проверьте модуль с помощью верификационного окружения, представленного в файле [`lab_05.tb_decoder.sv`](lab_05.tb_decoder.sv). Вполне возможно, что после первого запуска вы столкнётесь с сообщениями о множестве ошибок. Вам необходимо [исследовать](../../Vivado%20Basics/05.%20Bug%20hunting.md) эти ошибки на временной диаграмме и исправить их в вашем модуле.
1. Перед запуском моделирования убедитесь, что у вас выбран корректный модуль верхнего уровня в `Simulation Sources`.
5. Данная лабораторная работа не предполагает проверки в ПЛИС
1. Carefully study the instruction decoder output signals and how they control the functional blocks of the processor core shown in _Fig. 1_, as well as the instruction types. If you have questions, consult the instructor.
2. Add the file [`alu_opcodes_pkg.sv`](alu_opcodes_pkg.sv) to the project's `Design Sources` (if it has not already been added during Lab 2), as well as the files [`csr_pkg.sv`](csr_pkg.sv) and [`decoder_pkg.sv`](decoder_pkg.sv). These files contain parameters that will be useful when describing the decoder.
3. Describe the instruction decoder module with the same name and ports as specified in the task.
1. For convenience, it is recommended to first create `opcode`, `func3`, and `func7` signals and assign them the corresponding bits of the input instruction signal.
2. The module can be described in many ways: each output signal can be described through its own combinational logic in a separate `case` block, but the simplest approach is to describe all signals using nested `case` blocks inside a single `always_comb` block.
3. Inside the `always_comb` block, before the `case` block, you can specify default values for all output signals. This is not the same as the `default` branch in a `case` block. Here you can describe the states that will be most commonly used; a signal assignment then only needs to appear where an instruction requires a value different from the default.
4. You can then describe the main `case` block, which determines the operation type from the opcode.
5. Once the operation type is identified, you can determine the specific operation from the `func3` and `func7` fields (if present for that type).
6. Remember that if at any stage (type identification or specific operation identification) a field value arrives that is not defined in the ISA, the `illegal_instr_o` signal must be asserted.
7. In the case of an illegal instruction, you must guarantee that no conditional or unconditional jump occurs, and that nothing is written to external memory, the register file, or the CSRs. It does not matter what the ALU executes or what data is selected at the write-back multiplexer. What matters is that no write actually occurs to any of these units (consider which signal values are needed to ensure this).
4. Verify the module using the verification environment provided in the file [`lab_05.tb_decoder.sv`](lab_05.tb_decoder.sv). After the first run, you may encounter a large number of error messages. You must [investigate](../../Vivado%20Basics/05.%20Bug%20hunting.md) these errors on the waveform and fix them in your module.
1. Before starting simulation, make sure the correct top-level module is selected in `Simulation Sources`.
5. This lab does not include verification on the FPGA board.
## Список источников
## 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)
2. [The RISC-V Instruction Set Manual Volume II: Privileged Architecture](https://github.com/riscv/riscv-isa-manual/releases/download/20240411/priv-isa-asciidoc.pdf)