wire и reg, шины и разрядность
Разбираемся в двух главных типах сигналов Verilog и в том, как описывать многоразрядные шины.
wire — провод: значение определяется тем, что к нему подключено, и существует только пока есть источник. reg — переменная, которая «держит» присвоенное ей значение внутри процедурного блока.
В Verilog у каждого сигнала есть тип, и новичков чаще всего сбивает с толку пара wire и reg. Разберём их раз и навсегда, потому что неправильный выбор типа — частая причина ошибок синтеза.
wire: простой провод
wire моделирует физический провод. Он не хранит значение сам — значение на нём задаётся тем, что к нему подключено через assign или выход подмодуля. Уберите источник — и на проводе ничего нет. wire используют для комбинационных соединений: выходы вентилей, связи между подмодулями, результаты assign.
wire c;
assign c = a & b; // c постоянно «ведётся» вентилем AND — это законно для wirereg: значение, которое держится
Имя 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]; следите за разрядностью — лишние биты молча обрезаются.