Синхронизация с правками публикуемого издания (#101)

* СП. Обновление предисловия

* СП. Обновление введения

* СП. Обновление лаб

* СП. Обновление доп материалов

* СП. Введение

* СП. Введение

* СП. ЛР№4, 15

* СП. Базовые конструкции Verilog

* Update Implementation steps.md

* СП. ЛР 4,5,7,8,14

* СП. ЛР№8

* Синхронизация правок

* СП. Финал

* Исправление ссылки на рисунок

* Обновление схемы

* Синхронизация правок

* Добавление белого фона .drawio-изображениям

* ЛР2. Исправление нумерации рисунка
This commit is contained in:
Andrei Solodovnikov
2025-02-12 17:53:52 +03:00
committed by GitHub
parent d251574bbc
commit 9739429d6e
168 changed files with 79781 additions and 961 deletions

View File

@@ -24,35 +24,27 @@
**Арифметико-логическое устройство** (**АЛУ**, Arithmetic Logic Unit ALU) это блок процессора, выполняющий арифметические и поразрядно логические операции. Разница между арифметическими и логическими операциями в отсутствии у последних бита переноса, так как логические операции происходят между 1-битными числами и дают 1-битный результат, а в случае АЛУ (в рамках данной лабораторной работы) одновременно между 32-мя 1-битными парами чисел. В логических операциях результаты значений отдельных битов друг с другом никак не связаны.
Также, кроме результата операций, АЛУ формирует флаги, которые показывают выполняется ли заданное условие. Например, выведет `1`, если один операнд меньше другого.
Также, кроме результата операций, может формировать специальные сигналы-флаги, которые показывают выполняется ли заданное условие. Например, выводить `1`, если один операнд меньше другого, или если в результате суммы произошло переполнение.
Обычно АЛУ представляет собой комбинационную схему (то есть не имеет элементов памяти), на входы которой поступают информационные (операнды) и управляющие (код операции) сигналы, в ответ на что на выходе появляется результат заданной операции. АЛУ бывает не комбинационной схемой, но это скорее исключение.
Обычно АЛУ представляет собой комбинационную схему (то есть не имеет элементов памяти), на входы которой поступают информационные (операнды) и управляющие (код операции) сигналы, в ответ на что на выходе появляется результат заданной операции. АЛУ может быть реализовано и в виде последовательностной логики, но это скорее исключение.
![../../.pic/Labs/lab_02_alu/fig_01.drawio.svg](../../.pic/Labs/lab_02_alu/fig_01.drawio.svg)
_Рисунок 1. Структурное обозначение элемента АЛУ[[1, стр.305]](https://reader.lanbook.com/book/97336?lms=57991a6f83ced8530d7f0759ce4b95b7)._
_Рисунок 1. Структурное обозначение элемента АЛУ[[1, стр.304]](https://reader.lanbook.com/book/241166?lms=1b8d65a957786d4b32b8201bd30e97f3)._
На рис. 1 изображен пример АЛУ, используемый в книге "Цифровая схемотехника и архитектура компьютера" Харрис и Харрис. На входы `A` и `B` поступают операнды с разрядностью _N_. На 3-битный вход `F` подается код операции. Например, если туда подать `000`, то на выходе `Y` появится результат операции огическое И_ между битами операндов `A` и `B`. Если на `F` подать `010`, то на выходе появится результат сложения. Это лишь пример, разрядность и коды могут отличаться в зависимости от количества выполняемых операций и архитектуры.
На рис. 1 изображено структурное обозначение АЛУ, используемое в книге "Цифровая схемотехника и архитектура компьютера RISC-V" Харрис и Харрис. На входы `A` и `B` поступают операнды с разрядностью _N_. На 2-битный вход `ALUControl` подается код операции. Например, если туда подать `10`, то на выходе `Y` появится результат операции огическое И_ между битами операндов `A` и `B`. Если же подать `00`, то на выходе появится результат сложения. Это лишь пример, разрядность и коды могут отличаться в зависимости от количества выполняемых операций и архитектуры.
Существует несколько подходов к реализации АЛУ, отличающиеся внутренней организацией. В лабораторных работах применяется повсеместно используемый подход мультиплексирования операций, то есть подключения нескольких операционных устройств (которые выполняют какие-то операции, например сложения, логического ИЛИ и т.п.) к мультиплексору, который будет передавать результат нужного операционного устройства на выходы АЛУ.
Рассмотрим данный подход на примере все того же АЛУ MIPS из книги Харрисов. На рис. 2, в левой его части, изображена внутренняя организация этого АЛУ, справа таблица соответствия кодов операциям. На выходе схемы (внизу) стоит 4-входовой мультиплексор, управляемый двумя из трех битов `F`. К его входам подключены _N_ логических И (побитовое И _N_-битных операндов), _N_ логических ИЛИ, _N_-битный сумматор и Zero Extend устройство, дополняющее слева нулями 1-битное число до N-битного.
Рассмотрим данный подход на примере всё того же АЛУ из книги Харрисов. На рис. 2, в левой его части, изображена внутренняя организация этого АЛУ, справа таблица соответствия кодов операциям. На выходе схемы (внизу) стоит 4-входовой мультиплексор, управляемый 2-битным сигналом `ALUControl`. Ко входам мультиплексора подключены выходы `N` логических И (побитовое И N-битных операндов), `N` логических ИЛИ, а выход N-битного сумматора подключён дважды, позволяя выбирать его результат для двух кодов операции.
![../../.pic/Labs/lab_02_alu/fig_02.drawio.svg](../../.pic/Labs/lab_02_alu/fig_02.drawio.svg)
_Рисунок 2. Структурная схема АЛУ MIPS[[1, стр.305]](https://reader.lanbook.com/book/97336?lms=57991a6f83ced8530d7f0759ce4b95b7)._
_Рисунок 2. Структурная схема АЛУ [[1, стр.305]](https://reader.lanbook.com/book/241166?lms=1b8d65a957786d4b32b8201bd30e97f3)._
К одному из входов этих операционных устройств подключен без изменений вход `A`, а ко второму подключен выход двухвходового мультиплексора, управляемого оставшимся битом _F_. То есть `F[2]` определяет, что будет вторым операндом: `B` или `~B`. Вдобавок `F[2]` подается на входной перенос сумматора, то есть, когда `F[2] == 1` на выходе сумматора появляется результат операции `A + ~B + 1`, что (с учетом [дополнительного кода](https://ru.wikipedia.org/wiki/Дополнительный_код)) эквивалентно `A  B`.
Вход `A` подключается напрямую ко входам этих операционных устройств. Вход `B` тоже — за исключением подключения к сумматору. На вход второго операнда сумматора подаётся результат мультиплексирования сигналов `B` и `~B`. Управляющим сигналом этого мультиплексора является младший бит управляющего сигнала `ALUControl`. Кроме того, этот же младший бит подаётся и на сумматор в качестве входного бита переноса. Это означает, что когда `ALUControl[0] = 0`, вычисляется сумма `A + B + 0`, а когда `ALUControl[0] = 1`, вычисляется сумма `A + ~B + 1`, что (с учётом [дополнительного кода](https://ru.wikipedia.org/wiki/Дополнительный_код) ) эквивалентно `A B`.
Посмотрим, что произойдет, если на вход `F` такого АЛУ подать `111`. Будет выполняться операция `SLT`(сокращение от `Set Less Then`) выдать `1`, если `A` меньше `B`, в противном случае — выдать `0`. Биты `F[1:0]` переключат мультиплексор на выход блока Zero Extend. На вход Zero Extend поступает старший бит выхода сумматора, этот бит отвечает за знак результата. Так как `F[2] == 1`, сумматор вычисляет `A + ~B + 1`, то есть `A  B`. Если `A < B`, то результат вычитания будет отрицательный, а старший бит `Y[N-1] == 1`. Если `A` не меньше `B`, то разность будет неотрицательна, а `Y[N-1] == 0`, как и требуется от этой операции.
Сравнение двух чисел несколько сложнее чем просто проверка старшего бита разности и зависит от того, сравниваем ли мы знаковые числа или беззнаковые. Если знаковые — то произошло ли переполнение. Для простоты схемы, принято, что схема реализует операцию SLT для знаковых пар чисел, разность которых не вызывает переполнения [[2, 307]](https://reader.lanbook.com/book/241166?lms=1b8d65a957786d4b32b8201bd30e97f3).
![../../.pic/Labs/lab_02_alu/fig_03.drawio.svg](../../.pic/Labs/lab_02_alu/fig_03.drawio.svg)
_Рисунок 3. Пример исполнения операции АЛУ._
Преимущество такой организации АЛУ в его простой модификации, настройке под нужные коды операций, читаемости кода и масштабируемости. Можно легко добавить или убрать требуемые операции. Подумайте, как бы вы обновили данную схему, если бы от вас потребовалось расширить её функционал операциями XOR (Исключающее ИЛИ) и (SGE операция "больше либо равно")?
Преимущество такой организации АЛУ в простоте его модификации, настройке под нужные коды операций, читаемости кода и масштабируемости. Можно легко добавить или убрать требуемые операции. Подумайте, как бы вы обновили данную схему, если бы от вас потребовалось расширить её функционал операциями XOR (Исключающее ИЛИ) и (SGE операция "больше либо равно")?
## Инструменты
@@ -64,7 +56,7 @@ _Рисунок 3. Пример исполнения операции АЛУ._
Очень удобным на практике оказывается использование параметров. Параметры добавляют модулю гибкости, позволяя убрать ["магические"](https://ru.wikipedia.org/wiki/Магическое_число_(программирование)#Плохая_практика_программирования) константы из описания модулей, подставляя вместо них выразительное символьное имя. Параметры отдаленно схожи с макросами `#define` в языке Си, однако стоит понимать, что это не одно и то же. Дефайны представляют собой специальные текстовые макросы, которые автоматически заменяются на этапе препроцессора (как если бы вы прошлись по всем файлам своего кода и вручную заменили бы макросы на их значения). Например, с помощью дефайнов можно писать целые куски кода, а не просто одно какое-то число. При этом у дефайнов глобальная область видимости (объявив их в одном месте, этот макрос будет доступен во всем последующем коде). Параметр в свою очередь может хранить только значение какого-то конкретного типа (т.е. в параметр нельзя поместить фрагмент кода) а область видимости параметра ограничена тем модулем, где он был объявлен.
Допустим, ваше устройство должно включить тостер, если на вход ему придет сигнал `32'haf3c5bd0`. Человек, не знакомый с устройством, при прочтении этого кода будет недоумевать, что это за число и почему используется именно оно. Однако, скрыв его за параметром `TOASTER_EN`, читающий поймет, что это код включения тостера. Кроме того, если некоторая константа должна использоваться в нескольких местах кода, то определив её через в виде параметра, можно будет менять её в одном месте, и она тут же поменяется везде.
Допустим, ваше устройство должно включить тостер, если на вход ему придет сигнал `32'haf3c5bd0`. Человек, не знакомый с устройством, при прочтении этого кода будет недоумевать, что это за число и почему используется именно оно. Однако, скрыв его за параметром `TOASTER_EN`, читающий поймет, что это код включения тостера. Кроме того, если некоторая константа должна использоваться в нескольких местах кода, то определив её в виде параметра, можно будет менять её в одном месте, и она тут же поменяется везде.
Параметры позволяют влиять на структуру модуля. К примеру, описывая сумматор, можно параметризовать его разрядность и использовать этот параметр при описании модуля (например, в качестве диапазона массива модулей). В этом случае вы сможете создавать множество сумматоров различных разрядностей, подставляя при создании нужное вам значение параметра.
@@ -83,10 +75,10 @@ module overflow #(parameter WIDTH = 32)(
output logic overflow
);
logic [WIDTH : 0] sum;
logic [WIDTH : 0] sum;
assign sum = a + b;
assign overflow = sum[WIDTH];
assign sum = a + b;
assign overflow = sum[WIDTH];
endmodule
```
@@ -101,8 +93,8 @@ module toaster(
output logic power
)
parameter TOASTER_EN = 32'haf3c5bd0;
assign power = command == TOASTER_EN;
parameter TOASTER_EN = 32'haf3c5bd0;
assign power = command == TOASTER_EN;
endmodule
```
@@ -181,19 +173,12 @@ endmodule
- `>>` — логический сдвиг вправо
- `>>>` — арифметический сдвиг вправо
<br><br><br><br><br>
---
### Особенности реализации сдвига
**Для ВСЕХ операций сдвига вы должны брать только 5 младших бит операнда B.**
Сами посмотрите: пятью битами можно описать 32 комбинации [0-31], а у операнда А будет использоваться ровно 32 бита. **Это обязательное требование**, поскольку старшие биты в дальнейшем будут использоваться по другому назначению и, если вы упустите это, ваш будущий процессор станет работать неправильно.
---
<br><br><br><br><br>
> [!IMPORTANT]
> Для **ВСЕХ** операций сдвига вы должны брать только 5 младших бит операнда B.
>
> Сами посмотрите: выполнять операцию сдвига более чем на 31 для 32-битных чисел не имеет смысла, число полностью заполнится нулями (единицами). Т.е. сдвигая на любое число, большее 31, вы получите один и тот же результат. Для того чтобы закодировать 31 требуется минимум 5 бит, отсюда и это требование. Оно обязательно, поскольку старшие биты в дальнейшем будут использоваться по другому назначению и, если вы упустите это, ваш будущий процессор станет работать неправильно.
## Задание
@@ -209,7 +194,7 @@ module alu (
output logic [31:0] result_o
);
import alu_opcodes_pkg::*; // импорт параметров, содержащих
import alu_opcodes_pkg::*; // импорт параметров, содержащих
// коды операций для АЛУ
endmodule
@@ -219,33 +204,33 @@ endmodule
Для удобства чтения, список инструкций разбит на две таблицы.
В первой таблице перечислены операции, вычисляющие значение сигнала `result_o`. **При любом коде операции `alu_op_i` не входящим в эту таблицу, сигнал `result_o` должен быть равен нулю**.
В первой таблице перечислены операции, вычисляющие значение сигнала `result_o`. **При любом коде операции, не входящим в эту таблицу, сигнал `result_o` должен быть равен нулю**.
|alu_op_i|={cmp, add/sub, alu_op_i}|result_o |Операция |
|--------|-------------------------|-------------------------------------------|-------------------------------------------------------|
| ADD | 0 0 000 |result_o = a_i + b_i | Сложение |
| SUB | 0 1 000 |result_o = a_i b_i | Вычитание |
| SLL | 0 0 001 |result_o = a_i << b_i | Сдвиг влево |
| SLTS | 0 0 010 |result_o = a_i < b_i (знаковое сравнение)| Знаковое сравнение |
| SLTU | 0 0 011 |result_o = a_i < b_i | Беззнаковое сравнение |
| XOR | 0 0 100 |result_o = a_i ^ b_i | Побитовое исключающее **ИЛИ** |
| SRL | 0 0 101 |result_o = a_i >> b_i | Сдвиг вправо |
| SRA | 0 1 101 |result_o = a_i >>> b_i | Арифметический сдвиг вправо (операнд `a_i` — знаковый)|
| OR | 0 0 110 |result_o = a_i \| b_i | Побитовое логическое **ИЛИ** |
| AND | 0 0 111 |result_o = a_i & b_i | Побитовое логическое **И** |
|Операция|={cmp, mod, opcode}|Выражение | Действие |
|--------|-------------------|-----------------------|-------------------------------------------------------|
| ADD | 0 0 000 |result_o = a_i + b_i | Сложение |
| SUB | 0 1 000 |result_o = a_i b_i | Вычитание |
| SLL | 0 0 001 |result_o = a_i << b_i | Сдвиг влево |
| SLTS | 0 0 010 |result_o = a_i < b_i | **Знаковое** сравнение |
| SLTU | 0 0 011 |result_o = a_i < b_i | **Беззнаковое** сравнение |
| XOR | 0 0 100 |result_o = a_i ^ b_i | Побитовое исключающее **ИЛИ** |
| SRL | 0 0 101 |result_o = a_i >> b_i | Сдвиг вправо |
| SRA | 0 1 101 |result_o = a_i >>> b_i | Арифметический сдвиг вправо (операнд `a_i` — знаковый)|
| OR | 0 0 110 |result_o = a_i \| b_i | Побитовое логическое **ИЛИ** |
| AND | 0 0 111 |result_o = a_i & b_i | Побитовое логическое **И** |
_Таблица 1. Список вычислительных операций._
Во второй таблице перечислены операции, вычисляющие значение сигнала `flag_o`. **При любом коде операции `alu_op_i` не входящим в эту таблицу, сигнал `flag_o` должен быть равен нулю**.
Во второй таблице перечислены операции, вычисляющие значение сигнала `flag_o`. **При любом коде операции, не входящим в эту таблицу, сигнал flag_o должен быть равен нулю**.
|alu_op_i|={cmp, add/sub, alu_op_i}| flag_o | Операция |
|--------|-------------------------|------------------------------------------|-----------------------------------|
| EQ | 1 1 000 | flag_o = (a_i == b_i) | Выставить флаг, если **равны** |
| NE | 1 1 001 | flag_o = (a_i != b_i) | Выставить флаг, если **не равны** |
| LTS | 1 1 100 | flag_o = a_i < b_i (знаковое сравнение)| Знаковое сравнение **<** |
| GES | 1 1 101 | flag_o = a_i b_i (знаковое сравнение)| Знаковое сравнение **≥** |
| LTU | 1 1 110 | flag_o = a_i < b_i | Беззнаковое сравнение **<** |
| GEU | 1 1 111 | flag_o = a_i b_i | Беззнаковое сравнение **≥** |
|Операция|={cmp, mod, opcode}| Выражение | Действие |
|--------|-------------------|----------------------|-----------------------------------|
| EQ | 1 1 000 | flag_o = (a_i == b_i)| Выставить флаг, если **равны** |
| NE | 1 1 001 | flag_o = (a_i != b_i)| Выставить флаг, если **не равны** |
| LTS | 1 1 100 | flag_o = a_i < b_i | **Знаковое** сравнение **<** |
| GES | 1 1 101 | flag_o = a_i b_i | **Знаковое** сравнение **≥** |
| LTU | 1 1 110 | flag_o = a_i < b_i | **Беззнаковое** сравнение **<** |
| GEU | 1 1 111 | flag_o = a_i b_i | **Беззнаковое** сравнение **≥** |
_Таблица 2. Список операций сравнения._
@@ -280,16 +265,16 @@ _Таблица 2. Список операций сравнения._
Конструкция `$signed` говорит САПР интерпретировать число, переданное в качестве операнда, как знаковое.
```Verilog
assign Result = $signed(A) >>> B[4:0];
assign Result = $signed(A) / 10;
```
В этом примере некоторому сигналу `Result` присваивают результат сдвига знакового числа `A` на значение количества бит получаемых из младших 5 бит сигнала `B`.
В этом примере некоторому сигналу `Result` присваивают результат деления **знакового** числа `A` на `10`.
Так как используются не все возможные комбинации управляющего сигнала АЛУ, то **при описании через `case` не забывайте использовать `default`**. Если описать АЛУ как задумано, то получится что-то похожее на _рис. 4_. Но не обязательно, зависит от вашего описания.
![../../.pic/Labs/lab_02_alu/fig_04.png](../../.pic/Labs/lab_02_alu/fig_04.png)
_Рисунок 4. Пример схемы, реализующей АЛУ._
_Рисунок 3. Пример схемы, реализующей АЛУ._
### Порядок выполнения задания