由于Verilog HDL是硬件描述语言,因此在使用中不同于一般的计算机软件语言,更多的是数字电路图纸的另一种表达形式。可以说硬件数字图纸和Verilog HDL语言描述的电路可以互为转换的,只是复杂的数字设计,Verilog HDL描述比图纸设计更容易。我们知道在数字图纸设计中,电路之间是并发执行的,那么语言描述也是同样的。
1.连续赋值语句 assign
- 连续赋值语句的执行过程
关键字assign 对应的赋值语句又称为连续赋值语句,其含义是对赋值语句右边的变量表达式连续检测,一旦表达式中的变量发生变化,则立即评估表达式的值并赋值给左边的变量。
例:assign b[3:0] = a[3:0] + 1;
初始将a[3:0]的值加1赋给b[3:0];b[3:0] 得到a[3:0]+1的值,并保持。并时刻检测a[3:0]的值,一旦a[3:0]的有变化,重新计算a[3:0]+1的值,并立即赋值给b[3:0]。
- 延迟赋值
在连续赋值语句中可以使用延迟语句,但只能是inter delay, 如: #8 assign b[3:0]=a[3:0]+1; 即a[3:0]得值发生变化后,等待8个时间单位(时间单位由timescale设定)后计算表达式的值并赋值给b[3:0]。
注:延迟语句只在仿真时有效,综合时自动忽略。
- 连续赋值语句表达式
连续赋值语句可以使用常量,常数,reg型变量,wire型变量,integer变量等,并由各种运算符组成的表达式以及函数返回值等赋值给LHS 变量赋值。
例1:
assign a[3:0] = (b==c) || (d>e);
- 连续赋值语句只能给wire型变量赋值
连续赋值语句只能给wire型或wire型相似的变量赋值,如: wire, wand, wor, tri等变量赋值,不能给reg型变量赋值。reg声明的变量只能在顺序语句中使用。
-
- assign 赋值语句对同一个wire型变量只能赋值一次,除非在用于纯仿真的testbench代码中assign赋值语句才能对同一个wire型变量多次赋值。
- assign 语句 不能在顺序过程语句中使用
一般由always,initial,function引导的过程称为顺序语句过程,顺序过程或函数内部的语句具有顺序性。在顺序过程中语句的执行依赖于敏感表中的敏感量的变化而激发该过程的运行,不在敏感表中的变量变化不能保证该过程得到执行。在顺序语句过程中不能使用assign赋值语句。
例如下面的代码是verilog语法禁止的。
|
1 2 3 4 |
<span style="color: #800080;">always</span>@(*) begin <span style="color: #ff0000;"><s> assign c[3:0]=a[3:0]+b[3:0];</s></span><s> </s>end |
2.并发过程
- assign语句之间的并发过程
所有由assign 声明的赋值语句都是并发语句,也就是多个并发语句之间并没有绝对的依赖关系,都是并发执行的,并不依赖于书写顺序。
例2:
assign a[3:0] = b[3:0];
assign c[3:0] = a[3:0] + 1;
//===================交换书写顺序
assign c[3:0] = a[3:0] + 1;
assign a[3:0] = b[3:0];
虽然前后书写顺序交换了,但执行结果没有差别。
- assign并发语句与顺序过程的并发性
虽然函数(function),always过程等内部是顺序语句过程,但always作为块语句,整个块的行为与其他的assign赋值语句也是并发的,因此也不依赖书写顺序。
例3:
|
1 2 3 4 5 |
<span style="color: #008000;"><span style="color: #800080;">assign</span> a[3:0] = b[3:0];</span> <span style="color: #008000;"><span style="color: #800080;">assign</span> c[3:0] = a[3:0] + 1;</span> <span style="color: #008000;"><span style="color: #800080;">always</span>@(*)</span> <span style="color: #008000;"> y[3:0] = c[3:0];</span> |
如果调换顺序,如下例所示,整体代码的运行结果,不受影响。
例4:
|
1 2 3 4 5 |
<span style="color: #0000ff;"><span style="color: #800080;">always</span>@(*)</span> <span style="color: #0000ff;"> y[3:0] = c[3:0];</span> <span style="color: #0000ff;"><span style="color: #800080;">assign</span> a[3:0] = b[3:0];</span> <span style="color: #0000ff;"><span style="color: #800080;">assign</span> c[3:0] = a[3:0] + 1;</span> |
例3与例4的结果完全相同。
- always的各个过程之间也是并发的。
多个always过程块之间也是并发,虽然每个always块内部是顺序的。
例5:
//顺序块1
|
1 2 3 4 5 |
<span style="color: #0000ff;"><span style="color: #800080;">always</span>@(*)</span> <span style="color: #800080;">begin</span> <span style="color: #0000ff;"> y[3:0] = c[3:0];</span> <span style="color: #0000ff;"> d[3:0] = a[3:0];</span> <span style="color: #800080;">end</span> |
//顺序块2
|
1 2 3 4 5 |
<span style="color: #ff6600;"><span style="color: #800080;">always</span> @(*)</span> <span style="color: #800080;">begin</span> <span style="color: #ff6600;"> e[3:0] = c[3:0] + 1;</span> <span style="color: #ff6600;"> f[3:0] = a[3:0] + c[3:0];</span> <span style="color: #800080;">end</span> |
将两个顺序块整体调换顺序,代码总体运行结果不变,如下例:
例6:
//顺序块2
|
1 2 3 4 5 |
<span style="color: #ff6600;"><span style="color: #800080;">always</span> @(*)</span> <span style="color: #800080;">begin</span> <span style="color: #ff6600;"> e[3:0] = c[3:0] + 1;</span> <span style="color: #ff6600;"> f[3:0] = a[3:0] + c[3:0];</span> <span style="color: #800080;">end</span> |
//顺序块1
|
1 2 3 4 5 |
<span style="color: #333399;"><span style="color: #800080;">always</span> @(*)</span> <span style="color: #800080;">begin</span> <span style="color: #333399;"> y[3:0] = c[3:0];</span> <span style="color: #333399;"> d[3:0] = a[3:0];</span> <span style="color: #800080;">end</span> |
例5与例6的结果也完全相同。可见verilog HDL描述的电路,从总体上看都是并发的。当然并发的过程内部也各有其特殊性,下面请关注后续章节顺序过程的各个描述。
对应视频: