Что значит "оператор" в Verilog
Операторы берут выражения, несущие сигналы, и производят новые выражения, несущие сигналы. a + b - это железо: синтезатор превращает его в adder. a == b - железо: строит компаратор. Ничто из этого не "код, который выполняется"; всё это "схемы, которые описывает оператор".
Полное меню операторов небольшое, его можно запомнить. Этот документ покрывает арифметику, сравнение, логические и conditional. Bitwise and Reduction разбирает bit-level операторы, у которых нет прямых софтовых аналогов.
Арифметика: + - * / %
Три вещи, которые надо знать:
- Деление целочисленное.
7/2- это3, не3.5. У Verilog нет float-типа (ну, естьreal, но он simulation-only). - Деление и modulo дороги в железе.
/4- нормально (синтезатор превращает в правый сдвиг), а/nдля runtime-переменнойnстроит многотактовый divider, которого почти никогда не хочется. - Переполнение оборачивается. Сложение двух N-битных unsigned-значений, превышающих N бит, обрезается до N бит. Хочешь поймать carry - объяви результат на бит шире:
wire [7:0] a, b;
wire [8:0] sum = a + b; // 9-битная sum ловит carry out
Сравнение: == != < > <= >=
Все шесть операторов сравнения возвращают однобитный результат: 1, если истинно, 0, если ложно, x, если у любого операнда есть бит x или z. Чтобы обработать случай x/z, тянись к === и !== (case-equality операторы), разбираем в X and Z Values.
<= тут - оператор сравнения (меньше-или-равно). Те же символы также означают non-blocking присваивание в процедурном блоке (q <= d). Контекст разводит: внутри выражения - сравнение; на левой стороне присваивания - оператор присваивания. Совпадение сбивает с толку, но для парсера однозначно.
Логические: && || !
Логические операторы трактуют операнды как boolean (всё non-zero - true, zero - false) и дают однобитный результат:
Критическое различие, которое стоит усвоить: логическое && - это не побитовое &.
4'b1100 && 4'b0011 // 1 (оба non-zero, логическое AND true)
4'b1100 & 4'b0011 // 4'b0000 (никаких позиций бит, где оба 1)
То же самое с || vs |. Используй логические в условиях if и предикатах ?:; побитовые - когда хочешь, чтобы каждая позиция бита AND/OR-илась независимо.
&& и || short-circuit в смысле C: правая часть не вычисляется, если результат уже определён левой. Это важно для testbench, которые проверяют указатели или вызовы функций; для синтезируемого RTL почти не важно, так как всё всё равно происходит параллельно.
Conditional оператор: ?:
Ternary - твой лучший друг для построения mux и условных выражений:
Паттерн cond ? a : b - это 2-to-1 мультиплексор с 1-битным select в железе. Цепочки нормально работают для 3- и 4-сторонних select, но дальше case (разбираем в Case Statement) драматически читаемее.
?: - также стандартный способ дать регистру дефолтное значение, которое перезаписываешь только иногда:
next_value = update ? new_data : next_value;
Правила ширины: засада для новичков
Правила ширины для арифметических операторов Verilog классически удивляют. Кратко:
- Ширина результата - максимум из ширин входов.
- Если результат присваивается чему-то шире - он zero-extend'ится (или sign-extend'ится, если знаковый).
- Ширина промежуточных вычислений - тоже ширина результата, что значит: переполнение происходит в промежуточном.
Первое умножение переполняется, потому что ширина обоих операндов - 8 бит, ширина результата соответственно 8 бит, а 40000 не помещается. Второе работает, потому что мы явно расширили a перед умножением.
Техники фикса:
- Используй оператор конкатенации
{8'b0, a}для zero-extend. - Используй
$unsigned(a)или$signed(a), чтобы повлиять на интерпретацию. - Кастуй промежуточные выражения в более широкие, если сомневаешься.
Приоритет операторов
Таблицу запоминать не будешь, но опираться на несколько правил - да:
*,/,%связывают сильнее, чем+,-.- Сравнение связывает сильнее, чем логические (
&&,||). - Conditional
?:имеет очень низкий приоритет - обычно нужны скобки вокруг его веток в более крупных выражениях. - В сомнении - ставь скобки. Симулятор не берёт деньги за символы.
out = (mode == 2'd0) ? a + b : a - b; // понятнее со скобками
Что дальше
Ты прошёл по каждому оператору, который выглядит как софтовый родственник. Следующий документ - Bitwise and Reduction - разбирает операторы, у которых нет прямых софтовых аналогов: побитовые AND/OR/XOR на каждом бите плюс reduction-формы, схлопывающие весь вектор в один бит.
Часто задаваемые вопросы
Какие операторы поддерживает Verilog?
В Verilog четыре больших семейства операторов: арифметика (+, -, *, /, %), сравнение (==, !=, <, >, <=, >=), логические (&&, ||, !) и побитовые/reduction (&, |, ^, ~, ~&, ~|, ~^). Плюс операторы сдвига (<<, >>, <<<, >>>), конкатенация {}, replication {N{...}} и conditional ?:.
В чём разница между && и & в Verilog?
&& - логическое AND: трактует каждый операнд как boolean (zero или non-zero) и возвращает 1-битный результат. & - побитовое AND: спаривает биты поразрядно. 4'b1100 && 4'b0011 - это 1 (оба non-zero); 4'b1100 & 4'b0011 - это 4'b0000. Используй && в условиях, & для битовых операций.
Как работает деление в Verilog?
Целочисленное деление - дробная часть отбрасывается. 7 / 2 - это 3, не 3.5. Оператор % даёт остаток: 7 % 2 - это 1. Деление на ноль даёт x (unknown) в симуляции. Оба оператора технически синтезируемые, но дают очень большое и медленное железо на FPGA и ASIC - избегай их в синтезируемых путях, если только делитель не степень двойки.
Что такое conditional оператор в Verilog?
?: - ternary или conditional оператор, скопирован из C. cond ? a : b даёт a, если cond истинно (non-zero), и b иначе. Это самая частая конструкция для mux-подобной логики: out = sel ? in1 : in0 строит 2-to-1 mux. Цепочки ?: строят шире, хотя case обычно понятнее после двух входов.