Menu

Concatenação e Replicação em Verilog: Os operadores {}

Como colar sinais com {} e copiar um padrão N vezes com {N{...}} - os operadores indispensáveis do Verilog para construir barramentos mais largos a partir de pedaços.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Dois pequenos operadores que merecem seu lugar

Verilog tem duas formas de {}:

  1. Concatenação - {a, b, c} - cola múltiplos sinais em um mais largo.
  2. Replicação - {N{pattern}} - copia um padrão N vezes.

Você usará ambos constantemente. Toda vez que um barramento precisa ser dividido, unido, preenchido ou sign-extended, um desses dois operadores é a resposta.

Concatenação: {a, b, c}

O caso mais simples: empilhar dois sinais em um:

Lendo da esquerda para a direita: a é a metade alta, b é a metade baixa. O primeiro operando dentro de {} é o mais significativo. A largura do resultado é a soma das larguras dos operandos.

Você pode concatenar qualquer número de operandos:

wire [31:0] word = {byte3, byte2, byte1, byte0};

E os operandos podem ter larguras diferentes:

wire [11:0] frame = {start_bit, data_byte, parity, stop_bit};
//                    1 bit      8 bits     1 bit   2 bits  = 12

Uma regra comum: todos os operandos de uma concatenação precisam ter largura explícita. Literais sem tamanho (1 em vez de 1'b1) causam erros porque o parser não sabe quantos bits alocar. Sempre escreva 1'b0, 1'b1, 4'd0, nunca 0 ou 1 peladões dentro de {}.

Concatenação no lado esquerdo

Concatenação no LHS de uma atribuição divide um sinal largo de volta em suas partes:

Esse é o padrão canônico de somador-com-carry. {carry_out, sum_bits} é um único destino de 9 bits no lado esquerdo da atribuição, e os bits são distribuídos: bit do topo para carry_out, os 8 baixos para sum_bits.

Concatenação no LHS funciona em assign e em blocos procedurais:

always @(posedge clk) begin
    {high_byte, low_byte} <= incoming_word;
end

Replicação: {N{pattern}}

O operador de replicação copia um padrão um número fixo de vezes. A forma é {N{pattern}} - uma contagem, depois o padrão em seu próprio par de chaves:

N precisa ser uma constante - o compilador precisa saber a largura do resultado em tempo de elaboração. O padrão pode ser um literal, um parameter ou qualquer expressão cuja largura seja conhecida.

Não esqueça as chaves internas. {8 1'b1} é um erro de sintaxe; {8{1'b1}} é correto. As {} externas são o operador; as {...} internas embrulham o padrão sendo replicado.

Combinando os dois: Sign e zero extension

Os dois operadores compõem naturalmente para construir valores mais largos:

O { {8{a_negative[7]}}, a_negative } é o padrão padrão de sign-extension. Leia como: replica o sign bit oito vezes, depois anexa os 8 bits originais. Resultado: representação em complemento de dois de 16 bits do mesmo número.

Para alargamento unsigned, você pode confiar na atribuição - Verilog zero-extende automaticamente quando o destino é mais largo:

wire [15:0] zext_auto = a_unsigned;   // funciona, top 8 bits sao 0

Mas sign extension nunca acontece implicitamente a menos que ambos operando e destino sejam declarados signed. O idioma explícito de replicação é mais seguro.

Padrões úteis

Construindo uma máscara de N uns

parameter N = 5;
wire [31:0] mask = { {32-N{1'b0}}, {N{1'b1}} };
// ex.: com N=5, mask = 32'h0000_001F

Revertendo um vetor

Verilog não tem um reverse embutido, mas você pode escrever um inline com concatenação:

wire [3:0] forward  = 4'b1010;
wire [3:0] reversed = {forward[0], forward[1], forward[2], forward[3]};
// reversed = 4'b0101

Para vetores mais largos, um loop generate ou uma function é mais limpo.

Empacotando flags em um byte de status

wire [7:0] status = {
    error_flag,    // [7]
    overflow,      // [6]
    underflow,     // [5]
    ready,         // [4]
    busy,          // [3]
    1'b0,          // [2] reservado
    1'b0,          // [1] reservado
    valid          // [0]
};

Os slots reservados de 1 bit têm larguras explícitas - nunca 0 pelado dentro de {}.

Erros comuns

Literais sem tamanho dentro de {}. {a, 0} é um erro de sintaxe ou um zero de 32 bits. Sempre dê tamanho: {a, 1'b0}.

Esquecer as chaves internas em replicação. {8 1'b1} não compila; {8{1'b1}} compila.

Confundir a ordem. {a, b} coloca a no lado alto e b no baixo. Inverta sua suposição e você terá uma ordem de byte invertida em algum lugar.

Replicar zero vezes. {0{...}} é ilegal em Verilog padrão. SystemVerilog permite (e produz um resultado de largura zero). Verilog puro vai rejeitar.

O que vem a seguir

Você agora viu todo operador do Verilog. O próximo capítulo muda de assunto e mergulha em estrutura - como modules se conectam uns aos outros, as regras para ports e a sintaxe para instanciar submódulos para construir designs maiores.

Perguntas frequentes

Como concateno sinais em Verilog?

Use chaves: {a, b, c} produz um único vetor largo colando os operandos da esquerda para a direita. O MSB de a se torna o MSB do resultado; o LSB de c se torna o LSB. A largura do resultado é a soma das larguras dos operandos. Concatenação funciona tanto no lado direito de uma atribuição (para construir um valor) quanto no esquerdo (para dividir um valor).

O que {N{pattern}} significa em Verilog?

É o operador de replicação: produz N cópias de pattern concatenadas. {8{1'b1}} é um valor de 8 bits todo 1s (8'hFF). {4{2'b10}} é o valor de 8 bits 8'b10101010. Replicação é como você zero-extende, sign-extende ou constrói qualquer padrão de bit repetido sem escrevê-lo à mão.

Como sign-extend um sinal em Verilog?

Use replicação para repetir o sign bit: { {24{a[7]}}, a } estende um a de 8 bits para 32 bits replicando o bit 7 (o sign bit) 24 vezes e depois anexando o original. Para zero-extension unsigned, replique 1'b0 ou apenas deixe a atribuição fazer isso implicitamente quando o destino é mais largo.

Posso usar concatenação no lado esquerdo de uma atribuição?

Sim - é como você atribui múltiplos alvos a partir de uma fonte larga. {carry, sum} = a + b; coloca o resultado da soma em carry (o bit alto) e sum (o resto) em uma declaração. Cada alvo mantém sua largura; o parser distribui os bits do lado direito apropriadamente.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR