Menu

Операторы Verilog: арифметика, сравнение, логические и conditional

Базовые операторы в Verilog - арифметика, сравнение, логические и conditional ?: - с правилами и засадами по разной ширине и signedness.

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

Что значит "оператор" в Verilog

Операторы берут выражения, несущие сигналы, и производят новые выражения, несущие сигналы. a + b - это железо: синтезатор превращает его в adder. a == b - железо: строит компаратор. Ничто из этого не "код, который выполняется"; всё это "схемы, которые описывает оператор".

Полное меню операторов небольшое, его можно запомнить. Этот документ покрывает арифметику, сравнение, логические и conditional. Bitwise and Reduction разбирает bit-level операторы, у которых нет прямых софтовых аналогов.

Арифметика: + - * / %

Три вещи, которые надо знать:

  1. Деление целочисленное. 7/2 - это 3, не 3.5. У Verilog нет float-типа (ну, есть real, но он simulation-only).
  2. Деление и modulo дороги в железе. /4 - нормально (синтезатор превращает в правый сдвиг), а /n для runtime-переменной n строит многотактовый divider, которого почти никогда не хочется.
  3. Переполнение оборачивается. Сложение двух 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 обычно понятнее после двух входов.

Coddy programming languages illustration

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

НАЧАТЬ