mirror of
https://github.com/MPSU/APS.git
synced 2025-09-15 09:10:10 +00:00
ЛР5. Рефактор методички (#87)
* ЛР5. Рефактор методички * Apply suggestions from code review Co-authored-by: Mikhail Popov <gr33nka@icloud.com> * Замена "разрядности" на "битность" в рисунке --------- Co-authored-by: Mikhail Popov <gr33nka@icloud.com>
This commit is contained in:
committed by
GitHub
parent
cdba67e4a3
commit
a6bcc2f718
4
.pic/Other/rv32i/fig_01.drawio.svg
Normal file
4
.pic/Other/rv32i/fig_01.drawio.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 220 KiB |
@@ -1,99 +1,40 @@
|
||||
# Лабораторная работа 5 "Основной дешифратор команд"
|
||||
# Лабораторная работа 5 "Декодер инструкций"
|
||||
|
||||
Устройство управления – один из базовых блоков процессора, функцией которого является декодирование инструкций и выдача управляющих сигналов для всех блоков процессора.
|
||||
Устройство управления (УУ) – один из базовых блоков процессора, функцией которого является декодирование инструкций и выдача управляющих сигналов для всех блоков процессора. Роль УУ в данном курсе (с некоторыми оговорками) будет играть декодер инструкций.
|
||||
|
||||
## Цель
|
||||
|
||||
Описать на языке **SystemVerilog** блок основного дешифратора команд (модуль **Main Decoder**) для однотактного процессора с архитектурой **RISC-V**.
|
||||
Описать на языке **SystemVerilog** блок декодера инструкций (модуль **decoder_riscv**) для однотактного процессора с архитектурой **RISC-V**.
|
||||
|
||||
## Допуск к лабораторной работе
|
||||
## Материал для подготовки к лабораторной работе
|
||||
|
||||
- Изучить форматы кодирования инструкций базового набора команд [`RV32I`](../../Other/rv32i.md)
|
||||
- Изучить форматы кодирования инструкций базового набора команд [`RV32I`](../../Other/rv32i.md).
|
||||
- Изучить [теорию по регистрам контроля и статуса](../../Other/CSR.md).
|
||||
- Изучить [различия между блокирующими и неблокирующими присваиваниями](../../Basic%20Verilog%20structures/Assignments.md).
|
||||
|
||||
## Ход работы
|
||||
|
||||
1. Изучить особенности архитектуры **RISC-V** ([#теория](#архитектура-risc-v-и-предлагаемая-микроархитектура))
|
||||
2. Изучить конструкции **SystemVerilog**, с помощью которых будет описан дешифратор ([#инструменты](#инструменты))
|
||||
3. Реализовать на языке **SystemVerilog** модуль основного дешифратора команд – **Main Decoder** ([#задание](#задание))
|
||||
4. Верифицировать разработанное устройство с помощью предлагаемого **testbench** (в том же [#задании](#задание))
|
||||
1. Изучить микроархитектуру реализуемого процессорного ядра.
|
||||
1. Разобраться с логикой формирования управляющих сигналов для всех типов инструкций.
|
||||
2. Изучить [описание сигналов декодера инструкций](#описание-сигналов-декодера-инструкций).
|
||||
3. Изучить [набор поддерживаемых инструкций **RISC-V** и способы их кодирования](#набор-поддерживаемых-инструкций-risc-v-и-способы-их-кодирования)
|
||||
4. Изучить конструкции **SystemVerilog**, с помощью которых будет описан декодер ([#инструменты](#инструменты))
|
||||
5. Реализовать на языке **SystemVerilog** модуль декодера инструкций – **decoder_riscv** ([#задание](#задание))
|
||||
6. Верифицировать разработанное устройство с помощью предлагаемого **testbench** (в том же [#задании](#задание))
|
||||
|
||||
## Архитектура RISC-V и предлагаемая микроархитектура
|
||||
## Предлагаемая микроархитектура процессора RISC-V
|
||||
|
||||
### Набор инструкций **RISC-V** и способы их кодирования
|
||||
На _рис. 1_ приводится микроархитектура реализуемого ядра процессора RISC-V.
|
||||
|
||||
Все инструкции архитектуры **RISC-V** можно условно разделить на три категории:
|
||||
|
||||
- Вычислительные инструкции (операции выполняются на АЛУ)
|
||||
- Использующие в качестве операндов два регистра
|
||||
- Использующие в качестве операндов регистр и непосредственный операнд из инструкции (константу)
|
||||
- Инструкции для доступа к памяти
|
||||
- Загрузки из основной памяти в регистровый файл
|
||||
- Сохранения данных из регистрового файла в основную память
|
||||
- Инструкции управления программой (управляют тем, как изменится счетчик команд `PC`)
|
||||
- Условные переходы
|
||||
- Безусловные переходы
|
||||
|
||||
В _Таблице 1_ приводится фрагмент из `спецификации RISC-V`. В верхней её части приводится 6 форматов кодирования инструкций: **R**, **I**, **S**, **B**, **U** и **J**, затем идут конкретные значения полей внутри инструкции. Под `rd` подразумевается 5-битный адрес регистра назначения (**r**egister **d**estination), `rs1` и `rs2` —5-битные адреса регистров источников (**r**egister **s**ource), `imm` — константа (immediate), расположение и порядок битов которой указывается в квадратных скобках. Обратите внимание, что в разных форматах кодирования константы имеют различную разрядность, а их биты упакованы по-разному. Для знаковых операций константу предварительно знаково расширяют до 32 бит. Для беззнаковых расширяют нулями до 32 бит.
|
||||
|
||||

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

|
||||
|
||||
_Таблица 3. Инструкции набора RV32I с приведением их типов, функционального описания и примеров использования._
|
||||
|
||||
Обратите внимание на операции `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`.
|
||||
|
||||
Также обратите внимание на инструкции `ecall`, `ebreak` и `mret`. Все эти инструкции I-типа имеют поле func3, равное нулю. С точки зрения декодирования инструкции I-типа, это одна и та же инструкция с разными значениями поля `imm`. Однако конкретно в данном случае (SYSTEM_OPCODE и `func3 == 0`) эти инструкции должны рассматриваться как совокупность всех 32-бит сразу (см. _Таблицу 1_).
|
||||
|
||||
### Предлагаемая микроархитектура процессора RISC-V
|
||||
|
||||
На _рис. 1_ приводится микроархитектура ядра процессора RISC-V. Регистр `PC` (Program Counter – счетчик команд) подключен к адресному входу памяти инструкций. Считываемая инструкция декодируется основным дешифратором, после чего он выставляет управляющие сигналы для всех блоков процессора (мультиплексоры, АЛУ, интерфейс взаимодействия с памятью).
|
||||
|
||||
Приведенная архитектура не является заданием для текущей лабораторной работы, лишь отражает то, как в дальнейшем будет подключаться и использоваться реализуемый в данной лабораторной основной дешифратор.
|
||||
**Приведенная архитектура не является заданием для текущей лабораторной работы, лишь отражает то, как в дальнейшем будет подключаться и использоваться реализуемый в данной лабораторной работе декодер.**
|
||||
|
||||

|
||||
|
||||
_Рисунок 1. Микроархитектура будущего процессорного ядра._
|
||||
|
||||
Предложенная микроархитектура процессора `CYBERcobra 3000 Pro 2.0` из прошлой лабораторной имеет схожую структуру, с некоторыми изменениями.
|
||||
Предложенная микроархитектура похожа на микроархитектуру процессора `CYBERcobra 3000 Pro 2.0` из прошлой лабораторной, имеет схожую структуру, с некоторыми изменениями.
|
||||
|
||||
В первую очередь изменились входы и выходы процессора (помимо появления двух непонятных модулей):
|
||||
В первую очередь изменились входы и выходы процессора:
|
||||
|
||||
- память инструкций вынесена наружу процессора, таким образом, у процессора появляются входы и выходы: `instr_addr_o` и `instr_i`;
|
||||
- помимо прочего, у модуля появились сигналы интерфейса памяти данных:
|
||||
@@ -103,17 +44,82 @@ _Рисунок 1. Микроархитектура будущего проце
|
||||
- `mem_we_o` — сигнал разрешения записи во внешнюю память;
|
||||
- `mem_wd_o` — данные для записи во внешнюю память;
|
||||
- `mem_rd_i` — считанные из внешней памяти данные;
|
||||
- еще у процессора появился вход `stall_i`, отключающий программный счетчик.
|
||||
Эти сигналы используются при выполнении инструкций загрузки (сохранения) информации из (в) памяти данных.
|
||||
- еще у процессора появился вход `stall_i`, приостанавливающий обновление программного счётчика.
|
||||
|
||||
Так же добавились источники операндов АЛУ: программный счетчик, множество констант из инструкций и микроархитектурных констант. Выбор нужных операндов для АЛУ осуществляется с помощью двух мультиплексоров, управляемых сигналами декодера `a_sel_o` и `b_sel_o`.
|
||||
Кроме того, появилось два новых модуля: **Interrupt Controller** и **Control Status Registers**. Эти модули будут обеспечивать поддержку прерываний в процессорной системе.
|
||||
|
||||
Изменилось и число источников записи в регистровый файл: их стало 3: результат операции на АЛУ, данные, считанные с внешней памяти и данные из модуля регистров статуса и контроля. Выборка осуществляется сигналом декодера `wb_sel_o`.
|
||||
Так же добавились источники операндов АЛУ: программный счетчик, множество констант из инструкций и микроархитектурных констант.
|
||||
|
||||
Изменились и источники записи в регистровый файл, теперь это:
|
||||
|
||||
- результат операции на АЛУ;
|
||||
- данные, считанные с внешней памяти;
|
||||
- данные из модуля регистров контроля и статуса.
|
||||
|
||||
Для того, чтобы управлять усложнившимся набором мультиплексоров, интерфейсом памяти данных и появившимися модулями нужно специальное устройство — Устройство управления (УУ). В данной микроархитектуре логика устройства управления не вынесена в отдельный модуль, лишь выделена на схеме синим цветом. По большей части, в предложенной микроархитектуре роль устройства управления выполняет декодер инструкций.
|
||||
|
||||
## Описание сигналов декодера инструкций
|
||||
|
||||
Список портов декодера инструкций и их назначение представлен в _таблице 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 |Сигнал о некорректной инструкции |
|
||||
|
||||
_Таблица 1. Описание портов декодера инструкций._
|
||||
|
||||
У данного модуля будет лишь один вход: `fetched_instr_i` — декодируемая в данный момент инструкция. Все остальные сигналы — это выходы модуля, которые можно классифицировать следующим образом:
|
||||
|
||||
### Сигналы кода операции
|
||||
|
||||
В данный класс будут входить сигналы, которые сообщают отдельному функциональному блоку о том, какую из операций он должен выполнить. Таких блока два: **АЛУ** и модуль **регистров контроля и статуса**. АЛУ может выполнять одну из 16 операций, представленных в ЛР№2, для выбора которой и нужен подобный сигнал. Вы еще не знакомы с появившимся в микроархитектуре модулем регистров контроля и статуса, однако на текущий момент нужно лишь понимать, что он тоже может выполнять одну из нескольких операций и что для этого ему нужен специальный сигнал.
|
||||
|
||||
Таким образом, в класс сигналов кода операции входят:
|
||||
|
||||
- `alu_op_o`,
|
||||
- `csr_op_o`.
|
||||
|
||||
Для удобства использования, возможные значения этих сигналов определены в виде параметров в пакетах `alu_opcodes_pkg` и `csr_pkg` соответственно.
|
||||
|
||||
### Управляющие сигналы мультиплексоров стадии выполнения и записи результата
|
||||
|
||||
В данный класс входят сигналы, управляющие мультиплексорами, размещенными в правой части схемы:
|
||||
|
||||
- `a_sel_o`,
|
||||
- `b_sel_o`,
|
||||
- `wb_sel_o`.
|
||||
|
||||
Сигналы `a_sel_o` и `b_sel_o` определяют откуда пойдут данные на операнды АЛУ `a_i`, `b_i` соответственно. К примеру, если мы хотим, чтобы оба операнда брались из регистрового файла, нам необходимо подать значение `0` на оба соответствующих мультиплексора.
|
||||
|
||||
Сигнал `wb_sel_o` определяет источник данных для записи в регистровый файл: это либо результат операции на АЛУ, считанные данные из памяти данных, либо же данные, полученные из модуля регистров контроля и статуса.
|
||||
|
||||
### Интерфейс памяти
|
||||
|
||||
Интерфейс памяти использует несколько сигналов для взаимодействия с памятью: `mem_req_o` (этот выход должен быть выставлен в 1 каждый раз, когда необходимо обратиться к памяти – считать или записать), `mem_we_o` (выставляется в 1, если необходимо записать данные в память, и 0 – если считать из памяти) и `mem_size_o` (указывающий размер порции данных необходимых для передачи; возможные значения указаны в _Таблице 4_). Перечисленных сигналов достаточно для того, чтобы основная память понимала: обращаются ли к ней в данный момент, нужно ли записывать или считывать данные, и о какой порции данных идет речь.
|
||||
Память данных используется для хранения и доступа к информации, необходимой для выполнения программы. Несмотря на то что такая память, как и регистровый файл используются для хранения данных, назначение этих модулей различно: регистровый файл используется для хранения данных, работа над которыми осуществляется здесь и сейчас (в пределах нескольких инструкций процессора), в то время как память данных хранит всю остальную информацию, которая не может уместиться в регистровый файл в виду ограниченности его размера.
|
||||
|
||||
|Название|Значение `mem_size_o`| Пояснение |
|
||||
Для взаимодействия с подсистемой памяти данных, декодер инструкций будет использовать следующие сигналы:
|
||||
|
||||
- `mem_req_o` — этот сигнал должен быть выставлен в 1 каждый раз, когда необходимо обратиться к памяти (считать или записать данные);
|
||||
- `mem_we_o` — этот сигнал должен быть выставлен в 1, если необходимо записать данные в память, (0 при чтении);
|
||||
- `mem_size_o`— этот сигнал указывает размер порции данных для передачи (возможные значения этого сигнала указаны в _Таблице 2_). Для удобства использования, данные значения определены в виде параметров в пакете `riscv_pkg`.
|
||||
|
||||
|Параметр|Значение `mem_size_o`| Пояснение |
|
||||
|--------|---------------------|------------------------------|
|
||||
|LDST_B | 3'd0 |Знаковое 8-битное значение |
|
||||
|LDST_H | 3'd1 |Знаковое 16-битное значение |
|
||||
@@ -121,66 +127,100 @@ _Рисунок 1. Микроархитектура будущего проце
|
||||
|LDST_BU | 3'd4 |Беззнаковое 8-битное значение |
|
||||
|LDST_HU | 3'd5 |Беззнаковое 16-битное значение|
|
||||
|
||||
_Таблица 4. Значения сигнала `mem_size_o` при передаче различных порций данных._
|
||||
_Таблица 2. Значения сигнала `mem_size_o` при передаче различных порций данных._
|
||||
|
||||
### Main Decoder — Основной дешифратор команд RISC-V
|
||||
Перечисленных сигналов достаточно для того, чтобы основная память понимала: обращаются ли к ней в данный момент, нужно ли записывать или считывать данные, и о какой порции данных идет речь.
|
||||
|
||||
Как говорилось ранее, дешифратор инструкций в процессоре служит для преобразования инструкции в набор управляющих сигналов, необходимых для ее исполнения.
|
||||
### Сигналы разрешения записи
|
||||
|
||||
Пример: для выполнения инструкции записи 32-бит данных из регистрового файла во внешнюю память `sw`, дешифратор должен:
|
||||
В данную категорию входят два однобитных сигнала:
|
||||
|
||||
- направить в АЛУ два операнда (базовый адрес и смещение) вместе с кодом операции АЛУ (сложения) для вычисления адреса:
|
||||
- `a_sel_o = 2'd0`;
|
||||
- `b_sel_o = 3'd1`;
|
||||
- `alu_op_o= ALU_ADD`;
|
||||
- сформировать управляющие сигналы интерфейса памяти:
|
||||
- `mem_req_o = 1'b1`;
|
||||
- `mem_size_o= 3'd2`;
|
||||
- `mem_we_o = 1'b1`.
|
||||
- gpr_we_o — сигнал разрешения записи в регистровый файл (General Purpose Registers, GPR);
|
||||
- csr_we_o — сигнал разрешения записи в модуле регистров контроля и статуса.
|
||||
|
||||
Несмотря на то, что для записи во внешнюю память ключевыми сигналами будут описанные выше, это не означает, что остальные выходные сигналы декодера могут быть абы какими.
|
||||
### Сигналы управления программным счетчиком
|
||||
|
||||
Поскольку операция `sw` не является операцией перехода, сигналы `jal_o`, `jalr_o` и `branch_o` должны быть равны нулю (иначе процессор совершит переход, а инструкция `lw` этого не подразумевает). Точно так же, поскольку во время записи во внешнюю память, в регистровый файл не должно быть ничего записано, сигналы `gpr_we_o` и `csr_we_o` также должны быть равны нулю.
|
||||
В данную категорию входят однобитные сигналы, которые оповещают о том, что выполняется инструкция, связанная с изменением значения программного счетчика:
|
||||
|
||||
А вот сигнал `wb_sel` может принять любое значение (поскольку сигнал разрешения записи в регистровый файл равен нулю, не важно, каким будет источник данных для записи в регистровый файл, т.к. в него все равно ничего не будет записано).
|
||||
- branch_o — сигнал об инструкции условного перехода;
|
||||
- jal_o — сигнал об инструкции безусловного перехода jal;
|
||||
- jalr_o — сигнал об инструкции безусловного перехода jalr;
|
||||
- mret_o — сигнал об инструкции возврата из прерывания/исключения mret.
|
||||
|
||||
---
|
||||
### Сигнал нелегальной инструкции
|
||||
|
||||
Управляющие сигналы на выходе декодера зависят от трех полей инструкции: `opcode`, `func3` и `func7`. Обратите внимание, что расположение этих полей одинаково для всех типов инструкций. Это сделано для удобства декодирования. При этом для некоторых инструкций поля `func3` и `func7` могут отсутствовать.
|
||||
Это сигнал, который должен принять значение `1` в случае если пришла инструкция, которая не входит в список поддерживаемых процессором.
|
||||
|
||||
|Название сигнала| Пояснение | На каких опкодах может принять ненулевое значение (см. таблицу 6)|
|
||||
|----------------|------------------------------------------------------------------------------------------------|------------------------------------------------------------------|
|
||||
|fetched_instr_i |Инструкция для декодирования, считанная из памяти инструкций | — |
|
||||
|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 |
|
||||
Это не единственное, что должен сделать декодер в подобной ситуации. Давайте разберем подробней, что должно происходить по приходу нелегальной инструкции.
|
||||
|
||||
_Таблица 5. Описание портов основного дешифратора._
|
||||
## Обработка нелегальной инструкции
|
||||
|
||||
Единственным входным сигналом этого модуля является `fetched_instr_i`.
|
||||
В случае появления инструкции, которая не поддерживается процессором, устройство управления должно обеспечить стабильность системы. В самом простом случае, такую инструкцию необходимо пропустить, сохранив так называемое **архитектурное состояние** процессора — т.е. сохранив значение всех элементов системы, характеризующих состояние системы в текущий момент. К таким элементам относятся: содержимое регистрового файла, основой памяти, содержимое регистров контроля и статуса и т.п. Значение программного счетчика также входит в архитектурное состояние процессора, однако в контексте пропуска инструкции с сохранением архитектурного состояния, его значение нужно изменить, иначе система оказалась бы в бесконечном цикле (неизменный счетчик бы указывал на ту же самую инструкцию, которая не должна менять архитектурного состояния).
|
||||
|
||||
В системе команд **RV32I** два младших бита поля opcode всегда равны `11`, таким образом декодер понимает, что будут исполняться именно 32-битные инструкции, а не 16-битные, например. **Main decoder** должен выдать единицу на выходе `illegal_instr_o` в случае:
|
||||
Иными словами, в случае появления нелегальной инструкции, устройство управления (роль которого в нашей системе по большей части играет декодер) должно проследить за тем, чтобы в системе не изменилось ничего кроме программного счетчика. К сигналам, влияющим на изменение архитектурного состояния относятся:
|
||||
|
||||
- неравенства двух младших битов opcode значению `11`;
|
||||
- некорректного значения `func3` или `func7` для данной операции;
|
||||
- если значение `opcode` не совпадает ни с одним из известных и следовательно операция не определена.
|
||||
- если это инструкция `ECALL` / `EBREAK`.
|
||||
- mem_req_o,
|
||||
- mem_we_o,
|
||||
- gpr_we_o,
|
||||
- csr_we_o,
|
||||
- branch_o,
|
||||
- jal_o,
|
||||
- jalr_o,
|
||||
- mret_o.
|
||||
|
||||
При реализации декодера его удобнее описывать разбив все инструкции на однотипные группы, как это сделано в _Таблице 6_. Представленные в ней коды операций 5-битные потому, что 2 младших бита полноценного 7-битного кода операции должны всегда быть равны `11`. Если это не так, то вся инструкция уже запрещенная и не нуждается в дальнейшем декодировании.
|
||||
Иными словами, должны быть запрещены все запросы на запись, обращения в память и любые "прыжки" программного счетчика.
|
||||
|
||||
|Операция|Opcode| Описание операции | Краткая запись |
|
||||
Давайте теперь разберемся с тем, какие именно инструкции должен будет поддерживать наш процессор.
|
||||
|
||||
### Набор поддерживаемых инструкций **RISC-V** и способы их кодирования
|
||||
|
||||
Все инструкции архитектуры **RISC-V** можно условно разделить на три категории.
|
||||
|
||||
- Вычислительные инструкции (операции выполняются на АЛУ, с записью результата в регистровый файл). В основном, это инструкции:
|
||||
- использующие в качестве операндов два регистра;
|
||||
- использующие в качестве операндов регистр и непосредственный операнд из инструкции (константу).
|
||||
- Инструкции для доступа к памяти:
|
||||
- загрузки из основной памяти в регистровый файл;
|
||||
- сохранения данных из регистрового файла в основную память;
|
||||
- Инструкции управления:
|
||||
- Условные переходы
|
||||
- Безусловные переходы
|
||||
- Системные инструкции
|
||||
- обращение к регистрам контроля и статуса;
|
||||
- системные вызовы и возврат из обработчика прерываний
|
||||
|
||||
В _Таблице 3_ приводится фрагмент из `спецификации RISC-V`. В верхней её части приводится 6 форматов кодирования инструкций: **R**, **I**, **S**, **B**, **U** и **J** (описание типов представлено в _таблице 4_). Затем список всех инструкций с конкретными значениями полей, соответствующих формату кодирования инструкции данного типа.
|
||||
|
||||
Под `rd` подразумевается 5-битный адрес регистра назначения (**r**egister **d**estination), `rs1` и `rs2` — 5-битные адреса регистров источников (**r**egister **s**ource), `imm` — непосредственный (immediate, задающийся прямиком в инструкции) операнд (константа), расположение и порядок битов которого указывается в квадратных скобках. Обратите внимание, что в разных форматах кодирования константы имеют различную разрядность, а их биты расположены по-разному. Для знаковых операций константу предварительно знаково расширяют до 32 бит. Для беззнаковых расширяют нулями до 32 бит.
|
||||
|
||||

|
||||
|
||||
_Таблица 3. Базовый набор инструкций из спецификации RISC-V[[1, стр. 130]](https://github.com/riscv/riscv-isa-manual/releases/download/Ratified-IMAFDQC/riscv-spec-20191213.pdf), Стандартное расширение Zicsr[[1, стр. 131]](https://github.com/riscv/riscv-isa-manual/releases/download/Ratified-IMAFDQC/riscv-spec-20191213.pdf), а также привилегированная инструкция mret[[2, стр. 138]](https://github.com/riscv/riscv-isa-manual/releases/download/Priv-v1.12/riscv-privileged-20211203.pdf)._
|
||||
|
||||
| Кодирование | Описание |
|
||||
|-------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| R-тип | Арифметические и логические операции над двумя регистрами с записью результата в третий (регистр назначения может совпадать с одним из регистров-источников) |
|
||||
| I-тип | Инструкции с 12-битным непосредственным операндом |
|
||||
| S-тип | Инструкции записи в память (инструкции store) |
|
||||
| B-тип | Инструкции ветвления |
|
||||
| U-тип | Инструкции с 20-битным «длинным» непосредственным операндом, сдвинутым влево на 12 |
|
||||
| J-тип | Единственная инструкция jal, осуществляющая безусловный переход по адресу относительно текущего счетчика команд |
|
||||
|
||||
_Таблица 4. Описание типов форматов кодирования инструкций ISA RISC-V._
|
||||
|
||||
### Декодирование инструкций RISC-V
|
||||
|
||||
Как уже описывалось в дополнительных материалах, декодирование инструкции начинается с поля `opcode` (**op**eration **code**, опкод). По этому полю определяется группа инструкций одного типа. Далее (для большинства типов кодирования) инструкция доопределяется через поля `func3` и `func7` (при наличии). Обратите внимание, что расположение этих полей одинаково для всех типов инструкций (см. верхнюю часть _таблицы 3_).
|
||||
|
||||
Поля `rs1`/`rs2`/`imm` и `rd` декодеру не нужны и используются напрямую для адресации в регистровом файле / использования непосредственного операнда в АЛУ.
|
||||
|
||||
Существуют особые инструкции, не имеющие никаких переменных полей (к примеру инструкция ECALL в _таблице 3_). Такие инструкции необходимо проверять целиком (нужно убедиться что инструкция совпадает вплоть бита).
|
||||
|
||||
В _Таблице 5_ представлены все опкоды реализуемых нами инструкций. Представленные в ней коды операций 5-битные потому, что 2 младших бита полноценного 7-битного кода операции в реализуемых нами инструкциях должны всегда быть равны `11`. Если это не так, то вся инструкция уже запрещенная и не нуждается в дальнейшем декодировании.
|
||||
|
||||
Для удобства, значения кодов операций определены в виде параметров в пакете riscv_pkg.
|
||||
|
||||
|Параметр|Opcode| Описание группы операций | Краткая запись |
|
||||
|--------|------|-------------------------------------------------------------------------------------------------------|------------------------------------|
|
||||
|OP |01100 |Записать в `rd` результат вычисления АЛУ над `rs1` и `rs2` |`rd = alu_op(rs1, rs2)` |
|
||||
|OP_IMM |00100 |Записать в `rd` результат вычисления АЛУ над `rs1` и `imm` |`rd = alu_op(rs1, imm)` |
|
||||
@@ -194,15 +234,104 @@ _Таблица 5. Описание портов основного дешифр
|
||||
|MISC-MEM|00011 |Не производить операцию | `-` |
|
||||
|SYSTEM |11100 |Записать в `rd` значение `csr`. Обновить значение `csr` с помощью `rs1`. (либо `mret`/`ecall`/`ebreak`)|`csr = csr_op(rs1); rd = csr` |
|
||||
|
||||
_Таблица 6. Описание кодов операций_
|
||||
_Таблица 5. Описание кодов операций._
|
||||
|
||||
#### SYSTEM-инструкции
|
||||
|
||||
SYSTEM-инструкции используются для доступа к системным функциям и могут требовать привилегированный доступ. Данные инструкции могут быть разделены на два класса:
|
||||
|
||||
- Обращение к регистрам статуса и контроля (**Control and Status Registers**, **CSR**)
|
||||
- Все остальные инструкции (возможно из набора привилегированных инструкций)
|
||||
|
||||
Для того, чтобы в будущем процессор поддерживал прерывания, нам требуется декодировать инструкции обоих классов.
|
||||
|
||||
Обращение к регистрам контроля и статуса осуществляется шестью инструкциями стандартного расширения `Zicsr`. Каждая из этих инструкций (если у нее легальные поля) осуществляет запись в CSR и регистровый файл (блоки `Control Status Registers` и `Register File` на _рис. 1_ соответственно).
|
||||
|
||||
Кроме того, для возврата управления основному потоку инструкций, нужна дополнительная `SYSTEM`-инструкция привилегированного набора команд `MRET`.
|
||||
|
||||
Перечисленные выше инструкции являются "дополнительными" — их намеренно их добавили сверх стандартного набора инструкций, чтобы обеспечить требуемый нашей системе функционал. Однако осталось ещё две SYSTEM-инструкции, которые мы должны уметь декодировать, поскольку они есть в стандартном наборе инструкций.
|
||||
|
||||
Инструкции `ECALL` и `EBREAK` вызывают исключение. Подробнее исключения и прерывания будут разобраны в ЛР№10.
|
||||
|
||||
#### MISC-MEM инструкция
|
||||
|
||||
В базовом наборе инструкций **RISC-V** к `MISC-MEM`-операции относится инструкция `FENCE`. В реализуемом процессорном ядре эта инструкция не должны приводить ни к каким изменениям. Инструкция `FENCE` в **RISC-V** необходима при работе с несколькими аппаратными потоками, или "хартами" (hart – «hardware thread»). В **RISC-V** используется расслабленная модель памяти (**relaxed memory model**): потоки «видят» все инструкции чтения и записи, которые исполняются другими потоками, однако видимый порядок этих инструкций может отличаться от реального. Инструкция `FENCE`, использованная между двумя инструкциями чтения и/или записи гарантирует, что остальные потоки увидят первую инструкцию перед второй. Реализация `FENCE` является опциональной в **RISC-V** и в данном случае в ней нет необходимости, так как в системе не предполагается наличия нескольких аппаратных потоков. Данная инструкция должна быть реализована как `NOP` (**n**o **op**eration).
|
||||
|
||||
В _таблице 6_ представлены инструкции из таблицы 3 с приведением их типов, значениями полей `opcode`, `func3`, `func7`, функциональным описанием и примерами использования.
|
||||
|
||||

|
||||
|
||||
_Таблица 6. Расширенное описание инструкций RV32IZicsr._
|
||||
|
||||
Обратите внимание на операции `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`.
|
||||
|
||||
Также обратите внимание на инструкции `ecall`, `ebreak` и `mret`. Все эти инструкции I-типа имеют поле func3, равное нулю. С точки зрения декодирования инструкции I-типа, это одна и та же инструкция с разными значениями поля `imm`. Однако конкретно в данном случае (SYSTEM_OPCODE и `func3 == 0`) эти инструкции должны рассматриваться как совокупность всех 32-бит сразу (см. _таблицу 3_).
|
||||
|
||||
### Выставление управляющих сигналов
|
||||
|
||||
Как говорилось ранее, декодер инструкций в процессоре служит для преобразования инструкции в набор управляющих сигналов, необходимых для ее исполнения. Таким образом, для каждой инструкции из _таблицы 3_ декодер должен поставить в соответствие конкретное значение для каждого из выходов, перечисленных в _таблице 1_.
|
||||
|
||||
Пример: для выполнения инструкции записи 32-бит данных из регистрового файла во внешнюю память `sw`, дешифратор должен направить в АЛУ два операнда (базовый адрес и смещение) вместе с кодом операции АЛУ (сложения) для вычисления адреса. Базовый адрес берется из регистрового файла, а смещение является непосредственным операндом инструкции S-типа. Таким образом для вычисления адреса записи декодер должен выставить следующие значения на выходах:
|
||||
|
||||
- `a_sel_o = 2'd0`,
|
||||
- `b_sel_o = 3'd1`,
|
||||
- `alu_op_o= ALU_ADD`.
|
||||
|
||||
Кроме того, для самой операции записи в основную память, декодер должен сформировать управляющие сигналы интерфейса памяти (запрос на обращение в память, размер передаваемых данных и сигнал разрешения записи):
|
||||
|
||||
- `mem_req_o = 1'b1`,
|
||||
- `mem_size_o = LDST_W` (см. _таблицу 2_),
|
||||
- `mem_we_o = 1'b1`.
|
||||
|
||||
Несмотря на то, что для записи во внешнюю память ключевыми сигналами будут описанные выше, это не означает, что остальные выходные сигналы дешифратора команд могут быть абы какими.
|
||||
|
||||
Поскольку операция `sw` не является операцией перехода, сигналы `jal_o`, `jalr_o` и `branch_o` и `mret` должны быть равны нулю (иначе процессор совершит переход, а инструкция `sw` этого не подразумевает). Точно так же, поскольку во время записи во внешнюю память, в регистровый файл и регистры контроля и статуса ничего не должно быть записано, сигналы `gpr_we_o` и `csr_we_o` также должны быть равны нулю.
|
||||
|
||||
Иными словами, крайне важно следить выходными сигналами, влияющими на изменение архитектурного состояния процессора, не затрагиваемые инструкцией в явном виде.
|
||||
|
||||
А вот сигнал `wb_sel` может принять любое значение (поскольку сигнал разрешения записи в регистровый файл равен нулю, не важно, каким будет источник данных для записи в регистровый файл, т.к. в него все равно ничего не будет записано).
|
||||
|
||||
Разумеется, описывая модуль декодера инструкций, было бы нерационально прописывать для каждой из 47 инструкций значение 14 выходов модуля, особенно учитывая, что многие выходные сигналы будут иметь одно и то же значение для всех инструкций одного опкода, поэтому удобнее всего будет описывать их, сгруппировав по кодам операций.
|
||||
|
||||
В _таблице 7_ определен список выходных сигналов декодера инструкций и групп инструкций, при которых эти выходы могут принимать ненулевое значение.
|
||||
|
||||
|Название сигнала| Пояснение | На каких опкодах может принять ненулевое значение (см. таблицу 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 |
|
||||
|
||||
_Таблица 5. Описание портов дешифратора команд._
|
||||
|
||||
Дешифратор должен выдать единицу на выходе `illegal_instr_o` в случае:
|
||||
|
||||
- неравенства двух младших битов opcode значению `11`;
|
||||
- если значение поля `opcode` не совпадает ни с одним из известных и следовательно операция не определена.
|
||||
- некорректного значения полей `func3` или `func7` для данного опкода.
|
||||
|
||||
Кроме того, поскольку представленная на _рис. 1_ микроархитектура поддерживает только одно исключение (исключение через сигнал `illegal_instr_o`), этот сигнал должен быть равен единице и в случае:
|
||||
|
||||
- если это инструкция `ECALL` / `EBREAK`.
|
||||
|
||||
## Инструменты
|
||||
|
||||
В первую очередь язык описания аппаратуры **SystemVerilog** – это язык. С помощью этого языка человек объясняет либо синтезатору какое он хочет получить устройство, либо симулятору – как он хочет это устройство проверить. Синтезатор – это программа, которая создает из логических элементов цифровое устройство по описанию, предоставляемому человеком. Синтезатору внутри **Vivado** нужно объяснить, что ты от него хочешь. Например, чтобы спросить дорогу у испанца, придется делать это на испанском языке, иначе он ничем не сможет помочь. Если ты знаешь испанский, то это можно сделать еще и разными способами. В **SystemVerilog** точно также – одно и то же устройство можно описать разным кодом, но результат синтеза будет одним и тем же. Однако, часто два разных кода одинаковые по смыслу могут синтезироваться в разную аппаратуру, хотя функционально они будут идентичны, но могут отличаться, например, скоростью работы. Или одни и те же специальные языковые конструкции могут применяться для синтезирования разных цифровых элементов.
|
||||
В первую очередь язык описания аппаратуры **SystemVerilog** – это язык. С помощью этого языка человек объясняет либо синтезатору какое он хочет получить устройство, либо симулятору – как он хочет это устройство проверить. Синтезатор – это программа, которая создает из логических элементов цифровое устройство по описанию, предоставляемому человеком. Синтезатору внутри **Vivado** нужно объяснить, что от него нужно. Например, чтобы спросить дорогу у испанца, придется делать это на испанском языке, иначе он ничем не сможет помочь. А если вы знаете испанский, то скорее всего сможете это сделать еще и разными способами. В **SystemVerilog** точно также – одно и то же устройство можно описать разным кодом, но результат синтеза будет одним и тем же. Однако, часто два разных кода одинаковые по смыслу могут синтезироваться в разную аппаратуру, хотя функционально они будут идентичны, но могут отличаться, например, скоростью работы. Или одни и те же специальные языковые конструкции могут применяться для синтезирования разных цифровых элементов.
|
||||
|
||||
Основной дешифратор – это комбинационная схема. Это значит, что каждый раз подавая на вход одни и те же значения, вы будете получать на выходе один и тот же результат, потому что комбинационные схемы не содержат элементов памяти.
|
||||
Декодер – комбинационная схема. Это значит, что каждый раз подавая на вход одни и те же значения, вы будете получать на выходе один и тот же результат, потому что комбинационные схемы не содержат элементов памяти.
|
||||
|
||||
Можно по-разному описывать комбинационные схемы. Например — через конструкцию `assign`. Для основного дешифратора отлично подойдет конструкция `case`, которая превратится не в мультиплексор, а в комбинационную схему с оптимальными параметрами критического пути. В доверилоговую эпоху разработчикам пришлось бы строить гигантские таблицы истинности и какие-нибудь [карты Карно](https://ru.wikipedia.org/wiki/Карта_Карно), искать оптимальные схемы реализации. Сегодня эту задачу решает синтезатор, по описанию устройства сам находит наиболее эффективное решение.
|
||||
Можно по-разному описывать комбинационные схемы. Например — через конструкцию `assign`. Для описания декодера отлично подойдет конструкция `case`, которая превратится не в мультиплексор, а в комбинационную схему с оптимальными параметрами критического пути. В доверилоговую эпоху разработчикам пришлось бы строить гигантские таблицы истинности и какие-нибудь [карты Карно](https://ru.wikipedia.org/wiki/Карта_Карно), искать оптимальные схемы реализации. Сегодня эту задачу решает синтезатор, по описанию устройства сам находит наиболее эффективное решение.
|
||||
|
||||
Разница с реализацией мультиплексора в том, что в этом случае справа от знака равно всегда стоит константа. Получается это такой способ описать таблицу истинности. В такой код легко вносить правки и искать интересующие фрагменты.
|
||||
|
||||
@@ -246,9 +375,13 @@ module tequila (
|
||||
endmodule
|
||||
```
|
||||
|
||||
Имейте в виду, что значения по-умолчанию, описанные в начале блока `always_comb` можно использовать таким образом при помощи **блокирующих присваиваний** (которые следует использовать только в комбинационных блоках).
|
||||
|
||||
Кроме того, использование вложенных блоков `case` обосновано только в ситуации создания блока декодера (т.е. в случаях, когда справа от всех присваиваний будут использованы константы, а не другие сигналы). В случае описания мультиплексора, вложенные блоки `case` могут быть синтезированы в каскад мультиплексоров, что негативно скажется на временных характеристиках схемы.
|
||||
|
||||
## Задание
|
||||
|
||||
Необходимо реализовать на языке **SystemVerilog** модуль основного дешифратора команд однотактного процессора RISC-V в соответствии с предложенной микроархитектурой. Далее приводится прототип разрабатываемого модуля.
|
||||
Необходимо реализовать на языке **SystemVerilog** модуль декодера инструкций однотактного процессора RISC-V в соответствии с предложенной микроархитектурой. Далее приводится прототип разрабатываемого модуля.
|
||||
|
||||
```SystemVerilog
|
||||
module decoder_riscv (
|
||||
@@ -274,22 +407,22 @@ module decoder_riscv (
|
||||
endmodule
|
||||
```
|
||||
|
||||
В зависимости от стиля оформления, модуль может занимать больше сотни строк кода, но это не делает его реализацию сложной. По сути, дешифратор — это просто большой `case` с описанием того, в каком случае, какие сигналы и чему должны быть равны. Работа требует внимательности, немного усидчивости и понимания выполняемых действий. С огромной вероятностью в коде будут ошибки и их нужно будет исправлять. Ошибки — это нормально (не ошибается тот, кто ничего не делает), а исправление ошибок дает бесценный опыт разработки. Возможно, реализация этого модуля в какой-то момент покажется рутинной, но поверь, по окончании следующей лабораторной работы удовольствие от результата покажет, что оно того стоило.
|
||||
В зависимости от стиля оформления, модуль может занимать больше сотни строк кода, но это не делает его реализацию сложной. По сути, дешифратор — это просто большой `case` с описанием того, в каком случае, какие сигналы и чему должны быть равны. Работа требует внимательности, немного усидчивости и понимания выполняемых действий. С огромной вероятностью в коде будут ошибки и их нужно будет исправлять. Ошибки — это нормально (не ошибается тот, кто ничего не делает), а исправление ошибок дает бесценный опыт разработки. Возможно, реализация этого модуля в какой-то момент покажется рутинной, но по окончании следующей лабораторной работы удовольствие от результата покажет, что оно того стоило.
|
||||
|
||||
## Порядок выполнения задания
|
||||
|
||||
1. Внимательно ознакомьтесь с выходными сигналами декодера и тем, за что они отвечают, а также типами команд. В случае возникновения вопросов, проконсультируйтесь с преподавателем.
|
||||
1. Внимательно ознакомьтесь с выходными сигналами декодера инструкций и тем, за что они отвечают, а также типами команд. В случае возникновения вопросов, проконсультируйтесь с преподавателем.
|
||||
2. Реализуйте модуль `decoder_riscv`. Для этого:
|
||||
1. В `Design Sources` проекта с предыдущих лаб, создайте `SystemVerilog`-файл `decoder_riscv.sv`.
|
||||
2. Опишите в нем модуль основного дешифратора с таким же именем и портами, как указано в [задании](#задание).
|
||||
2. Опишите в нем модуль декодера инструкций с таким же именем и портами, как указано в [задании](#задание).
|
||||
1. Для удобства дальнейшего описания модуля, рекомендуется сперва создать сигналы `opcode`, `func3`, `func7` и присвоить им соответствующие биты входного сигнала инструкции.
|
||||
2. При описании модуля вы можете воспользоваться параметрами, объявленными **пакетах** `riscv_pkg`, `csr_pkg` и `alu_opcodes_pkg`, описанных в файлах [riscv_pkg.sv](riscv_pkg.sv), [csr_pkg.sv](csr_pkg.sv) и [alu_opcodes_pkg.sv](alu_opcodes_pkg.sv) соответственно.
|
||||
3. Модуль может быть описан множеством способов: каждый выходной сигнал может быть описан через собственную комбинационную логику в отдельном блоке `case`, однако проще всего будет описать все сигналы через вложенные `case` внутри одного блока `always_comb`.
|
||||
4. Внутри блока `always_comb` до начала блока `case` можно указать базовые значения для всех выходных сигналов. Это не то же самое, что вариант `default` в блоке `case`. Здесь вы можете описать состояния, которые будут использованы чаще всего, и в этом случае, присваивание сигналу будет выполняться только в том месте, где появится инструкция, требующая значение этого сигнала, отличное от базового.
|
||||
5. Далее вы можете описать базовый блок `case`, где будет определен тип операции по ее коду.
|
||||
6. Определив тип операции, вы сможете определить какая конкретно операция по полям `func3` и `func7` (если данный тип имеет такие поля).
|
||||
7. Не забывайте, что в случае, если на каком-то из этапов (определения типа, или определения конкретной операции) вам приходит какое-то неправильное поле, необходимо выставить сигнал `illegal_instr_o`.
|
||||
8. В случае некорректной инструкции, вы должны гарантировать, что не произойдет условный/безусловный переход, а во внешнюю память и регистровый файл ничего не запишется. Не важно, что будет выполняться на АЛУ, не важно какие данные будут выбраны на мультиплексоре источника записи. Важно чтобы не произошел сам факт записи в любое из устройств (подумайте какие значения для каких сигналов необходимо для этого выставить).
|
||||
7. Не забывайте, что в случае, если на каком-то из этапов (определения типа, или определения конкретной операции) вам приходит непредусмотренное ISA значение какого-либо поля, необходимо выставить сигнал `illegal_instr_o`.
|
||||
8. В случае некорректной инструкции, вы должны гарантировать, что не произойдет условный/безусловный переход, а во внешнюю память, регистровый файл, а также регистры контроля и статуса ничего не запишется. Не важно, что будет выполняться на АЛУ, не важно какие данные будут выбраны на мультиплексоре источника записи. Важно чтобы не произошел сам факт записи в любое из устройств (подумайте какие значения для каких сигналов необходимо для этого выставить).
|
||||
3. После описания модуля, его необходимо проверить с помощью тестового окружения.
|
||||
1. Тестовое окружение находится [`здесь`](tb_decoder_riscv.sv).
|
||||
2. Для запуска симуляции воспользуйтесь [`этой инструкцией`](../../Vivado%20Basics/Run%20Simulation.md).
|
||||
|
36
Other/CSR.md
Normal file
36
Other/CSR.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# О регистрах контроля и статуса
|
||||
|
||||
Для поддержания работы операционной системы, виртуализации, системы прерывания и тому подобное, в архитектуре RISC-V предусмотрено использование группы регистров, под общим названием **Control and Status Registers** (**CSR**), обеспечивающих управление элементами процессора и доступ к статусной информации о системе. С помощью этих регистров реализуются привилегированные режимы работы процессора, хранение указателей на различные программные стеки, статус различных подсистем, регистры для обеспечения работы прерываний и многое другое.
|
||||
|
||||
Все регистры имеют уникальные 12-битные адреса, а их роли определены в спецификации на архитектуру RISC-V. В _Таблице 1_ приводится фрагмент спецификации привилегированной архитектуры[[5, стр. 10]](https://github.com/riscv/riscv-isa-manual/releases/download/Priv-v1.12/riscv-privileged-20211203.pdf), иллюстрирующая некоторые из регистров. В левом столбце указан 12-битный адрес. Далее указывается, что можно делать с этим регистром и в каком режиме. После идет название, а в правом столбце описание.
|
||||
|
||||
В этой таблице можно увидеть регистры для сохранения адреса возврата из прерывания, адрес вектора прерывания, регистры причины (cause), регистры настройки безопасности и защиты памяти. И это далеко не полный список регистров, предоставляемых стандартом (который помимо прочего, оставляет место в адресном пространстве для ваших собственных регистров).
|
||||
|
||||

|
||||
|
||||
_Таблица 1. Регистры контроля и состояния машинного (наивысшего) уровня привилегий._
|
||||
|
||||
Для работы с CS-регистрами используются специальные **SYSTEM**-инструкции I-типа (код операции `1110011`), хранящие в 12-битном поле **imm** адрес регистра, к которому будет осуществлен доступ и адреса в регистровом файле откуда будет считан или куда будет записан один из CS-регистров. Вы уже добавляли поддержку этих инструкций во время выполнения [лабораторной работы №5](../05.%20Main%20decoder/) "Основной дешифратор".
|
||||
|
||||
| opcode | func3 | Тип | Инструкция | Описание | Операция |
|
||||
|--------|-------|-----|---------------------|---------------------------|-----------------------------|
|
||||
|1110011 | 000 | I | mret | Возврат из прерывания | PC = mepc |
|
||||
|1110011 | 001 | I | csrrw rd, csr, rs1 | Чтение и Запись CSR | rd = csr, csr = rs1 |
|
||||
|1110011 | 010 | I | csrrs rd, csr, rs1 | Чтение и Установка бит CSR| rd = csr, csr = csr \| rs1 |
|
||||
|1110011 | 011 | I | csrrc rd, csr, rs1 | Чтение и Очистка бит CSR | rd = csr, csr = csr & ~rs1 |
|
||||
|1110011 | 101 | I | csrrwi rd, csr, rs1 | Чтение и Запись CSR | rd = csr, csr = imm |
|
||||
|1110011 | 110 | I | csrrsi rd, csr, rs1 | Чтение и Установка бит CSR| rd = csr, csr = csr \| imm |
|
||||
|1110011 | 111 | I | csrrci rd, csr, rs1 | Чтение и Очистка бит CSR | rd = csr, csr = csr & ~imm |
|
||||
|
||||
_Таблица 2. Список инструкций для работы с регистрами контроля и статуса._
|
||||
|
||||
Для удобства программирования на языке ассемблера RISC-V существуют псевдоинструкции для работы с CS-регистрами.
|
||||
|
||||
| Псевдоинструкция | Инструкция RISC-V | Описание | Операция |
|
||||
|------------------|--------------------|-------------|-----------|
|
||||
| csrr rd, csr | csrrs rd, csr, x0 | Чтение CSR | rd = csr |
|
||||
| csrw csr, rs1 | csrrw x0, csr, rs1 | Запись CSR | csr = rs1 |
|
||||
|
||||
_Таблица 3. Псевдоинструкции для работы с регистрами контроля и статуса._
|
||||
|
||||
Операция логического ИЛИ нулевого регистра с содержимым CS-регистра не меняет его содержимого, поэтому при использовании инструкции `csrr` происходит только операция чтения. Подобным образом реализована псевдоинструкция `csrw`.
|
@@ -17,8 +17,6 @@ RISC-V — открытая и свободная система набора к
|
||||
|
||||
Строго говоря RISC-V — это семейство родственных ISA, из которых в настоящее время существует четыре базовые ISA. Каждый базовый целочисленный набор инструкций характеризуется `шириной целочисленных регистров` и соответствующим `размером адресного пространства`, а также `количеством целочисленных регистров`. Существует два основных базовых целочисленных варианта, `RV32I` и `RV64I`, которые, соответственно, обеспечивают 32- или 64-битное адресное пространство и соответствующие размеры регистров регистрового файла. На основе базового набора инструкций `RV32I` существует вариант подмножества `RV32E`, который был добавлен для поддержки небольших микроконтроллеров и имеет вдвое меньшее количество целочисленных регистров — 16, вместо 32. Разрабатывается вариант `RV128I` базового целочисленного набора инструкций, поддерживающий плоское 128-битное адресное пространство. Также, стоит подчеркнуть, что размеры регистров и адресного пространства, во всех перечисленных стандартных наборах инструкций, не влияют на размер инструкций — во всех случаях они кодируются 32-битными числами. То есть, и для `RV32I`, и для `RV64I` одна инструкция будет кодироваться 32 битами. Базовые целочисленные наборы команд используют представление знаковых целых чисел в дополнительном коде.
|
||||
|
||||
В рамках дисциплины АПС затрагивается только `RV32I`, то есть стандартный набор целочисленных инструкций, предусматривающий в процессоре регистровый файл из 32-х 32-битных регистров, и использующий 32-битное адресное пространство памяти.
|
||||
|
||||
Кроме обязательного подмножества целочисленных инструкций, RISC-V предусматривает несколько стандартных опциональных расширений. Вот некоторые из них:
|
||||
|
||||
- **M** — Целочисленное умножение и деление (Integer Multiplication and Division)
|
||||
@@ -35,23 +33,27 @@ RISC-V — открытая и свободная система набора к
|
||||
|
||||
> Чтобы устройство управления понимало, когда оно имеет дело с набором сжатых команд **C**, то есть с 16-битными инструкциями, а когда с другими наборами команд, то есть с инструкциями длиной 32 бита, каждая 32-битная инструкция в младших битах имеет `11`. Если в двух младших битах что-то отличное от `11`, значит это 16-битная инструкция!
|
||||
|
||||
На _рис. 1_ показана видимая пользователю структура для основного подмножества команд для целочисленных вычислений `RV32I`. Она содержит `регистровый файл`, состоящий из 31 регистра общего назначения **x1** — **x31**, каждый из которых может содержать целочисленное значение, и регистра **x0**, жестко привязанного к константе 0. В случае `RV32`, регистры **xN**, и вообще все регистры, имеют длину в 32 бита. Также есть `АЛУ`, выполняющее операции над данными в регистровом файле (концепция RISC - load&store), и `память` с побайтовой адресацией и шириной адреса 32 бита.
|
||||
В рамках дисциплины АПС изучается базовая ISA `RV32I` и расширение для работы с регистрами контроля и статуса `Zicsr`, обеспечивающими поддержку подсистемы прерываний.
|
||||
|
||||
На _рис. 1_ показана видимая пользователю структура для основного подмножества команд для целочисленных вычислений `RV32I`, а также расширения `Zicsr`. Эта структура содержит `регистровый файл`, состоящий из 31 регистра общего назначения **x1** — **x31**, каждый из которых может содержать целочисленное значение, и регистра **x0**, жестко привязанного к константе 0. В случае `RV32`, регистры **xN**, и вообще все регистры, имеют длину в 32 бита. Кроме того, в структуре присутствует `АЛУ`, выполняющее операции над данными в регистровом файле, `память` с побайтовой адресацией и шириной адреса 32 бита, а также блок 32-битных регистров контроля и статуса с шириной адреса в 12 бит.
|
||||
|
||||
Также существует еще один дополнительный видимый пользователю регистр: счетчик команд — `pc` (program counter), который содержит адрес текущей инструкции. `pc` изменяется либо автоматически, указывая на следующую инструкцию, либо в результате использования инструкций управления (операции условного и безусловного переходов).
|
||||
|
||||

|
||||

|
||||
|
||||
_Рисунок 1. Основные компоненты архитектуры RISC-V._
|
||||
|
||||
RISC-V является load&store архитектурой (все операции с числами выполняются над данными только в регистровом файле), поэтому глядя на рисунок выше можно легко заключить, что функционально все инструкции сводятся к трём типам:
|
||||
Поскольку RISC-V является `Load & Store` архитектурой, все операции с числами выполняются только над данными в регистровом файле (если необходимо обработать данные из основной памяти, их необходимо сперва загрузить в регистровый файл (Load), а после обработки выгрузить обратно в основную память (Store)).
|
||||
|
||||
- Операции на АЛУ над числами в регистровом файле
|
||||
- Операции обмена данными между регистровым файлом и памятью
|
||||
- Манипуляции с `pc` (другими словами — управление программой)
|
||||
Из _рисунка 1_ можно легко заключить, что функционально все инструкции сводятся к трём типам:
|
||||
|
||||
- операции на АЛУ над числами в регистровом файле;
|
||||
- операции обмена данными между регистровым файлом и памятью;
|
||||
- манипуляции с `pc` (другими словами — управление программой) или системой (через регистры контроля и статуса).
|
||||
|
||||
Как было сказано ранее, память имеет 32-битную шину адреса и имеет побайтовую адресацию. Это значит, что каждый из 2<sup>32</sup> байт памяти имеет свой уникальный адрес, по которому к нему можно обратиться, чтобы считать из него или записать в него новую информацию. Однако, инструкции кодируются 32-битными числами, а один байт это всего 8 бит, значит одна инструкция занимает сразу 4 адреса в памяти. Подразумевается, что из такой памяти можно читать одновременно из нескольких последовательных адресов, то есть устройство управления процессора сообщает памяти начальный адрес требуемой ячейки, и количество ячеек (одну, две или четыре), которые нужно прочитать или записать.
|
||||
|
||||
Одна ячейка называется `байт` - 8 бит. Две последовательные 8-битные ячейки называются `полуслово` - 16 бит. Четыре последовательные 8-битные ячейки называются `словом` - 32 бита. Например, если процессор собирается выполнить инструкцию, которая занимает четыре байта по адресам `0x00000007 — 0x00000004`, то он обращается к памяти, сообщая, что "нужны 4 байта начиная с адреса 0x00000004", взамен процессор получает 32-битное число — инструкцию, которая была слеплена из байт, хранящихся в памяти по адресам: 4, 5, 6 и 7, для данного примера. К памяти также можно обратиться за полусловом или за байтом. Предполагается реализация выровненного доступа к памяти, то есть адреса слов и полуслов должны быть кратны 4 и 2, соответственно.
|
||||
Одна ячейка называется `байт` — 8 бит. Две последовательные 8-битные ячейки называются `полуслово` — 16 бит. Четыре последовательные 8-битные ячейки называются `словом` — 32 бита. Например, если процессор собирается выполнить инструкцию, которая занимает четыре байта по адресам `0x00000007 — 0x00000004`, то он обращается к памяти, сообщая, что "нужны 4 байта начиная с адреса 0x00000004", взамен процессор получает 32-битное число — инструкцию, которая была слеплена из байт, хранящихся в памяти по адресам: 4, 5, 6 и 7, для данного примера. К памяти также можно обратиться за полусловом или за байтом. Предполагается реализация выровненного доступа к памяти, то есть адреса слов и полуслов должны быть кратны 4 и 2, соответственно.
|
||||
|
||||
Аппаратное обеспечение компьютера «понимает» только нули и единицы, поэтому инструкции закодированы двоичными числами в формате, который называется машинным языком.
|
||||
|
||||
@@ -61,9 +63,9 @@ RISC-V является load&store архитектурой (все операц
|
||||
|
||||
## RV32I
|
||||
|
||||
В _таблице 1_ приводятся 40 команд стандартного набора целочисленных инструкций `RV32I`: мнемоники языка ассемблера, функции, описания, форматы кодирования и значения соответствующих полей при кодировании. В RISC-V предусмотрено несколько форматов кодирования инструкций (_рис. 3_), то есть договоренность какая информация в каком месте 32-битной инструкции хранится и как она представлена. У всех операций есть поле `opcode` (operation code - код операции), в котором закодировано "что нужно сделать". По полю `opcode` устройство управления понимает, что требуется сделать процессору и каким именно способом закодирована инструкция (**R**, **I**, **S**, **B**, **U** или **J**). В 32-битных инструкциях два младших бита всегда равны `11`.
|
||||
В _таблице 1_ приводятся 47 команд стандартного набора целочисленных инструкций `RV32I`: мнемоники языка ассемблера, функции, описания, форматы кодирования и значения соответствующих полей при кодировании. В RISC-V предусмотрено несколько форматов кодирования инструкций (_рис. 3_). Формат кодирования — это договоренность о том, какая информация в каком месте 32-битной инструкции хранится и как она представлена. У всех операций есть поле `opcode` (operation code - код операции), в котором закодировано "что нужно сделать". По полю `opcode` устройство управления понимает, что требуется сделать процессору и каким именно способом закодирована инструкция (**R**, **I**, **S**, **B**, **U** или **J**). В 32-битных инструкциях два младших бита всегда равны `11` (бывают 16-битные инструкции из набора сжатых инструкций).
|
||||
|
||||
Почти все инструкции имеют поле `Func3`, и некоторые — поле `Func7`. Их названия определены их разрядностью: 3 и 7 бит, соответственно. В этих полях, если они есть у инструкции, закодировано уточнение операции. Например, код операции 0010011 указывает на то, что будет выполняться некоторая операция на АЛУ между значением из регистрового файла и константой. Поле `Func3` уточняет операцию, для данного примера, если оно будет равно 0x0, то АЛУ выполнит операцию сложения между значением из регистра и константой из инструкции. Если `Func3` равно 0x6, то будет выполнена операция "логическое ИЛИ".
|
||||
Почти все инструкции имеют поле `func3`, и некоторые — поле `func7` (в зависимости от формата кодирования и некоторых исключений). Их названия определены разрядностью: 3 и 7 бит, соответственно. В этих полях, если они есть у инструкции, закодировано уточнение операции. Например, код операции `0010011` указывает на то, что будет выполняться некоторая операция на АЛУ между значением из регистрового файла и константой. Поле `func3` уточняет операцию, для данного примера, если оно будет равно 0x0, то АЛУ выполнит операцию сложения между значением из регистра и константой из инструкции. Если `func3` равно 0x6, то будет выполнена операция "логическое ИЛИ".
|
||||
|
||||

|
||||
|
||||
|
Reference in New Issue
Block a user