Comprender el control de retraso en Verilog: Técnicas de temporización y mejores prácticas
Hay dos tipos de controles de temporización en Verilog: expresiones de retraso y evento. El control de retraso es solo una forma de agregar un retraso entre el momento en que el simulador encuentra la declaración y cuando realmente la ejecuta. La expresión de evento permite que la declaración se retrase hasta la ocurrencia de algún evento de simulación que puede ser un cambio de valor en una red o variable (evento implícito) o un evento nombrado explícitamente que se dispara en otro procedimiento.
El tiempo de simulación puede avanzar mediante uno de los siguientes métodos.
Las puertas y redes que se han modelado para tener retrasos internos también avanzan el tiempo de simulación.
Control de retraso
Si la expresión de retraso se evalúa a un valor desconocido o de alta impedancia, se interpretará como retraso cero. Si se evalúa a un valor negativo, se interpretará como un entero sin signo en complemento a 2 del mismo tamaño que una variable time.
`timescale 1ns/1ps
module tb;
reg [3:0] a, b;
initial begin
{a, b} <= 0;
$display ("T=%0t a=%0d b=%0d", $realtime, a, b);
#10; a <= $random;
$display ("T=%0t a=%0d b=%0d", $realtime, a, b);
#10 b <= $random;
$display ("T=%0t a=%0d b=%0d", $realtime, a, b);
#(a) $display ("T=%0t Después de un retraso de a=%0d unidades", $realtime, a);
#(a+b) $display ("T=%0t Después de un retraso de a=%0d + b=%0d = %0d unidades", $realtime, a, b, a+b);
#((a+b)*10ps) $display ("T=%0t Después de un retraso de %0d * 10ps", $realtime, a+b);
#(b-a) $display ("T=%0t Expr evalúa a un retraso negativo", $realtime);
#('h10) $display ("T=%0t Retraso en hex", $realtime);
a = 'hX;
#(a) $display ("T=%0t Retraso es desconocido, tomado como cero a=%h", $realtime, a);
a = 'hZ;
#(a) $display ("T=%0t Retraso es alta impedancia, tomado como cero a=%h", $realtime, a);
#1ps $display ("T=%0t Retraso de 10ps", $realtime);
end
endmodule
Tenga en cuenta que la precisión de timescale es de 1ps y por tanto se requiere $realtime para mostrar el valor de precisión para la declaración con expresión de retraso (a+b)*10ps.
xcelium> run T=0 a=x b=x T=10000 a=0 b=0 T=20000 a=4 b=0 T=24000 Después de un retraso de a=4 unidades T=29000 Después de un retraso de a=4 + b=1 = 5 unidades T=29050 Después de un retraso de 5 * 10ps T=42050 Expr evalúa a un retraso negativo T=58050 Retraso en hex T=58050 Retraso es desconocido, tomado como cero a=x T=58050 Retraso es alta impedancia, tomado como cero a=z T=58051 Retraso de 10ps xmsim: *W,RNQUIE: Simulation is complete.
Control de evento
Los cambios de valor en redes y variables pueden usarse como evento de sincronización para disparar la ejecución de otras declaraciones procesuales y es un evento implícito. El evento también puede basarse en la dirección del cambio como hacia 0 lo que lo hace un negedge y un cambio hacia 1 lo hace un posedge.
- Un
negedgees cuando hay una transición de 1 a X, Z o 0 y de X o Z a 0 - Un
posedgees cuando hay una transición de 0 a X, Z o 1 y de X o Z a 1
Una transición del mismo estado al mismo estado no se considera un flanco. Un evento de flanco como posedge o negedge solo puede detectarse en el LSB de una señal o variable vectorial. Si una expresión se evalúa al mismo resultado no puede considerarse como un evento.
module tb;
reg a, b;
initial begin
a <= 0;
#10 a <= 1;
#10 b <= 1;
#10 a <= 0;
#15 a <= 1;
end
// Iniciar otro bloque procesual que espera una actualización a
// señales hechas en el bloque procesual anterior
initial begin
@(posedge a);
$display ("T=%0t Flanco positivo de a detectado para 0->1", $time);
@(posedge b);
$display ("T=%0t Flanco positivo de b detectado para X->1", $time);
end
initial begin
@(posedge (a + b))
$display ("T=%0t Flanco positivo de a+b", $time);
@(a)
$display ("T=%0t Cambio en a encontrado", $time);
end
endmodule
ncsim> run T=10 Flanco positivo de a detectado para 0->1 T=20 Flanco positivo de b detectado para X->1 T=30 Flanco positivo de a+b T=45 Cambio en a encontrado ncsim: *W,RNQUIE: Simulation is complete.
Eventos nombrados
La palabra clave event puede usarse para declarar un evento nombrado que puede dispararse explícitamente. Un event no puede contener datos, no tiene duración de tiempo y puede hacerse ocurrir en cualquier momento particular. Un evento nombrado se dispara por el operador -> prefijándolo antes del manejador del evento nombrado. Un evento nombrado puede esperarse usando el operador @ descrito arriba.
module tb;
event a_event;
event b_event[5];
initial begin
#20 -> a_event;
#30; ->a_event;
#50 ->a_event;
#10 ->b_event[3];
end
always @ (a_event)
$display ("T=%0t [always] a_event se dispara", $time);
initial begin
#25;
@(a_event) $display ("T=%0t [initial] a_event se dispara", $time);
#10 @(b_event[3]) $display ("T=%0t [initial] b_event se dispara", $time);
end
endmodule
Los eventos nombrados pueden usarse para sincronizar dos o más procesos que se ejecutan concurrentemente. Por ejemplo, el bloque always y el segundo bloque initial se sincronizan por a_event. Los eventos pueden declararse como arrays como en el caso de b_event que es un array de tamaño 5 y el índice 3 se usa para propósito de disparo y espera.
ncsim> run T=20 [always] a_event se dispara T=50 [always] a_event se dispara T=50 [initial] a_event se dispara T=100 [always] a_event se dispara T=110 [initial] b_event se dispara ncsim: *W,RNQUIE: Simulation is complete.
Operador or de evento
El operador or puede usarse para esperar hasta que cualquiera de los eventos listados se dispare en una expresión. La coma , también puede usarse en lugar del operador or.
module tb;
reg a, b;
initial begin
$monitor ("T=%0t a=%0d b=%0d", $time, a, b);
{a, b} <= 0;
#10 a <= 1;
#5 b <= 1;
#5 b <= 0;
end
// Usar "or" entre eventos
always @ (posedge a or posedge b)
$display ("T=%0t flanco positivo de a o b encontrado", $time);
// Usar coma entre
always @ (posedge a, negedge b)
$display ("T=%0t flanco positivo de a o flanco negativo de b encontrado", $time);
always @ (a, b)
$display ("T=%0t Cualquier cambio en a o b", $time);
endmodule
ncsim> run T=0 flanco positivo de a o flanco negativo de b encontrado T=0 Cualquier cambio en a o b T=0 a=0 b=0 T=10 flanco positivo de a o b encontrado T=10 flanco positivo de a o flanco negativo de b encontrado T=10 Cualquier cambio en a o b T=10 a=1 b=0 T=15 flanco positivo de a o b encontrado T=15 Cualquier cambio en a o b T=15 a=1 b=1 T=20 flanco positivo de a o flanco negativo de b encontrado T=20 Cualquier cambio en a o b T=20 a=1 b=0 ncsim: *W,RNQUIE: Simulation is complete.
Lista de expresión de evento implícita
La lista de sensibilidad o lista de expresión de evento es a menudo una causa común de muchos errores funcionales en el RTL. Esto es porque el usuario puede olvidar actualizar la lista de sensibilidad después de introducir una nueva señal en el bloque procesual.
module tb;
reg a, b, c, d;
reg x, y;
// Lista de sensibilidad/expresión de evento formada por todas las
// señales dentro de () después del operador @ y en este caso
// es a, b, c o d
always @ (a, b, c, d) begin
x = a | b;
y = c ^ d;
end
initial begin
$monitor ("T=%0t a=%0b b=%0b c=%0b d=%0b x=%0b y=%0b", $time, a, b, c, d, x, y);
{a, b, c, d} <= 0;
#10 {a, b, c, d} <= $random;
#10 {a, b, c, d} <= $random;
#10 {a, b, c, d} <= $random;
end
endmodule
ncsim> run T=0 a=0 b=0 c=0 d=0 x=0 y=0 T=10 a=0 b=1 c=0 d=0 x=1 y=0 T=20 a=0 b=0 c=0 d=1 x=0 y=1 T=30 a=1 b=0 c=0 d=1 x=1 y=1 ncsim: *W,RNQUIE: Simulation is complete.
Si el usuario decide agregar nueva señal e y capturar la inversa en z, se debe tener especial cuidado de agregar e también en la lista de sensibilidad.
module tb;
reg a, b, c, d, e;
reg x, y, z;
// Agregar "e" también en lista de sensibilidad
always @ (a, b, c, d, e) begin
x = a | b;
y = c ^ d;
z = ~e;
end
initial begin
$monitor ("T=%0t a=%0b b=%0b c=%0b d=%0b e=%0b x=%0b y=%0b z=%0b", $time, a, b, c, d, e, x, y, z);
{a, b, c, d, e} <= 0;
#10 {a, b, c, d, e} <= $random;
#10 {a, b, c, d, e} <= $random;
#10 {a, b, c, d, e} <= $random;
end
endmodule
ncsim> run T=0 a=0 b=0 c=0 d=0 e=0 x=0 y=0 z=1 T=10 a=0 b=0 c=1 d=0 e=0 x=0 y=1 z=1 T=20 a=0 b=0 c=0 d=0 e=1 x=0 y=0 z=0 T=30 a=0 b=1 c=0 d=0 e=1 x=1 y=0 z=0 ncsim: *W,RNQUIE: Simulation is complete.
Verilog ahora permite que la lista de sensibilidad sea reemplazada por * que es un atajo conveniente que elimina estos problemas agregando todas las redes y variables que son leídas por la declaración como se muestra a continuación.
module tb;
reg a, b, c, d, e;
reg x, y, z;
// Usar @* o @(*)
always @ * begin
x = a | b;
y = c ^ d;
z = ~e;
end
initial begin
$monitor ("T=%0t a=%0b b=%0b c=%0b d=%0b e=%0b x=%0b y=%0b z=%0b", $time, a, b, c, d, e, x, y, z);
{a, b, c, d, e} <= 0;
#10 {a, b, c, d, e} <= $random;
#10 {a, b, c, d, e} <= $random;
#10 {a, b, c, d, e} <= $random;
end
endmodule
ncsim> run T=0 a=0 b=0 c=0 d=0 e=0 x=0 y=0 z=1 T=10 a=0 b=0 c=1 d=0 e=0 x=0 y=1 z=1 T=20 a=0 b=0 c=0 d=0 e=1 x=0 y=0 z=0 T=30 a=0 b=1 c=0 d=0 e=1 x=1 y=0 z=0 ncsim: *W,RNQUIE: Simulation is complete.
Control de evento sensible a nivel
La ejecución de una declaración procesual también puede retrasarse hasta que una condición se vuelva verdadera y puede lograrse con la palabra clave wait y es un control sensible a nivel.
La declaración wait evaluará una condición y si es falsa, las declaraciones procesuales que la siguen permanecerán bloqueadas hasta que la condición se vuelva verdadera.
module tb;
reg [3:0] ctr;
reg clk;
initial begin
{ctr, clk} <= 0;
wait (ctr);
$display ("T=%0t Contador alcanzó valor no-cero 0x%0h", $time, ctr);
wait (ctr == 4)
$display ("T=%0t Contador alcanzó 0x%0h", $time, ctr);
$finish;
end
always #10 clk = ~clk;
always @ (posedge clk)
ctr <= ctr + 1;
endmodule
ncsim> run T=10 Contador alcanzó valor no-cero 0x1 T=70 Contador alcanzó 0x4 T=90 Contador alcanzó 0x5 T=170 Contador alcanzó 0x9 Simulation complete via $finish(1) at time 170 NS + 1
Verilog
- Dominar Verilog: Tutorial completo para diseño de circuitos digitales
- Dominar la concatenación en Verilog: Guía práctica
- Comprender las asignaciones en Verilog: tipos, sintaxis y mejores prácticas
- Bloqueante vs. No bloqueante en Verilog: Diferencias clave explicadas
- Dominar el flujo de control en Verilog: IF-ELSE, Case y más
- Dominar las funciones en Verilog para diseño RTL eficiente
- Comprender las Tasks en Verilog: Funciones vs Tasks, sintaxis y uso
- Comprender los retrasos inter e intra asignación en Verilog
- Comprender los retrasos de puertas en Verilog: Temporización precisa para diseños digitales confiables
- Generador de reloj en Verilog: Guía de diseño e implementación
- Dominar las funciones matemáticas en Verilog para diseño de hardware eficiente