在Verilog中不仅有并发语句、并发过程,也有顺序语句。顺序语句是由always,initial,function,task等关键字引导的块结构组成。在块结构(block)中可以由一条或多条语句组成,多条语句组成的块由begin…end结构隔离。
顺序语句的特点:
-
顺序性
就是由always,initial,function,task等引导的过程块内部语句是顺序执行,是从上到下或按书写顺序执行的,因此语句书写的顺序会影响代码的执行结果。
例1:
reg [3:0] a, b, c, d;
always@(*)
begin
a = 2;
b = 3;
c = a + b;
c = 4;
d = a + b - c;
end
运行结果 d=1;
如果调换顺序,如例2,
例2:
reg [3:0] a, b, c, d;
always@(*)
begin
a = 2;
b = 3;
c = a + b; // c=5
d = a + b - c; // d=0
c = 4; // c=4
end
运行结果 d=0;
2.在顺序语句中可以对reg型变量多次赋值。
reg型变量只能在顺序语句中被赋值,而且可以多次赋值,如上面的例1,例2.
但在同一个module内reg型变量依然不能在多个顺序过程内赋值,因为这些顺序过程之间是并发的。
例3:
reg [3:0] a, b, c, d, e;
always@(*)
begin
a = 2;
b = 3;
c = a + b; //c=5
d = a + b - c; //d=0
c = 4; //c=4
end
always@(*)
begin
d = a + c - b;
e = a * b;
end
这两个独立always块,在同一module内出现赋值冲突,因为变量d在两个always块中赋值。由于这两个独立的always块是两个并发过程,因此不允许在两个并发过程中对同一个变量赋值。像上面的例子在综合时会有错误提示,在仿真时变量d在赋值冲突时会有’X’值生成,并且会引起 ‘X ’ 在整个系统中传递。
3. 在顺序语句中不能对wire型变量赋值。
由于wire 型变量语句,以及wire型的输出接口只接受并发赋值,即每个wire型变量的赋值都是并发过程(连续检测的),而对于always块结构而言也是一个并发过程,并发过程是不允许嵌套的。就像不能在一个assign语句嵌套另一个assign 或always语句一样。因此在always块中只能对寄存器赋值。并发赋值语句可以等效成一个always过程。
例:并发赋值(assign)与always的等效性
(1) wire [3:0] c;
assign c = a + b;
(2) reg [3:0] c;
always@(*) c = a + b;
4. 顺序语句赋值
顺序过程中的赋值语句有两种,分别为阻塞赋值语句与非阻塞赋值语句,这两种赋值语句是Verilog在顺序赋值语句中的建模,既有区别又有联系。下面对这两种赋值做简单的描述,跟多的细节请关注后续课程中更专业的文章或视频。
(1)阻塞赋值语句 “=”
-
- 阻塞赋值语句在顺序执行时,被赋值变量值立即更新。
- 对同一变量后面赋值语句覆盖前面的赋值,但覆盖前已被使用的表达式的值不受影响
- 阻塞赋值语句不会引起always重入,或者可以说重入不会影响运行结果。
(2)非阻塞赋值语句 “<=”
-
- 非阻塞赋值语句”<=”在顺序执行的过程中赋值不能立即实现,而是当该过程中所有语句完成后一起评估执行结果。
- 本次得到的赋值不能被其它表达式直接使用,本次过程执行只能使用进入该过程之前的变量值。
- 对同一变量的赋值,后面的赋值覆盖前面的赋值。
- 非阻塞赋值语句会引起always重入,重入会影响运行结果的改变。
例4:
reg [3:0] a, b, c, d, e;
//假设在进入always过程之前, a,b,c的值分别为7,8,9,分析本例的运行结果。
always@(*)
begin
a <= 2;
b <= 3;
c <= a + b; //c=?
d <= a + b - c; //d=?
c <= 4; //c=?
end
执行结果如下:
a = 2,
b = 3,
c = 4,
d = 6;
5. always过程可以使用边沿激励,从而实现时序电路设计 详细内容参见时序电路设计部分。 可见,阻塞与非阻塞赋值语句差别还是很大的,注意在学习与使用过程中不断体会。
对应视频: