simulacion-fpga-testbench-gtkwave

Simulación de FPGA con testbench y GTKWave

  • 6 min

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.

  1. La síntesis puede tardar horas.
  2. Si algo falla en el hardware, no tienes un printf ni 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:

  1. Generar Estímulos: Crear señales falsas de reloj, botones que se pulsan, datos que llegan…
  2. 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

endmodule
Copied!

Có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

endmodule
Copied!

Comandos de simulación

  • initial: A diferencia de always, 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”.
  • $dumpfile y $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:

  1. Asegúrate de tener el archivo de testbench (_tb.v) en la carpeta.
  2. Abre la paleta de comandos o usa los botones de Apio.
  3. 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

  1. Panel Izquierdo (SST): Aquí ves la jerarquía de tu diseño. Haz clic en testbench y luego en uut. Verás las señales clk, a, b, q.
  2. Arrastrar señales: Selecciona las señales y arrástralas al área negra de la derecha (o haz doble clic).
  3. 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.");
    end
Copied!

De esta forma, al ejecutar la simulación, la consola nos dirá si nuestro circuito ha pasado el control de calidad.