Bloqueante vs. No bloqueante en Verilog: Diferencias clave explicadas
Asignación bloqueante
Las declaraciones de asignación bloqueante usan = y se ejecutan una tras otra en un bloque procesual. Sin embargo, esto no impide la ejecución de declaraciones que se ejecutan en bloques paralelos.
module tb;
reg [7:0] a, b, c, d, e;
initial begin
a = 8'hDA;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
b = 8'hF1;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
c = 8'h30;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
end
initial begin
d = 8'hAA;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
e = 8'h55;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
end
endmodule
Tenga en cuenta que hay dos bloques initial que se ejecutan en paralelo al iniciar la simulación. Las declaraciones se ejecutan secuencialmente en cada bloque y ambos bloques terminan en 0ns. Para ser más específico, la variable a se asigna primero, seguida por la declaración display, que a su vez es seguida por todas las demás declaraciones. Esto es visible en la salida donde las variables b y c son 8'hxx en la primera declaración display. Esto es porque las asignaciones de b e c aún no se han ejecutado cuando se llama al primer $display.
ncsim> run [0] a=0xda b=0xx c=0xx [0] a=0xda b=0xf1 c=0xx [0] a=0xda b=0xf1 c=0x30 [0] d=0xaa e=0xx [0] d=0xaa e=0x55 ncsim: *W,RNQUIE: Simulation is complete.
En el siguiente ejemplo, agregaremos algunos retrasos al mismo conjunto de declaraciones para ver cómo se comporta.
module tb;
reg [7:0] a, b, c, d, e;
initial begin
a = 8'hDA;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
#10 b = 8'hF1;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
c = 8'h30;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
end
initial begin
#5 d = 8'hAA;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
#5 e = 8'h55;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
end
endmodule
ncsim> run [0] a=0xda b=0xx c=0xx [5] d=0xaa e=0xx [10] a=0xda b=0xf1 c=0xx [10] a=0xda b=0xf1 c=0x30 [10] d=0xaa e=0x55 ncsim: *W,RNQUIE: Simulation is complete.
Asignación no bloqueante
La asignación no bloqueante permite que las asignaciones se programen sin bloquear la ejecución de las declaraciones siguientes y se especifica con el símbolo <=. Es interesante notar que el mismo símbolo se usa como operador relacional en expresiones y como operador de asignación en el contexto de una asignación no bloqueante. Si tomamos el primer ejemplo de arriba y reemplazamos todos los símbolos = con el operador de asignación no bloqueante <=, veremos alguna diferencia en la salida.
module tb;
reg [7:0] a, b, c, d, e;
initial begin
a <= 8'hDA;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
b <= 8'hF1;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
c <= 8'h30;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
end
initial begin
d <= 8'hAA;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
e <= 8'h55;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
end
endmodule
Observe que todas las declaraciones $display imprimieron 'h'x. La razón de este comportamiento radica en la forma en que se ejecutan las asignaciones no bloqueantes. El RHS de cada declaración no bloqueante de un paso de tiempo particular se captura y pasa a la siguiente declaración. El valor RHS capturado se asigna a la variable LHS solo al final del paso de tiempo.
ncsim> run [0] a=0xx b=0xx c=0xx [0] a=0xx b=0xx c=0xx [0] a=0xx b=0xx c=0xx [0] d=0xx e=0xx [0] d=0xx e=0xx ncsim: *W,RNQUIE: Simulation is complete.
Así, si desglosamos el flujo de ejecución del ejemplo anterior obtendremos algo como lo que se muestra a continuación.
|__ Spawn Block1: initial | |___ Time #0ns : a <= 8'DA, es no bloqueante, así que anota valor de RHS (8'hDA) y ejecuta siguiente paso | |___ Time #0ns : $display() es bloqueante, así que ejecuta esta declaración: Pero a no ha recibido nuevos valores así que a=8'hx | |___ Time #0ns : b <= 8'F1, es no bloqueante, así que anota valor de RHS (8'hF1) y ejecuta siguiente paso | |___ Time #0ns : $display() es bloqueante, así que ejecuta esta declaración. Pero b no ha recibido nuevos valores así que b=8'hx | |___ Time #0ns : c <= 8'30, es no bloqueante, así que anota valor de RHS (8'h30) y ejecuta siguiente paso | |___ Time #0ns : $display() es bloqueante, así que ejecuta esta declaración. Pero c no ha recibido nuevos valores así que c=8'hx | |___ Fin de paso de tiempo y bloque initial, asigna valores capturados a variables a, b, c | |__ Spawn Block2: initial | |___ Time #0ns : d <= 8'AA, es no bloqueante, así que anota valor de RHS (8'hAA) y ejecuta siguiente paso | |___ Time #0ns : $display() es bloqueante, así que ejecuta esta declaración: Pero d no ha recibido nuevos valores así que d=8'hx | |___ Time #0ns : e <= 8'55, es no bloqueante, así que anota valor de RHS (8'h55) y ejecuta siguiente paso | |___ Time #0ns : $display() es bloqueante, así que ejecuta esta declaración. Pero e no ha recibido nuevos valores así que e=8'hx | |___ Fin de paso de tiempo y bloque initial, asigna valores capturados a variables d y e | |__ Fin de simulación en #0ns
A continuación, usemos el segundo ejemplo y reemplacemos todas las declaraciones bloqueantes por no bloqueantes.
module tb;
reg [7:0] a, b, c, d, e;
initial begin
a <= 8'hDA;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
#10 b <= 8'hF1;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
c <= 8'h30;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
end
initial begin
#5 d <= 8'hAA;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
#5 e <= 8'h55;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
end
endmodule
Una vez más podemos ver que la salida es diferente a la que obtuvimos antes.
ncsim> run [0] a=0xx b=0xx c=0xx [5] d=0xx e=0xx [10] a=0xda b=0xx c=0xx [10] a=0xda b=0xx c=0xx [10] d=0xaa e=0xx ncsim: *W,RNQUIE: Simulation is complete.
Si desglosamos el flujo de ejecución obtendremos algo como lo que se muestra a continuación.
|__ Spawn Block1 en #0ns: initial | |___ Time #0ns : a <= 8'DA, es no bloqueante, así que anota valor de RHS (8'hDA) y ejecuta siguiente paso | |___ Time #0ns : $display() es bloqueante, así que ejecuta esta declaración: Pero a no ha recibido nuevos valores así que a=8'hx | |___ Fin de paso de tiempo: Asigna valor capturado a variable a, y a ahora es 8'hDA | |___ Espera hasta que el tiempo avance 10 unidades de tiempo a #10ns | | |___ Time #10ns : b <= 8'F1, es no bloqueante, así que anota valor de RHS (8'hF1) y ejecuta siguiente paso | |___ Time #10ns : $display() es bloqueante, así que ejecuta esta declaración. Pero b no ha recibido nuevos valores así que b=8'hx | |___ Time #10ns : c <= 8'30, es no bloqueante, así que anota valor de RHS (8'h30) y ejecuta siguiente paso | |___ Time #10ns : $display() es bloqueante, así que ejecuta esta declaración. Pero c no ha recibido nuevos valores así que c=8'hx | |___ Fin de paso de tiempo y bloque initial, asigna valores capturados a variables b, c | |__ Spawn Block2 en #0ns: initial | |___ Espera hasta que el tiempo avance 5 unidades de tiempo a #5ns | | |___ Time #5ns : d <= 8'AA, es no bloqueante, así que anota valor de RHS (8'hAA) y ejecuta siguiente paso | |___ Time #5ns : $display() es bloqueante, así que ejecuta esta declaración: Pero d no ha recibido nuevos valores así que d=8'hx | |___ Fin de paso de tiempo: Asigna valor capturado a variable d, y d ahora es 8'hAA | |___ Espera hasta que el tiempo avance 5 unidades de tiempo a #5ns | | |___ Time #10ns : e <= 8'55, es no bloqueante, así que anota valor de RHS (8'h55) y ejecuta siguiente paso | |___ Time #10ns : $display() es bloqueante, así que ejecuta esta declaración. Pero e no ha recibido nuevos valores así que e=8'hx | |___ Fin de paso de tiempo y bloque initial, asigna valores capturados a variable e, y e ahora es 8'h55 | |__ Fin de simulación en #10ns
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
- Dominar las funciones en Verilog para diseño RTL eficiente
- Comprender las Tasks en Verilog: Funciones vs Tasks, sintaxis y uso
- 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
- Dominar $timeformat en Verilog: Control de precisión y visualización
- Comprender e imprimir timescales de Verilog en cualquier alcance
- Dominando la E/S de Archivos en Verilog: Abrir, Leer, Escribir y Cerrar
- Guía para Principiantes de Verilog: Ejemplo Hola Mundo