Два крошечных оператора, которые окупаются
В Verilog две формы {}:
- Concatenation -
{a, b, c}- склеивает несколько сигналов в один пошире. - Replication -
{N{pattern}}- копирует паттерн N раз.
Оба будешь использовать постоянно. Каждый раз, когда шину нужно разделить, объединить, добить или sign-extend'ить - ответом будет один из этих двух операторов.
Concatenation: {a, b, c}
Самый простой случай - сложить два сигнала в один:
Читаем слева направо: a - старшая половина, b - младшая. Первый операнд внутри {} - most significant. Ширина результата - сумма ширин операндов.
Конкатенировать можно любое количество операндов:
wire [31:0] word = {byte3, byte2, byte1, byte0};
И операнды могут быть разной ширины:
wire [11:0] frame = {start_bit, data_byte, parity, stop_bit};
// 1 бит 8 бит 1 бит 2 бита = 12
Частое правило: у всех операндов конкатенации должна быть явная ширина. Безразмерные литералы (1 вместо 1'b1) вызывают ошибки, потому что парсер не знает, сколько бит выделить. Всегда пиши 1'b0, 1'b1, 4'd0, никогда голые 0 или 1 внутри {}.
Concatenation на левой стороне
Concatenation на LHS присваивания разделяет широкий сигнал на части:
Это канонический паттерн adder-with-carry. {carry_out, sum_bits} - это одна 9-битная цель на левой стороне присваивания, биты распределяются: старший - в carry_out, младшие 8 - в sum_bits.
LHS concatenation работает и в assign, и в процедурных блоках:
always @(posedge clk) begin
{high_byte, low_byte} <= incoming_word;
end
Replication: {N{pattern}}
Оператор replication копирует паттерн фиксированное число раз. Форма - {N{pattern}}: счёт, затем паттерн в своей паре скобок:
N должно быть константой - компилятору нужно знать ширину результата на elaboration. Паттерн может быть литералом, parameter или любым выражением известной ширины.
Не забывай внутренние скобки. {8 1'b1} - синтаксическая ошибка; {8{1'b1}} - правильно. Внешние {} - оператор; внутренние {...} оборачивают повторяемый паттерн.
Комбинируя оба: sign и zero extension
Два оператора композиционно строят более широкие значения:
{ {8{a_negative[7]}}, a_negative } - стандартный паттерн sign-extension. Читай так: повтори знаковый бит восемь раз, потом припиши оригинальные 8 бит. Итог - 16-битное two's-complement-представление того же числа.
Для unsigned-расширения можно положиться на присваивание - Verilog zero-extend'ит автоматически, когда приёмник шире:
wire [15:0] zext_auto = a_unsigned; // работает, верхние 8 бит - 0
Но sign-extension никогда не происходит неявно, если только и операнд и приёмник не объявлены signed. Явная идиома с replication безопаснее.
Полезные паттерны
Сборка маски из N единиц
parameter N = 5;
wire [31:0] mask = { {32-N{1'b0}}, {N{1'b1}} };
// напр., при N=5, mask = 32'h0000_001F
Реверс вектора
В Verilog нет встроенного reverse, но можно написать inline через concatenation:
wire [3:0] forward = 4'b1010;
wire [3:0] reversed = {forward[0], forward[1], forward[2], forward[3]};
// reversed = 4'b0101
Для более широких векторов цикл generate или function чище.
Упаковка флагов в status-байт
wire [7:0] status = {
error_flag, // [7]
overflow, // [6]
underflow, // [5]
ready, // [4]
busy, // [3]
1'b0, // [2] reserved
1'b0, // [1] reserved
valid // [0]
};
Однобитные reserved-слоты имеют явную ширину - никаких голых 0 внутри {}.
Частые ошибки
Безразмерные литералы внутри {}. {a, 0} - синтаксическая ошибка или 32-битный ноль шириной. Всегда задавай размер: {a, 1'b0}.
Забыли внутренние скобки в replication. {8 1'b1} не парсится; {8{1'b1}} - да.
Перепутали порядок. {a, b} кладёт a на старшую сторону, b - на младшую. Перевернёшь - где-то получишь инвертированный порядок байт.
Replication ноль раз. {0{...}} нелегален в стандартном Verilog. SystemVerilog это разрешает (и даёт ширину ноль). Чистый Verilog отвергнет.
Что дальше
Ты увидел каждый оператор Verilog. Следующая глава переключает передачу и ныряет в структуру - как модули соединяются друг с другом, правила для ports и синтаксис инстанцирования подмодулей для построения более крупных проектов.
Часто задаваемые вопросы
Как конкатенировать сигналы в Verilog?
Через фигурные скобки: {a, b, c} даёт один широкий вектор, склеивая операнды слева направо. MSB у a становится MSB результата; LSB у c становится LSB. Ширина результата - сумма ширин операндов. Concatenation работает и на правой стороне присваивания (чтобы собрать значение), и на левой (чтобы разбить значение).
Что значит {N{pattern}} в Verilog?
Это оператор replication: даёт N копий pattern, конкатенированных вместе. {8{1'b1}} - это 8-битное all-ones значение (8'hFF). {4{2'b10}} - это 8-битное значение 8'b10101010. Replication - это то, как делают zero-extend, sign-extend или собирают любой повторяющийся битовый паттерн, не выписывая его руками.
Как sign-extend'ить сигнал в Verilog?
Используй replication для повторения знакового бита: { {24{a[7]}}, a } расширяет 8-битный a до 32 бит, повторяя бит 7 (знаковый) 24 раза и приписывая оригинал. Для unsigned zero-extension повторяй 1'b0 или просто дай присваиванию сделать это неявно, когда приёмник шире.
Можно ли использовать concatenation на левой стороне присваивания?
Да - это способ присвоить несколько целей из одного широкого источника. {carry, sum} = a + b; кладёт результат сложения в carry (старший бит) и sum (остальное) в одном операторе. Каждая цель сохраняет свою ширину; парсер распределяет биты правой стороны соответственно.