Entendendo Controle de Atraso em Verilog: Técnicas de Timing e Boas Práticas
Existem dois tipos de controle de tempo em Verilog: expressões de atraso (delay) e evento (event). O controle de atraso adiciona um atraso entre o momento que o simulador encontra a instrução e quando a executa. A expressão de evento atrasa a instrução até a ocorrência de algum evento de simulação: mudança de valor em net ou variável (evento implícito) ou evento explicitamente nomeado disparado em outro procedimento.
O tempo de simulação pode ser avançado por:
- Controle de atraso (#)
- Controle de evento (@)
- Instrução wait
Portas e redes modeladas com atrasos internos também avançam o tempo de simulação.
Controle de Atraso (Delay Control)
Se a expressão de atraso avaliar para valor desconhecido (X) ou alta impedância (Z), será interpretado como atraso zero. Se avaliar para valor negativo, será interpretado como inteiro sem sinal em complemento de 2 do mesmo tamanho de uma variável 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 After a delay of a=%0d units", $realtime, a);
#(a+b) $display ("T=%0t After a delay of a=%0d + b=%0d = %0d units", $realtime, a, b, a+b);
#((a+b)*10ps) $display ("T=%0t After a delay of %0d * 10ps", $realtime, a+b);
#(b-a) $display ("T=%0t Expr evaluates to a negative delay", $realtime);
#('h10) $display ("T=%0t Delay in hex", $realtime);
a = 'hX; #(a) $display ("T=%0t Delay is unknown, taken as zero a=%h", $realtime, a);
a = 'hZ; #(a) $display ("T=%0t Delay is in high impedance, taken as zero a=%h", $realtime, a);
#1ps $display ("T=%0t Delay of 10ps", $realtime);
end
endmodule
Note que a precisão do timescale é 1ps, por isso $realtime é necessário para mostrar a precisão na instrução com expressão de atraso (a+b)*10ps.
Log de Simulação
xcelium> run T=0 a=x b=x T=10000 a=0 b=0 T=20000 a=4 b=0 T=24000 After a delay of a=4 units T=29000 After a delay of a=4 + b=1 = 5 units T=29050 After a delay of 5 * 10ps T=42050 Expr evaluates to a negative delay T=58050 Delay in hex T=58050 Delay is unknown, taken as zero a=x T=58050 Delay is in high impedance, taken as zero a=z T=58051 Delay of 10ps xmsim: *W,RNQUIE: Simulation is complete.
Controle de Evento (Event Control)
Mudanças de valor em nets e variáveis podem ser usadas como evento de sincronização para disparar execução de outras instruções procedurais (evento implícito). O evento pode ser baseado na direção da mudança: para 0 = negedge, para 1 = posedge.
negedge: transição de 1 para X, Z ou 0; e de X ou Z para 0posedge: transição de 0 para X, Z ou 1; e de X ou Z para 1
Transição do mesmo estado para o mesmo estado não é considerada edge. Edge só pode ser detectado no LSB de sinal/vetor. Se expressão avaliar para mesmo resultado, não é evento.
module tb;
reg a, b;
initial begin
a <= 0;
#10 a <= 1;
#10 b <= 1;
#10 a <= 0;
#15 a <= 1;
end
// Bloco que espera atualização de sinais do bloco acima
initial begin
@(posedge a);
$display ("T=%0t Posedge of a detected for 0->1", $time);
@(posedge b);
$display ("T=%0t Posedge of b detected for X->1", $time);
end
initial begin
@(posedge (a + b))
$display ("T=%0t Posedge of a+b", $time);
@(a)
$display ("T=%0t Change in a found", $time);
end
endmodule
Log de Simulação
ncsim> run T=10 Posedge of a detected for 0->1 T=20 Posedge of b detected for X->1 T=30 Posedge of a+b T=45 Change in a found ncsim: *W,RNQUIE: Simulation is complete.
Eventos Nomeados (Named Events)
A palavra-chave event declara um evento nomeado que pode ser disparado explicitamente. Um event não guarda dados, não tem duração e pode ocorrer a qualquer tempo. Disparado pelo operador -> prefixado ao handle do evento. Esperado com operador @.
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 is triggered", $time);
initial begin
#25; @(a_event) $display ("T=%0t [initial] a_event is triggered", $time);
#10 @(b_event[3]) $display ("T=%0t [initial] b_event is triggered", $time);
end
endmodule
Eventos nomeados sincronizam processos concorrentes. O bloco always e o segundo initial são sincronizados por a_event. Events podem ser arrays como b_event (tamanho 5), índice 3 usado para trigger e wait.
Log de Simulação
ncsim> run T=20 [always] a_event is triggered T=50 [always] a_event is triggered T=50 [initial] a_event is triggered T=100 [always] a_event is triggered T=110 [initial] b_event is triggered ncsim: *W,RNQUIE: Simulation is complete.
Operador or de Evento
O operador or espera até que qualquer um dos eventos listados seja disparado. Vírgula , também pode ser usada.
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
// Usa "or" entre eventos
always @ (posedge a or posedge b)
$display ("T=%0t posedge of a or b found", $time);
// Usa vírgula
always @ (posedge a, negedge b)
$display ("T=%0t posedge of a or negedge of b found", $time);
always @ (a, b)
$display ("T=%0t Any change on a or b", $time);
endmodule
Log de Simulação
ncsim> run T=0 posedge of a or negedge of b found T=0 Any change on a or b T=0 a=0 b=0 T=10 posedge of a or b found T=10 posedge of a or negedge of b found T=10 Any change on a or b T=10 a=1 b=0 T=15 posedge of a or b found T=15 Any change on a or b T=15 a=1 b=1 T=20 posedge of a or negedge of b found T=20 Any change on a or b T=20 a=1 b=0 ncsim: *W,RNQUIE: Simulation is complete.
Lista de Expressão de Evento Implícita
A lista de sensibilidade (event expression list) é causa comum de erros funcionais no RTL, pois o usuário pode esquecer de atualizá-la após introduzir novo sinal no bloco procedural.
module tb;
reg a, b, c, d;
reg x, y;
// Lista de sensibilidade formada por sinais dentro de () após @
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
Log de Simulação
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.
Se o usuário adicionar novo sinal e e capturar inverso em z, deve adicionar e na lista de sensibilidade.
module tb;
reg a, b, c, d, e;
reg x, y, z;
// Adiciona "e" na lista de sensibilidade
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
Log de Simulação
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 permite substituir a lista de sensibilidade por *, atalho conveniente que elimina esses problemas adicionando todas as nets e variáveis lidas pela instrução.
module tb;
reg a, b, c, d, e;
reg x, y, z;
// Use @* ou @(*)
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
Log de Simulação
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.
Controle de Evento Sensível a Nível (Level Sensitive Event Control)
Execução de instrução procedural pode ser atrasada até condição se tornar verdadeira com wait (controle sensível a nível).
O wait avalia condição; se falsa, instruções seguintes permanecem bloqueadas até condição tornar-se verdadeira.
module tb;
reg [3:0] ctr;
reg clk;
initial begin
{ctr, clk} <= 0;
wait (ctr);
$display ("T=%0t Counter reached non-zero value 0x%0h", $time, ctr);
wait (ctr == 4) $display ("T=%0t Counter reached 0x%0h", $time, ctr);
$finish;
end
always #10 clk = ~clk;
always @ (posedge clk) ctr <= ctr + 1;
endmodule
Log de Simulação
ncsim> run T=10 Counter reached non-zero value 0x1 T=70 Counter reached 0x4 T=90 Counter reached 0x5 T=170 Counter reached 0x9 Simulation complete via $finish(1) at time 170 NS + 1
Verilog
- Domine o Verilog: Tutorial Completo para Projeto de Circuitos Digitais
- Dominando Concatenação Verilog: Guia Prático
- Entendendo Atribuições em Verilog: Tipos, Sintaxe e Boas Práticas
- Verilog Blocking vs Non-Blocking: Diferenças Essenciais Explicadas
- Dominando Fluxo de Controle em Verilog: IF-ELSE, Case, Loops e Mais
- Dominando Funções em Verilog para Design RTL Eficiente
- Entendendo Tasks em Verilog: Funções vs Tasks, Sintaxe e Uso
- Entendendo Atrasos Inter- e Intra-Atribuição em Verilog
- Entendendo Atrasos de Porta em Verilog: Timing Preciso para Designs Digitais Confiáveis
- Gerador de Clock Verilog: Guia de Design e Implementação