mirror of
https://github.com/MPSU/APS.git
synced 2025-09-15 17:20:10 +00:00
* СП. Обновление предисловия * СП. Обновление введения * СП. Обновление лаб * СП. Обновление доп материалов * СП. Введение * СП. Введение * СП. ЛР№4, 15 * СП. Базовые конструкции Verilog * Update Implementation steps.md * СП. ЛР 4,5,7,8,14 * СП. ЛР№8 * Синхронизация правок * СП. Финал * Исправление ссылки на рисунок * Обновление схемы * Синхронизация правок * Добавление белого фона .drawio-изображениям * ЛР2. Исправление нумерации рисунка
232 lines
22 KiB
Markdown
232 lines
22 KiB
Markdown
# Описание мультиплексоров в SystemVerilog
|
||
|
||
**Мультипле́ксор** — устройство, имеющее **несколько сигнальных входов**, **один или более управляющих входов** и **один выход**. Мультиплексор позволяет передавать сигнал **с одного из входов на выход**; при этом выбор желаемого входа осуществляется подачей соответствующей комбинации управляющих сигналов[[1]](https://ru.wikipedia.org/wiki/Мультиплексор_(электроника)).
|
||
|
||
Иными словами, мультиплексор — это переключатель (коммутатор), соединяющий выход с одним из множества входов.
|
||
|
||

|
||
|
||
Для начала создадим простой двухвходовой мультиплексор. Предположим, на `Y` нам необходимо передать один из сигналов — `D0` или `D1` в зависимости от значения управляющего сигнала `S`: когда `S==0`, на `Y` подается сигнал `D0`, в противном случае — `D1`.
|
||
|
||

|
||
|
||
На языке SystemVerilog это можно описать несколькими способами. Первый — с помощью **[тернарного условного оператора](https://ru.wikipedia.org/wiki/Тернарная_условная_операция)**:
|
||
|
||
## Тернарный условный оператор
|
||
|
||
<details>
|
||
|
||
<summary>О тернарном условном операторе</summary>
|
||
|
||
Операторы бывают различной **[арности](https://ru.wikipedia.org/wiki/Арность)**(количества аргументов оператора[операндов]):
|
||
|
||
- унарный (с одним операндом), пример: `-a`;
|
||
- бинарный (с двумя операндами), пример: `a+b`;
|
||
- тернарный (с тремя операндами), пример: `cond ? if_true : if_false`;
|
||
- и др.
|
||
|
||
Несмотря на то, что тернарным оператором может быть любой оператор, принимающий три операнда, обычно под ним подразумевается **тернарный условный оператор**, работающий следующим образом:
|
||
|
||
```text
|
||
<условие> ? <значение_если_условие_истинно> : <значение_если_условие_ложно>
|
||
```
|
||
|
||
Первым операндом идет некоторое условие (любое выражение, которое может быть сведено к 1 или 0). Далее ставится знак вопроса (часть тернарного оператора, отделяющая выражение первого операнда от выражения второго операнда). Далее пишется выражение, которое будет результатом тернарного условного оператора в случае, если условие оказалось истинным. После чего ставится двоеточие (часть тернарного условного оператора, отделяющая выражение второго операнда от выражения третьего операнда). Затем пишется выражение, которое будет результатом тернарного условного оператора в случае, если условие оказалось ложным.
|
||
|
||
Пример для языка C++:
|
||
|
||
```c++
|
||
a = b+c >= 5 ? b+c : b+d;
|
||
```
|
||
|
||
Сперва вычисляется первый операнд (выражение `b+c >= 5`). Если это выражение оказалось истинным (равно единице), то переменной `a` будет присвоено значение второго операнда (выражения `b+c`), в противном случае переменной `a` будет присвоено значение третьего операнда (выражения `b+d`).
|
||
</details>
|
||
|
||
```Verilog
|
||
logic Y;
|
||
assign Y = S==1 ? D1 : D0;
|
||
```
|
||
|
||
Данное выражение говорит нам, что если `S==1`, то `Y` присваивается значение `D1`, в противном случае — значение `D0`.
|
||
|
||

|
||
|
||
Также мультиплексор можно описать через конструкцию `if-else` в блоке `always`.
|
||
|
||
> Далее будет ключевой параграф сложного для понимания текста, очень важно запомнить, что там написано и разобрать приведенные листинги.
|
||
|
||
<br><br>
|
||
|
||
---
|
||
|
||
## Блок always
|
||
|
||
Блок `always` — это специальный блок, который позволяет описывать комбинационные и последовательностные схемы (см. документ "[Последовательностная логика](../Introduction/Sequential%20logic.md)"), используя более сложные конструкции, такие как `if-else`, `case`. На самом деле, в языке SystemVerilog помимо общего блока `always`, которым можно описать любой вид логики, существует множество специализированных блоков, предназначенных для описания отдельно комбинационной, синхронной и последовательностной асинхронной логики соответственно:
|
||
|
||
- always_comb
|
||
- always_ff
|
||
- always_latch
|
||
|
||
Мультиплексор можно описать в любом из этих блоков, разница будет лишь в том, к чему именно будет подключен выход мультиплексора: к проводу, регистру, или защелке.
|
||
|
||
В зависимости от вида `always`-блока используется один из двух видов присваиваний: **блокирующее присваивание** (`=`) и **неблокирующего присваивания** (`<=`). Подробно о различиях между присваиваниями рассказано в [этом документе](Assignments.md). До его прочтения запомните:
|
||
|
||
- внутри блока `always_ff` и `always_latch` необходимо использовать оператор неблокирующего присваивания (`<=`);
|
||
- внутри блока `always_comb` необходимо использовать оператор блокирующего присваивания (`=`).
|
||
|
||
---
|
||
|
||
> Остановитесь на выделенном выше фрагменте документа, пока полностью не разберете его. Без освоения всех описанных выше особенностей языка SystemVerilog вы столкнетесь в будущем с множеством ошибок.
|
||
|
||
<br><br>
|
||
|
||
## Блок if-else
|
||
|
||
Реализация мультиплексора через блок `if-else` похожа на реализацию мультиплексора через тернарный оператор. Если в тернарном операторе управляющий сигнал указывался в качестве первого операнда, отделяемого оператором `?`, то в данном блоке управляющий сигнал указывается в скобках после ключевого слова `if`.
|
||
|
||
Далее описывается присваивание сигнала, который должен идти на выход при управляющем сигнале равном единице (значение до оператора `:` в тернарном операторе).
|
||
|
||
После, в блоке `else` описывается присваивание сигнала, который должен идти на выход при управляющем сигнале равном нулю (значение после оператора `:` в тернарном операторе).
|
||
|
||
```Verilog
|
||
logic Y;
|
||
always_comb begin // 1) Используется always_comb, т.к. мы хотим подключить
|
||
// выход мультиплексора к проводу
|
||
if(S) begin // 2) if-else может находиться только внутри блока always.
|
||
Y = D1; // 3) Используется оператор блокирующего присваивания.
|
||
end else begin
|
||
Y = D0;
|
||
end
|
||
end
|
||
```
|
||
|
||
Кроме того, важно запомнить, что присваивание сигналу допускается **только в одном** блоке always.
|
||
|
||
Неправильно:
|
||
|
||
```Verilog
|
||
logic Y;
|
||
always_comb begin
|
||
if(S==1) begin
|
||
Y = D1;
|
||
end
|
||
end
|
||
|
||
always_comb begin
|
||
if(S==0) begin // Нельзя выполнять операцию присваивания
|
||
Y = D0; // для одного сигнала (Y) в нескольких
|
||
end // блоках always!
|
||
end
|
||
```
|
||
|
||
Если нарушить это правило, то в будущем (возможно не сразу, но в любом случае — обязательно), возникнет ошибка, которая так или иначе будет связана с **multiple drivers**.
|
||
|
||
Будьте **очень внимательны** при использовании данного блока. Он обманчиво похож на условный блок в языках программирования, из-за чего возникает желание пользоваться им так же, как можно пользоваться условными блоками в языках программирования. Это не так. Обратите внимание на то, что данный блок выше упоминается исключительно как блок `if-else`. При реализации мультиплексора, у любого блока `if` должен быть соответствующий блок `else`, как у тернарного оператора должно быть два выходных операнда. Если не указать блок `else` при описании мультиплексора, у него будет только один вход, и в итоге на выходе мультиплексора будет сгенерирована **защелка**. Подробнее о защелках описано [здесь](Latches.md).
|
||
|
||
Существуют ситуации, когда блок `if` может быть использован без блока `else` (например, при описании дешифраторов или сигналов разрешения записи). Однако при описании мультиплексоров таких ситуаций не бывает.
|
||
|
||
## case-блок
|
||
|
||
Мультиплексор также можно описать с использованием **конструкции case**. Блок `case` лучше подходит для описания мультиплексора, когда у того более двух входов (ведь в случае конструкции `if-else` пришлось бы делать вложенное ветвление).
|
||
|
||
Конструкция `case` представляет собой инструмент множественного ветвления, который сравнивает значение заданного выражения с множеством вариантов, и, в случае первого совпадения, использует соответствующую ветвь. Как и блок `if-else`, блок `case` должен описывать все возможные комбинации управляющего сигнала.На случай, если ни один из вариантов не совпадет с заданным выражением, конструкция `case` поддерживает вариант `default`. Данная конструкция визуально похожа на оператор `switch-case` в Си, однако вы должны понимать, что используется она не для написания программы, а описания аппаратуры, в частности **мультиплексоров**/**демультиплексоров** и **дешифраторов**.
|
||
|
||
**Конструкция `case`, наряду с `if-else`, может быть описана только в блоке `always`**.
|
||
|
||
Реализация двухвходового мультиплексора с помощью `case` может выглядеть так:
|
||
|
||
```Verilog
|
||
logic Y;
|
||
always_comb begin
|
||
case(S) // Описываем блок case, где значение сигнала S
|
||
// будет сравниваться с различными возможными его значениями
|
||
1'b0: Y = D0; // Если S==0, то Y = D0
|
||
1'b1: Y = D1;
|
||
endcase // Каждый case должен заканчиваться endcase
|
||
end // (так же как каждый begin должен оканчиваться end)
|
||
```
|
||
|
||
Рассмотрим вариант посложнее и опишем следующую схему:
|
||
|
||

|
||
|
||
Здесь уже используется мультиплексор 8в1. Управляющий сигнал `S` в данном случае 3-битный. В блоке `case` мы перечисляем все возможные варианты значений `S` и описываем выход мультиплексора.
|
||
|
||
```Verilog
|
||
module case_mux_ex(
|
||
input logic A,
|
||
input logic B,
|
||
input logic C,
|
||
input logic D,
|
||
input logic [2:0] S,
|
||
|
||
output logic Y
|
||
|
||
);
|
||
always_comb begin
|
||
case(S)
|
||
3'b000: Y = A;
|
||
3'b001: Y = C | B; // в блоке case можно мультиплексировать
|
||
// не только провода, но и логические выражения
|
||
3'b010: Y = (C|B) & D;
|
||
/*
|
||
Обратите внимание, что разрядность сигнала S — 3 бита.
|
||
Это означает, что есть 8 комбинаций его разрядов.
|
||
Выше было описано только 3 комбинации из 8.
|
||
Если для всех остальных комбинаций на выходе мультиплексора должно
|
||
быть какое-то одно значение "по умолчанию", используется специальная
|
||
комбинация "default":
|
||
*/
|
||
default: Y = D;
|
||
endcase
|
||
end
|
||
endmodule
|
||
```
|
||
|
||
## Оператор адресации
|
||
|
||
Представим, что нам необходимо мультиплексировать реально много сигналов. Например, отдельные биты 1024-разрядной шины. Описывать `case` на 1024 варианта будет сущим безумием. В этом случае, можно будет воспользоваться оператором '[]', который наверняка известен вам как "оператор адресации по массиву" в Си-подобных языках. Работает он интуитивно понятно:
|
||
|
||
- перед оператором указывается имя массива или вектора (читай как "памяти или шины"), по которым будет идти индексация;
|
||
- за именем в квадратных скобках указывается индекс (не важно, в виде константы, или выражения, использующего другие сигналы).
|
||
|
||
В контексте примера по мультиплексированию 1024 бит использование оператора может быть выполнено следующим образом:
|
||
|
||
```Verilog
|
||
logic [1023:0] bus1024;
|
||
logic [ 9:0] select;
|
||
|
||
logic one_bit_result;
|
||
|
||
assign one_bit_result = bus1024[select];
|
||
```
|
||
|
||
Реализация мультиплексоров через оператор '[]' будет активно применяться вами при реализации различных памятей.
|
||
|
||
## Итоги главы
|
||
|
||
1. Мультиплексор — это **комбинационный** блок, подающий на выход один из нескольких входных сигналов.
|
||
2. Мультиплексор можно описать множеством способов, среди них:
|
||
1. использование [тернарного условного оператора](#тернарный-условный-оператор);
|
||
2. использование конструкции [`if-else`](#блок-if-else) внутри блока [`always`](#блок-always);
|
||
3. использование конструкции [`case`](#case-блок) внутри блока [`always`](#блок-always);
|
||
4. использование [оператора '[]'](#оператор-адресации).
|
||
3. Во избежание появления [защелок](Latches.md) при описании мультиплексора, необходимо убедиться что у блоков `if` есть соответствующие им блоки `else`, а в блоке `case` описаны все комбинации управляющего сигнала (при необходимости, множество оставшихся комбинаций можно покрыть с помощью комбинации `default`). Появление непреднамеренной защелки в дизайне ведет к ухудшению временных характеристик, избыточному использованию ресурсов, а также непредсказуемому поведению схемы из-за возможного удержания сигнала.
|
||
4. Важно отметить, что блоки `if-else` и `case` могут использоваться не только для описания мультиплексоров.
|
||
5. Конструкции `if-else` и `case` в рамках данных лабораторных работ можно описывать только внутри блока [`always`](#блок-always). При работе с этим блоком необходимо помнить следующие особенности:
|
||
1. Существует несколько типов блока `always`: `always_comb`, `always_ff`, `always_latch`, определяющих то, к чему будет подключена описанная в этом блоке логика: проводу, регистру или защелке соответственно. В данных лабораторных работах вам нужно будет пользоваться блоками `always_ff` и `always_comb`, причем:
|
||
1. внутри блока `always_ff` необходимо использовать оператор неблокирующего присваивания (`<=`);
|
||
2. внутри блока `always_comb` необходимо использовать оператор блокирующего присваивания (`=`).
|
||
2. Присваивание для любого сигнала возможно только внутри **одного** блока always. Два разных сигнала могут присваиваться как в одном блоке always, так и каждый в отдельном, но операция присваивания одному и тому же сигналу в двух разных блоках always — нет.
|
||
|
||
---
|
||
|
||
## Проверь себя
|
||
|
||
Как описать на языке SystemVerilog следующую схему?
|
||
|
||

|
||
|
||
## Список источников
|
||
|
||
1. [Мультиплексор (электроника)](https://ru.wikipedia.org/wiki/Мультиплексор_(электроника)). |