Управление задержками в Verilog: техники таймингов и лучшие практики
В Verilog два типа таймингового управления: задержка (delay) и событие (event). Задержка — способ добавить задержку между моментом, когда симулятор встречает оператор, и моментом его фактического выполнения. Событийное выражение откладывает выполнение до возникновения события симуляции: изменения значения на net или переменной (неявное событие) или явно именованного события, инициированного в другой процедуре.
Время симуляции может продвигаться следующими способами. Гейты и nets с внутренними задержками также продвигают время симуляции.
Управление задержкой (Delay Control)
Если выражение задержки оценивается в неизвестное (X) или высокоомное (Z) значение, оно интерпретируется как нулевая задержка. Если отрицательное — как 2's complement беззнаковое целое того же размера, что и переменная 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
Обратите внимание: точность timescale 1ps, поэтому для оператора с задержкой (a+b)*10ps требуется $realtime для отображения точности.
Simulation Log
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.
Управление событиями (Event Control)
Изменения значений на nets и переменных можно использовать как синхронизирующие события для запуска других процедурных операторов — это неявное событие. Событие также может основываться на направлении изменения: к 0 — negedge, к 1 — posedge.
negedge— переход 1→X, 1→Z, 1→0, X→0, Z→0posedge— переход 0→X, 0→Z, 0→1, X→1, Z→1
Переход из состояния в то же состояние не считается фронтом. Фронт (posedge/negedge) детектируется только на младшем бите векторного сигнала или переменной. Если выражение оценивается в тот же результат, это не считается событием.
module tb;
reg a, b;
initial begin
a <= 0;
#10 a <= 1;
#10 b <= 1;
#10 a <= 0;
#15 a <= 1;
end
// Блок, ждущий обновления сигналов из вышеуказанного блока
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
Simulation Log
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.
Именованные события (Named Events)
Ключевое слово event объявляет именованное событие, которое можно инициировать явно. event не хранит данных, не имеет длительности и может происходить в любой момент. Именованное событие инициируется оператором -> перед дескриптором события. Ожидание именованного события — оператор @.
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
Именованные события синхронизируют параллельно выполняющиеся процессы. Например, always и второй initial синхронизированы через a_event. События можно объявлять как массивы: b_event — массив размером 5, индекс 3 используется для триггера и ожидания.
Simulation Log
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.
Оператор or для событий
Оператор or позволяет ждать, пока сработает любое из перечисленных событий. Запятая , также допускается вместо 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
// "or" между событиями
always @ (posedge a or posedge b) $display ("T=%0t posedge of a or b found", $time);
// Запятая между событиями
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
Simulation Log
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.
Неявный список выражений событий (Implicit Event Expression List)
Список чувствительности (sensitivity list) — частая причина функциональных ошибок в RTL. Пользователь может забыть обновить его после добавления нового сигнала в процедурный блок.
module tb;
reg a, b, c, d;
reg x, y;
// Список чувствительности: a, b, c, 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
Simulation Log
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.
Если добавить новый сигнал e и захватить инверс в z, нужно обязательно добавить e в список чувствительности.
module tb;
reg a, b, c, d, e;
reg x, y, z;
// Добавляем "e" в список чувствительности
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
Simulation Log
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 позволяет заменить список чувствительности на * — удобный сокращенный синтаксис, автоматически включающий все nets и переменные, читаемые оператором.
module tb;
reg a, b, c, d, e;
reg x, y, z;
// @* или @(*)
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
Simulation Log
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.
Уровневое управление событиями (Level Sensitive Event Control)
Выполнение процедурного оператора можно задержать до истинности условия с помощью ключевого слова wait — это уровень-чувствительное управление.
wait оценивает условие: если ложно, последующие операторы остаются заблокированными до истинности условия.
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
Simulation Log
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
- Освоение Verilog: полное руководство по проектированию цифровых схем
- Освоение конкатенации в Verilog: практическое руководство
- Понимание назначений в Verilog: типы, синтаксис и лучшие практики
- Блокирующие и неблокирующие назначения в Verilog: ключевые различия
- Управление потоком в Verilog: IF-ELSE, Case, циклы и не только
- Мастерство функций в Verilog для эффективного RTL дизайна
- Понимание задач (Tasks) в Verilog: отличия от функций, синтаксис и применение
- Межназначенные и внутриназначенные задержки в Verilog: понимание и применение
- Понимание задержек гейтов в Verilog: точное тайминг для надежных цифровых схем
- Генератор тактового сигнала в Verilog: проектирование и реализация