Мультиплексоры, таблицы истинности и базовые вентили
Собираем классические комбинационные блоки — вентили и мультиплексор — и связываем их с таблицами истинности.
Мультиплексор (MUX) — комбинационный «переключатель»: по управляющему сигналу select он пропускает на выход один из нескольких входов.
Комбинационная логика строится из базовых вентилей — И, ИЛИ, НЕ, XOR. Любую булеву функцию можно задать таблицей истинности (что на выходе для каждой комбинации входов) и реализовать вентилями. В этом уроке соберём важнейший комбинационный блок — мультиплексор — и увидим, что одну схему можно описать несколькими способами.
Базовые вентили и их таблицы
Напомним таблицы истинности базовых вентилей (фундамент из курса «Логика и булева алгебра»):
a b | AND OR XOR | a | NOT ----+--------------+ --+---- 0 0 | 0 0 0 | 0 | 1 0 1 | 0 1 1 | 1 | 0 1 0 | 0 1 1 | 1 1 | 1 1 0 |
В Verilog они выражаются операторами напрямую: assign y = a & b; (И), a | b (ИЛИ), a ^ b (XOR), ~a (НЕ). Синтезатор сам подберёт, как уложить это в LUT.
Мультиплексор: переключатель сигналов
Мультиплексор 2-в-1 пропускает на выход y либо a, либо b — в зависимости от управляющего бита sel. Его таблица проста: при sel=0 выход равен a, при sel=1 — равен b. В Verilog его можно описать тремя эквивалентными способами:
// Способ 1: условный (тернарный) оператор
assign y = sel ? b : a;
// Способ 2: булева формула из вентилей
assign y = (~sel & a) | (sel & b);
// Способ 3: процедурный блок с case (комбинационный always)
always @(*) begin
case (sel)
1'b0: y = a;
1'b1: y = b;
endcase
endВсе три после синтеза дадут одинаковое железо — мультиплексор. Выбор способа — вопрос читаемости. Тернарный оператор компактен для 2-в-1; case удобнее для мультиплексоров на много входов.
Как работает под капотом
Промоделируем мультиплексор 4-в-1, где 2-битный sel выбирает один из четырёх входов. Это раскрывает идею «адресации» входов управляющим кодом:
# MUX 4-в-1: 2-битный sel выбирает один из входов d0..d3
def mux4(d0, d1, d2, d3, sel):
inputs = [d0, d1, d2, d3]
return inputs[sel] # sel как индекс выбирает нужный вход
d = [0, 1, 0, 1] # d0=0, d1=1, d2=0, d3=1
print("sel | выбранный вход -> y")
print("----+------------------")
for sel in range(4):
y = mux4(d[0], d[1], d[2], d[3], sel)
print(f" {sel:02b} | d{sel}={d[sel]} -> {y}")Вывод:
sel | выбранный вход -> y ----+------------------ 00 | d0=0 -> 0 01 | d1=1 -> 1 10 | d2=0 -> 0 11 | d3=1 -> 1
Управляющий код sel работает как адрес: его значение прямо указывает, какой вход попадёт на выход. В железе мультиплексор 4-в-1 — это дерево из трёх MUX 2-в-1, и именно из таких переключателей, кстати, частично построена матрица соединений FPGA.
Частые ошибки
- Неполный
caseбезdefault. Если в комбинационномalwaysне покрыты все вариантыsel, синтезатор может вывести нежелательную защёлку (latch). Добавляйтеdefaultилиelse. - Перепутать ветви тернарного оператора. В
sel ? b : aпри истинномselвыбираетсяb(первая ветвь), при ложном —a. Легко спутать порядок. - Думать, что разные способы дают разное железо. Тернарник, формула и
caseдля одной логики синтезируются в одинаковую схему.
Итог
- Базовые вентили выражаются операторами Verilog напрямую.
- Мультиплексор по
selпропускает один из входов; его пишут тернарником, формулой илиcase. - Управляющий код мультиплексора работает как адрес входа.
- Неполный
caseбезdefaultможет породить нежелательную защёлку.