Verilog基础:时序调度中的竞争(三)(#0的使用)

相关阅读

Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm=1001.2014.3001.5482


作为一个硬件描述语言,Verilog HDL常常需要使用语句描述并行执行的电路,但其实在仿真器的底层,这些并行执行的语句是有先后顺序的,然而Verilog标准并没有将这些事件调度的顺序定死,而是给予了仿真器厂商一定的自由去实现自己的产品,这就导致了设计者如果不遵循一定的编程习惯,会导致意想不到的仿真结果,下面是一些相关的规则。

3、在设计中不要使用#0延时

尽管使用#0延迟可以消除一些竞争情况,但如果设计者不是对Verilog的时序调度有充分的了解,就不要使用#0延迟,这会使时序调度变得复杂难懂。

#0延迟将一个事件调度到inactive events region,这个region是处于active events region和nonblocking events region之间的,所以使用#0可以避免一个事件与当前的活跃事件竞争,下面举一个Verilog基础:时序调度中的竞争(一)-CSDN博客中竞争的例子加以说明。

复制代码
//例1
`timescale 1ns/1ps
module test();
    initial $display("The initial_0 execute");
    initial #0 $display("The initial_1 execute");
    initial $display("The initial_2 execute");
endmodule

输出:
The initial_2 execute //前两个输出的顺序是不定的
The initial_0 execute 
The initial_1 execute //这个输出一定是最后的

在上例中,三个initial结构同时执行,执行顺序是不定的,但是#0能保证#0 display("The initial_1 execute");语句被调度至inactive events region,而display("The initial_0 execute");和$display("The initial_2 execute");仍然被调度至active events region执行,所以The initial_1 execute一定最后输出,而后面两个输出的顺序仍然是不确定的,因为他们都被调度至active events region执行。但如果出现了两个#0,则这两个被调度至inactive events region的事件的执行顺序仍然是不确定的,如下所示。

复制代码
//例2
`timescale 1ns/1ps
module test();
    initial $display("The initial_0 execute");
    initial #0 $display("The initial_1 execute");
    initial #0 $display("The initial_2 execute");
endmodule

输出:
The initial_0 execute //这个输出一定是最先的
The initial_2 execute //后两个输出的顺序是不定的
The initial_1 execute

在其他竞争的例子中使用#0,也可以达到相同的效果,如下例所示。

复制代码
//例3
`timescale 1ns/1ps
module test();
  	reg a, b, c;
    initial begin
        #5;
        a = 1;
        b = 1;
        c = 1;
    end
    
   always @(a) $display("The initial_0 execute");
   always @(b) #0 $display("The initial_1 execute");
   always @(c) $display("The initial_2 execute");
endmodule

输出:
The initial_2 execute //前两个输出的顺序是不定的
The initial_0 execute 
The initial_1 execute //这个输出一定是最后的

按照Verilog基础:时序调度中的竞争(一)-CSDN博客中所说,被触发的三个always结构的执行顺序是不定的,但由于这里有#0,导致了#0 $display("The initial_1 execute");被调度到inactive events region执行,因此它在其他两个被调度至active events region的语句之后执行。

以及前文说过的结构交错的例子,他也可以用#0消除竞争,如下所示。

复制代码
//例4
`timescale 1ns/1ps
module test();
      reg a;
      wire b;
   initial begin
        #1;
        a = 1;
        #1;
        a = 0;
        #0 $display("b is %b", b);
   end
   assign b = a;
  
endmodule

输出:
b is 0

在使用了#0延迟后,#0 display("b is %b", b);被调度到inactive events region执行,因此在a=0执行后,首先会去执行位于active events regionassign b = a;,然后再执行#0 display("b is %b", b);,所以最后显示的结果是确定的0,而不会产生不确定。

下面的例子,显示了#0在并行块中的作用。

复制代码
//例5
`timescale 1ns/1ps
module test();

   initial fork
       $display("The initial_0 execute");
       #0 $display("The initial_1 execute");
       $display("The initial_2 execute"); 
   join
  
endmodule

输出:
The initial_2 execute  //前两个输出得顺序是不定的
The initial_0 execute
The initial_1 execute  //这个输出一定是最后的

并行块本不保证块内语句的执行顺序,但有了#0后,#0 $display("The initial_1 execute");被调度到inactive events region执行,因此它是最后输出的,而两个在active events region执行的语句执行顺序不定。

但需要注意的是,即使#0可以将语句调度至inactive events region执行,但它仍然需要遵循串行块的执行顺序原则,即一条语句一定在前一条语句执行完后再执行,举例如下。

复制代码
//例6
`timescale 1ns/1ps
module test();

   initial begin
       $display("The initial_0 execute");
       #0 $display("The initial_1 execute");
       $display("The initial_2 execute"); 
   end
  
endmodule

输出:
The initial_0 execute  //这三个输出的执行顺序是确定的
The initial_2 execute
The initial_1 execute  

即使display("The initial_2 execute");被调度到active events region执行,而#0 display("The initial_1 execute");被调度到inactive events region执行,但由于begin-end块的特性,display("The initial_2 execute");无法在#0 display("The initial_1 execute");前执行。而#0的执行,需要等待其他结构的active events region执行完成后才能执行,继而$display("The initial_2 execute");再被放入active events region执行,如下例所示。

复制代码
//例7
`timescale 1ns/1ps
module test();

   initial begin
       $display("The initial_0 execute");
       #0 $display("The initial_1 execute");
       $display("The initial_2 execute"); 
   end
   initial begin
       $display("The initial_3 execute")
       $display("The initial_4 execute")
    end
endmodule

输出:
//前三个输出能保证的是,The initial_4 execute在The initial_3 execute后输出,
//而且他们三个必须在#0 $display("The initial_1 execute");执行前输出
The initial_0 execute  
The initial_3 execute
The initial_4 execute  
//下面的语句一定在第四个输出
The initial_1 execute
//下面的语句一定在上条语句后(第五个输出)
The initial_2 execute

可以看到,对于#0的使用需要对事件调度有较深的理解,而#0是为了消除竞争的,因此这只是治标不治本的方法,因此最好的方法是避免写出有竞争的语句,而不是盲目使用#0,这就是本文的标题所说的。

相关推荐
俺不是西瓜太郎´•ﻌ•`6 小时前
大实验:基于赛灵思csg324100T,pmodMAXsonar的危险距离警报
fpga开发
ThreeYear_s9 小时前
基于FPGA的超声波显示水位距离,通过蓝牙传输水位数据到手机,同时支持RAM存储水位数据,读取数据。
fpga开发
szxinmai主板定制专家10 小时前
【飞腾AI加固服务器】全国产化飞腾+昇腾310+PCIe Switch的AI大模型服务器解决方案
运维·服务器·arm开发·人工智能·fpga开发
GateWorld11 小时前
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (三)数据格式
fpga开发·mipi csi2
hahaha601614 小时前
FPGA静态功耗
fpga开发
碎碎思14 小时前
FPGA定点和浮点数学运算-实例对比
fpga开发
GateWorld1 天前
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析LLP (二)
fpga开发·mipi csi2
hahaha60162 天前
Xilinx 325T FPGA 中的 GT(GTP 或 GTX)收发器和普通 LVDS 接口的差模和共模电压
fpga开发
hahaha60162 天前
FPGA没有使用的IO悬空对漏电流有没有影响
fpga开发
贝塔实验室2 天前
FPGA 动态重构配置流程
驱动开发·fpga开发·硬件架构·硬件工程·射频工程·fpga·基带工程