|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
阻塞赋值“=”与非阻塞赋值“<=”你懂了吗?
1 E9 `& G* p: K/ w* M初学Verilog HDL被那个“=”与“<=”整迷糊啦,然后找度娘,一篇文章写得不错嘛,不迷糊啦。如下,还迷糊的小伙伴凑个热闹吧,undefined,另外,感谢作者喽。
' L) v( Z$ k' e6 ]& h5 l
4 k6 \' [, \$ D
( A" b! X/ X( z
$ Y4 m% c7 R$ B首先我们要理解两种变量类型 Net Type(连线型)和 Register Type (寄存器型)。(有些参考书上有分为3种类型,这个无关紧要)Net Type(连线型),从名字上理解就是“导线”呗,导线的这头和导线的另一头始终是直接连通的,这头是什么值,那头就是什么值,所以输出随着输入随时变化的。连线型中 wire 最常见。
6 c. q+ z* u, c. P3 ^ Register Type(寄存器型),寄存器就不像普通导线了,它可以把值给存住,你只要给它赋一次值,它都会存住那个值,直到你给它赋一个新的值它才会改变。寄存器型中 reg 最常见。
* r7 N4 Q L; o+ o7 V% _8 i 最常用到的是 wire 和 reg 这两种类型,其他的对我们初学者来说一般很少用到,可以暂时跳过,以后慢慢学下去自然会理解。
! z/ M0 _$ G5 y" z7 _" ` 注意:wire型变量如果没有赋予初始值,默认初始值为高阻态“Z”。! s0 ^+ T1 ?; x: F& ?" P, Q) ]
reg 型变量如果没有赋予初始值,默认初始值为不定态“X”。
& R! ?# ^* c8 P3 r; F4 W2 K! D' s1 L: m4 _9 M$ `- i' O
在理解这两种基本的数据类型之后,我们来看看verilog语言中的赋值语句。verilog语言中的赋值语句有两种,一种是持续赋值语句(assign语句),另一种是过程赋值语句(always语句)。, @. P. z4 q' H( @
持续赋值语句(assign语句)主要用于对wire型变量的赋值,因为wire(线型)的值不能存住,需要一直给值,所以需要用持续赋值。
! }" k/ ?1 P1 _& O* _/ V l a6 n 例如:assign c = a + b; 只要a和b有任意变化,都可以立即反映到c上,也就是说c的值是根据a,b的值随时变化的。1 X, U3 c* H% T, {3 |
过程赋值语句(always语句)主要用于reg 型变量的赋值 ,因为always语句被执行是需要满足触发条件的,所以always过程块里面的内容不是每时每刻都被执行,因此需要将被赋值的对象定义成寄存器类型,以便这个值能被保持住。3 f, i/ K1 y. n1 B X4 i, ?
过程赋值又分为 阻塞赋值 “=” 和 非阻塞赋值 “<=” 两种。这里的非阻塞赋值符号 “<=” 与 “小于等于” 符号相同,他们在不同的语境下表示不同含义,要注意区分,例如在“if-else”等判断语句中,一般都表示为“小于等于”。6 I( K( ^" q& y$ ~
% v3 A9 A8 [: x+ I 接下来对这两种赋值作具体讲解..." M) U; O5 k7 L, y
① 阻塞赋值 “=“ 。 阻塞赋值和我们平时理解的赋值差不多,不用太多解释,就是按照语句的顺序,一句句往下顺序执行。一个赋值语句执行完,然后执行下一个赋值语句。
/ ?. j$ F; G0 a8 l9 p+ H ② 非阻塞赋值 “<=” 。非阻塞赋值就比较特别了,在同一个always过程块中,非阻塞赋值语句都是同时并发执行的,并且在过程块结束时才执行赋值操作。也就是说,在同一个always过程块中,非阻塞赋值语句被执行没有先后顺序,在过程快结束时,大家一起被赋值。5 ~; Y* {0 |& q7 w+ B. K
) n0 D* T `& d- k: N' P9 K. R 给大家举一个具体的例子:
, \1 N# ^0 ]# v2 ^module test (clk, a1, a2, b1, b2, c1, c2); // test为module名称,括号内的是端口列表,包含所有输入输出的变量名称
0 G8 D$ t. E( v$ Sinput clk, a1, a2; // 定义输入变量,这里没有定义位宽,默认为1位宽度
Y1 C3 o6 \5 M g% j3 \output b1, b2, c1, c2; // 定义输出变量,这里没有定义位宽,默认为1位宽度
& h! {/ x' T a8 j0 w, c; Greg b1 = 0 , b2 = 0, c1 = 0 , c2 = 0; // 注意!因为这些变量将会在always过程块中被赋值,所以必须定义成 reg 型 2 h, |; L1 s( z+ \/ D
// 注意!这里省略了对输入信号clk, a1, a2 的类型定义,它们默认为1位的wire 型(因为输入信号是随时要变化,所以必须用wire型)
. R* M$ }) T, O: C* ]# Oalways @ (posedge clk) // always 用 clk 上升沿触发
: a2 {6 u9 Y. G9 F begin$ p2 x# Y( c+ Y% e
b1 = a1; // 这里采用的是阻塞赋值: y p7 z0 ^1 R* W# ^
c1 = b1;
3 A5 y2 m( J3 d8 g6 e) N6 @6 K, D end
' u8 P ]4 ?# x+ T8 M! ?' K# s& |; c. h% |: |$ i( A
always @ (posedge clk) // always 用 clk 上升沿触发0 r6 P2 w8 Q ~- _
begin
$ V& H- _9 m: \/ N. o b2 <= a2; // 这里采用的是非阻塞赋值$ E" I4 w* J2 w! ]
c2 <= b2;
9 Q. y' D( Y2 ~8 P r5 o9 X6 U% L end
" z, |! p0 F" Q1 i* tendmodule // endmodule 别忘了,与 module 成对使用3 A/ _+ Q* l" }5 K+ d
* _' U) Z1 ~9 R- \. i5 R仿真输入值设置图如下:5 v2 D7 w* q1 N' g e7 M
我们只需给输入信号赋值,输出信号根据输入信号的变化而变化。
4 S1 J' v( l7 }下面请看用quartus 2 软件仿真的波形图6 j4 w, F t7 f4 l1 t
; F8 h4 |0 }2 W" L2 b
4 R" k& @: v, v首先请看这两张图的区别,第一张是时序仿真波形图(timing),黄色标记部分因为延时而产生,第二张图为功能仿真波形图(function),不考虑器件的延时。
* q4 K4 U% k( S" @; u8 x9 t8 Y Y 在①时刻,第一个时钟上升沿到来(posedge clk), 两个always过程块同时被触发(这就是PFGA器件强大的一点,可以并发执行)8 A) O! a% V4 o; m2 W
a1,b1,c1采用的是阻塞赋值。阻塞赋值语句顺序执行,先执行 b1 = a1; 由于 a1 此刻的值为 1,所以b1 变为 1,然后执行 c1 = b1;由于b1的值刚才已经变成 1 了,所以c1也变成了 1 。
/ ]$ {( [% E: `2 E% A c9 `, G: L a2,b2,c2采用的是非阻塞赋值。非阻塞赋值语句并发执行, 也就是说 b2 <= a2; c2 <= b2; 这两句同时执行。由于a2 此刻的值为1,所以 b2 变为1,与此同时,b2 的当前值也将赋值给 c2 , b2 的当前值是是多少呢? 这里一定要分清楚了,b2 的当前值是 0,并不是 1 ,因为在 b2 还没有变为 1 之前,b2 的值就要赋值给 c2 了,所以 c2 的值仍然是 0 。 8 `5 e9 }6 v! ~. e2 t5 Y3 l2 v
直到在②时刻,第二个时钟上升沿到来,两个always再一次被同时触发,这次在执行 c2 <= b2; 这条赋值语句时,b2 的当前值为 1 ,所以 c2 才被赋值为 1 。3 }) x2 L( r, m9 }5 x- i; J# `
7 F h9 ~5 r% B$ t' l9 w+ S9 a
大家可以根据上面的分析方法,自己分析一下在③、④时刻 b1,b2,c1,c2 应该分别为什么值,与波形图对照着理解一下。
' l* Y" Z. O) i$ s! N其实verilog语言和 c语言大同小异,比较着来学习,会比较容易......
8 |. M4 l1 u* \0 h) T7 |6 j |
|