在Verilog中不仅有并发语句、并发过程,也有顺序语句。顺序语句是由always,initial,function,task等关键字引导的块结构组成。在块结构(block)中可以由一条或多条语句组成,多条语句组成的块由begin…end结构隔离。
顺序语句的特点:
-
顺序性
就是由always,initial,function,task等引导的过程块内部语句是顺序执行,是从上到下或按书写顺序执行的,因此语句书写的顺序会影响代码的执行结果。
例1:
|
1 2 3 4 5 6 7 8 9 10 |
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:
|
1 2 3 4 5 6 7 8 9 10 |
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:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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;
|
1 |
assign c = a + b; |
(2) reg [3:0] c;
|
1 2 |
always@(*) c = a + b; |
4. 顺序语句赋值
顺序过程中的赋值语句有两种,分别为阻塞赋值语句与非阻塞赋值语句,这两种赋值语句是Verilog在顺序赋值语句中的建模,既有区别又有联系。下面对这两种赋值做简单的描述,跟多的细节请关注后续课程中更专业的文章或视频。
(1)阻塞赋值语句 “=”
-
- 阻塞赋值语句在顺序执行时,被赋值变量值立即更新。
- 对同一变量后面赋值语句覆盖前面的赋值,但覆盖前已被使用的表达式的值不受影响
- 阻塞赋值语句不会引起always重入,或者可以说重入不会影响运行结果。
(2)非阻塞赋值语句 “<=”
-
- 非阻塞赋值语句”<=”在顺序执行的过程中赋值不能立即实现,而是当该过程中所有语句完成后一起评估执行结果。
- 本次得到的赋值不能被其它表达式直接使用,本次过程执行只能使用进入该过程之前的变量值。
- 对同一变量的赋值,后面的赋值覆盖前面的赋值。
- 非阻塞赋值语句会引起always重入,重入会影响运行结果的改变。
例4:
|
1 2 3 4 5 6 7 8 9 10 |
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过程可以使用边沿激励,从而实现时序电路设计 详细内容参见时序电路设计部分。 可见,阻塞与非阻塞赋值语句差别还是很大的,注意在学习与使用过程中不断体会。
对应视频: