文章转自微信公众号,花蚂蚁
1 初步理解阻塞赋值与非阻塞赋值
在Verilog HDL语言中,信号有两种赋值方式:
(1).非阻塞(Non_Blocking)赋值方式( 如 b <= a; )
块结束后才完成赋值操作。
b的值并不是立刻就改变的。
这是一种比较常用的赋值方法。(特别在编写可综合模块时)
(2).阻塞(Blocking)赋值方式( 如 b = a; )
赋值语句执行完后,块才结束。
b的值在赋值语句执行完后立刻就改变的。
可能会产生意想不到的结果。
非阻塞赋值方式和阻塞赋值方式的区别常给设计人员带来问题。问题主要是给"always"块内的reg型信号的赋值方式不易把握。
到目前为止,前面所举的例子中的"always"模块内的reg型信号都是采用下面的这种赋值方式:
这种方式的赋值并不是马上执行的,也就是说"always"块内的下一条语句执行后,b并不等于a,而是保持原来的值。"always"块结束后,才进行赋值。
而另一种赋值方式阻塞赋值方式,如下所示:
这种赋值方式是马上执行的。也就是说执行下一条语句时,b已等于a。尽管这种方式看起来很直观,但是可能引起麻烦。
下面举例说明:
[例1] 中的"always"块中用了非阻塞赋值方式,定义了两个reg型信号b和c,clk信号的上升沿到来时,b就等于a,c就等于b,这里应该用到了两个触发器。
请注意:赋值是在"always"块结束后执行的,c应为原来b的值。
这个"always"块实际描述的电路功能如下图所示:
[例2]中的 "always"块用了阻塞赋值方式。clk信号的上升沿到来时,将发生如下的变化:b马上取a的值,c马上取b的值(即等于a)。
生成的电路图如下所示,只用了一个触发器来寄存器a的值,又输出给b和c。这大概不是设计者的初衷,如果采用[例1]所示的非阻塞赋值方式就可以避免这种错误。
2 深入理解阻塞和非阻塞赋值
在描述组合逻辑的always 块中用阻塞赋值,则综合成组合逻辑的电路结构。
在描述时序逻辑的always 块中用非阻塞赋值,则综合成时序逻辑的电路结构。
为什么一定要这样做呢?这是因为要使综合前仿真和综合后仿真一致的缘故。
首先了解两个定义:
RHS – 方程式右手方向的表达式或变量可分别缩写为:RHS 表达式或 RHS 变量。
LHS – 方程式左手方向的表达式或变量可分别缩写为:LHS 表达式或 LHS 变量。
阻塞赋值的执行可以认为是只有一个步骤的操作:
-
计算RHS 并更新LHS,此时不能允许有来自任何其他Verilog 语句的干扰。
所谓阻塞的概念是指在同一个always 块中,其后面的赋值语句从概念上(即使不设定延迟)是在前一句赋值语句结束后再开始赋值的。
[例1]用阻塞赋值的反馈振荡器(不好的例子)
例1中,如果前一个always块的复位信号先到0 时刻,则y1 和y2 都会取1,而如果后一个always 块的复位信号先到0 时刻,则y1 和y2 都会取0。
这清楚地说明这个Verilog 模块是不稳定的会产生冒险和竞争的情况。
如果在一个过程块中阻塞赋值的RHS 变量正好是另一个过程块中阻塞赋值的LHS 变量,这两个过程块又用同一个时钟沿触发,如果阻塞赋值的次序安排不好,就会出现竞争。若这两个阻塞赋值操作用同一个时钟沿触发,则执行的次序是无法确定的。
非阻塞赋值的操作可以看作为两个步骤的过程:
在赋值时刻开始时,计算非阻塞赋值RHS 表达式。
在赋值时刻结束时,更新非阻塞赋值LHS 表达式。
[例2] 用非阻塞赋值的反馈振荡器(正确示范)
例2中,无论哪一个always 块的复位信号先到, 两个always 块中的非阻塞赋值都在赋值开始时刻计算RHS 表达式,而在结束时刻才更新LHS 表达式。
所以这两个always 块在复位信号到来后,在always 块结束时 y1 为0 而y2为1 是确定的。
从用户的角度看这两个非阻塞赋值正好是并行执行的。
非阻塞赋值操作只能用于对寄存器类型变量进行赋值,因此只能用在"initial"块和"always"块等过程块中。非阻塞赋值不允许用于连续赋值。
下面通过几个通过移位寄存器的例子来更好的理解阻塞赋值与非阻塞赋值
下图表示是一个简单的移位寄存器方框图:
[例3] 不正确地使用的阻塞赋值来描述移位寄存器。(方式 #1)
在上面的模块中,按顺序进行的阻塞赋值将使得在下一个时钟上升沿时刻,所有的寄存器输出值都等于输入值d。在每个时钟上升沿,输入值d 将无延时地直接输出到q3。
[例4] 用阻塞赋值来描述移位寄存器也是可行的,但这种风格并不好。(方式 #2 )
在上面的模块中,阻塞赋值的次序是经过仔细安排的,以使仿真的结果与移位寄存器相一致。虽然该模块可被综合成移位寄存器,但我们不建议使用这种风格的模块来描述时序逻辑。
[例5] 不好的用阻塞赋值来描述移位时序逻辑的风格(方式 #3)
本例中,阻塞赋值分别被放在不同的always 块里。
仿真时,这些块的先后顺序是随机的,因此可能会出现错误的结果。
这是Verilog 中的竞争冒险。按不同的顺序执行这些块将导致不同的结果。
但是, 这些代码的综合结果却是正确的流水线寄存器。
也就是说,前仿真和后仿真结果可能会不一致。
[例6] 不好的用阻塞赋值来描述移位时序逻辑的风格(方式 #4)
[例7] 正确使用非阻塞赋值来描述时序逻辑的设计风格 (方式 #1)
[例8] 正确使用非阻塞赋值来描述时序逻辑的设计风格 (方式 #2)
[例9] 正确使用非阻塞赋值来描述时序逻辑的设计风格 (方式 #3)
[例10] 正确使用非阻塞赋值来描述时序逻辑的设计风格 (方式 #4)
以上移位寄存器时序逻辑电路设计的例子表明:
-
•4种阻塞赋值设计方式中有1种可以保证仿真正确
-
•4种阻塞赋值设计方式中有3种可以保证综合正确
-
•4种非阻塞赋值设计方式全部可以保证仿真正确
-
•4种非阻塞赋值设计方式全部可以保证综合正确
阻塞赋值和非阻塞赋值的原则归纳如下:
原则1:时序电路建模时,用非阻塞赋值。
原则2:锁存器电路建模时,用非阻塞赋值。
原则3:用always 块写组合逻辑时,采用阻塞赋值。
原则4:在同一个always 块中同时建立时序和组合逻辑电路时,用非阻塞赋值。
原则5:在同一个always 块中不要同时使用非阻塞赋值和阻塞赋值。
原则6:不要在多个always 块中为同一个变量赋值。
对语法知识有了初步的了解之后,下一步就应该“模仿”了。
我给小白们准备了一份礼物,下面是我精心整理的n多verilog代码。帮助小白们由浅至深的“模仿”。在模仿的过程中能够进一步的巩固之前了解的语法知识。
这些Verilog例程基本涵盖了以后可能遇到的所有用法,参考这些代码,可以做出你想要的任何设计。
当你想要用Verilog编写一段代码实现某个功能时,如果你能够想到参考哪段代码能够实现你的设计,那么恭喜你,你已经入门了!!!
所有示例代码,点击链接获取。
01-锁存器、触发器、寄存器、移位寄存器等
【免费】VerilogHDL示例代码之01-锁存器、触发器、寄存器、移位寄存器等资源-优快云文库
02-逻辑门、三态门、mux等
【免费】VerilogHDL示例代码之02-逻辑门、三态门、mux等资源-优快云文库
03-各种计数器
【免费】VerilogHDL示例代码之03-各种计数器资源-优快云文库
04-各类加法器
【免费】VerilogHDL示例代码之04-各类加法器资源-优快云文库
05-乘法器
【免费】VerilogHDL示例代码之05-乘法器资源-优快云文库
06-异步复位同步释放
【免费】VerilogHDL示例代码之06-异步复位同步释放资源-优快云文库
07-分频
【免费】VerilogHDL示例代码之07-分频资源-优快云文库
08-语法语句
【免费】VerilogHDL示例代码之08-语法语句资源-优快云文库
09-串并转换
【免费】VerilogHDL示例代码之09-串并转换资源-优快云文库
10-状态机设计
【免费】VerilogHDL示例代码之10-状态机设计资源-优快云文库
11-编解码应用
【免费】VerilogHDL示例代码之11-编解码应用资源-优快云文库
12-仿真语法举例
【免费】VerilogHDL示例代码之12-仿真语法举例资源-优快云文库
13-进阶设计
我将Verilog的语法进行了总结,写了13篇文章,让小白“快速见识猪如何跑”,每一篇讲解一类语法,结合正确的示例代码和错误的示例代码,有的章节对个别语法进行了归纳总结,帮助初学者加深理解。详情点击
Verilog语法之〇:Verilog HDL简介/Verilog语法介绍-优快云博客
Verilog语法之一:简单的Verilog HDL模块-优快云博客
Verilog语法之四:运算符_verilog异或运算符-优快云博客
。。。。。。
更多课程,敬请期待