Menu

Побитовые и reduction-операторы в Verilog

Bit-level операторы Verilog - побитовые AND/OR/XOR, инвертирующие формы и reduction-операторы, которые схлопывают весь вектор в один бит.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Две роли у одного символа

&, |, ^ и их инвертированные братья появляются в двух разных формах:

  • Binary-форма (два операнда): a & b - побитовая операция между векторами одинаковой ширины.
  • Унарная форма (один операнд): &a - reduction по всем битам a в один бит.

Компилятор различает их по количеству операндов. Принятая терминология: "побитовые операторы" для binary-формы и "reduction-операторы" для унарной.

Побитовые: бит за раз

Полный набор:

ОператорИмяВыходной бит на позиции N
a & bANDa[N] AND b[N]
a | bORa[N] OR b[N]
a ^ bXORa[N] XOR b[N]
a ~& bNANDNOT (a[N] AND b[N])
a ~| bNORNOT (a[N] OR b[N])
a ~^ bXNORNOT (a[N] XOR b[N])
~aNOTNOT 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).

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