Una puerta lógica es un circuito que calcula una función booleana a partir de una o varias entradas binarias.
Cualquier procesador moderno, desde el de tu lavadora hasta el último i9 de Intel, está compuesto por miles de millones de los mismos bloques básicos: las puertas lógicas.
En electrónica clásica, tendrías que comprar chips de la serie 7400 (como el 7408 para AND o el 7432 para OR) y llenar una protoboard de cables. En una FPGA podemos describir estas funciones con una línea de código, siempre dentro de los recursos disponibles.
Hoy vamos a ver cómo traducir las puertas clásicas a Verilog.
Operadores bit a bit (bitwise)
En Verilog, al igual que en C, Java o Python, utilizamos símbolos específicos para realizar operaciones lógicas.
Es fundamental entender que en Verilog estos operadores son Bitwise (Bit a Bit). Esto significa que si operamos buses de varios bits, la operación se realiza independientemente para cada posición (el bit 0 con el bit 0, el 1 con el 1, etc.).
Esta es la tabla de operadores principales:
| Puerta Lógica | Símbolo en Verilog | Descripción |
|---|---|---|
| NOT (Inversor) | ~ | Invierte el valor (0 1, 1 0). |
| AND | & | 1 solo si todas las entradas son 1. |
| OR | | | 1 si alguna entrada es 1. |
| XOR (O exclusiva) | ^ | 1 si las entradas son diferentes. |
| NAND | ~& | Lo contrario a AND. |
| NOR | ~| | Lo contrario a OR. |
Nota para programadores:
Fíjate en que usamos & y | (simples), no && y || (dobles).
Los dobles son operadores lógicos booleanos que devuelven un solo bit (true/false). En hardware, casi siempre queremos operar con los bits reales, así que usaremos los simples.
Implementación básica
Vamos a crear un módulo que tenga dos entradas (a, b) y nos saque el resultado de estas operaciones por distintas salidas. Como son relaciones directas, usaremos asignación continua (assign).
module mis_puertas (
input wire a,
input wire b,
output wire y_not,
output wire y_and,
output wire y_or,
output wire y_xor
);
// 1. Puerta NOT (Inversor)
// Solo aplica a 'a'. Si a=0 -> y=1.
assign y_not = ~a;
// 2. Puerta AND
// Solo se enciende si A y B están encendidos.
assign y_and = a & b;
// 3. Puerta OR
// Se enciende si A o B (o ambos) están encendidos.
assign y_or = a | b;
// 4. Puerta XOR (Exclusive OR)
// Se enciende si A es diferente de B.
// Es fundamental para hacer sumas binarias.
assign y_xor = a ^ b;
endmoduleOperaciones con buses
Aquí la FPGA empieza a enseñar una de sus mejores ventajas. Supongamos que queremos hacer una operación AND entre dos buses de 4 bits.
En electrónica discreta, necesitaríamos 4 puertas AND físicas (un chip 7408 entero). En Verilog, la sintaxis es idéntica.
module and_cuadruple (
input wire [3:0] bus_a, // Ejemplo: 1010
input wire [3:0] bus_b, // Ejemplo: 1100
output wire [3:0] resultado
);
// Verilog expande la operación automáticamente
assign resultado = bus_a & bus_b;
/* Explicación de lo que ocurre bit a bit:
1 0 1 0 (bus_a)
& 1 1 0 0 (bus_b)
-----------
1 0 0 0 (resultado)
bit 3: 1 & 1 = 1
bit 2: 0 & 1 = 0
bit 1: 1 & 0 = 0
bit 0: 0 & 0 = 0
*/
endmoduleLa puerta XOR
Quiero hacer una mención especial a la puerta XOR (^). A menudo, cuando empezamos a programar, solo pensamos en AND y OR.
Sin embargo, en electrónica digital, la XOR es especialmente útil.
- Detector de diferencias: Nos dice si dos bits son distintos.
- Inversor controlado: Si haces
A ^ 0, saleA. Si hacesA ^ 1, sale~A(A invertida). - Sumador: La suma de 1 + 1 en binario es 0 (y me llevo 1). ¡Eso es exactamente lo que hace una XOR para el bit de resultado!
Combinando puertas
Podemos combinar operadores en una sola línea para crear lógica más compleja. El operador ~ tiene mayor precedencia que &, y & mayor que |. Ante la duda, usa paréntesis.
// Ejemplo: Una función lógica compleja
// Y = (A AND B) OR (NOT C)
assign salida = (a & b) | (~c);