Verilog初级教程(15)Verilog中的阻塞与非阻塞语句

举报
李锐博恩 发表于 2021/07/15 00:13:46 2021/07/15
【摘要】 文章目录 前言正文阻塞赋值非阻塞赋值 往期回顾参考资料以及推荐关注 前言 本文通过仿真的方式,形象的说明阻塞赋值以及非阻塞赋值的区别,希望和其他教程相辅相成,共同辅助理解。 正文 阻塞赋值 阻塞赋值语句使用=进行赋值,并在程序块中一个接一个地执行。但是,这不会阻止在并行块中运行的语句的执行。 通过仿真最容易理解,下面是仿真文件: module...

前言

本文通过仿真的方式,形象的说明阻塞赋值以及非阻塞赋值的区别,希望和其他教程相辅相成,共同辅助理解。

正文

阻塞赋值

阻塞赋值语句使用=进行赋值,并在程序块中一个接一个地执行。但是,这不会阻止在并行块中运行的语句的执行。
通过仿真最容易理解,下面是仿真文件:

module tb;
  reg [7:0] a, b, c, d, e; initial begin a = 8'hDA; $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c); b = 8'hF1; $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c); c = 8'h30; $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
  end initial begin d = 8'hAA; $display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
 	e = 8'h55; $display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
  end
endmodule

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

请注意,当仿真开始时,有两个初始块是并行执行的。语句在每个块中依次执行,两个块在时间0ns处结束。更具体的说,变量a首先被分配,然后是显示语句,接着是所有其他语句。这在输出中可以看到,变量b和c在第一条显示语句中是8’hxx。这是因为当调用第一个$display时,变量b和c的赋值还没有被执行。

打印执行结果:

[0] a=0xda b=0xxx c=0xxx
[0] a=0xda b=0xf1 c=0xxx
[0] a=0xda b=0xf1 c=0x30
[0] d=0xaa e=0xxx
[0] d=0xaa e=0x55

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

在接下来的例子中,我们将在同一组语句中添加一些延迟,看看它的表现。

module tb;
  reg [7:0] a, b, c, d, e; initial begin a = 8'hDA; $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c); #10 b = 8'hF1; $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c); c = 8'h30; $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
  end initial begin #5 d = 8'hAA; $display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
 	#5 e = 8'h55; $display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
  end
endmodule

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

仿真记录:

Time resolution is 1 ps
[0] a=0xda b=0xxx c=0xxx
[5000] d=0xaa e=0xxx
[10000] a=0xda b=0xf1 c=0xxx
[10000] a=0xda b=0xf1 c=0x30
[10000] d=0xaa e=0x55


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

综上,顺序执行或者串行执行一览无余。

非阻塞赋值

非阻塞赋值允许在不阻塞下面语句执行的情况下安排赋值,并由<=符号指定。值得注意的是,同一个符号在表达式中被用作关系运算符,在非阻塞赋值的上下文中被用作赋值运算符。如果我们以上面的第一个例子为例,将all = symobls替换为非阻塞赋值操作符<=,我们会看到输出的结果有一些不同。

module tb;
  reg [7:0] a, b, c, d, e; initial begin a <= 8'hDA; $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c); b <= 8'hF1; $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c); c <= 8'h30; $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
  end initial begin d <= 8'hAA; $display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
 	e <= 8'h55; $display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
  end
endmodule

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

先给出仿真结果:

Time resolution is 1 ps
[0] a=0xxx b=0xxx c=0xxx
[0] a=0xxx b=0xxx c=0xxx
[0] a=0xxx b=0xxx c=0xxx
[0] d=0xxx e=0xxx
[0] d=0xxx e=0xxx

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

看到所有的$display语句都打印了’h’x。这种行为的原因在于非阻塞赋值的执行方式。特定时间步长的每一条非阻塞语句的RHS都会被捕获,并转入下一条语句。被捕获的RHS值只有在时间步长结束时才会分配给LHS变量。

所以,如果我们把上面例子的执行流程分解一下,我们会得到如下图所示的东西。
注:从前几期可以知道RHS为右值。

|生成块1:初始化
| 时间 #0ns : a <= 8'DA, 为非阻塞,所以记下 RHS (8'hDA) 的值并执行下一步。
| 时间#0ns : $display()阻塞了,所以执行这条语句,但是a还没有收到新的值,所以a=8'hx。
| 时间 #0ns : b <= 8'F1, 为非阻塞,所以记下 RHS 的值 (8'hF1) 并执行下一步。
| 时间#0ns : $display()阻塞,所以执行此语句。但b还没有收到新的值,所以b=8'hx。
| 时间#0ns : c <= 8'30, 非阻塞,所以记下RHS值(8'h30)并执行下一步。
| 时间#0ns : $display()被阻塞,所以执行这条语句,但c还没有收到新的值,所以c=8'hx。
| 时间步骤和初始块结束,将捕获的值分配到变量a、b、c中。
|
|生成块2:初始化
| 时间#0ns : d <= 8'AA, 为非阻塞,所以记下RHS值(8'hAA)并执行下一步。
| 时间#0ns : $display()被阻塞,所以执行这条语句,但d还没有收到新的值,所以d=8'hx。
| 时间#0ns : e <= 8'55, 是非阻塞的,所以记下RHS值(8'h55)并执行下一步。
| 时间#0ns : $display()阻塞,所以执行这条语句,但e没有收到新的值,所以e=8'hx。
| 时间步骤和初始块结束,将捕获的值分配到变量d和e中。
|
|仿真结束在#0ns

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

