Verilog assign: Guía completa para principiantes con ejemplos prácticos

目次

1. ¿Qué es la sentencia assign en Verilog?【Explicación para principiantes】

¿Qué es Verilog HDL?

Verilog HDL (Hardware Description Language) es un lenguaje de descripción de hardware utilizado para modelar circuitos digitales. A diferencia de los lenguajes de programación en desarrollo de software, describe la estructura y el comportamiento de los circuitos lógicos, permitiendo simular y sintetizar diseños en hardware real como FPGA o ASIC.

Dentro de Verilog, una de las sentencias más utilizadas es assign. En especial, es fundamental para describir circuitos combinacionales.

¿Cuál es el rol de la sentencia assign?

La sentencia assign se utiliza para realizar asignaciones continuas a señales de tipo wire. «Continuas» significa que cada vez que cambia una señal de entrada, la salida se actualiza automáticamente y de inmediato.

Ejemplo: para calcular la operación lógica AND de dos señales y enviar el resultado a la salida, se escribe:

assign out = in1 & in2;

Con una sola línea se logra que out refleje en todo momento el AND entre in1 y in2. En otras palabras, assign cumple el rol de definir explícitamente las conexiones de hardware.

Uso de assign en circuitos combinacionales

En diseño digital, los circuitos se dividen en combinacionales y secuenciales:

  • Circuitos combinacionales: la salida cambia de inmediato según la entrada (ej.: compuertas lógicas, sumadores).
  • Circuitos secuenciales: dependen de relojes y elementos de memoria para mantener estados (ej.: flip-flops, contadores).

La sentencia assign se utiliza en los circuitos combinacionales, ya que requieren actualizaciones instantáneas de la salida en función de la entrada.

¿Por qué es importante el assign para principiantes?

En la etapa inicial del aprendizaje de Verilog, entender los circuitos combinacionales es esencial, y para ello la sentencia assign resulta clave. Permite describir desde operaciones lógicas básicas hasta sumadores y comparadores de forma simple y directa.

Además, su uso ayuda a visualizar el flujo de señales en hardware, una habilidad crítica al avanzar hacia circuitos secuenciales y testbenches.

Resumen: la base de assign

La sentencia assign en Verilog es fundamental para describir circuitos combinacionales. Al simplificar la conexión y las operaciones lógicas, se convierte en una de las primeras construcciones que todo principiante debe dominar.

2. Sintaxis básica y ejemplos de la sentencia assign en Verilog

Sintaxis básica de assign

La sintaxis de assign en Verilog es muy sencilla. Se utiliza principalmente para asignar operaciones lógicas o aritméticas a señales de tipo wire. Su estructura es:

assign señal_salida = expresión;

En la “expresión” pueden usarse otras señales, operaciones lógicas o bit a bit. Es importante recordar que assign solo se aplica a variables de tipo wire, nunca a reg.

Ejemplo 1: Operaciones lógicas simples

El uso más común es la implementación de compuertas lógicas:

assign and_out = a & b;   // AND
assign or_out  = a | b;   // OR
assign xor_out = a ^ b;   // XOR

De esta forma se combinan señales y el resultado se actualiza en todo momento.

Ejemplo 2: Operaciones a nivel de bit

La sentencia assign también permite trabajar con bits específicos de un bus:

assign upper_4bits = data[7:4];           // extraer 4 bits superiores
assign lower_4bits = data[3:0];           // extraer 4 bits inferiores
assign combined = {data1[3:0], data2[3:0]};  // concatenar 2 señales de 4 bits

Esto resulta útil para manipular y reconstruir datos.

Asignación continua

En Verilog, el uso de assign se denomina asignación continua (continuous assignment). Esto significa que la salida se actualiza automáticamente cada vez que la entrada cambia, reproduciendo el comportamiento físico de las conexiones en hardware.

Especificar retrasos en assign

