ЛР2. Добавление информации по битовым сдвигам

This commit is contained in:
Andrei Solodovnikov
2025-03-03 12:19:52 +03:00
parent 049b510df0
commit 1ef6fd9c5c
4 changed files with 46 additions and 8 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 203 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 197 KiB

View File

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

View File

@@ -167,16 +167,46 @@ endmodule
---
При реализации АЛУ, вам также потребуется использовать [операции сдвига](https://ru.wikipedia.org/wiki/Битовый_сдвиг), к которым относятся:
Реализуемое в данной лабораторной работе АЛУ использует операции битового сдвига. **Битовый сдвиг** — это операция, при которой все биты числа смещаются на заданное количество позиций. Сдвиг числа на _N_ бит эквивалентен _N_ сдвигам на 1 бит. В архитектуре RISC-V используются два типа сдвигов: **логический** и **арифметический**.
- `<<` — логический сдвиг влево
- `>>` — логический сдвиг вправо
- `>>>` — арифметический сдвиг вправо
При **логическом сдвиге** биты сдвигаются влево или вправо, а освободившиеся позиции заполняются нулями. При этом разряды, "вытолкнутые" за пределы разрядной сетки числа, пропадают. Например, если выполнить логический сдвиг двоичного числа _1<u>0011010</u>_ на один бит влево, получится _<u>0011010</u>0_. Обратите внимание, что старшая единица была вытолкнута за границу и исчезла.
![../../.pic/Labs/lab_02_alu/fig_03.drawio.svg](../../.pic/Labs/lab_02_alu/fig_03.drawio.svg)
_Рисунок 3. Иллюстрация преобразования двоичного числа при логическом сдвиге._
При арифметическом сдвиге заполнение освобождённых битов выполняется так, чтобы сохранился знак числа. В дополнительном коде знак определяется старшим битом, поэтому:
- при **арифметическом сдвиге вправо** освободившиеся позиции заполняются значением старшего бита исходного числа. Это позволяет сохранить знак. Например, арифметический сдвиг на два бита вправо числа _<u>100110</u>10_ даёт _11<u>100110</u>_;
- **арифметический сдвиг влево** эквивалентен логическому, так как заполнение младших битов нулями не влияет на знак числа.
![../../.pic/Labs/lab_02_alu/fig_04.drawio.svg](../../.pic/Labs/lab_02_alu/fig_04.drawio.svg)
_Рисунок 4. Иллюстрация преобразования двоичного числа при арифметическом сдвиге._
Битовый сдвиг имеет важный арифметический смысл — он соответствует умножению или делению числа на степень двойки:
- сдвиг влево на _N_ бит эквивалентен умножению на _2<sup>N</sup>_,
- сдвиг вправо на _N_ бит эквивалентен целочисленному делению на _2<sup>N</sup>_.
Этот приём знаком должен быть знаком вам при работе с десятичной системой: умножая число на 10, мы просто дописываем справа ноль. То же самое работает и для деления: если отрезать последний разряд у числа 42, получится 4, что соответствует целочисленному делению на 10. В двоичной системе добавление (стирание) нуля справа эквивалентно умножению (делению) на 2.
Арифметический сдвиг важен тем, что сохраняет это свойство для знаковых чисел, представленных в дополнительном коде. Логический сдвиг вправо изменяет и знак, и модуль числа, поэтому не может использоваться для деления знаковых чисел.
Операции умножения и деления — это очень «дорогие» операции как с точки зрения элементов схемы (если эти операции реализуются аппаратно), так и с точки зрения времени их вычисления. Поэтому выполнение сдвигов в качестве замены умножения применяется повсеместно. Например, написав в коде языка C++ выражение `var * 8`, после компиляции вы наверняка получите операцию сдвига влево на 3.
Ещё одно применение сдвигов: установка и очищение флагов управляющих регистров. Дело в том, что обычно процессоры не имеют доступа к отдельным битам многоразрядных регистров — их значения читаются записываются целиком. Вот как можно реализовать битовые операции с помощью сдвигов:
```C++
X = X | (1 << N); // Установка N-го бита
X = X & ~(1 << N); // Очистка N-го бита
Y = (X & (1 << N)) != 0; // Чтение N-го бита
```
### Особенности реализации сдвига
> [!IMPORTANT]
> Для **ВСЕХ** операций сдвига вы должны брать только 5 младших бит операнда B.
> Для **ВСЕХ** операций сдвига в данной лабораторной работе вы должны брать только 5 младших бит операнда B.
>
> Сами посмотрите: выполнять операцию сдвига более чем на 31 для 32-битных чисел не имеет смысла, число полностью заполнится нулями (единицами). Т.е. сдвигая на любое число, большее 31, вы получите один и тот же результат. Для того чтобы закодировать 31 требуется минимум 5 бит, отсюда и это требование. Оно обязательно, поскольку старшие биты в дальнейшем будут использоваться по другому назначению и, если вы упустите это, ваш будущий процессор станет работать неправильно.
@@ -270,13 +300,13 @@ assign Result = $signed(A) / 10;
В этом примере некоторому сигналу `Result` присваивают результат деления **знакового** числа `A` на `10`.
Так как используются не все возможные комбинации управляющего сигнала АЛУ, то **при описании через `case` не забывайте использовать `default`**. Если описать АЛУ как задумано, то получится что-то похожее на _рис. 4_. Но не обязательно, зависит от вашего описания.
Так как используются не все возможные комбинации управляющего сигнала АЛУ, то **при описании через `case` не забывайте использовать `default`**. Если описать АЛУ как задумано, то получится что-то похожее на _рис. 5_. Но не обязательно, зависит от вашего описания.
![../../.pic/Labs/lab_02_alu/fig_04.png](../../.pic/Labs/lab_02_alu/fig_04.png)
_Рисунок 3. Пример схемы, реализующей АЛУ._
_Рисунок 5. Пример схемы, реализующей АЛУ._
Обратите внимание на то, что сумматор на _рис. 3_ отличается от всех остальных блоков. Для того, чтобы спроектированный в ЛР1 32-разрядный сумматор был создан не зазря, а также для закрепления навыков по созданию экземпляров модулей внутри других модулей, вам предлагается использовать его при реализации АЛУ.
Обратите внимание на то, что сумматор на _рис. 5_ отличается от всех остальных блоков. Для того, чтобы спроектированный в ЛР№1 32-разрядный сумматор был создан не зазря, а также для закрепления навыков по созданию экземпляров модулей внутри других модулей, вам предлагается использовать его при реализации АЛУ.
Другие блоки распознаны Vivado на основе представленных в описании АЛУ арифметических или логических выражений и в процессе синтеза будут реализованы через те компоненты ПЛИС, которые позволят лучше всего удовлетворить временным и физическим ограничениям проекта (см. главу "Этапы реализации проекта в ПЛИС"). Сумматор же будет реализован так, как это описали мы, поскольку вместо использования абстрактной операции "+" в описании АЛУ было сказано разместить конкретный модуль, реализующий конкретную схему. Такая реализация сумматора не является эффективной ни в плане временных характеристик, ни в плане количества затраченных на реализацию ресурсов ПЛИС. Но как уже упоминалось в ЛР№1, цель этой реализации — воспроизвести простоту логики рассуждений о том, как спроектировать сумматор.