阻塞(blocking)赋值方式(如 b=a)
①赋值语句立即执行,执行完毕后才执行下一条语句(即为阻塞的含义,依次顺序执行);
②b的值在赋值语句执行完后立即改变。
非阻塞(Non_blocking)赋值方式(如 b<=a)
①语句执行到此时,先计算“<=”右侧a的值,但不立即赋值给b;
②always块结束后才完成此次赋值操作;
③这是时序逻辑模块最常用的赋值方法。
对于组合逻辑电路:
①可以用assign语句描述较为简单的组合逻辑电路;
②也可以使用电平敏感的always块来描述:
所谓电平敏感的触发条件,是指@后的括号内必须包含块中所有输入信号(也可以用“@(*)”表示,否侧可能产生意想不到的latch,下同),且其中任一个电平发生变化(与时序逻辑不同,它在@后的括号内没有沿敏感关键词,如posedge或negedge),就能触发always块的动作。
此时,always块中需要将else(在if...else结构中)和default(在case结构中)补充完整,且被赋值的变量一定要定义为reg型,即可综合为纯组合逻辑。
综上所述:
①用always块建立纯组合逻辑模型时,用阻塞赋值“=”;
②时序电路或锁存器建模时,用非阻塞赋值“<=”。
③在同一个always块中建立时序和组合逻辑电路时,用非阻塞赋值“<=”;
④在同一个always块中不要既用非阻塞赋值又用阻塞赋值;
⑤用always块建立纯组合逻辑模型时,在赋值表达式右端参与赋值的信号都必须在always @(敏感电平列表)中列出完整,否则将生成透明锁存器;用always块建立纯时序逻辑模型时,事件控制语法为“always @(posedge clk)”(同步电路)或者“always @(posedge clk or negedge rst)”(异步电路)。
补充:
位于begin/end块内的多条阻塞赋值语句是串行执行的,这一点同标准的程序设计语言是相同的。但是多条非阻塞赋值语句却是并行执行的,这些非阻塞赋值语句都会在其中任何一条语句执行完成之前开始执行。这正是硬件电路的特点,因为实际的逻辑门电路都是独立运转的,而不是等到其他门电路运转结束之后自己才开始运转。
每个always语句块都隐含表示一个独立的逻辑电路模块。因此,对于特定的reg类型的变量,只能在一个always语句块中对其进行赋值;否则就可能会出现两个硬件模块同时从同一个输出端口输出数据的情况,这种情况一般称为短路输出(shorted output)。
下面举例说明:
ex1:阻塞赋值
//阻塞赋值
module ex(clk,a,b,c);
//-----------------------
input clk,a;
output reg b,c;
//-----------------------
always @(posedge clk)
begin
b = a;
c = b;
end
//-----------------------
endmodule
//-----------------------
clk信号的上升沿到来时,b马上取a的值,执行完毕后c取b的值(即等于a),生成的电路如下所示(左图和右图等效)。
2:非阻塞赋值
//非阻塞赋值
module ex(clk,a,b,c);
//-----------------------
input clk,a;
output reg b,c;
//-----------------------
always @(posedge clk)
begin
b <= a; //区别点
c <= b; //区别点
end
//-----------------------
endmodule
//-----------------------
clk信号的上升沿到来时,“<=”号右侧的a、b值先计算,等always块结束后,b就等于a,而c等于刚才计算的b(即原来的b值) ,这里生成了2个寄存器。
分析如下:
在posedge_clk_1处:判断a=1,b=0,always块结束后(即为触发沿_1)b=1,c=0;
在posedge_clk_2处:判断a=1,b=1,always块结束后(即为触发沿_2)b=1,c=1;
在posedge_clk_3处:判断a=0,b=1,always块结束后(即为触发沿_2)b=0,c=1;
在posedge_clk_4处:判断a=0,b=0,always块结束后(即为触发沿_2)b=0,c=0。
可以理解为考虑寄存器的硬件延时特性,“b <= a”和“c <= b”在posedge_clk处类似于“并发执行”。