wire и reg, шины и разрядность

Разбираемся в двух главных типах сигналов Verilog и в том, как описывать многоразрядные шины.

wire — провод: значение определяется тем, что к нему подключено, и существует только пока есть источник. reg — переменная, которая «держит» присвоенное ей значение внутри процедурного блока.

В Verilog у каждого сигнала есть тип, и новичков чаще всего сбивает с толку пара wire и reg. Разберём их раз и навсегда, потому что неправильный выбор типа — частая причина ошибок синтеза.

wire: простой провод

wire моделирует физический провод. Он не хранит значение сам — значение на нём задаётся тем, что к нему подключено через assign или выход подмодуля. Уберите источник — и на проводе ничего нет. wire используют для комбинационных соединений: выходы вентилей, связи между подмодулями, результаты assign.

wire c;
assign c = a & b;   // c постоянно «ведётся» вентилем AND — это законно для wire

reg: значение, которое держится

Имя reg исторически вводит в заблуждение: reg — это не обязательно регистр (триггер). Это просто переменная, которой можно присваивать значение внутри процедурного блока (always, о котором — в следующем разделе), и она удерживает это значение до следующего присваивания. Станет ли reg в железе настоящим триггером или просто комбинационным проводом — зависит от того, как вы его используете, а не от типа.

reg y;
always @(*) begin     // комбинационный блок
    y = a | b;        // здесь reg y станет обычным проводом, а не триггером!
end

Простое правило для старта: то, что присваивается через assign (вне процедурных блоков), должно быть wire; то, чему присваивают внутри always, должно быть reg. В SystemVerilog обе роли объединили в тип logic, но классический Verilog требует различать.

Шины: многоразрядные сигналы

Реальные данные редко умещаются в один бит. Чтобы описать 8-битную шину, указывают диапазон разрядов в квадратных скобках — от старшего к младшему:

wire [7:0] data;     // 8-битная шина: биты с 7-го (старший) по 0-й (младший)
reg  [15:0] counter; // 16-битный регистр-счётчик
wire [3:0] nibble;   // 4-битный полубайт

Запись [7:0] читается «от 7 до 0» — это 8 проводов, объединённых в шину. К отдельному биту обращаются по индексу (data[0] — младший), к диапазону — срезом (data[3:0] — младшие 4 бита). Биты можно склеивать оператором конкатенации {}:

wire [7:0] joined;
assign joined = {nibble_hi, nibble_lo};  // склейка двух 4-битных в одну 8-битную

Как работает под капотом: разрядность важна

Ширина шины — это число физических проводов и, для регистров, число триггеров. Если результат операции не помещается в объявленную ширину, старшие биты молча отбрасываются. Промоделируем переполнение 8-битного сложения на Python:

# 8-битная шина хранит значения 0..255; результат берётся по модулю 256
def add8(a, b):
    full = a + b
    truncated = full & 0xFF      # оставляем только младшие 8 бит (маска 0xFF)
    carry = (full >> 8) & 1       # старший бит — это перенос, уходящий за пределы шины
    return truncated, carry

for a, b in [(100, 50), (200, 100), (255, 1)]:
    res, c = add8(a, b)
    print(f"{a:3} + {b:3} = {res:3} (перенос={c})")

Вывод:

100 +  50 = 150 (перенос=0)
200 + 100 =  44 (перенос=1)
255 +   1 =   0 (перенос=1)

200+100=300 не влезает в 8 бит и превращается в 44 (300 − 256), а старший бит уходит в перенос. В железе это происходит «само», без предупреждения, — поэтому за разрядностью шин надо следить вручную, закладывая запас или явный бит переноса.

Частые ошибки

  • Считать reg синонимом триггера. reg — это лишь переменная для присваивания в always; триггером она становится только в тактируемом блоке.
  • Присваивать wire внутри always или reg через assign. Это нарушение правил: тип сигнала должен соответствовать способу присваивания.
  • Забывать про разрядность. Слишком узкая шина молча обрезает старшие биты, давая неверный результат.

Итог

  • wire — провод без памяти, для assign и связей подмодулей.
  • reg — переменная для присваивания в always; не обязательно триггер.
  • Шины объявляют как [N-1:0]; следите за разрядностью — лишние биты молча обрезаются.
Проверьте себя
1. Верно ли, что тип reg в Verilog всегда означает аппаратный триггер (регистр)?
AДа, reg всегда становится триггером
BНет, reg — это переменная для присваивания в always; триггером она становится только в тактируемом блоке
Creg вообще не используется в синтезе
Dreg означает константу
2. Как объявить 8-битную шину в Verilog?
Awire data[8]
Bwire [7:0] data
Cwire data : 8
Dreg data(8)
3. Что произойдёт при сложении 200 + 100 на 8-битной шине без отдельного бита переноса?
AРезультат будет 300
BРезультат будет 44 (300 по модулю 256), старший бит потеряется
CВозникнет ошибка синтеза
DШина автоматически расширится до 16 бит