Menu

Vectores y arrays en Verilog: señales multibit explicadas

Cómo declarar señales multibit con [7:0], slicearlas, combinarlas y la diferencia entre un vector empaquetado y un array de memoria.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Un wire vs muchos

Hasta ahora cada señal que hemos visto ha sido de un bit de ancho. Los diseños reales casi nunca se ven así - las direcciones son de 16 o 32 bits, los buses de datos son de 8 o 64, los píxeles RGB son de 24. Verilog te da un único mecanismo para hacer cualquier señal multibit: añadir un rango entre corchetes.

wire [7:0] data;       // 8-bit wire, bit 7 is MSB, bit 0 is LSB
reg  [15:0] address;   // 16-bit reg
output reg [31:0] result; // 32-bit module output

Los números entre los corchetes son las posiciones de bit del bit más y menos significativos. [7:0] significa "esta señal tiene bits numerados del 7 al 0", que sale a 8 bits en total. El primer número es el índice alto. El segundo es el bajo.

Ocasionalmente verás [0:7] en su lugar - mismo número de bits, endianness opuesta para fines de slicing. La forma [high:low] es la convención abrumadora de la industria; quédate con ella a menos que tengas una buena razón para no hacerlo.

Un ejemplo trabajado: un sumador de 8 bits

Cada + suma los dos vectores de 8 bits y produce un resultado de 9 bits. Los literales numéricos como 8'd10 dicen "un valor decimal de 10 con ancho de 8 bits" - los cubriremos en Number Literals.

Slicing: extraer bits

Una vez que tienes un vector, puedes sacar bits individuales o rangos contiguos:

No te enredes con la línea force del testbench - solo necesitamos una forma de inyectar un valor para demostrar el slicing. La parte interesante son los slices mismos.

Algunas reglas:

  • La dirección del slice tiene que coincidir con la declaración. Si declaraste [7:0], haces slice con [high:low]. Invertir la dirección es un error de sintaxis.
  • Hacer slice fuera de rango produce x (desconocido) en simulación. La herramienta de síntesis puede avisar o dar error.
  • La selección de bits es base cero en el índice que escribiste - data[0] es el bit llamado 0, que (para una declaración [7:0]) es el LSB.

Slices con base variable: +: y -:

Una necesidad común: "dame 8 bits empezando en el bit N". No puedes escribir data[N+7:N] directamente porque Verilog requiere que ambos extremos del rango sean constantes. La sintaxis que resuelve esto:

data[base +: width]   // width bits starting at `base`, going UP
data[base -: width]   // width bits starting at `base`, going DOWN

El ancho es constante (estamos tomando 8 bits a la vez), pero la base puede ser una expresión en runtime. Eso es exactamente lo que necesitas para memorias direccionables por byte, taps de shift register, etc.

Arrays: un paso más allá de los vectores

Un vector es una única señal multibit. Un array es una colección de vectores, indexados independientemente:

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

Esa declaración tiene dos rangos, y los dos rangos significan cosas distintas:

  • [31:0] es la dimensión packed - el ancho de cada palabra individual.
  • [0:1023] es la dimensión unpacked - cuántas palabras hay.

Así que mem son 1024 registros separados de 32 bits. Accedes a uno con un único índice:

mem[5] = 32'hCAFE_BABE;       // write word 5
data   = mem[address];        // read the word at `address`

Esa es una memoria diminuta que contiene cuadrados. Los diseños reales usan el mismo patrón para mantener register files, tablas de lookup, FIFOs y cualquier otro almacenamiento on-chip que sea más grande que un único vector.

Packed vs unpacked: por qué importa

La división entre dimensiones packed y unpacked aparece por todas partes. Saber cuál es cuál ahorra mucha depuración:

  • Un vector packed es una señal. Puedes tratar todo como un número: data + 1 funciona, data == 32'h0 funciona, data[7:0] funciona.
  • Un array unpacked son muchas señales. No puedes tratar todo como un número: mem + 1 es un error de sintaxis. Tienes que sacar una palabra específica primero.

Varias dimensiones packed también son legales:

reg [3:0][7:0] regs;   // 4 bytes packed together into a 32-bit signal

regs[0] es un byte (el byte bajo). regs como un todo son 32 bits. SystemVerilog usa esto mucho.

Varias dimensiones unpacked crean una memoria 2D:

reg [31:0] frame [0:479][0:639];  // 480x640 of 32-bit pixels

Accedes a un único píxel con frame[y][x]. Así es como se vería un buffer de imagen en HDL.

Qué viene a continuación

Ya puedes declarar y manipular señales de cualquier ancho que necesites. El siguiente doc - Parameters - muestra cómo hacer esos anchos configurables para que el mismo módulo funcione a 8 bits en una instancia y a 32 en otra. Luego pasaremos a las reglas para escribir números literales (8'b1010_1100, 32'hDEAD_BEEF), y los valores x/z que aparecen cuando algo no está excitado.

Preguntas frecuentes

¿Qué es un vector en Verilog?

Un vector es una señal multibit. Declaras uno añadiendo un rango a un wire o reg: wire [7:0] data es un wire de 8 bits. Los números entre los corchetes son las posiciones de bit - en este caso el bit 7 es el más significativo y el bit 0 es el menos. Puedes hacer slice de bits individuales (data[3]) o de rangos contiguos (data[7:4]).

¿Qué significa [7:0] en Verilog?

[7:0] declara un rango del bit 7 hasta el bit 0, inclusive - una señal de 8 bits con el bit 7 como bit más significativo. El primer número es el índice alto, el segundo es el bajo. También puedes escribir [0:7] para indexación little-endian, pero la forma [high:low] es de lejos la convención más común en código de la industria.

¿Cómo se hace slice de bits en Verilog?

Usa indexación con corchetes. data[3] toma un único bit. data[7:4] toma los cuatro bits altos como un vector de 4 bits. El slice tiene que ir en la misma dirección que la declaración - si declaraste [7:0], haz slice con [high:low]. SystemVerilog también añade data[3 +: 4] para slices con base variable de ancho constante.

¿Cuál es la diferencia entre un array packed y unpacked en Verilog?

Un array packed es un único bus contiguo - reg [31:0] word es una única señal de 32 bits. Un array unpacked (o 'memoria') es una colección de palabras independientes - reg [31:0] mem [0:1023] son 1024 registros separados de 32 bits cada uno. Puedes leer o escribir una palabra completa de un array unpacked pero no puedes operar sobre todo como una sola señal.

¿Cómo se declara una memoria en Verilog?

reg [31:0] mem [0:1023]; declara una memoria de 1024 entradas, cada una de 32 bits de ancho. El primer conjunto de corchetes es el ancho de palabra (packed); el segundo es el número de palabras (unpacked). Accede a una entrada con mem[address], y puedes leer o escribir un slice de esa entrada con mem[address][7:0] una vez que la indexación de SystemVerilog-2005 está habilitada.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR