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 chamado0, 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 + 1funciona,data == 32'h0funciona,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.