Menu

Verilog wire vs reg: когда использовать каждый (с примерами)

Два главных типа данных в Verilog - wire для непрерывных соединений и reg для процедурного хранилища - и правило, как выбирать между ними каждый раз.

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

Два имени, одна работа

У каждого сигнала в Verilog есть тип. Первые два типа, с которыми встречаешься - wire и reg. Оба могут держать значение. Оба могут быть однобитными или многобитными. Разница не в том, чем сигнал является, а в том, кто его приводит в действие.

  • wire приводится вне процедурных блоков - через assign, через выходы подмодулей или через input-ports module.
  • reg приводится внутри процедурных блоков - initial или always.

Это всё правило. Названия неуклюжие, потому что они старше, чем современный способ писать Verilog. reg не всегда становится регистром в железе. Дальше разберём почему.

Wire: непрерывное соединение

wire - это электрический провод. Он несёт то, что выдаёт его driver, непрерывно. Два способа, которыми wire приводится:

Три вещи:

  • y и z объявлены как wire в module и снова как wire в testbench.
  • Они приводятся через assign - это форма continuous-assignment. Правая часть = пересчитывается, когда любой сигнал в ней меняется.
  • Мы не можем написать y = a & b внутри always, оставив y как wire. Компилятор это отвергнет.

Если забыть ключевое слово wire, Verilog неявно объявит сигнал как однобитный wire - иногда удобно, иногда тихий баг. Большинство команд включает опцию тула, которая выдаёт ошибку на неявных wires. Будь явным и избеги ловушки.

Reg: хранилище внутри процедурного блока

reg - это сигнал, которому присваивают внутри initial или always. Имя - реликт ранних дней языка, когда "reg" звучало как "register"; в современном использовании reg - это просто тип любого сигнала, в который пишет процедурный код.

count - это reg внутри module (потому что блок always в него пишет) и wire в testbench (потому что его приводит выходной port DUT). Один и тот же сигнал, разные роли, разные типы в разных scope.

Почему "reg" не всегда означает "register"

Вот самая частая засада новичков. Этот module объявляет y как reg, но в синтезированном железе нет flip-flop:

Блок always @(*) чувствителен к любому изменению входов. Он комбинационный. Синтезатор видит этот паттерн и выдаёт AND-вентиль - никакого flip-flop, никакого clock, просто логика. Ключевое слово reg - чисто синтаксическое требование, потому что y присваивается внутри always.

Чтобы получить настоящий flip-flop, блок always должен быть чувствителен к фронту clock:

always @(posedge clk) begin
    q <= d;
end

Это паттерн "синтезатор, пожалуйста, сделай flip-flop": тактируемый sensitivity list, non-blocking присваивание. То же ключевое слово reg, совершенно другое железо. Различие разбираем в Always Block и Blocking vs Non-blocking.

Решение на практике

Каждый раз, когда объявляешь сигнал, спроси: как я буду его приводить?

  • Приводить через assign? → wire.
  • Подключать к выходу подмодуля? → wire.
  • Input-port module? → wire. (Inputs всегда wire.)
  • Приводить из initial или always? → reg.
  • Output-port module, приводимый процедурным кодом? → output reg.
  • Output-port module, приводимый через assign? → output wire. (Или просто output - направление само по себе делает дефолтным wire.)

Это дерево решений покрывает каждую ситуацию, с которой столкнёшься в чистом Verilog.

Конфликты driver

Сигналы wire теоретически могут иметь несколько driver - так работают tri-state шины, где несколько модулей могут приводить один wire, а неактивные уходят в high-impedance (z). Для обычной логики два assign, пишущих в один wire, дают неопределённое поведение:

assign y = a;
assign y = b;   // ПЛОХО - у y два driver

Симулятор может выбрать один, может X-нуть сигнал - зависит от тула. В любом случае это баг. Один driver на wire, если только ты явно не строишь шину.

reg можно приводить только из одного процедурного блока. Два always, оба присваивающих один reg, - синтез-ошибка и странность в симуляции. То же правило: один driver.

Обновление SystemVerilog: logic

SystemVerilog (надмножество, в которое эволюционировал Verilog) добавляет одно ключевое слово, заменяющее оба: logic. Сигнал logic можно приводить через assign или через процедурный блок - но не оба одновременно - и компилятор не даст случайно создать многодрайверный баг.

module modern(input logic a, input logic b, output logic y);
    assign y = a ^ b;
endmodule

Если начинаешь проект с нуля и твой тул поддерживает SystemVerilog (а редактор на этой странице поддерживает, с -g2012), использовать logic везде упрощает правила. Чистые .v файлы по-прежнему требуют разделения на wire/reg, и оба стиля ещё долго будут жить рядом.

Что дальше

Следующий документ - Vectors and Arrays - берёт те же два типа и показывает, как сделать их многобитными. Ширина шин, диапазоны, packed-измерения, разница между вектором бит и массивом векторов. Всё, что нужно, прежде чем строить что-то крупнее однобитного сумматора.

Часто задаваемые вопросы

В чём разница между wire и reg в Verilog?

wire и reg оба держат значение сигнала в каждый момент симуляции, но выбирать между ними нужно по тому, кто приводит сигнал в действие. wire приводится снаружи процедурного блока - через assign или как выход подмодуля. reg приводится изнутри блока initial или always. Названия исторические и сбивают с толку; reg не всегда значит 'register'.

Reg в Verilog означает flip-flop?

Не обязательно. reg синтезируется во flip-flop только тогда, когда его блок always чувствителен к фронту clock и использует non-blocking присваивание. reg, которому присваивают внутри комбинационного always @(*), синтезируется в обычную комбинационную логику. Ключевое слово выбирает тип данных Verilog, а не железо.

Когда использовать wire, а когда reg?

Правило: если будешь приводить сигнал через assign или подключать его к выходу подмодуля - используй wire. Если будешь присваивать ему внутри always или initial - используй reg. Inputs module всегда wire. Outputs - wire, если приводятся через assign, и reg, если приводятся процедурным блоком.

Можно ли присвоить wire внутри always-блока?

Нет - это синтаксическая ошибка. wire можно приводить только через непрерывные присваивания (assign) или подключая как выход подмодуля. Внутри initial или always целью должен быть reg (или logic в SystemVerilog). Компилятор это поймает и пожалуется на 'left-hand side type mismatch'.

Что такое logic в SystemVerilog?

logic - это объединение wire и reg в SystemVerilog. Его можно приводить либо непрерывным присваиванием, либо процедурным блоком (но не обоими одновременно). Современный код всё чаще использует logic везде и забывает про различие wire/reg. Файлы на чистом Verilog по-прежнему должны выбирать.

Coddy programming languages illustration

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

НАЧАТЬ