Una simulación HDL es la ejecución virtual de un diseño para comprobar sus señales y su comportamiento temporal antes de cargarlo en la FPGA.
Hasta ahora hemos sido valientes: escribíamos código, sintetizábamos y lo cargábamos a la placa esperando que funcionara. A esta técnica la llamamos “Compile & Pray” (Compilar y Rezar).
En el mundo profesional y en diseños complejos, esto es inviable.
- La síntesis puede tardar horas.
- Si algo falla en el hardware, no tienes un
printfni un debugger fácil de usar para ver qué pasa dentro.
Para evitarlo usamos la simulación. Hoy vamos a crear un laboratorio virtual para probar nuestros circuitos antes de que toquen el silicio.
El hábito sano del diseñador de hardware. Si no lo has simulado, no funciona.
¿Qué es un Testbench?
Un Testbench (Banco de Pruebas) es un archivo Verilog que no se carga en la FPGA. Es un programa que se ejecuta en tu ordenador para probar tu diseño.
Imagina que estás diseñando un motor de coche (tu módulo). El Testbench no es parte del motor; es el banco de ensayos donde montas el motor, le conectas mangueras de gasolina, le das al acelerador y mides las revoluciones.
El Testbench tiene dos misiones:
- Generar Estímulos: Crear señales falsas de reloj, botones que se pulsan, datos que llegan…
- Observar Respuestas: Ver si las salidas de tu módulo hacen lo que esperabas.
El módulo que vamos a probar (DUT)
Para el ejemplo, vamos a usar un circuito sencillo: una puerta AND combinada con un Flip-Flop. Lo llamaremos mi_circuito. En la jerga, a esto se le llama DUT (Device Under Test) o UUT (Unit Under Test).
// mi_circuito.v
module mi_circuito (
input wire clk,
input wire a,
input wire b,
output reg q
);
always @(posedge clk) begin
q <= a & b; // AND registrada
end
endmoduleCómo escribir el testbench
Ahora creamos un archivo nuevo, por ejemplo mi_circuito_tb.v.
Un Testbench tiene una peculiaridad: No tiene entradas ni salidas. Es un mundo cerrado.
// mi_circuito_tb.v
`timescale 1ns / 1ps // Definimos la escala de tiempo (Unidad / Precisión)
module testbench();
// 1. Señales internas para conectar al DUT
// Usamos 'reg' para las entradas porque nosotros las vamos a controlar
reg clk;
reg a;
reg b;
// Usamos 'wire' para las salidas porque solo las vamos a leer
wire q;
// 2. Instancia del DUT (Device Under Test)
// Conectamos nuestras señales al módulo real
mi_circuito uut (
.clk(clk),
.a(a),
.b(b),
.q(q)
);
// 3. Generador de Reloj
// Esto crea un reloj infinito que cambia cada 1 unidad de tiempo
initial begin
clk = 0;
forever #1 clk = ~clk;
end
// 4. Bloque de Estímulos (El guion de la película)
initial begin
// Configuración para guardar las ondas (necesario para GTKWave)
$dumpfile("ondas.vcd"); // Nombre del archivo de salida
$dumpvars(0, testbench); // Guardar todas las variables de este módulo
// Inicializamos valores
a = 0;
b = 0;
// Esperamos y cambiamos valores
#10 a = 1; b = 0; // En el tiempo 10ns
#10 a = 1; b = 1; // En el tiempo 20ns (Esperamos que q se ponga a 1 tras el flanco)
#10 a = 0; b = 1; // En el tiempo 30ns
#10 a = 1; b = 1;
#20; // Esperamos un poco más
$finish; // Terminamos la simulación
end
endmoduleComandos de simulación
initial: A diferencia dealways, este bloque se ejecuta una sola vez al principio. Es perfecto para escribir el guion de la prueba.#10: Significa “espera 10 unidades de tiempo”.$dumpfiley$dumpvars: Son comandos del sistema (empiezan por$) que le dicen al simulador que grabe todo lo que pase en un archivo.vcd(Value Change Dump).$finish: Detiene la simulación. Si no lo pones, el reloj infinito (forever) hará que la simulación nunca acabe.
Cómo ejecutar la simulación
Para simular necesitamos un motor. El estándar en el mundo Open Source es Icarus Verilog (iverilog).
Si estás usando Apio y VSCode, es trivial:
- Asegúrate de tener el archivo de testbench (
_tb.v) en la carpeta. - Abre la paleta de comandos o usa los botones de Apio.
- Pulsa “Apio: Sim”.
Por debajo, Apio compila el testbench y el diseño, ejecuta la simulación y genera el archivo ondas.vcd.
Visualización con GTKWave
Una vez termina la simulación, Apio nos abrirá automáticamente GTKWave. Si no, puedes abrirlo manualmente y cargar el archivo .vcd.
GTKWave es nuestro osciloscopio digital.
Cómo leer la interfaz
- Panel Izquierdo (SST): Aquí ves la jerarquía de tu diseño. Haz clic en
testbenchy luego enuut. Verás las señalesclk,a,b,q. - Arrastrar señales: Selecciona las señales y arrástralas al área negra de la derecha (o haz doble clic).
- Lupa: Usa los botones de Zoom (la lupa con el cuadrado “Fit” es muy útil) para ver la cronología completa.
Interpretación de las ondas
Verás líneas verdes (o rojas si hay error) que suben y bajan.
- Línea Arriba: 1 Lógico (High).
- Línea Abajo: 0 Lógico (Low).
- Áreas Sombreades/Rojas (X): Valor desconocido. Esto pasa a menudo al principio de la simulación antes de dar un valor inicial a los registros.
En nuestro ejemplo, si a y b se ponen a 1, la salida q no cambia instantáneamente. Cambiará justo después del siguiente flanco de subida del reloj clk. ¡Eso es el comportamiento síncrono que buscábamos!
Testbenches con comprobación automática
Mirar ondas está bien para empezar, pero en diseños grandes te dejarás la vista.
Un Testbench profesional se comprueba a sí mismo. Podemos usar if para verificar resultados automáticamente:
// Dentro del initial...
#10 a = 1; b = 1;
#2; // Esperamos un poco tras el flanco de reloj
if (q !== 1'b1) begin
$display("ERROR: 1 AND 1 debería ser 1, pero tengo %b", q);
end else begin
$display("PRUEBA PASADA: La puerta AND funciona.");
endDe esta forma, al ejecutar la simulación, la consola nos dirá si nuestro circuito ha pasado el control de calidad.