Verilog Blocking vs Non-Blocking: Diferenças Essenciais Explicadas
Atribuição Bloqueante (Blocking)
Atribuições blocking usam o operador = e são executadas sequencialmente dentro de um bloco procedural. No entanto, isso não impede a execução de instruções que rodam em blocos 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
Note que existem dois blocos initial executados em paralelo no início da simulação. As instruções são executadas sequencialmente em cada bloco e ambos terminam no tempo 0ns. Para ser mais específico, a variável a recebe atribuição primeiro, seguida pela instrução $display e depois todas as outras. Isso é visível na saída onde b e c mostram 8'hxx no primeiro $display, pois suas atribuições ainda não foram executadas.
Log de Simulação
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.
No próximo exemplo, adicionamos atrasos ao mesmo conjunto de instruções para ver o comportamento.
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
Log de Simulação
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.
Atribuição Não-Bloqueante (Non-blocking)
Atribuição non-blocking permite agendar atribuições sem bloquear a execução das instruções seguintes, especificada pelo símbolo <=. É interessante notar que o mesmo símbolo é usado como operador relacional em expressões e como operador de atribuição no contexto de atribuição não-bloqueante. Se pegarmos o primeiro exemplo e substituirmos todos os = por <=, veremos diferença na saída.
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
Veja que todos os $display imprimiram 'h'x. A razão está na forma como atribuições não-bloqueantes são executadas: o RHS de cada instrução não-bloqueante de um determinado passo de tempo é capturado, e a execução passa para a próxima instrução. O valor RHS capturado é atribuído à variável LHS apenas no final do passo de tempo.
Log de Simulação
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.
Analisando o fluxo de execução:
|__ Spawn Bloco1: initial | |___ Tempo #0ns: a <= 8'DA, não-bloqueante, captura RHS (8'hDA) e executa próximo passo | |___ Tempo #0ns: $display() é bloqueante, executa: mas a não recebeu novo valor, a=8'hx | |___ Tempo #0ns: b <= 8'F1, não-bloqueante, captura RHS (8'hF1) e executa próximo passo | |___ Tempo #0ns: $display() é bloqueante, executa. Mas b não recebeu novo valor, b=8'hx | |___ Tempo #0ns: c <= 8'30, não-bloqueante, captura RHS (8'h30) e executa próximo passo | |___ Tempo #0ns: $display() é bloqueante, executa. Mas c não recebeu novo valor, c=8'hx | |___ Fim do passo de tempo e bloco initial, atribui valores capturados a a, b, c |__ Spawn Bloco2: initial | |___ Tempo #0ns: d <= 8'AA, não-bloqueante, captura RHS (8'hAA) e executa próximo passo | |___ Tempo #0ns: $display() é bloqueante, executa: mas d não recebeu novo valor, d=8'hx | |___ Tempo #0ns: e <= 8'55, não-bloqueante, captura RHS (8'h55) e executa próximo passo | |___ Tempo #0ns: $display() é bloqueante, executa. Mas e não recebeu novo valor, e=8'hx | |___ Fim do passo de tempo e bloco initial, atribui valores capturados a d e e |__ Fim da simulação em #0ns
Agora, usamos o segundo exemplo substituindo todas as atribuições bloqueantes por não-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
Mais uma vez, a saída é diferente da anterior.
Log de Simulação
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.
Fluxo de execução detalhado:
|__ Spawn Bloco1 em #0ns: initial | |___ Tempo #0ns: a <= 8'DA, não-bloqueante, captura RHS (8'hDA) e executa próximo passo | |___ Tempo #0ns: $display() é bloqueante, executa: mas a não recebeu novo valor, a=8'hx | |___ Fim do passo de tempo: Atribui valor capturado a a, a agora é 8'hDA | |___ Espera até tempo avançar 10 unidades para #10ns | | |___ Tempo #10ns: b <= 8'F1, não-bloqueante, captura RHS (8'hF1) e executa próximo passo | |___ Tempo #10ns: $display() é bloqueante, executa. Mas b não recebeu novo valor, b=8'hx | |___ Tempo #10ns: c <= 8'30, não-bloqueante, captura RHS (8'h30) e executa próximo passo | |___ Tempo #10ns: $display() é bloqueante, executa. Mas c não recebeu novo valor, c=8'hx | |___ Fim do passo de tempo e bloco initial, atribui valores capturados a b, c |__ Spawn Bloco2 em #0ns: initial | |___ Espera até tempo avançar 5 unidades para #5ns | | |___ Tempo #5ns: d <= 8'AA, não-bloqueante, captura RHS (8'hAA) e executa próximo passo | |___ Tempo #5ns: $display() é bloqueante, executa: mas d não recebeu novo valor, d=8'hx | |___ Fim do passo de tempo: Atribui valor capturado a d, d agora é 8'hAA | |___ Espera até tempo avançar 5 unidades para #5ns | | |___ Tempo #10ns: e <= 8'55, não-bloqueante, captura RHS (8'h55) e executa próximo passo | |___ Tempo #10ns: $display() é bloqueante, executa. Mas e não recebeu novo valor, e=8'hx | |___ Fim do passo de tempo e bloco initial, atribui valores capturados a e, e agora é 8'h55 |__ Fim da simulação em #10ns
Verilog
- Domine o Verilog: Tutorial Completo para Projeto de Circuitos Digitais
- Dominando Concatenação Verilog: Guia Prático
- Dominando Funções em Verilog para Design RTL Eficiente
- Entendendo Tasks em Verilog: Funções vs Tasks, Sintaxe e Uso
- Gerador de Clock Verilog: Guia de Design e Implementação
- Dominando Funções Matemáticas do Verilog para Design de Hardware Eficiente
- Dominando $timeformat do Verilog: Precisão e Controle de Exibição
- Entendendo e Imprimindo Timescales do Verilog em Qualquer Escopo
- Dominando I/O de Arquivos em Verilog: Abrir, Ler, Escrever e Fechar
- Guia para Iniciantes em Verilog: Exemplo Hello World