En simulaciones, se puede añadir un retardo para observar el comportamiento temporal de un circuito. Por ejemplo:

assign #5 out = a & b;  // salida retrasada 5 unidades de tiempo

Este tipo de retardo es útil en simulación, pero se ignora durante la síntesis en FPGA o ASIC.

Ejemplo con condición (operador ternario)

La sentencia assign también puede usarse con el operador ternario, similar a un if simple:

assign out = sel ? data1 : data2;

Esto significa: “si sel es 1, salida = data1, en caso contrario salida = data2”. Muy útil en multiplexores y condiciones simples.

Resumen: dominar la sintaxis de assign

La sentencia assign es una herramienta simple pero poderosa. Permite realizar operaciones lógicas, manipulación de bits, condiciones y retardos en simulación. Para principiantes, comprender y practicar esta sintaxis es el primer paso hacia el dominio de Verilog.

3. Relación entre assign y wire|Desde la declaración hasta el uso

Relación fundamental entre assign y wire

Uno de los puntos más importantes al usar assign en Verilog es que solo puede aplicarse a señales de tipo wire. Si intentas usarlo con otro tipo de variable, obtendrás un error de sintaxis.

Las asignaciones realizadas con assign son asignaciones continuas (continuous assignments), y solo están permitidas en señales declaradas como wire.

¿Qué es wire? ─ Imagen de un “cable” físico

El tipo wire en Verilog representa literalmente un cable o conexión física dentro del circuito. No almacena valores por sí mismo, sino que transmite el valor de otras fuentes (asignaciones, salidas de módulos, etc.).

Ejemplo:

wire a, b, out;

assign out = a & b;  // out siempre refleja a AND b

Aquí, out debe ser declarado como wire. Si se declarara como reg, se produciría un error de compilación.

Diferencia con reg: ¿por qué no se puede usar assign?

El tipo reg se utiliza para almacenar valores en circuitos secuenciales dentro de bloques always. No está pensado para recibir asignaciones continuas.

Ejemplo (código erróneo):

reg out;
assign out = a & b;  // ❌ Error: assign no puede usarse con reg

La regla básica es: assign → wire, always → reg.

Declaración de buses con wire

El tipo wire puede declararse como un bus de múltiples bits:

wire [3:0] a, b;
wire [3:0] out;

assign out = a & b;  // AND bit a bit entre a y b

Esto permite trabajar con vectores de datos sin cambiar la lógica básica de assign.

Uso de wire en la conexión entre módulos

Además de en las asignaciones, wire es esencial para conectar módulos en Verilog. Ejemplo:

wire result;

module1 u1 (.a(a), .b(b), .out(result));
module2 u2 (.in(result), .y(y));

Así, wire actúa como la “línea de transmisión” entre módulos.

Resumen: entender wire para usar assign correctamente

En Verilog, el uso correcto de assign depende totalmente de wire. Un wire no guarda datos, sino que transmite valores de forma continua, y assign define esas conexiones. Por eso, comprender la diferencia entre wire y reg es un paso fundamental para escribir código correcto y eficiente.

4. Diferencias entre assign y always【Puntos que suelen confundir a principiantes】

¿Por qué confunden assign y always?

Cuando se empieza a aprender Verilog, una de las dudas más comunes es cómo diferenciar el uso de la sentencia assign y los bloques always. Ambos sirven para asignar valores a señales, pero sus reglas y aplicaciones son distintas.

Características de assign

Recordemos las principales propiedades de assign:

  • Uso: descripción de circuitos combinacionales
  • Tipo de variable: solo en wire
  • Asignación: continua (se actualiza de inmediato con los cambios de entrada)
  • Palabra clave: assign

Ejemplo: compuerta AND de 2 entradas (assign)

wire a, b;
wire out;

assign out = a & b;

Aquí, la salida cambia instantáneamente cuando cambian las entradas, típico de un circuito combinacional.

Características de always

