在Verilog HDL语言中,信号有两种赋值方式,即阻塞赋值和非阻塞赋值。
1、阻塞(Blocking)赋值(如 b = a)
阻塞赋值,顾名思义,即在一个always块中,后面的语句会受到前语句的影响,具体来说,在同一个always中,一条阻塞赋值语句如果没有执行结束,那么该语句后面的语句就不能被执行,即被“阻塞”。在一个过程块内多个阻塞赋值语句是顺序执行的。
- 赋值语句执行完后,块才结束。
- b的值在赋值语句执行完后立刻就改变的。
- 每条语句执行之后才能执行下一条语句。
- 可能会产生意想不到的结果。
- 在串行语句块中,顺序执行;在并行语句块中,同时执行。
- 执行顺序:先计算等号右端表达式的值,然后立刻将计算的值赋给左边变量,与仿真时间无关。
2、非阻塞(Non_Blocking)赋值(如 b <= a)
从字面意思上来看,赋值过程是没有阻塞的。一条非阻塞赋值语句的执行是不会阻塞下一条语句的执行,也就是说在本条非阻塞赋值语句执行完毕前,下一条语句也可开始执行。非阻塞赋值语句在过程块结束时才完成赋值操作。在一个过程块内的多个非阻塞赋值语句是并行执行的。
为什么没有阻塞呢?因为非阻塞赋值用于时序电路,综合出来的是寄存器。非阻塞赋值的操作可以看作为两个步骤的过程:①在赋值开始时刻,计算赋值号右边的语句;②在赋值结束时刻,更新赋值号左边的语句。注意:非阻塞操作只能用于对寄存器类型变量进行赋值,因此只能用于“initial”和“always”块中,不允许用于连续赋值“assign”。
- 块结束后才完成赋值操作。
- b的值并不是立刻就改变的。
- 这是一种比较常用的赋值方法。(特别在编写可综合模块时)
- 在串行语句块中,执行没有先后之分,各条语句并行执行。
3、实际使用
注:在实际使用中,对于可综合电路,只要记住一点,组合电路要使用阻塞赋值,时序电路使用非阻塞赋值。
阻塞赋值和非阻塞赋值的原则归纳如下:
原则1:时序电路建模时,用非阻塞赋值。
计算RHS时,所有的变量值均是触发沿到来前的值,更新的值LHS作为触发沿后的值,并且保持到下一个触发沿到来时候,等待更新。这样,就可以使得在同一个块中非阻塞赋值语句不必要求出现的顺序,都是在全部进行RHS计算后同时更新LHS的值。非阻塞赋值可以简单的认为是赋予下一状态的值。
原则2:锁存器电路建模时,用非阻塞赋值。
虽然锁存器电路建模是我们不推荐的,但是如果使用到要采用非阻塞赋值的方式。使用非阻塞赋值实现时序逻辑,实现锁存器是最为安全的。
原则3:用always 块写组合逻辑时,采用阻塞赋值。
使用always块建立组合逻辑电路模型时不要忘记always块中的敏感列表一定要使用电平触发的方式,然后在always块中使用阻塞赋值语句就可以实现组合逻辑,这样做既简单且方针又快又好,这样的风格是值得推荐的。
原则4:在同一个always 块中同时建立时序和组合逻辑电路时,用非阻塞赋值。
原则5:在同一个always 块中不要同时使用非阻塞赋值和阻塞赋值。
在同一个always块中对同一个变量既进行阻塞赋值又进行非阻塞赋值会产生综合不可预测的结果,不是可综合的Verilog风格。
原则6:不要在多个always 块中为同一个变量赋值。
因为always块是并行的,执行的顺序是随机的,综合时会报多驱动的错误,所以严禁在多个always块中对同一个变量赋值;当然也不推荐一个always对多个变量进行赋值,虽然这种方式是允许的,但如果过多会导致代码的混乱且不易后期的维护和修改。
原则7:用$strobe系统任务来显示用非阻塞赋值的变量值。
原则8:在赋值时不要使用#0延时。
对于我这样的初学者而言,首先要掌握可综合风格的Verilog模块编程的原则,并且牢记,才能在综合布局布线的仿真中避免出现竞争冒险现象。
4、组合逻辑电路与时序逻辑电路
数字电路根据逻辑功能的不同特点,可以分成两大类,一类叫组合逻辑电路(简称组合电路),另一类叫做时序逻辑电路(简称时序电路)。
组合逻辑电路在逻辑功能上的特点是任意时刻的输出仅仅取决于该时刻的输入,与电路原来的状态无关。
时序逻辑电路在逻辑功能上的特点是任意时刻的输出不仅取决于当时的输入信号,而且还取决于电路原来的状态,或者说,还与以前的输入有关。