Qué significa "tiempo" en Verilog
Verilog no tiene una noción incorporada de segundos. El simulador avanza "unidades de tiempo" - ticks enteros arbitrarios - y tu código usa #N para esperar N de ellos. La directiva `timescale es lo que mapea esos ticks a tiempo real.
`timescale 1ns / 1ps
module test;
initial begin
$display("t = %0t", $time); // 0
#5;
$display("t = %0t", $time); // 5 ns
#1.5;
$display("t = %0t", $time); // 6500 ps (or 6.5 ns)
$finish;
end
endmodule
Dos parámetros:
- Unidad (primer número): qué significa
#1.1nsdice que un tick es un nanosegundo. - Precisión (segundo número): con qué grado de finura sigue la simulación el tiempo dentro de esa unidad.
1psdice que los retardos fraccionarios se redondean a picosegundos.
La precisión no puede ser más gruesa que la unidad (1ps / 1ns es ilegal). Opciones comunes:
1ns / 1ps- el estándar de facto. Todo en nanosegundos, precisión sub-nanosegundo para cualquier retardo de puerta.1ps / 1ps- cuando modelas circuitos extremadamente rápidos o cuando cada retardo es sub-nanosegundo.1us / 1ns- para simulaciones lentas a escala embebida (tiempos de byte de UART, protocolos lentos).
Dónde ponerlo
`timescale es una directiva de compilador, no una construcción de nivel de módulo. Va en la parte superior de un archivo, antes de cualquier module:
`timescale 1ns / 1ps
module foo(...);
// ...
endmodule
Su ámbito es "desde este punto en adelante en el orden de compilación, hasta el siguiente `timescale o el final de la compilación". Eso tiene una implicación sutil: un archivo sin un `timescale explícito hereda lo que sea que el archivo compilado previamente declaró, lo que depende del orden en que el compilador lee los archivos. Evita la sorpresa: pon `timescale en la parte superior de cada archivo.
El editor del navegador en estos docs establece un timescale por defecto por ti (típicamente 1ns / 1ps), que es por lo que los ejemplos en docs anteriores funcionaron sin declarar uno. En un proyecto real serías explícito.
#delay en la práctica
Tras `timescale 1ns / 1ps:
#5 // wait 5 ns
#100 // wait 100 ns
#1.5 // wait 1.5 ns (precision allows it)
#0.001 // wait 1 ps (just barely above precision)
#0 // zero-delay; useful for ordering events at the same time
Dentro de un bloque initial o always, #N bloquea el flujo procedural durante N unidades de tiempo. El simulador pausa este bloque (otros bloques concurrentes siguen corriendo) y reanuda tras el retardo.
Puedes prefijar una asignación con un retardo:
#10 a = 1; // wait 10 ns, then assign
data <= #2 new_value; // schedule the non-blocking assignment 2 ns from now
La primera forma es la habitual de los testbenches. La segunda (non-blocking con retardo) se usa en simulación a nivel de puerta para modelar retardo de propagación.
Generar un reloj
El patrón clásico:
`timescale 1ns / 1ps
module test;
reg clk = 0;
always #5 clk = ~clk;
// ...
endmodule
Con timescale 1ns / 1ps, #5 es 5 ns. El reloj conmuta cada 5 ns, dando un periodo de 10 ns - un reloj de 100 MHz. Para cambiar la frecuencia, cambia el retardo:
| Medio periodo | Periodo | Frecuencia |
|---|---|---|
#1 | 2 ns | 500 MHz |
#2.5 | 5 ns | 200 MHz |
#5 | 10 ns | 100 MHz |
#10 | 20 ns | 50 MHz |
#25 | 50 ns | 20 MHz |
#50 | 100 ns | 10 MHz |
Si quieres un medio periodo fraccionario (#2.5), tu precisión necesita soportarlo - la precisión 1ps maneja cualquier cosa hasta 0.001 ns, así que cualquier frecuencia razonable está bien.
Mezclar timescales entre archivos
Un diseño real tiene muchos archivos, y pueden declarar timescales distintos. El simulador usa el timescale propio de cada archivo para interpretar los retardos en ese archivo. Si module_a.v dice `timescale 1ns / 1ps y usa #5, eso son 5 ns. Si module_b.v dice `timescale 1us / 1ns y usa #5, eso son 5 us.
Esto es mayormente invisible porque el simulador presenta un eje de tiempo global de todos modos - pero significa que el mismo #N en dos archivos puede significar cosas tremendamente distintas. La solución: elige un timescale (el estándar de la industria es 1ns / 1ps) y ponlo en la parte superior de cada archivo. No los mezcles.
Los retardos no son sintetizables
Punto crucial: #delay solo existe para simulación. La herramienta de síntesis lee:
// In synthesizable RTL - WRONG
always @(posedge clk) begin
out <= #2 in;
end
…y o bien ignora el #2 (la mayoría de las herramientas) o rechaza la construcción (linters más estrictos). El timing del hardware real lo determinan el reloj y el retardo de propagación de las puertas - ambos invisibles para el código fuente.
La regla: usa # solo en testbenches. El RTL sintetizable no tiene retardos #. Si te encuentras queriendo un retardo en código sintetizable, en realidad quieres un contador que decrementa con el reloj - así es como espera el hardware real.
$time vs $realtime
Dos maneras de leer el tiempo de simulación actual:
$timedevuelve un entero de 64 bits en unidades de la unidad del timescale actual.$realtimedevuelve unrealen unidades de la unidad del timescale actual, pero con precisión completa.
Para logging de testbench, $time casi siempre es suficiente. Recurre a $realtime solo cuando necesites precisión sub-tick en sentencias de impresión.
Consejos prácticos
- Declara siempre
`timescaleen la parte superior de cada archivo.1ns / 1pses el valor por defecto seguro. - Usa
#delaysolo en testbenches. Trata su ausencia en código sintetizable como una regla estática. - Adapta los periodos de reloj a tu frecuencia objetivo. Si estás simulando un diseño de 50 MHz, usa un periodo de reloj de 20 ns - periodos mal emparejados pueden enmascarar bugs sensibles al timing.
- Para estímulos contados por ciclos, usa
@(posedge clk)en vez de#. Es robusto frente a cambios en el periodo del reloj.
Qué viene a continuación
Ya has visto cada doc de estos tutoriales de Verilog. Desde los fundamentos del lenguaje (wire vs reg, módulos, operadores), pasando por bloques procedurales y control de flujo, hasta diseño síncrono y máquinas de estados, y finalmente las herramientas de testbench que demuestran que todo funciona. Hora de construir algo - el playground al lado de estos docs es el mismo simulador que hemos estado usando todo el rato, listo para cualquier módulo que esbozes.
Preguntas frecuentes
¿Qué significa `timescale 1ns / 1ps en Verilog?
Le dice al simulador: 'una unidad de tiempo en este archivo es un nanosegundo, y los tiempos se siguen con precisión de picosegundo.' Tras esa directiva, #5 espera 5 ns, #1.5 espera 1.5 ns (redondeado a picosegundos), y $time se reporta en nanosegundos. El primer número es la unidad; el segundo es la precisión.
¿Necesito `timescale en cada archivo Verilog?
Buena práctica: sí. El ámbito de la directiva termina en el siguiente `timescale o en el final de la compilación, así que los archivos sin uno heredan lo que sea que el archivo compilado previamente declarara. Eso hace el timing no determinista entre builds. Pon `timescale 1ns / 1ps en la parte superior de cada archivo fuente - es la convención más común - y nunca tendrás una sorpresa.
¿Qué significa #5 en Verilog?
#5 avanza el tiempo de simulación en 5 unidades de tiempo. La unidad viene de la directiva `timescale activa. Con `timescale 1ns / 1ps, #5 es 5 nanosegundos. Con `timescale 1us / 1ns, #5 es 5 microsegundos. El número puede ser fraccionario - #1.5 funciona si tu precisión es más fina que la unidad.
¿Es sintetizable #delay en Verilog?
No. #delay solo afecta a la simulación - la herramienta de síntesis lo ignora o lo rechaza. El timing del hardware real viene de la señal de reloj y del retardo de propagación de las puertas, no de sentencias #. Usa # libremente en testbenches; nunca lo escribas en RTL sintetizable.