Menu

Concatenación y replicación en Verilog: los operadores {}

Cómo pegar señales juntas con {} y copiar un patrón N veces con {N{...}} - los operadores indispensables de Verilog para construir buses más anchos a partir de piezas.

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

Dos pequeños operadores que se ganan su sitio

Verilog tiene dos formas de {}:

  1. Concatenación - {a, b, c} - pega varias señales en una más ancha.
  2. Replicación - {N{pattern}} - copia un patrón N veces.

Vas a usar los dos constantemente. Cada vez que un bus tenga que ser dividido, juntado, rellenado o extendido con signo, uno de estos dos operadores es la respuesta.

Concatenación: {a, b, c}

El caso más simple: apilar dos señales en una:

Leyendo de izquierda a derecha: a es la mitad alta, b es la mitad baja. El primer operando dentro de {} es el más significativo. El ancho del resultado es la suma de los anchos de los operandos.

Puedes concatenar cualquier número de operandos:

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

Y los operandos pueden tener anchos distintos:

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

Una regla común: todos los operandos de una concatenación deben tener un ancho explícito. Los literales sin tamaño (1 en vez de 1'b1) causan errores porque el parser no sabe cuántos bits reservar. Escribe siempre 1'b0, 1'b1, 4'd0, nunca 0 o 1 pelados dentro de {}.

Concatenación en el lado izquierdo

La concatenación en el LHS de una asignación divide una señal ancha de vuelta en sus partes:

Ese es el patrón canónico de sumador con acarreo. {carry_out, sum_bits} es un único destino de 9 bits en el lado izquierdo de la asignación, y los bits se distribuyen: bit superior a carry_out, los 8 bajos a sum_bits.

La concatenación en LHS funciona en assign y en bloques procedurales:

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

Replicación: {N{pattern}}

El operador de replicación copia un patrón un número fijo de veces. La forma es {N{pattern}} - un contador, luego el patrón entre su propio par de llaves:

N tiene que ser una constante - el compilador necesita conocer el ancho del resultado en elaboración. El patrón puede ser un literal, un parámetro o cualquier expresión cuyo ancho se conozca.

No te olvides de las llaves internas. {8 1'b1} es un error de sintaxis; {8{1'b1}} es correcto. Las {} externas son el operador; las {...} internas envuelven el patrón que se replica.

Combinando ambos: extensión de signo y de ceros

Los dos operadores se componen de forma natural para construir valores más anchos:

El { {8{a_negative[7]}}, a_negative } es el patrón estándar de extensión de signo. Léelo como: replica el bit de signo ocho veces, luego añade los 8 bits originales. Resultado neto: representación en complemento a dos de 16 bits del mismo número.

Para ensanchar sin signo, puedes confiar en la asignación - Verilog hace zero-extend automáticamente cuando el destino es más ancho:

wire [15:0] zext_auto = a_unsigned;   // works, top 8 bits are 0

Pero la extensión con signo nunca ocurre implícitamente a menos que tanto el operando como el destino estén declarados signed. El modismo explícito de replicación es más seguro.

Patrones útiles

Construir una máscara de N unos

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

Invertir un vector

Verilog no tiene un reverse incorporado, pero puedes escribir uno en línea con concatenación:

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

Para vectores más anchos, un loop generate o una function es más limpio.

Empaquetar flags en un byte de estado

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

Los huecos reservados de 1 bit tienen anchos explícitos - nunca 0 pelado dentro de {}.

Errores comunes

Literales sin tamaño dentro de {}. {a, 0} es un error de sintaxis o un cero de 32 bits de ancho. Tamaña siempre: {a, 1'b0}.

Olvidar las llaves internas en la replicación. {8 1'b1} no parsea; {8{1'b1}} sí.

Confundir el orden. {a, b} pone a en el lado alto y b en el bajo. Invierte tu suposición y te saldrá un orden de bytes invertido en alguna parte.

Replicar cero veces. {0{...}} es ilegal en Verilog estándar. SystemVerilog lo permite (y produce un resultado de ancho cero). Verilog clásico lo rechazará.

Qué viene a continuación

Ya has visto cada operador de Verilog. El siguiente capítulo cambia de marcha y se sumerge en la estructura - cómo se conectan los módulos entre sí, las reglas para los puertos y la sintaxis para instanciar submódulos para construir diseños más grandes.

Preguntas frecuentes

¿Cómo se concatenan señales en Verilog?

Usa llaves: {a, b, c} produce un único vector ancho pegando los operandos de izquierda a derecha. El MSB de a se convierte en el MSB del resultado; el LSB de c se convierte en el LSB. El ancho del resultado es la suma de los anchos de los operandos. La concatenación funciona tanto en el lado derecho de una asignación (para construir un valor) como en el izquierdo (para dividir un valor).

¿Qué significa {N{pattern}} en Verilog?

Es el operador de replicación: produce N copias de pattern concatenadas juntas. {8{1'b1}} es un valor de 8 bits todo unos (8'hFF). {4{2'b10}} es el valor de 8 bits 8'b10101010. La replicación es cómo extiendes con ceros, extiendes con signo o construyes cualquier patrón de bits repetido sin escribirlo a mano.

¿Cómo se hace sign-extend de una señal en Verilog?

Usa replicación para repetir el bit de signo: { {24{a[7]}}, a } extiende un a de 8 bits a 32 bits replicando el bit 7 (el bit de signo) 24 veces y luego añadiendo el original. Para zero-extension sin signo, replica 1'b0 o simplemente deja que la asignación lo haga implícitamente cuando el destino es más ancho.

¿Se puede usar concatenación en el lado izquierdo de una asignación?

Sí - es cómo asignas a varios destinos desde una sola fuente ancha. {carry, sum} = a + b; pone el resultado de la suma en carry (el bit alto) y sum (el resto) en una única sentencia. Cada destino mantiene su ancho; el parser distribuye los bits del lado derecho de la forma correspondiente.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR