本文介绍: 在SystemVerilog中,并行块除了forkjoin这种形式之外,还多出了两个变种,forkjoin_anyforkjoin_none本文将对此进行讨论

相关阅读

SystemVerilog基础icon-default.png?t=N7T8https://blog.csdn.net/weixin_45791458/category_12517449.html?spm=1001.2014.3001.5482


        有关Verilog顺序块和并行块的相关内容已经在之前的Verilog基础的文章讲过,如下所示

Verilog基础:块语句icon-default.png?t=N7T8https://blog.csdn.net/weixin_45791458/article/details/132561861?spm=1001.2014.3001.5502        但在SystemVerilog中,并行块除了forkjoin这种形式之外,还多出了两个变种,forkjoin_anyforkjoin_none,本文将对此进行讨论

        下面是Verilog并行语法的BNF范式,有关BNF范式相关内容,可以之前的文章

Verilog基础:巴科斯范式(BNF)icon-default.png?t=N7T8https://blog.csdn.net/weixin_45791458/article/details/132567389?spm=1001.2014.3001.5502

        下面是SystemVerilog并行语法的BNF范式可以看到其中变动较大的就是并行块结尾的关键词可以join、join_any、join_none之间的任何一个

        SystemVerilog中的并行具有以下几个性质:

1、块内的语句进入并行块后同时执行(同时执行代表连续执行,并行块执行过程可能会发生进程挂起与切换,这点在之后说明)。

2、块内各语句过程赋值延迟全部是相对进入并行块时而言的(有关过程赋值延迟的内容,可以Verilog基础:延时模型)。

3、块内各语句的过程赋值延迟可以提供语句赋值时间顺序

4、根据join keyword关键词不同控制流可能不同方式传出并行块(这与Verilog中的并行块不一样)。

        首先回顾一下Verilog中的并行块,在并行块开始执行时,块内语句同时执行(这个同时不是严格意义的同时,指的是同一个仿真时间),在被调度仿真时间最后的语句执行完后,则并行块整体执行完毕。

        下面的代码片是一个例子显示了并行块语句的执行情况。

//例1
`timescale 1ns/1ns
initial begin
    #2 $display("Enter fork at %t", $time);
    fork
       #1 $display("Statement1 execute at %t", $time);
       #2 $display("Statement2 execute at %t", $time);
       #3 $display("Statement3 execute at %t", $time); 
    join
    $display("Exit fork at %t", $time);
end


输出
Enter fork at                   2
Statement1 execute at           3
Statement2 execute at           4
Statement3 execute at           5
Exit fork at                    5

        但是要注意的是,对于有内嵌延迟的过程赋值语句,一旦右端的表达式的值计算完毕,即视作此表达式执行完毕,详细请看下面的例子。 

//例2
reg [1:0]a;
initial begin
    #2 $display("Enter fork at %t", $time);
    $monitor("a = %d at %t", a, $time);
    fork
        a <= #1 1;
        a <= #2 2;
        a <= #3 3;
    join
    $display("Exit fork at %t", $time);
end

输出:
Enter fork at               2
Exit fork at                2
a = x at                    2
a = 1 at                    3
a = 2 at                    4
a = 3 at                    5

         可以看到进入并行块和退出并行块的仿真时间是相同的,因为非阻塞赋值内嵌延迟并不会阻塞控制流执行并行块内的其他非阻塞赋值语句,而真正的赋值需要等到3、4、5ns时。

        将上例中的非阻塞赋值改为阻塞赋值后会发生什么?下面给出了这种情况下的输出。

//例3
reg [1:0]a;
initial begin
    #2 $display("Enter fork at %t", $time);
    $monitor("a = %d at %t", a, $time);
    fork
        a = #1 1;
        a = #2 2;
        a = #3 3;
    join
    $display("Exit fork at %t", $time);
end

输出:
Enter fork at               2
a = x at                    2
a = 1 at                    3
a = 2 at                    4
Exit fork at                5
a = 3 at                    5

        尽管并行块内各个阻塞赋值语句在同一仿真时间的执行顺序不同,但有内嵌延迟的阻塞赋值必须等待延迟时间后才视为执行完毕,三条阻塞赋值语句同时执行,则延迟最长的那条阻塞赋值语句执行完毕后退出并行块,即2+3=5ns时退出并行块。

        对于fork-join的讨论到此为止,下面是对于fork-any的讨论。当并行块末尾关键词使用fork-any时,当并行块中有任意一条语句执行完毕,控制便可以跳出并行块执行之后的语句(当然,对于并行块中其他尚未执行的语句,它们仍然会在被调度的仿真时间执行),下面举例说明

//例4
reg [1:0]a;
initial begin
    #2 $display("Enter fork at %t", $time);
    $monitor("a = %d at %t", a, $time);
    fork
       #1 a = 1;
       #2 a = 2;
       #3 a = 3;
    join_any
    $display("Exit fork at %t", $time);
end

输出:
Enter fork at               2
a = x at                    2
Exit fork at                3
a = 1 at                    3
a = 2 at                    4
a = 3 at                    5

        可以看到,如果fork-join_any块中的任何一个语句执行完毕,控制就可以跳出并行块了,即在3ns时就退出并行块了,但并行块内部的后两个赋值仍然会在其被调度的时间执行,所以在4ns和5ns时,a的值发生了改变。

        这样就会出现一种问题:当fork-join块内的语句和块后的语句被调度到同一时间执行时,它们的执行顺序有保证吗?我们知道begin end块能保证顺序块内语句依次执行,但fork-join_any块使得在结束一个并行块前,可以执行后面的语句,下面看一个例子

//例5
reg [1:0]a;
initial begin
    #2 $display("Enter fork at %t", $time);
    $monitor("a = %d at %t", a, $time);
    fork
       #1 a = 1;
       #2 a = 2;
    join_any
    $display("Exit fork at %t", $time);
    #1 a = 0;
end

输出:
Enter fork at               2
a = x at                    2
Exit fork at                3
a = 1 at                    3
a = 0 at                    4

         上例中,当a=1在3ns时执行完毕后,控制即可跳出并行块,但在随后1ns,仿真器就要面临一个选择,是先执行a=2还是a=0,begin-end块的性质是否能保证a=2一定在a=0之前执行?答案是否定的,虽然结果表明a=2在a=0之前执行(最终结果为0),但这不是一定的,比如下面的反例。

//例6
reg [1:0]a;
initial begin
    #2 $display("Enter fork at %t", $time);
    $monitor("a = %d at %t", a, $time);
    fork
       a = 1;
       #1 a = 2;
    join_any
    $display("Exit fork at %t", $time);
    #1 a = 0;
end

输出:
Enter fork at               2
Exit fork at                2
a = 1 at                    2
a = 2 at                    3

        由输出可以清晰地看出,a=2时在a=0后发生(最终结果为2)。即begin-end块只能保证在同一仿真时间,fork块中的第一条执行的语句是在fork块后语句之前的,而无法保证其他同一仿真时间的语句的执行顺序。

原文地址:https://blog.csdn.net/weixin_45791458/article/details/134694526

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_38924.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注