第2章 谆谆教诲莫相忘,字字珠玑记心头
2.1 赋值语句实战演练
在编写代码的过程中,用得最多的语句恐怕就是赋值语句了。常用的赋值方式有两种:阻塞型过程赋值和非阻塞型过程赋值。小芯刚开始编写代码时就被这两种赋值方式搞晕了—什么是阻塞型过程赋值?什么是非阻塞型过程赋值?什么时候用阻塞型过程赋值?什么时候用非阻塞型过程赋值?这两种赋值方式到底有哪些不同?什么时候两种赋值方式可以结合起来使用?
由于当时好的教材比较少,因此小芯被这些简单的问题困扰了很久。下面小芯将通过一些实例来解答这些问题。
2.1.1 非阻塞型过程赋值语句
以赋值操作符 “<=” 来标识的赋值操作被称为非阻塞型过程赋值(Nonblocking Assignment)。非阻塞型过程赋值语句的特点如下。
● 在begin-end 串行语句块中,一条非阻塞型过程赋值语句的执行不会阻塞下一条语句的执行,也就是说,在本条非阻塞型过程赋值语句对应的赋值操作执行完之前,下一条语句也可以执行。
● 仿真进程在遇到非阻塞型过程赋值语句后,首先计算其右端赋值表达式的值,然后等到仿真进程结束后再将该计算结果赋给变量,也就是说,此时的赋值操作是在同一仿真时刻上的其他普通操作结束后才得以执行的。
例如,有如下代码:
在上述语句中,包含两条非阻塞型过程赋值语句S1 和S2。在仿真进程遇到begin-end串行语句块后(0 时刻),语句S1 开始执行,即B 的值得到计算(对A 的赋值操作要等到当前时间步结束才能执行)。由于语句S1 是一条非阻塞型过程赋值语句,所以语句S1 的执行不会阻塞语句S2 的执行,语句S2 随即开始执行,即A 的值得到计算。由于此时在语句S1 中对A 的赋值操作还没有执行,所以计算得到的赋值表达式取值是A 的初值。由于语句S2 也是一条非阻塞型过程赋值语句,对B 的赋值操作也要等到当前时间步结束时才能执行,所以在当前时间步结束时,S1、S2 两条语句对应的赋值操作同时执行,即分别将计算得到的A和B 的初值赋给变量B 和A,从而交换了A 与B 的取值。
下面小芯将和大家一起设计一个实例,实例代码如下。
编写如下测试代码。
运行代码,得到的仿真波形如图2.1 所示。
图2.1
从仿真波形可以看出,使用非阻塞型过程赋值语句可以把a 的初值赋给b,把b 的初值赋给a。非阻塞型过程赋值语句一般用在时序逻辑中。
2.1.2 阻塞型过程赋值语句
以赋值操作符 “=” 来标识的赋值操作被称为阻塞型过程赋值(Blocking Assignment)。阻塞型过程赋值语句的特点如下:
● 串行语句块(begin-end)中的各条阻塞型过程赋值语句将以它们的排列次序依次执行。
● 阻塞型过程赋值语句的执行过程:首先,计算右端赋值表达式的值;然后,立即将计算结果赋给 “=” 左端的被赋值变量。
阻塞型过程赋值语句的这两个特点表明:仿真进程在遇到阻塞型过程赋值语句时,将计算表达式的值,并立即将其结果赋给等式左边的被赋值变量;在串行语句块中,下一条语句的执行会被本条阻塞型过程赋值语句所阻塞,只有在当前这条阻塞型过程赋值语句所对应的赋值操作执行完后,下一条语句才能开始执行。
例如,有如下代码:
在这段语句中包含两条阻塞型过程赋值语句S1 和S2,它们都是在仿真0 时刻得到执行的。由于语句S1 和S2 都是阻塞型过程赋值语句,所以在执行语句S1 时语句S2 因被阻塞而不能得到执行。只有在语句S1 执行完,a 被赋值为0 之后,语句S2 才能开始执行。而S2的执行将使a 被重新赋值为1,所以上述代码执行后,变量a 的值最终为1。
下面小芯将和大家一起设计一个实例,实例代码如下。
运行代码,得到的仿真波形如图2.2 所示。
图2.2
通过对比图2.1 和图2.2 可以看出,哪怕仅是把赋值方式换成阻塞型,其产生的结果也和非阻塞型的不同。阻塞型过程赋值语句一般用在组合逻辑中。