Menu

Vetores e Arrays em Verilog: Sinais multi-bit explicados

Como declarar sinais multi-bit com [7:0], fatiá-los, combiná-los e a diferença entre um vetor packed e um array de memória.

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

Um wire vs muitos

Até agora todo sinal que vimos teve um bit de largura. Designs reais quase nunca ficam assim - endereços têm 16 ou 32 bits, barramentos de dados têm 8 ou 64, pixels RGB têm 24. Verilog te dá um único mecanismo para tornar qualquer sinal multi-bit: adicione um range entre colchetes.

wire [7:0] data;       // wire de 8 bits, bit 7 é MSB, bit 0 é LSB
reg  [15:0] address;   // reg de 16 bits
output reg [31:0] result; // saída de module de 32 bits

Os números entre os colchetes são as posições de bit dos bits mais e menos significativos. [7:0] significa "este sinal tem bits numerados de 7 até 0", o que dá 8 bits no total. O primeiro número é o índice alto. O segundo é o baixo.

Você ocasionalmente verá [0:7] - mesmo número de bits, endianness oposto para fins de fatia. A forma [high:low] é a convenção esmagadora da indústria; fique com ela a menos que tenha uma razão forte para não ficar.

Um exemplo: somador de 8 bits

Cada + soma os dois vetores de 8 bits e produz um resultado de 9 bits. Literais numéricos como 8'd10 dizem "um valor decimal 10 com largura de 8 bits" - cobriremos isso em Number Literals.

Fatias: Pegando bits

Uma vez que você tem um vetor, pode tirar bits individuais ou ranges contíguos:

Não se prenda à linha force do testbench - só precisamos de uma forma de injetar um valor para demonstrar fatias. O interessante são as fatias em si.

Algumas regras:

  • A direção da fatia precisa bater com a declaração. Se você declarou [7:0], fatie com [high:low]. Reverter a direção é um erro de sintaxe.
  • Fatiar fora do range produz x (desconhecido) na simulação. A ferramenta de síntese pode avisar ou dar erro.
  • Seleção de bit é baseada em zero no índice que você escreveu - data[0] é o bit chamado 0, que (para uma declaração [7:0]) é o LSB.

Fatias de base variável: +: e -:

Uma necessidade comum: "me dê 8 bits começando do bit N". Você não pode escrever data[N+7:N] diretamente porque o Verilog exige que ambos os lados do range sejam constantes. A sintaxe que resolve isso:

data[base +: width]   // width bits começando em `base`, subindo
data[base -: width]   // width bits começando em `base`, descendo

A largura é constante (estamos pegando 8 bits por vez), mas a base pode ser uma expressão de runtime. É exatamente o que você precisa para memórias endereçáveis por byte, taps de shift register, e por aí vai.

Arrays: Um passo além dos vetores

Um vetor é um único sinal multi-bit. Um array é uma coleção de vetores, indexados independentemente:

reg [31:0] mem [0:1023];

Essa declaração tem dois ranges, e os dois ranges significam coisas diferentes:

  • [31:0] é a dimensão packed - a largura de cada palavra individual.
  • [0:1023] é a dimensão unpacked - quantas palavras existem.

Então mem é 1024 registers separados de 32 bits. Você acessa um com um único índice:

mem[5] = 32'hCAFE_BABE;       // escreve a palavra 5
data   = mem[address];        // lê a palavra em `address`

Essa é uma memória minúscula segurando quadrados. Designs reais usam o mesmo padrão para segurar register files, lookup tables, FIFOs e qualquer outro armazenamento on-chip maior que um único vetor.

Packed vs Unpacked: Por que importa

A divisão entre dimensões packed e unpacked aparece em todos os lugares. Saber qual é qual economiza muito debug:

  • Um vetor packed é um sinal. Você pode tratar o todo como um número: data + 1 funciona, data == 32'h0 funciona, data[7:0] funciona.
  • Um array unpacked é muitos sinais. Você não pode tratar o todo como um número: mem + 1 é um erro de sintaxe. Você tem que escolher uma palavra específica primeiro.

Múltiplas dimensões packed também são legais:

reg [3:0][7:0] regs;   // 4 bytes empacotados juntos em um sinal de 32 bits

regs[0] é um byte (o byte baixo). regs como um todo tem 32 bits. SystemVerilog usa isso intensamente.

Múltiplas dimensões unpacked criam uma memória 2D:

reg [31:0] frame [0:479][0:639];  // 480x640 de pixels de 32 bits

Você acessa um único pixel com frame[y][x]. É assim que um buffer de imagem ficaria em HDL.

O que vem a seguir

Você agora consegue declarar e manipular qualquer sinal de qualquer largura que precisar. O próximo doc - Parameters - mostra como tornar essas larguras configuráveis para que o mesmo module funcione com 8 bits em uma instância e 32 em outra. Depois vamos para as regras para escrever números literais (8'b1010_1100, 32'hDEAD_BEEF) e os valores x/z que aparecem sempre que algo não é conduzido.

Perguntas frequentes

O que é um vetor em Verilog?

Um vetor é um sinal multi-bit. Você declara um adicionando um range a um wire ou reg: wire [7:0] data é um wire de 8 bits. Os números entre os colchetes são as posições dos bits - neste caso o bit 7 é o mais significativo e o bit 0 é o menos. Você pode fatiar bits individuais (data[3]) ou ranges contíguos (data[7:4]).

O que [7:0] significa em Verilog?

[7:0] declara um range do bit 7 até o bit 0, inclusive - um sinal de 8 bits com o bit 7 como o bit mais significativo. O primeiro número é o índice alto, o segundo é o baixo. Você também pode escrever [0:7] para indexação little-endian, mas a forma [high:low] é de longe a convenção mais comum em código da indústria.

Como fatio bits em Verilog?

Use indexação com colchetes. data[3] pega um único bit. data[7:4] pega os quatro bits do topo como um vetor de 4 bits. A fatia precisa ter a mesma direção da declaração - se você declarou [7:0], fatie com [high:low]. SystemVerilog também adiciona data[3 +: 4] para fatias de base variável com largura constante.

Qual a diferença entre um array packed e unpacked em Verilog?

Um array packed é um único barramento contíguo - reg [31:0] word é um sinal de 32 bits. Um array unpacked (ou 'memória') é uma coleção de palavras independentes - reg [31:0] mem [0:1023] é 1024 registers separados de 32 bits. Você pode ler ou escrever uma palavra inteira de um array unpacked mas não consegue operar sobre o todo como um sinal.

Como declaro uma memória em Verilog?

reg [31:0] mem [0:1023]; declara uma memória de 1024 entradas, cada uma de 32 bits de largura. O primeiro conjunto de colchetes é a largura da palavra (packed); o segundo é o número de palavras (unpacked). Acesse uma entrada com mem[address], e você pode ler ou escrever uma fatia dessa entrada com mem[address][7:0] uma vez que a indexação SystemVerilog-2005 esteja habilitada.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR