Два имени, одна работа
У каждого сигнала в 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 по-прежнему должны выбирать.