Две роли у одного символа
&, |, ^ и их инвертированные братья появляются в двух разных формах:
- Binary-форма (два операнда):
a & b- побитовая операция между векторами одинаковой ширины. - Унарная форма (один операнд):
&a- reduction по всем битамaв один бит.
Компилятор различает их по количеству операндов. Принятая терминология: "побитовые операторы" для binary-формы и "reduction-операторы" для унарной.
Побитовые: бит за раз
Полный набор:
| Оператор | Имя | Выходной бит на позиции N |
|---|---|---|
a & b | AND | a[N] AND b[N] |
a | b | OR | a[N] OR b[N] |
a ^ b | XOR | a[N] XOR b[N] |
a ~& b | NAND | NOT (a[N] AND b[N]) |
a ~| b | NOR | NOT (a[N] OR b[N]) |
a ~^ b | XNOR | NOT (a[N] XOR b[N]) |
~a | NOT | NOT a[N] |
Заметь, что ~&, ~|, ~^ пишутся: сначала тильда, потом оператор. Это один токен; между ними нет пробела.
Когда у двух операндов разная ширина, более узкий zero-extend'ится слева, чтобы соответствовать. Если хочешь sign-extension, используй signed-операнды явно через $signed().
Reduction: из многих бит в один
Унарные формы тех же операторов схлопывают вектор в один бит:
Что значит каждый:
&dataвозвращает1, если все битыdataравны 1, иначе0. Полезен для проверок "это все-единицы?".|dataвозвращает1, если хоть один битdataравен 1, иначе0. Полезен для "это non-zero?".^dataвозвращает parity - XOR всех бит.1, если нечётное число единиц,0, если чётное.~&data,~|data,~^data- инверсии вышеперечисленного.
В реальном коде их встретишь повсюду:
wire empty = ~|fifo_count; // empty, если count = 0
wire all_ones = &mask; // все биты установлены
wire parity_bit = ^data; // parity байта
wire any_request = |request_vector; // что-то запрошено?
Reduction-формы лаконичны и синтезируются в очевидные деревья вентилей: & - в многовходовый AND, | - в многовходовый OR, ^ - в XOR-дерево (это и есть генератор parity в железе).
Частые паттерны
Установка и сброс бит
wire [7:0] data;
wire [7:0] mask = 8'b0000_1000;
wire [7:0] set = data | mask; // выставляет бит 3 в 1
wire [7:0] cleared = data & ~mask; // сбрасывает бит 3 в 0
wire [7:0] toggled = data ^ mask; // переключает бит 3
wire [7:0] tested = data & mask; // ноль, если бит 3 был 0, иначе non-zero
Это те же идиомы битовых манипуляций, что и в C. Синтезируются в операции из одного вентиля.
Проверка, что значение равно all-ones
wire is_max = &counter; // 1, если каждый бит counter - 1
Reduction AND из одного вентиля - vs counter == 8'hFF (это тоже работает; синтезаторы обычно дают идентичное железо).
Генерация бита parity
Обнаружение любого активного сигнала
wire any_pending = |request_vector;
Если request_vector широкий (скажем, 64 запрашивающих), reduction OR схлопывает его в один сигнал, который можно скормить priority-encoder'у или arbiter'у.
Операторы сдвига
Раз уж разбираем bit-level операторы - сдвиги:
a << Nсдвигаетaвлево наNпозиций, справа заполняет нулями.a >> Nсдвигаетaвправо наNпозиций, слева заполняет нулями.a <<< Nарифметический левый сдвиг (то же, что<<для unsigned).a >>> Nарифметический правый сдвиг - заполняет знаковым битом, когдаaзнаковое.
Сдвиги на константную степень в железе бесплатны (просто перекоммутация). Сдвиги на runtime-переменную дают barrel shifter - больше, но всё ещё дешёво.
Что дальше
Ты увидел каждый оператор, возвращающий одно значение. Следующий документ - Concatenation and Replication - разбирает синтаксис {} и {N{...}}, который строит более широкие векторы из кусочков, что ты будешь использовать постоянно при стыковке модулей разной ширины.
Часто задаваемые вопросы
Что такое побитовые операторы в Verilog?
Побитовые операторы комбинируют два вектора одинаковой ширины поразрядно. a & b AND-ит бит 0 у a с битом 0 у b, бит 1 с битом 1 и так далее, давая вектор той же ширины. Полный набор: & (AND), | (OR), ^ (XOR), ~ (NOT) и инвертирующие формы ~&, ~|, ~^ (NAND, NOR, XNOR).
Что такое reduction-оператор в Verilog?
Reduction-оператор - это унарная форма побитового оператора, схлопывающая весь вектор в один бит. &data возвращает 1 только если каждый бит data равен 1. |data возвращает 1, если хоть один бит 1. ^data возвращает XOR всех бит - чётность. У reduction-форм нет левого операнда, только правый.
В чём разница между & и && в Verilog?
& - побитовое AND: спаривает биты поразрядно, ширина результата равна ширине операндов. && - логическое AND: трактует каждый операнд как boolean (zero vs non-zero) и возвращает 1-битный результат. 4'b1100 & 4'b0011 - это 4'b0000; 4'b1100 && 4'b0011 - это 1.
Как вычислить parity в Verilog?
Используй reduction XOR: parity = ^data. Он XOR-ит все биты data вместе. Для 8-битного вектора это data[7] ^ data[6] ^ ... ^ data[0]. Результат - 1, если нечётное количество единиц, 0, если чётное. Инверсия через ~^ даёт even-parity.
Что делает ~ в Verilog?
~ - побитовое NOT: инвертирует каждый бит операнда. ~4'b1100 - это 4'b0011. Не путай с ! (логический NOT), который схлопывает операнд в 1-битный boolean и инвертирует его. !4'b1100 - это 1'b0 (операнд non-zero, NOT-нув истину, получаем 0).