文章目录

  • 前言
  • 正文
    • 阻塞赋值
    • 非阻塞赋值
  • 往期回顾
  • 参考资料以及推荐关注

前言

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

正文

阻塞赋值

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

module tb;reg [7:0] a, b, c, d, e;initial begina = 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);endinitial begind = 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

请注意,当仿真开始时,有两个初始块是并行执行的。语句在每个块中依次执行,两个块在时间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

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

module tb;reg [7:0] a, b, c, d, e;initial begina = 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);endinitial 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

仿真记录:

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

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

非阻塞赋值

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

module tb;reg [7:0] a, b, c, d, e;initial begina <= 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);endinitial begind <= 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

先给出仿真结果:

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

看到所有的$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

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

module tb;reg [7:0] a, b, c, d, e;initial begina <= 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);endinitial 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

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

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

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

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

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

由于是非阻塞赋值,所以当前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);

整体程序为:

module assign_tb();reg [7:0] a, b, c, d, e;initial begina <= 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);    endinitial 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

仿真结果:

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

我们主要关注:

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

可见,得到了验证。

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

module tb;reg [7:0] a, b, c, d, e;initial begina <= 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);endinitial 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
|在#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

往期回顾

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

  • 个人微信公众号: FPGA LAB
    交个朋友

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

  1. Verilog初级教程(23)Verilog仿真中的显示任务

    文章目录 前言 正文 Display/Write Tasks Verilog Strobes Verilog Continuous Monitors Verilog Format Specifiers ...

  2. Verilog初级教程(21)Verilog中的延迟控制语句

    文章目录 前言 正文 延迟控制语句 事件控制语句 Named Events Event or operator 往期回顾 参考资料及推荐关注 前言 Verilog中的延迟控制有两种类型–延迟和事件表达 ...

  3. Verilog初级教程(20)Verilog中的`ifdef 条件编译语句

    文章目录 前言 正文 语法 示例 Testbench文件 往期回顾 参考资料及推荐关注 前言 `ifdef条件编译语句在逻辑设计中还是很常见的,但也常见一些滥用的情况,这应该避免. 应该在什么情况下使 ...

  4. Verilog初级教程(19)Verilog中的参数

    文章目录 前言 正文 模块参数 覆盖参数 例子说明 递增计数器 递减计数器 Specify参数 模块参数与Specify参数的区别 往期回顾 参考资料及推荐关注 前言 Verilog中的参数是使得设计 ...

  5. Verilog初级教程(16)Verilog中的控制块

    文章目录 前言 正文 条件语句if 语法 硬件实现 循环语句 forever repeat while for 往期回顾 参考资料及推荐关注 前言 硬件行为的实现离不开条件语句和其他控制逻辑流的方式. ...

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

    文章目录 前言 正文 合理的左值 过程性赋值(Procedural assignment) 连续赋值 过程连续性赋值 往期回顾 前言 何为赋值语句?即将值放到线网或者变量上,这种操作称为赋值,英文:a ...

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

    文章目录 前言 正文 generate for generate if generate case 参考资料 本系列博文 前言 verilog中的generate块可以称为生成块,所谓生成,可以理解为 ...

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

    文章目录 前言 正文 语法格式 initial块是用来干什么的? initial块何时开始又何时结束? 一个模块中允许有多少个initial块? 参考资料 写在最后 前言 仿真中通常会依次执行一组Ve ...

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

    博文目录 写在前面 正文 Verilog算术运算符 Verilog关系运算符 Verilog等价运算符 Verilog逻辑运算符 Verilog位元运算符 Verilog移位运算符 参考资料 交个朋友 ...

最新文章

  1. 简明python教程购买-简明Python3教程 6.基础
  2. Linux ipcs命令与ipcrm命令的用法详解
  3. TensorFlow 笔记5--模型复用
  4. 手机端展现table_百度信息流广告外包丨百度信息流广告的展现样式和收费方式?...
  5. 全文检索技术_中药图片拍照识别系统,爬虫、深度学习技术android app 源码分享...
  6. (组合数学)AtCoder Grand Contest 019 F - Yes or No
  7. 手摸手,带你用vue撸后台 系列一(基础篇) - 掘金
  8. mysql数据库引擎InnoDB和MyISAM
  9. python数据预处理案例_对pandas进行数据预处理的实例讲解
  10. SQL 对结果集进行分组排序过滤重复数据 ROW_NUMBER
  11. windows8.1下安装Cygwin并通过apt-cyg安装软件包
  12. [LeetCode#81]Search in Rotated Sorted Array II
  13. javaScript this指向问题
  14. 2021年下半年软考网络工程师真题答案解析
  15. CentOS配置国内(阿里云)镜像加速器
  16. 云课堂智慧职教网页版登录入口_智慧职教云课堂登录
  17. 微信小程序——服务器接口
  18. python的dbf操作
  19. 2018年8月27日英语学习
  20. JZ2440开发板学习------中级(二十七)

热门文章

  1. 利用autobench测试web服务器极限并发数
  2. 网管员应掌握好的学习方法
  3. 选择DataGrid中的CheckBox控件后该行背景变色
  4. yarn的组成部分_图解YARN工作原理
  5. viso 画背景框_3分钟漫画教程 | Q版漫画少女线稿,画完暖暖的
  6. python截取html图片大小,Python打开html文件,截取屏幕截图,裁剪并保存为图像
  7. php增删改查心得体会,php增删改查入门示例
  8. oauth过滤login_OAuth2AuthenticationProcessingFilter资源认证服务器过滤器
  9. react 引入html文件_javascript – React:如何加载和呈现外部html文件?
  10. bal插口_EBS R12各模块接口表大全