ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

从事件调度理解阻塞和非阻塞

2022-08-14 09:32:26  阅读:155  来源: 互联网

标签:begin initial 阻塞 调度 理解 strobe 事件 赋值


0 为什么要有事件调度

我们知道Verilog是一种并行编程语言,然而Verilog是通过计算机执行的,那么必然要遵循计算机顺序执行的逻辑

当多条语句都被触发时,我们如何确定语句的执行顺序就需要一种规则来做出限定

1 几个关键信息

仿真的代码是由一个个离散事件组成,运行Verilog也就是执行一个个时间和线程

进程包括UDP、module、initial块、always块、连续赋值语句、异步任务和过程赋值语句

在进行仿真时,所有线网、变量和命名块发生变化时,都被认为是更新事件,而进程对更新事件是敏感的,更新事件执行时,所有对该实践敏感的进程都会按照任意顺序进行评估

仿真时间用来模拟被仿真电路所需的实际时间

2 事件队列

Verilog事件队列被分为五个区域:活跃事件、非活跃事件、非阻塞赋值更新时间、监视事件和将来事件

下面是大佬总结的图

在执行顺序上:活跃事件 -> 非活跃事件 -> 非阻塞赋值更新事件 -> 监控事件 -> 将来事件

不过这五个事件内包含的操作,它们的执行顺序是随机的

我对当前仿真时间的理解是当T,将来仿真时间是次T

3 确定性和不确定性

3.1 确定性
  1. begin...end中的语句都是按顺序执行的
  2. 非阻塞赋值的执行顺序也是按照语句出现的顺序执行
initial begin
   a <= 0;
   a <= 1;
end

以上面这个例子来说,根据确定性可知,两条语句的执行按顺序执行的,在进入非阻塞赋值更新时,变量a的值先被更新为0,后被更新为1

3.2 不确定性

在确定性中指出,在一个block中,所有语句的执行都是顺序执行的,那如果语句不在同一个block中呢?

对于不同的block,具体是哪个block先执行,这个行为是不确定的,比如下面这个例子

assign q = a;

always @(*) begin
    q = b;
end

两条语句在不同的block中,而这两个block我们没办法预知到底是哪个block先执行,因此最终变量q是a的值还是b的值是不确定的,这也是为什么在学习Verilog时一直在强调,同一个变量不能在多个block中进行赋值

4 阻塞和非阻塞

从上面的调度表可以看出,阻塞赋值在活跃事件中;非阻塞的右式计算在活跃事件中,而更在非阻塞赋值更新事件中

由于事件队列的执行是顺序执行的,当仿真进入当前仿真时间时,先执行活跃事件,对于阻塞和非阻塞来说,当进入活跃事件时,阻塞赋值进行右式计算,并将计算的结果赋值给左式,而对于非阻塞赋值,此时只进行右式的计算,但不会立即将结果更新到左式,直到事件队列进入非阻塞赋值更新事件时,才更新非阻塞赋值的左式

说的可能不好理解,我们用例子来做说明

module test;
    reg [1:0] a,b;

    initial begin
         a = 2'b11;
    end

    initial begin
        b <= 2'b1;
    end

    initial begin
        $display($time, ,"\$display: a-> %b", a);
        $display($time, ,"\$display: b-> %b", b);
        #10;
        $finish;
    end

endmodule

从上面的事件调度表中可以知道,在这个例子中,阻塞赋值、非阻塞赋值的右式计算和$display都属于活跃事件

那么对于这个例子可以做出提前预测结果,变量a成功赋值,变量b没有完成赋值,来看看运行结果

可以看到运行结果与我们的猜测一致

同样的,如果我们加入监控事件,那么在监控事件中可以看到变量b的成功赋值

module test;
    reg [1:0] a,b;

    initial begin
         a = 2'b11;
    end

    initial begin
        b <= 2'b1;
    end

    initial begin
        $display($time, ,"\$display: a-> %b", a);
        $display($time, ,"\$display: b-> %b", b);
        #10;
        $finish;
    end

    initial begin
        $strobe($time, ,"\$strobe: a-> %b", a);
        $strobe($time, ,"\$strobe: b-> %b", b);
    end
endmodule

可以看到,在$strobe中,变量b成功赋值

回观调度表,有一个显示零延迟(#0),而#0延迟是处于非活跃事件的,那么我们再做一个小更改

module test;
    reg [1:0] a,b;

    initial begin
         #0 a = 2'b11;
    end

    initial begin
        b <= 2'b1;
    end

    initial begin
        $display($time, ,"\$display: a-> %b", a);
        $display($time, ,"\$display: b-> %b", b);
        #10;
        $finish;
    end

    initial begin
        $strobe($time, ,"\$strobe: a-> %b", a);
        $strobe($time, ,"\$strobe: b-> %b", b);
    end
endmodule

可以看到,当我们给阻塞赋值加上#0延迟后,$display不在显示变量a的赋值,因为加上#0延迟后,阻塞赋值进入非活跃事件队列,而处于活跃事件队列的$display自然无法打印变量a的值

同时监控事件$strobe能够打印变量a的值,说明加上#0延迟后,阻塞赋值还是属于当前仿真时间中,并未进入将来仿真事件,而如果我们给阻塞赋值加入一个非0延迟

module test;
    reg [1:0] a,b;

    initial begin
         #1 a = 2'b11;
    end

    initial begin
        b <= 2'b1;
    end

    initial begin
        $display($time, ,"\$display: a-> %b", a);
        $display($time, ,"\$display: b-> %b", b);
        #10;
        $finish;
    end

    initial begin
        $strobe($time, ,"\$strobe: a-> %b", a);
        $strobe($time, ,"\$strobe: b-> %b", b);
    end
endmodule

当加入非0延迟,$strobe便无法获取变量a的赋值

参考资料

( Verilog HDL分层事件队列_formerman的博客-CSDN博客

verilog-std-1364-2005

标签:begin,initial,阻塞,调度,理解,strobe,事件,赋值
来源: https://www.cnblogs.com/ICcode/p/16584796.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有