Por otro lado, el bloque always ofrece mayor flexibilidad. Se utiliza para describir circuitos secuenciales, condiciones complejas o procesos dependientes de un reloj.

  • Uso: circuitos secuenciales o lógica condicional
  • Tipo de variable: reg
  • Asignación: condicional (se ejecuta bajo ciertas condiciones)
  • Palabra clave: always

Ejemplo: registro sincronizado con el reloj

reg out;

always @(posedge clk) begin
  out <= a & b;
end

Aquí, la salida out solo cambia en el flanco positivo del reloj. Es decir, incluye el concepto de tiempo.

Comparación entre wire y reg

Característicawirereg
Usoassignalways
Almacena valorNo (solo transmite)
InicializaciónNoSí (en simulación)
AsignaciónContinuaCondicional (bloqueante o no bloqueante)

¿Cuándo usar assign y cuándo usar always?

Regla práctica:

ObjetivoConstrucciónTipo
Operaciones lógicas combinacionalesassignwire
Procesos con reloj o almacenamientoalwaysreg
Condiciones con if/casealwaysreg
Conexiones simples entre señalesassignwire

Ejemplo: condición con if (usar always)

reg y;
always @(a or b) begin
  if (a == 1) y = b;
  else        y = 0;
end

En este caso, como se trata de una condición, debe usarse always.

¿Se pueden usar assign y always al mismo tiempo?

No es posible asignar a la misma señal con assign y always al mismo tiempo, ya que generaría conflictos de control (múltiples drivers).

Ejemplo incorrecto:

assign y = a & b;

always @(posedge clk)
  y <= a | b;  // ❌ Error: y recibe dos asignaciones

Siempre debe quedar claro qué bloque controla cada señal.

Resumen: distinguir assign y always

  • Si la salida depende solo de la entrada actual → usa assign con wire.
  • Si la salida depende de tiempo, condiciones o estados → usa always con reg.

Con esta regla básica, los principiantes pueden evitar gran parte de los errores comunes en Verilog.

5. Ejemplos prácticos de circuitos combinacionales con assign【Con diagramas】

¿Qué es un circuito combinacional?

Un circuito combinacional es aquel en el que la salida depende únicamente de las entradas actuales, sin depender de estados previos. Es decir, no posee elementos de memoria. Por lo tanto, el resultado cambia de inmediato al modificarse las entradas.

En Verilog, la sentencia assign es la forma más adecuada de describir este tipo de circuitos.

Implementación de compuertas lógicas (AND, OR, XOR)

Ejemplo de un módulo que describe varias compuertas lógicas básicas:

module logic_gates(
  input  wire a,
  input  wire b,
  output wire and_out,
  output wire or_out,
  output wire xor_out
);

  assign and_out = a & b;
  assign or_out  = a | b;
  assign xor_out = a ^ b;

endmodule

Este módulo implementa AND, OR y XOR en paralelo usando solo assign. No necesita reloj ni condiciones.

Half Adder (medio sumador)

Un ejemplo clásico en diseño digital es el medio sumador, que suma dos bits e indica el resultado y el acarreo.

Ecuaciones lógicas

  • Suma (sum) = A ⊕ B
  • Acarreo (carry) = A · B

Implementación en Verilog

module half_adder(
  input  wire a,
  input  wire b,
  output wire sum,
  output wire carry
);

  assign sum   = a ^ b;
  assign carry = a & b;

endmodule

Con solo dos sentencias assign, el medio sumador queda completamente descrito.

Full Adder (sumador completo)

El sumador completo extiende el medio sumador, incluyendo un bit de acarreo de entrada (Cin).

Ecuaciones lógicas

  • Suma (sum) = A ⊕ B ⊕ Cin
  • Acarreo (carry) = (A · B) + (Cin · (A ⊕ B))

Implementación en Verilog