接下来,我们用第二个例子,将所有阻塞语句替换成非阻塞语句。

module tb;
  reg [7:0] a, b, c, d, e; initial begin a <= 8'hDA; $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c); #10 b <= 8'hF1; $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c); c <= 8'h30; $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
  end initial begin #5 d <= 8'hAA; $display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
 	#5 e <= 8'h55; $display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
  end
endmodule

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

恐怕这就有点让人头大了。

给出仿真结果,在进行分析:

Time resolution is 1 ps
[0] a=0xxx b=0xxx c=0xxx
[5000] d=0xxx e=0xxx
[10000] a=0xda b=0xxx c=0xxx
[10000] a=0xda b=0xxx c=0xxx
[10000] d=0xaa e=0xxx


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这种仿真结果的意思说明了阻塞赋值的特性,即在当前时刻不立即赋值,只有在一定时间步长结束时才赋值。
上面的例子,在0时刻,执行了如下语句:

 a <= 8'hDA; $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);

  
 
  • 1
  • 2

由于是非阻塞赋值,所以当前a并不能立即得到右值8’hDA,拿第一个initial为例,在10ns,就得到了这个值8’hda,但是其他值(b和c)仍然为x。
下一个非10ns时刻,我们就可以得到a、b和c的具体值,为了验证,我们添加一条语句:

#1 $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);

  
 
  • 1
  • 2

整体程序为:

module assign_tb();
  reg [7:0] a, b, c, d, e; initial begin a <= 8'hDA; $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c); #10 b <= 8'hF1; $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c); c <= 8'h30; $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c); #1 $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c); end initial begin #5 d <= 8'hAA; $display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
 	#5 e <= 8'h55; $display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
  end
endmodule

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

仿真结果:

Time resolution is 1 ps
[0] a=0xxx b=0xxx c=0xxx
[5000] d=0xxx e=0xxx
[10000] a=0xda b=0xxx c=0xxx
[10000] a=0xda b=0xxx c=0xxx
[10000] d=0xaa e=0xxx
[11000] a=0xda b=0xf1 c=0x30

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

我们主要关注:

[11000] a=0xda b=0xf1 c=0x30

  
 
  • 1

可见,得到了验证。

最后,给出这段非阻塞赋值仿真程序的执行过程:

module tb;
  reg [7:0] a, b, c, d, e; initial begin a <= 8'hDA; $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c); #10 b <= 8'hF1; $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c); c <= 8'h30; $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
  end initial begin #5 d <= 8'hAA; $display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
 	#5 e <= 8'h55; $display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
  end
endmodule

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
|在#0ns处生成Block1:初始化。
| 时间 #0ns : a <= 8'DA, 为非阻塞,所以记下 RHS (8'hDA) 的值并执行下一步。
| 时间#0ns : $display()阻塞了,所以执行这条语句,但是a还没有收到新的值,所以a=8'hx。
| 时间步骤结束:将捕获的值分配给变量a,现在a是8'hDA。
| 等到时间前进10个时间单位到#10ns。
|	
| 时间 #10ns : b <= 8'F1, 非阻塞,所以记下 RHS 的值 (8'hF1) 并执行下一步。
|时间#10ns : $display()阻塞,所以执行这条语句。但b还没有收到新的值,所以b=8'hx。
| 时间#10ns : c <= 8'30, 是非阻塞的,所以记下RHS值(8'h30)并执行下一步。
| 时间#10ns : $display()阻塞,所以执行此语句。但是c没有收到新的值,所以c=8'hx。
| 时间步骤和初始块结束,将捕获的值分配到变量b、c中。
|	
|在#0ns处生成Block2:初始化。
| 等到时间前进5个时间单位到#5ns。
|	
| 时间 #5ns : d <= 8'AA, 为非阻塞,所以记下 RHS 的值 (8'hAA) 并执行下一步。
| 时间#5ns : $display()阻塞,所以执行此语句。但d没有收到新的值,所以d=8'hx。
| 时间步骤结束:将捕获的值分配给变量d,现在d是8'hAA。
| 等到时间前进5个时间单位到#5ns。
|	
| 时间#10ns : e <= 8'55, 非阻塞,所以记下RHS值(8'h55)并执行下一步。
| 时间#10ns : $display()阻塞,所以执行这条语句,但e还没有收到新的值,所以e=8'hx。
| 时间步骤和初始块结束,将捕获的值分配给变量e,现在e是8'h55。
|
|仿真结束在#10ns

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

往期回顾

Verilog初级教程(14)Verilog中的赋值语句

Verilog初级教程(13)Verilog中的块语句

Verilog初级教程(12)Verilog中的generate块

Verilog初级教程(11)Verilog中的initial块

Verilog初级教程(10)Verilog的always块

Verilog初级教程(9)Verilog的运算符

Verilog初级教程(8)Verilog中的assign语句

Verilog初级教程(7)Verilog模块例化以及悬空端口的处理

Verilog初级教程(6)Verilog模块与端口

Verilog初级教程(5)Verilog中的多维数组和存储器

Verilog初级教程(4)Verilog中的标量与向量

Verilog初级教程(3)Verilog 数据类型

Verilog初级教程(2)Verilog HDL的初级语法

Verilog初级教程(1)认识 Verilog HDL

芯片设计抽象层及其设计风格

Verilog以及VHDL所倡导的的代码准则

FPGA/ASIC初学者应该学习Verilog还是VHDL?

参考资料以及推荐关注

Verilog Blocking & Non-Blocking

文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。

原文链接:reborn.blog.csdn.net/article/details/107436015

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。