Cómo describir circuitos combinacionales en Verilog: operadores condicionales y bloques Always
Este artículo explica cómo modelar lógica combinacional en Verilog, cubriendo el operador condicional para implementación de tablas de verdad y el potente bloque "always" para diseño conciso.
En esta guía, recorremos las técnicas clave para describir circuitos combinacionales en Verilog. Primero, revisamos la sintaxis de asignación continua usando la palabra clave assign, luego introducimos el operador condicional (?:) como forma concisa de expresar lógica de multiplexor. Finalmente, mostramos cómo el bloque always puede proporcionar una descripción procedimental aún más clara.
Anteriormente cubrimos la palabra clave assign para asignaciones continuas. Estas declaraciones permanecen activas y nos permiten obtener una descripción a nivel de compuertas de un circuito. Por ejemplo, el siguiente código modela una compuerta AND:
assign out1 = a & b;
El operador condicional de Verilog permite que una sola asignación cambie su valor basado en una condición. La sintaxis es:
assign [signal_name] = [conditional_expression] ? [value_if_true] : [value_if_false];
El conditional_expression se evalúa primero. Si es verdadero, value_if_true se asigna a signal_name; de lo contrario se usa value_if_false. Por ejemplo:
assign out1 = (sel) ? (a & b) : (a | b);
Cuando sel es verdadero, el AND de a y b impulsa out1; de lo contrario se selecciona el OR de a y b. Esta única línea implementa un multiplexor 2-a-1.
Como el operador condicional se comporta como una declaración if en programación tradicional, permite una representación más abstracta de lógica combinacional. Al anidar el operador, puede construir estructuras complejas, como se ilustra en el Ejemplo 1.
Ejemplo 1: Operadores condicionales anidados
A continuación se muestra un codificador de prioridad 4-a-2 implementado con operadores condicionales anidados. La tabla de verdad se muestra en la Figura 1.

El código Verilog es:
module Prio_4_to_2 ( input wire [3:0] x, output wire [1:0] y, output wire v ); assign y = x[3] ? 2'b11 : x[2] ? 2'b10 : x[1] ? 2'b01 : 2'b00; assign v = ( x[3] | x[2] | x[1] | x[0] ) ? 1'b1 : 1'b0; endmodule
Las líneas 7-10 muestran el operador condicional anidado. Cada condición se evalúa secuencialmente, dando mayor prioridad a términos anteriores —perfecto para lógica de prioridad. El assign final para v usa un OR bit a bit para indicar si alguna entrada está en alto.
Los resultados de simulación de Xilinx ISE confirman el comportamiento (Figura 2).

Figura 2. Simulación Xilinx ISE del codificador de prioridad.
Es esencial notar que la expresión condicional se evalúa en orden hasta encontrar una condición verdadera; el valor correspondiente se asigna entonces. Consecuentemente, esta construcción es más adecuada para redes de prioridad (Figura 3) que para multiplexores balanceados (Figura 4).

Figura 3. Una red de prioridad.

Figura 4. Un multiplexor n-a-1 sin prioridad de entrada.
En un artículo anterior, discutimos asignaciones concurrentes en VHDL; los conceptos se traducen de manera similar a Verilog.
Declaraciones procedimentales en Verilog
Si bien assign y operadores condicionales bastan para muchos diseños, el bloque always ofrece un enfoque más expresivo y procedimental. Dentro de un bloque always, las declaraciones se ejecutan secuencialmente y pueden incluir construcciones de alto nivel como if y case, reflejando el flujo natural del razonamiento humano.
El Ejemplo 2 demuestra un bloque always básico usado para implementar una compuerta AND simple.
Ejemplo 2: Declaraciones de bloque "Always"
La sintaxis simplificada de un bloque always es:
always @(sensitivity_list) begin sequential_statements; end
El sensitivity_list especifica cuándo el bloque debe re-evaluarse. Para un circuito puramente combinacional, todas las señales de entrada deben aparecer en esta lista.
Considere el circuito en la Figura 5, donde la salida cambia cuando a o b conmutan.

Figura 5. Circuit_1
El siguiente bloque always implementa la operación AND usando una asignación bloqueante:
always @(a, b) begin out1 = a & b; end
Las asignaciones bloqueantes (=) actualizan el lado izquierdo inmediatamente, mientras que las asignaciones no bloqueantes (<=) retrasan la actualización hasta el final del bloque. Para lógica combinacional, es más seguro usar asignaciones bloqueantes para evitar condiciones de carrera no intencionadas.
Debido a que un bloque procedimental no puede impulsar un wire, la salida debe declararse como reg, que puede o no inferir un elemento de almacenamiento. El módulo completo se muestra a continuación:
module Circuit_1 ( input wire a, input wire b, output reg out1 ); always @(a, b) begin out1 = a & b; end endmodule
En resumen, este artículo cubrió el operador condicional para lógica compacta de tabla de verdad, operadores anidados para codificación de prioridad, y el bloque always procedimental para descripciones combinacionales más claras. Futuros posts explorarán el uso de always para lógica secuencial.
Incrustado
- Fundamentos de circuitos DC: entendiendo la corriente continua y conceptos eléctricos básicos
- Entendiendo Circuitos AC: Guía para Principiantes
- Diseñando Display 7-Segmentos de 4 Dígitos con un Solo Codificador Binario
- Circuitos Rectificadores: Desde Media Onda hasta Diseños Polifásicos de Onda Completa
- Circuitos de Control: Fundamentos, Aplicaciones y Mejores Prácticas
- Operadores en C# – Guía completa de operadores en C#
- Operador ternario en C#: Guía concisa para expresiones condicionales
- Fundamentos de Verilog: Diseñando tu primera compuerta AND
- Dominar Verilog: Tutorial completo para diseño de circuitos digitales
- Dominar la concatenación en Verilog: Guía práctica
- Compilación condicional en Verilog: `ifdef explicado