module full_adder(
  input  wire a,
  input  wire b,
  input  wire cin,
  output wire sum,
  output wire cout
);

  wire ab_xor;

  assign ab_xor = a ^ b;
  assign sum    = ab_xor ^ cin;
  assign cout   = (a & b) | (cin & ab_xor);

endmodule

Se utiliza una señal intermedia (ab_xor) para simplificar la descripción. Todo con wire y assign.

Multiplexor (MUX)

Un multiplexor selecciona entre varias entradas según una señal de control.

module mux2to1(
  input  wire a,
  input  wire b,
  input  wire sel,
  output wire y
);

  assign y = sel ? b : a;

endmodule

Aquí, si sel = 1, la salida será b; si sel = 0, la salida será a. Se usa el operador ternario en un assign.

Buenas prácticas al implementar con assign

  • Declarar todas las señales como wire cuando se usen en assign.
  • Usar múltiples assign para mejorar la legibilidad en lugar de expresiones demasiado largas.
  • Definir señales intermedias para operaciones complejas y mejorar la claridad del código.

Resumen: asignaciones que cubren todo lo esencial

Con la sentencia assign es posible implementar desde compuertas lógicas básicas hasta sumadores y multiplexores. Los circuitos combinacionales más comunes pueden describirse de forma simple y clara, facilitando el aprendizaje de Verilog a los principiantes.

6. Precauciones y errores comunes al usar assign

¿Cuáles son las trampas típicas para principiantes?

La sentencia assign es una de las construcciones más simples de Verilog, pero esa misma simplicidad puede llevar a errores comunes. Si no se comprende bien, puede generar errores de compilación o comportamientos inesperados.

1. Intentar usar assign con variables reg

❌ Ejemplo incorrecto:

reg out;
assign out = a & b;  // Error: assign no se puede usar con reg

💡 Solución:

Usar wire con assign o, si se necesita reg, hacerlo dentro de un bloque always.

2. Múltiples assign sobre la misma señal

❌ Ejemplo incorrecto:

assign y = a & b;
assign y = a | b;  // Error o comportamiento indefinido

💡 Solución:

Cada señal debe tener un único driver. Si se necesitan varias condiciones, utilizar un bloque always o señales intermedias.

3. Usar assign para inicialización

Ejemplo:

assign a = 1'b0;  // Siempre será 0, pero no es una inicialización

assign no sirve para inicializar valores, sino para conducir señales de manera continua.

Solución: usar initial en simulación o lógica de reset en síntesis.

4. Señales sin declarar

❌ Ejemplo incorrecto:

assign result = a & b;  // Error si result no está declarado

En Verilog, todas las señales deben declararse explícitamente como wire o reg.

5. Operaciones poco sintetizables

Algunas operaciones como división (/) o módulo (%) pueden causar errores en síntesis aunque funcionen en simulación.

assign out = a / 3;  // Posible error en herramientas de síntesis

Solución: verificar la compatibilidad con la herramienta o rediseñar la lógica.

6. Uso excesivo del operador ternario

Demasiadas condiciones en una sola sentencia reducen la legibilidad:

assign out = sel1 ? a : (sel2 ? b : (sel3 ? c : d));  // Difícil de leer

Solución: dividir en varias señales intermedias o usar un bloque always.

Consejos para depurar assign

  • Verificar siempre el tipo de señal (wire vs reg).
  • Revisar las advertencias del simulador (a menudo avisan de problemas potenciales).
  • Confirmar si la operación es sintetizable en la herramienta de destino.

Resumen: reglas clave

assign es poderoso y simple, pero requiere disciplina:

  • Usar solo con wire.
  • Un único assign por señal.
  • No usarlo para inicialización.
  • Cuidar operaciones sintetizables.

Siguiendo estas reglas, se evitan errores comunes y se asegura un código más claro y confiable.

7. Preguntas frecuentes (FAQ)

