📜  Verilog时序控制

📅  最后修改于: 2021-01-11 14:53:36             🧑  作者: Mango

Verilog时序控制

在仿真中需要时序控制语句以延长时间。程序语句的执行时间应使用时序控制来指定。

Verilog中有以下类型的定时控件:

  • 延迟控制
  • 边缘敏感事件控制
  • 水平敏感事件控制
  • 命名事件

延迟控制是在模拟器遇到语句与执行语句之间增加延迟的一种方式。

通过事件表达式,可以将语句延迟到某个模拟事件发生之前,该事件可以更改网络或变量上的值或在另一个过程中触发的显式命名的事件。

可以通过以下方法之一来提高仿真时间。网和门的建模具有内部延迟,也可以延长仿真时间。

延迟控制

通过指定遇到该语句时的执行等待时间来实现延迟控制。符号#用于指定延迟。

我们可以通过三种方式指定基于延迟的时序控制:

  • 常规延迟控制:将在程序分配中将其指定为非零数字。
  • 分配内延迟控制:在这种情况下,延迟将在分配操作员的右侧指定。右侧表达式将在当前时间求值,并且赋值仅在延迟之后发生。
  • 零延迟控制:零延迟控制语句在程序分配的左侧指定零延迟值。此方法用于确保在模拟时间结束时执行该语句。这意味着,在该模拟时间内执行所有其他语句之后,将执行零延迟控制语句。

事件控制

事件控制语句或语句块的执行。变量和网络上的值更改可以用作同步事件来触发其他过程语句的执行,并且是隐式事件。

该事件基于朝向0的变化方向,这使其成为一个负值,而朝向1的变化则使其成为一个姿势

  • 忽略是从1到X,Z或0以及从X或Z到0的过渡
  • 姿势是从0到X,Z或1以及从X或Z到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.