Dois pequenos operadores que merecem seu lugar
Verilog tem duas formas de {}:
- Concatenação -
{a, b, c}- cola múltiplos sinais em um mais largo. - 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.