📅  最后修改于: 2021-01-11 14:53:36             🧑  作者: Mango
在仿真中需要时序控制语句以延长时间。程序语句的执行时间应使用时序控制来指定。
Verilog中有以下类型的定时控件:
延迟控制是在模拟器遇到语句与执行语句之间增加延迟的一种方式。
通过事件表达式,可以将语句延迟到某个模拟事件发生之前,该事件可以更改网络或变量上的值或在另一个过程中触发的显式命名的事件。
可以通过以下方法之一来提高仿真时间。网和门的建模具有内部延迟,也可以延长仿真时间。
通过指定遇到该语句时的执行等待时间来实现延迟控制。符号#用于指定延迟。
我们可以通过三种方式指定基于延迟的时序控制:
事件控制语句或语句块的执行。变量和网络上的值更改可以用作同步事件来触发其他过程语句的执行,并且是隐式事件。
该事件基于朝向0的变化方向,这使其成为一个负值,而朝向1的变化则使其成为一个姿势。
从相同状态到相同状态的过渡不应该视为边缘。边缘事件只能在矢量信号或变量的LSB上检测到。
如果表达式的计算结果相同,则不能将其视为事件。有不同类型的基于事件的控件。
1.常规事件控制:语句的执行将在信号变化或信号的正向或负向转换时发生。例如,时钟的姿势,以及对重置的忽略等。
@(posedge clock) out = in;
@(clock) z = n << 2;
2.命名事件控件: 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
命名事件用于同步两个或多个同时运行的进程。事件可以声明为数组。
例如,始终块和第二个初始块通过a_event同步。事件可以声明为数组,例如b_event,它是大小为5的数组,索引3用于触发和等待目的。
3.事件OR控制:信号或事件的转换可以触发语句的执行,如下所示。
or运算符可以等到表达式中触发任何列出的事件。可以使用逗号(,)代替or运算符。
always @(clock or in) //Wait for the clock or in to change
OR
always @(clock, in)
在Verilog中,@字符指定一个边缘敏感事件控件,该控件将阻塞,直到事件标识符之一的值(边缘)发生过渡为止。
边缘事件被排队,然后由@(…)防护服务,而不是@(?)是等待边缘事件的防护,直到发生边缘事件才阻塞。
与@(…)防护有关的唯一边缘事件是等待时发生的事件。在到达后卫之前发生的边缘事件与后卫无关。
可以延迟过程语句的执行,直到条件变为true并使用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
执行完成后,将产生以下输出:
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
事件表达列表或敏感性列表通常是RTL中许多功能错误的常见原因,因为用户可能会在程序块中引入新信号后忘记更新敏感性列表。
module tb;
reg a, b, c, d;
reg x, y;
// signals inside () after @ operator
// it is a, b, c or 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.
如果用户决定添加新信号e并将其倒数捕获到z中,则必须格外小心将e也添加到灵敏度列表中。
module tb;
reg a, b, c, d, e;
reg x, y, z;
// Add "e" also into sensitivity list
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允许将灵敏度列表替换为*,这是一种便捷的速记方式,它通过添加语句读取的所有网络和变量来消除这些问题,如以下代码所示。
module tb;
reg a, b, c, d, e;
reg x, y, z;
// Use @* or @(*)
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.