Menu

Bloque initial en Verilog: código de configuración al inicio de la simulación

Cómo los bloques initial se diferencian de always, por qué solo existen en simulación, y los patrones comunes - estímulos, configuración de forma de onda, encabezados de log - para los que se usan.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Un bloque procedural de un solo disparo

initial begin ... end es el hermano de always. La diferencia: initial se ejecuta exactamente una vez, empezando en el tiempo 0, y luego termina. No hay lista de sensibilidad ni bucle.

Existe por una razón: configuración de testbench. Establecer valores iniciales, lanzar estímulos, abrir archivos de log, llamar a $finish tras un tiempo fijo de simulación - todas las cosas que quieres que sucedan una vez al inicio de una ejecución, en un orden conocido.

Repasa lo que hace el simulador:

  1. El tiempo avanza a 0.
  2. El bloque initial comienza. El primer $display imprime.
  3. data se pone a 8'hA5.
  4. #10 avanza el tiempo simulado en 10 unidades.
  5. El segundo $display imprime.
  6. $finish termina la simulación.

El bloque corrió una vez. No hay segunda pasada.

El esqueleto canónico de un testbench

Casi cada testbench que vayas a escribir usa un bloque initial así:

Mira la forma - es la plantilla de testbench a la que recurrirás cada vez:

  1. Abre el archivo VCD ($dumpfile, $dumpvars) - cubierto en Dumpfile and VCD.
  2. Establece valores iniciales y mantén el reset durante unos ciclos.
  3. Excita los estímulos mediante una serie de cambios intercalados con retardos #.
  4. $finish para terminar la simulación limpiamente.

Eso es todo. Cada test de Verilog que veas, incluidos los de los ejemplos de los proveedores, sigue este patrón.

Varios bloques initial son paralelos

Varios bloques initial en el mismo módulo arrancan todos en el tiempo 0 simultáneamente. Cada uno ejecuta sus propias sentencias en su propio slice de tiempo:

El simulador arranca los dos bloques en el tiempo 0. El primer $display del bloque B corre inmediatamente. El bloque A espera 5 unidades antes de imprimir. El bloque B imprime de nuevo en el tiempo 2. La segunda impresión del bloque A se dispara en el tiempo 15. El $finish del bloque B mata la simulación en el tiempo 22.

Dividir setup, generación de reloj y estímulos en bloques initial separados es un estilo común - cada bloque es corto y hace una cosa.

Inicialización en línea en las declaraciones

Una forma compacta común: inicializar un reg justo en el momento de la declaración. La mayoría de los simuladores tratan esto igual que un initial:

reg clk = 0;
reg reset = 1;
reg [7:0] count = 0;

Eso es equivalente a:

reg clk;
reg reset;
reg [7:0] count;

initial begin
    clk = 0;
    reset = 1;
    count = 0;
end

La forma compacta es la que verás en testbenches - pone el valor inicial justo al lado de la declaración, donde es fácil de leer.

initial es solo para simulación

Las herramientas de síntesis ignoran los bloques initial. El hardware real no tiene un "momento cero" cuando corre código de setup - tiene encendido, señales de reset y la configuración que viene de esos eventos. Si necesitas que un registro arranque en un estado conocido en hardware real, excítalo con una señal de reset dentro de un always @(posedge clk):

always @(posedge clk) begin
    if (reset) state <= IDLE;
    else       state <= next_state;
end

Esa rama if (reset) es el equivalente sintetizable de initial state = IDLE. El reset es la respuesta a "¿cómo inicializo un registro en hardware real?"

Algunos flujos de FPGA aceptan un initial restringido para valores de reset de registros (las herramientas de Xilinx, por ejemplo), pero eso es una extensión por proveedor. No dependas de eso en código portable.

Cosas que verás dentro de initial

Algunos patrones comunes más allá del esqueleto estándar del testbench:

Terminación con retardo de tiempo

initial begin
    #1000 $finish;   // safety net: kill the sim after 1000 time units
end

Un initial separado cuyo único trabajo es poner un límite superior duro a la simulación. Aunque tu bloque principal de estímulos se quede bloqueado esperando una señal que nunca llega, este bloque se dispara y termina la ejecución.

Configuración de forma de onda

initial begin
    $dumpfile("dump.vcd");
    $dumpvars(0, test);   // dump everything under the `test` scope
end

Estas dos líneas le dicen al simulador que escriba un archivo VCD con cada señal en el scope test. Sin ellas, no obtienes forma de onda.

Imagen inicial de memoria

reg [7:0] memory [0:255];

initial begin
    $readmemh("image.hex", memory);
end

$readmemh carga un archivo con formato hex en el array. Se usa en testbenches de CPU para precargar memoria de instrucciones. Solo simulación.

Errores comunes

Usar initial para lógica sintetizable. No sintetizará. Usa señales de reset en su lugar.

Olvidar $finish. Sin él, el simulador corre hasta que algo más lo pare (un límite de tiempo por defecto, interrupción manual, etc.). Para un test rápido vale; para un script de regresión, siempre $finish.

Olvidar #delay entre asignaciones de estímulo. Si escribes a = 0; b = 1; sin retardo, ambos suceden en el mismo tiempo de simulación y el DUT podría verlos simultáneamente en vez de como eventos separados. Inserta #1 o más entre eventos de estímulo distintos.

Intentar excitar un wire desde initial. Misma regla que always: solo reg es un destino legal.

Qué viene a continuación

Has visto los dos sabores de bloque procedural. El siguiente doc cubre el tema más confuso para principiantes en Verilog: Blocking vs Non-blocking Assignment. Saber cuándo usar = y cuándo usar <= es la diferencia entre un flip-flop funcionando y un lío de condiciones de carrera.

Preguntas frecuentes

¿Qué es un bloque initial en Verilog?

initial begin ... end es un bloque procedural que se ejecuta exactamente una vez al inicio de la simulación, en el tiempo 0. Es el lugar estándar para configurar el estado del testbench: inicializar señales, abrir archivos de log, llamar a $dumpfile/$dumpvars, excitar estímulos y terminar la simulación con $finish. Varios bloques initial pueden coexistir en un módulo; todos arrancan en el tiempo 0 en paralelo.

¿Cuál es la diferencia entre initial y always en Verilog?

initial se ejecuta una vez en el tiempo 0 y luego termina. always se vuelve a ejecutar para siempre - tiene una lista de sensibilidad y se despierta cuando las señales listadas cambian. initial se usa casi exclusivamente en testbenches. always es el caballo de batalla tanto en testbenches como en RTL sintetizable.

¿Es sintetizable el bloque initial?

No en Verilog clásico. Las herramientas de síntesis ignoran los bloques initial porque el hardware real no tiene un momento de 'tiempo cero' cuando se ejecuta código de setup. Algunas cadenas de FPGA aceptan una forma restringida para establecer valores de reset de registros, pero el caso general es solo de simulación. Mantén los bloques initial en testbenches; usa señales de reset para inicializar lógica sintetizable.

¿Puedes tener varios bloques initial en un módulo Verilog?

Sí. Cada bloque initial empieza en el tiempo 0 y se ejecuta hasta su finalización de forma independiente. Dividir el setup en varios bloques es un patrón común de testbench - un bloque para generación de reloj, uno para estímulos, uno para volcado de formas de onda. Corren concurrentemente desde el tiempo 0; el simulador intercala sus sentencias a medida que avanza el tiempo.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR