-
作者:
- 姚纪元
很多刚学Verilog HDL (硬件描述语言)的朋友肯定会对阻塞赋值和非阻塞赋值比较疑惑,那我们就一起来抛开这层迷雾吧。
首先我们要理解两种变量类型 Net Type(连线型)和 Register Type (寄存器型)。(有些参考书上有分为3种类型,这个无关紧要)
id="iframe_0.7008475568145514" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.sitongweilai.com/Services/BlogAttachment.ashx?AttachmentID=145&_=2213476%22%20style=%22border:none;max-width:987px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.7008475568145514',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 0px; height: 0px;">
Net Type(连线型),从名字上理解就是“导线”呗,导线的这头和导线的另一头始终是直接连通的,这头是什么值,那头就是什么值,所以输出随着输入随时变化的。连线型中 wire 最常见。
Register Type(寄存器型),寄存器就不像普通导线了,它可以把值给存住,你只要给它赋一次值,它都会存住那个值,直到你给它赋一个新的值它才会改变。寄存器型中 reg 最常见。
最常用到的是 wire 和 reg 这两种类型,其他的对我们初学者来说一般很少用到,可以暂时跳过,以后慢慢学下去自然会理解。
注意:wire型变量如果没有赋予初始值,默认初始值为高阻态“Z”。
reg 型变量如果没有赋予初始值,默认初始值为不定态“X”。
在理解这两种基本的数据类型之后,我们来看看verilog语言中的赋值语句。verilog语言中的赋值语句有两种,一种是持续赋值语句(assign语句),另一种是过程赋值语句(always语句)。
持续赋值语句(assign语句)主要用于对wire型变量的赋值,因为wire(线型)的值不能存住,需要一直给值,所以需要用持续赋值。
例如:assign c = a + b; 只要a和b有任意变化,都可以立即反映到c上,也就是说c的值是根据a,b的值随时变化的。
过程赋值语句(always语句)主要用于reg 型变量的赋值 ,因为always语句被执行是需要满足触发条件的,所以always过程块里面的内容不是每时每刻都被执行,因此需要将被赋值的对象定义成寄存器类型,以便这个值能被保持住。
过程赋值又分为 阻塞赋值 “=” 和 非阻塞赋值 “<=” 两种。这里的非阻塞赋值符号 “<=” 与 “小于等于” 符号相同,他们在不同的语境下表示不同含义,要注意区分,例如在“if-else”等判断语句中,一般都表示为“小于等于”。
接下来对这两种赋值作具体讲解...
① 阻塞赋值 “=“ 。 阻塞赋值和我们平时理解的赋值差不多,不用太多解释,就是按照语句的顺序,一句句往下顺序执行。一个赋值语句执行完,然后执行下一个赋值语句。
② 非阻塞赋值 “<=” 。非阻塞赋值就比较特别了,在同一个always过程块中,非阻塞赋值语句都是同时并发执行的,并且在过程块结束时才执行赋值操作。也就是说,在同一个always过程块中,非阻塞赋值语句被执行没有先后顺序,在过程快结束时,大家一起被赋值。
给大家举一个具体的例子:
module test (clk, a1, a2, b1, b2, c1, c2); // test为module名称,括号内的是端口列表,包含所有输入输出的变量名称
input clk, a1, a2; // 定义输入变量,这里没有定义位宽,默认为1位宽度
output b1, b2, c1, c2; // 定义输出变量,这里没有定义位宽,默认为1位宽度
reg b1 = 0 , b2 = 0, c1 = 0 , c2 = 0; // 注意!因为这些变量将会在always过程块中被赋值,所以必须定义成 reg 型
// 注意!这里省略了对输入信号clk, a1, a2 的类型定义,它们默认为1位的wire 型(因为输入信号是随时要变化,所以必须用wire型)
always @ (posedge clk) // always 用 clk 上升沿触发
begin
b1 = a1; // 这里采用的是阻塞赋值
c1 = b1;
end
always @ (posedge clk) // always 用 clk 上升沿触发
begin
b2 <= a2; // 这里采用的是非阻塞赋值
c2 <= b2;
end
endmodule // endmodule 别忘了,与 module 成对使用
仿真输入值设置图如下:
我们只需给输入信号赋值,输出信号根据输入信号的变化而变化。
id="iframe_0.16532674571499228" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.sitongweilai.com/Services/BlogAttachment.ashx?AttachmentID=156&_=2213476%22%20style=%22border:none;max-width:987px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.16532674571499228',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 20px; height: 20px;">
下面请看用quartus 2 软件仿真的波形图