# Описание мультиплексора на языке SystemVerilog **Мультипле́ксор** — устройство, имеющее **несколько сигнальных входов**, **один или более управляющих входов** и **один выход**. Мультиплексор позволяет передавать сигнал **с одного из входов на выход**; при этом выбор желаемого входа осуществляется подачей соответствующей комбинации управляющих сигналов. Иными словами, мультиплексор — это переключатель (коммутатор), соединяющий выход с одним из множества входов. ![../.pic/Basic%20Verilog%20structures/multiplexor/fig_01.drawio.png](../.pic/Basic%20Verilog%20structures/multiplexors/fig_01.drawio.png) Для начала создадим простой двухвходовой мультиплексор. Предположим, на `Y` нам необходимо передать один из сигналов — `D0` или `D1` в зависимости от значения управляющего сигнала `S`: когда `S==0`, на `Y` подается сигнал `D0`, в противном случае — `D1`. ![../.pic/Basic%20Verilog%20structures/multiplexors/fig_02.drawio.png](../.pic/Basic%20Verilog%20structures/multiplexors/fig_02.drawio.png) На языке SystemVerilog это можно описать несколькими способами. Первый — с помощью **[тернарного условного оператора](https://ru.wikipedia.org/wiki/Тернарная_условная_операция)**: ## Тернарный условный оператор
О тернарном условном операторе Операторы бывают различной **[арности](https://ru.wikipedia.org/wiki/Арность)**(количества аргументов оператора[операндов]): - унарный (с одним операндом), пример: `-a`; - бинарный (с двумя операндами), пример: `a+b`; - тернарный (с тремя операндами), пример: `cond ? if_true : false`; - и др. Несмотря на то, что тернарным оператором может быть любой оператор, принимающий три операнда, обычно под ним подразумевается **тернарный условный оператор**, работающий следующим образом: ```text <условие> ? <значение_если_условие_истинно> : <значение_если_условие_ложно> ``` Первым операндом идет некоторое условие (любое выражение, которое может быть сведено к 1 или 0). Далее ставится знак вопроса (часть тернарного оператора, отделяющая выражение первого операнда от выражения второго операнда). Далее пишется выражение, которое будет результатом тернарного условного оператора в случае, если условие оказалось истинным. После чего ставится двоеточие (часть тернарного условного оператора, отделяющая выражение второго операнда от выражения третьего операнда). Затем пишется выражение, которое будет результатом тернарного условного оператора в случае, если условие оказалось ложным. Пример для языка C++: ```c++ a = b+c >= 5 ? b+c : b+d; ``` Сперва вычисляется первый операнд (выражение `b+c >= 5`). Если это выражение оказалось истинным (равно единице), то переменной `a` будет присвоено значение второго операнда (выражения `b+c`), в противном случае переменной `a` будет присвоено значение третьего операнда (выражения `b+d`).
```SystemVerilog logic Y; assign Y = S==1 ? D1 : D0; ``` Данное выражение говорит нам, что если `S==1`, то `Y` присваивается значение `D1`, в противном случае — значение `D0`. ![../.pic/Basic%20Verilog%20structures/multiplexors/fig_03.drawio.png](../.pic/Basic%20Verilog%20structures/multiplexors/fig_03.drawio.png) Также мультиплексор можно описать через конструкцию `if-else` в блоке `always`. ## Блок if-else > Далее будет ключевой параграф сложного для понимания текста, очень важно запомнить что там написано и разобрать приведенные листинги.

--- ### Блок always Блок `always` — это специальный блок, который позволяет описывать комбинационные и последовательностные схемы, используя более сложные конструкции, такие как `if-else`, `case`. На самом деле, в языке SystemVeriog помимо общего блока `always`, которым можно описать любой вид логики, существует множество специализированных блоков, предназначенных для описания отдельно комбинационной, синхронной и последовательностной асинхронной логики соответственно: - always_comb - always_ff - always_latch Мультиплексор можно описать в любом из этих блоков, разница будет лишь в том, к чему именно будет подключен выход мультиплексора: к проводу, регистру, или защелке. При присваивании внутри блоков `always` используйте специальный оператор **неблокирующего присваивания** `<=`. Бывает еще оператор **блокирующего присваивания** `=`, объяснение различий в этих операторах требует отдельного документа, поэтому на текущий момент, во избежание проблем в будущем просто запомните: **внутри любого блока always необходимо использовать только оператор неблокирующего присваивания <=**. ```SystemVerilog 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. Неправильно: ```SystemVerilog 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**. --- > Остановитесь на выделенном выше фрагменте документа, пока полностью не разберете его. Без освоения всех описанных выше особенностей языка SystemVerilog вы столкнетесь в будущем с множеством ошибок.

## case-блок Мультиплексор также можно описать с использованием **конструкции case**. Блок `case` лучше подходит для описания мультиплексора, когда у того более двух входов (ведь в случае конструкции `if-else` пришлось бы делать вложенное ветвление). Конструкция `case` представляет собой инструмент множественного ветвления, который сравнивает значение заданного выражения с множеством вариантов, и, в случае первого совпадения, использует соответствующую ветвь. На случай, если ни один из вариантов не совпадет с заданным выражением, конструкция `case` поддерживает вариант `default`. Данная конструкция визуально похожа на оператор `switch-case` в Си, однако вы должны понимать, что используется она не для написания программы, а описания аппаратуры, в частности **мультиплексоров**/**демультиплексоров** и **дешифраторов**. **Конструкция `case`, наряду с `if-else`, может быть описана только в блоке `always`**. Реализация двухвходового мультиплексора с помощью `case` может выглядеть так: ```SystemVerilog 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) ``` Рассмотрим вариант посложнее и опишем следующую схему: ![../.pic/Basic%20Verilog%20structures/multiplexors/fig_04.drawio.png](../.pic/Basic%20Verilog%20structures/multiplexors/fig_04.drawio.png) Здесь уже используется мультиплексор 4в1. Управляющий сигнал `S` в данном случае двухбитный. В блоке `case` мы перечисляем всевозможные варианты значений `S` и описываем выход мультиплексора. ```SystemVerilog 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'b00: Y <= A; 3'b01: Y <= C | B; // в блоке case можно мультиплексировать // не только провода, но и логические выражения 3'b10: Y <= (C|B) & D; /* Обратите внимание, что разрядность сигнала S — 3 бита. Это означает, что есть 8 комбинаций его разрядов. Выше было описано только 3 комбинации из 8. Если для всех остальных комбинаций на выходе мультиплексора должно быть какое-то одно значение "по умолчанию", используется специальная комбинация "default": */ default: Y <= D; endcase end endmodule ``` ### Защелка Очень важно при описании блока `case` описывать оставшиеся комбинации управляющего сигнала с помощью `default` — в противном случае в вашей схеме может появиться [защелка](https://www.build-electronic-circuits.com/d-latch/) (даже несмотря на то, что для описания защелок в SytemVerilog есть отдельный блок `always`: `always_latch`). Защелка позволяет хранить данные, причем данные в нее записываются не по тактовому синхроимпульсу, а на протяжении относительно длинного промежутка времени, когда управляющий сигнал "открывает" защелку. Из-за этого она не является ни комбинационной, ни синхронной схемой. Обычно появление защелки в цифровой схеме говорит об ошибке разработки: в случае, если планировалась комбинационная логика, добавление защелки приведет к непредвиденному удержанию предыдущих значений (поскольку защелка сохраняет предыдущее значение до прихода очередной комбинации управляющего сигнала, описанной в блоке `case`). В случае синхронной логики, будет непредсказуемое поведение данных, т.к. информация в защелку будет записываться не синхронно, как это ожидается, а на протяжении длительного промежутка времени, пока защелка "открыта". Пример: ```SystemVerilog module unexpected_d_latch_ex( input logic [1:0] S, input logic D0, input logic D1, output logic R ); always_comb begin case(S) 2'b00: R <= D0; 2'b01: R <= D1; // Поскольку сигнал S двухразрядный, осталось еще две комбинации: // S == 2'b10 // S == 2'b11 endcase end endmodule ``` ![../.pic/Basic%20Verilog%20structures/multiplexors/fig_06.png](../.pic/Basic%20Verilog%20structures/multiplexors/fig_06.png) На данной схеме различные её части обозначены следующим образом: 1. Мультиплексор, который мы хотели описать 2. Защелка 3. Мультиплексор, который был добавлен чтобы генерировать сигнал, "открывающий" защелку 4. Константная единица (питание) 5. Константный ноль (земля). В случае, если `S == 0` или `S == 1`, на выход мультиплексора 3 будет подана единица, которая переведет защелку в "прозрачный" режим (данные с выхода мультиплексора 1 будут проходить сквозь защелку). В случае, если `S > 1`, на выход мультиплексора 3 будет подан ноль, который переведет защелку в "непрозрачный" режим (данные с выхода мультиплексора 1 не будут идти сквозь защелку, вместо этого на выходе защелки останутся последние данные, которые шли через нее, пока она была "открыта"). ![../.pic/Basic%20Verilog%20structures/multiplexors/fig_07.png](../.pic/Basic%20Verilog%20structures/multiplexors/fig_07.png) Таким образом, во избежание появление защелки, необходимо описывать все возможные комбинации в блоке `case` (при необходимости покрывая множество оставшихся комбинаций с помощью `default`). ## Итоги 1. Мультиплексор — это **комбинационный** блок, подающий на выход один из нескольких входных сигналов. 2. Мультиплексор можно описать множеством способов, среди них: 1. Использование [тернарного условного оператора](#тернарный-условный-оператор) через непрерывное присваивание; 2. Использование конструкции [`if-else`](#блок-if-else) внутри блока `always`; 3. Использование конструкции [`case`](#case-блок) внутри блока always (при этом стоит помнить, что через `case` можно описывать не только мультиплексоры). 1. При использовании `case`, во избежание появления [защелок](#защелка), необходимо убедиться в том, что описаны все возможные комбинации управляющего сигнала (при необходимости, множество оставшихся комбинаций можно покрыть с помощью комбинации `default`) 3. Конструкции `if-else` и `case` в рамках данных лабораторных работ можно описывать только внутри блока [`always`](#блок-always). При работе с этим блоком необходимо помнить следующие особенности: 1. Существует несколько типов блока `always`: `always_comb`, `always_ff`, `always_latch`, определяющих то, к чему будет подключена описанная в этом блоке логика: проводу, регистру или защелке соответственно. 2. Внутри блока always следует использовать оператор неблокирующего присваивания `<=`. 3. Присваивание для любого сигнала возможно только внутри **одного** блока always. Два разных сигнала могут присваиваться как в одном блоке always, так каждый в отдельном, но операция присваивания одному и тому же сигналу в двух разных блоках always — нет. --- ## Проверь себя Как, по-вашему, описать на языке SystemVerilog схему, приведённую ниже? ![../.pic/Basic%20Verilog%20structures/multiplexors/fig_04.drawio.png](../.pic/Basic%20Verilog%20structures/multiplexors/fig_04.drawio.png)