A continuación se presentan algunas de las dudas más comunes sobre el uso de la sentencia assign en Verilog, especialmente entre principiantes e intermedios.

Q1: ¿Qué es más fácil para principiantes, assign o always?

R: Es recomendable comenzar con assign.

La sentencia assign permite describir circuitos combinacionales de forma sencilla y directa. En cambio, always se usa para lógica secuencial y condiciones más complejas. Para aprender de forma progresiva:

  • Operaciones simples → assign
  • Lógica con reloj o condiciones → always

Q2: ¿Se puede usar assign con variables reg?

R: No, está prohibido.

Si se necesita trabajar con reg, debe hacerse dentro de un bloque always:

// Correcto (uso de reg con always)
reg out;
always @(a or b)
  out = a & b;

// Incorrecto (assign con reg)
reg out;
assign out = a & b;  // ❌ Error

Q3: ¿Puedo tener varios assign asignando a la misma señal?

R: No, nunca.

En Verilog, cada señal debe tener un único driver. Varios assign sobre la misma señal causan conflictos y errores de síntesis.

Q4: ¿Qué sentido tiene usar retardos (#) en assign?

R: Solo en simulación.

Por ejemplo:

assign #5 out = a & b;

Esto retrasa la salida 5 unidades de tiempo, pero en síntesis para FPGA o ASIC será ignorado. Es útil para verificar comportamiento en pruebas, no para hardware real.

Q5: ¿Cómo escribir condiciones con assign?

R: Usar el operador ternario (?:).

assign out = sel ? a : b;

Esto equivale a: si sel es 1, salida = a; en caso contrario, salida = b. Para condiciones complejas, se debe usar always.

Q6: ¿Por qué mi salida no cambia en la simulación?

R: Porque las entradas no se están modificando.

El comportamiento de assign depende directamente de los cambios en las señales de entrada. Si estas no varían, la salida permanece constante.

  • ¿Las entradas se inicializan correctamente?
  • ¿El banco de pruebas (testbench) genera cambios en las entradas?
  • ¿Se observa actividad en las señales durante la simulación?

Q7: ¿Todo lo escrito con assign se puede sintetizar?

R: En general sí, pero depende de la operación.

  • Operaciones lógicas (AND, OR, XOR) → ✅ Sí
  • Operaciones aritméticas básicas (+, -) → ✅ Sí
  • División, módulo, punto flotante → ⚠️ Puede fallar en síntesis

Por lo tanto, antes de implementar, conviene revisar la documentación de la herramienta de síntesis.

Resumen FAQ

  • assign es ideal para empezar y entender la lógica combinacional.
  • No se puede usar con reg, solo con wire.
  • Los retardos (#) son solo para simulación.
  • El operador ternario es clave para condiciones simples.
  • Siempre verificar si la operación es sintetizable.

8. Glosario: términos básicos para principiantes en Verilog

Para comprender mejor el uso de assign y la lógica combinacional, es importante conocer algunos conceptos clave de Verilog HDL. A continuación, un resumen de los términos más utilizados.

wire (cable)

Definición: Tipo de señal que representa una conexión física en el circuito. No almacena valores, solo transmite los que recibe de otra fuente.

Características:

  • Puede recibir asignaciones mediante assign.
  • No mantiene su valor por sí mismo.
  • Se usa principalmente en circuitos combinacionales.

Ejemplo:

wire a, b, out;
assign out = a & b;

reg (registro)

Definición: Tipo de señal que puede almacenar un valor. Se usa en bloques always para describir circuitos secuenciales.

Características:

  • No se puede usar con assign.
  • Sirve para retener valores y estados.
  • Se usa con lógica de reloj y control.

Ejemplo:

reg out;
always @(posedge clk) out <= a;

assign

Definición: Sentencia que realiza una asignación continua a señales de tipo wire.

Características:

  • Usada en lógica combinacional.
  • La salida cambia automáticamente con la entrada.
  • Permite operaciones lógicas, aritméticas y condicionales.

Ejemplo:

assign y = a & b;

always

Definición: Bloque que describe procesos que se ejecutan en función de un evento (ej.: flanco de reloj o cambio de señal).

Características:

  • Se usa con reg.
  • Permite condiciones (if, case).
  • Indispensable para circuitos secuenciales.

Ejemplo:

always @(posedge clk) begin
  out <= a + b;
end

Circuito combinacional

Definición: Circuito en el que la salida depende únicamente de las entradas actuales.

Características:

  • No tiene memoria.
  • Ejemplos: compuertas lógicas, sumadores, multiplexores.
  • Se describe con assign o always @(*).

Circuito secuencial

Definición: Circuito en el que la salida depende de las entradas y de los estados anteriores.

Características:

  • Usa registros o flip-flops.
  • Requiere reloj (clk).
  • Se describe con bloques always @(posedge clk).

Operador ternario (?:)

Definición: Operador condicional que permite elegir entre dos valores en una sola línea.

Ejemplo:

assign y = sel ? a : b;  // si sel=1, y=a; si sel=0, y=b

Módulo (module)

Definición: Unidad básica de diseño en Verilog que describe un bloque de hardware con entradas y salidas.

Ejemplo:

module adder(input a, input b, output sum);
  assign sum = a + b;
endmodule

initial

Definición: Bloque que se ejecuta una sola vez al inicio de la simulación.

Características:

  • No se sintetiza en hardware real.
  • Se usa en testbenches.
initial begin
  a = 0;
  b = 1;
end

Asignación no bloqueante (<=)

Definición: Forma de asignación dentro de always que permite ejecutar múltiples asignaciones en paralelo.

Ejemplo:

always @(posedge clk) begin
  out1 <= in1;
  out2 <= in2;
end

Resumen

Estos conceptos forman la base del aprendizaje en Verilog. Conocer la diferencia entre wire, reg, assign y always es esencial para evitar errores y comprender cómo se describen los circuitos en HDL.

9. Conclusión|Cómo dominar la sentencia assign en Verilog

En este artículo hemos recorrido, paso a paso, todo lo relacionado con la sentencia assign en Verilog HDL: desde los conceptos básicos hasta ejemplos prácticos y errores comunes. Para los principiantes, assign es la primera herramienta que permite comprender cómo se describen los circuitos combinacionales en un lenguaje de descripción de hardware.

Puntos clave de assign

✅ Rol de assign

  • Permite asignaciones continuas a señales de tipo wire.
  • La salida se actualiza inmediatamente al cambiar las entradas.
  • Ideal para describir circuitos combinacionales.

✅ Reglas de uso

  • No se puede usar con reg.
  • No se puede asignar múltiples veces a la misma señal.
  • No sirve para inicialización de valores.

✅ Consejos para aprovechar assign

  • Diferenciar bien entre assign y always según el caso.
  • Usar el operador ternario para condiciones simples.
  • Definir señales intermedias para mejorar la legibilidad en expresiones complejas.

Próximos pasos en el aprendizaje

Una vez dominado assign, el siguiente paso natural es avanzar hacia:

  • Uso de always para circuitos secuenciales.
  • Implementación de estructuras de control como if y case.
  • Creación de testbenches para simulación.
  • Diseños jerárquicos que integren múltiples módulos.

La clave es practicar con ejemplos pequeños y avanzar hacia diseños más complejos.

Reflexión final

Comprender y dominar la sentencia assign significa haber superado una de las barreras iniciales más importantes en Verilog. Con ella, ya es posible describir gran parte de la lógica combinacional, base de todo diseño digital.

Este artículo busca servir como una “guía de referencia rápida” a la que se pueda volver en cualquier momento durante el aprendizaje. Al afianzar estos conceptos, estarás preparado para dar el salto hacia un diseño digital más avanzado y profesional.