Menu

Concatenation и replication в Verilog: операторы {}

Как склеивать сигналы через {} и копировать паттерн N раз через {N{...}} - незаменимые операторы Verilog для построения широких шин из кусочков.

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

Два крошечных оператора, которые окупаются

В Verilog две формы {}:

  1. Concatenation - {a, b, c} - склеивает несколько сигналов в один пошире.
  2. 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 (остальное) в одном операторе. Каждая цель сохраняет свою ширину; парсер распределяет биты правой стороны соответственно.

Coddy programming languages illustration

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

НАЧАТЬ