用一个例子来学习阻塞赋值和非阻塞赋值
阻塞赋值与非阻塞赋值
阻塞赋值的一般表达式为:目标变量名=驱动表达式 阻塞赋值是一种理想化的数据传输,赋值立即发生,不存在延时行为
非阻塞赋值一般表达式为:目标变量名<=驱动表达式 非阻塞赋值比较接近真实的电路工作状态,应为他从综合的角度考虑到了延时和并行性。
在过程启动中,非阻塞赋值使三条语句同时运行,而阻塞赋值是按顺序方式完成更新的数据的。
新建工程,编写示例代码
module block_nonblock(clk,rst_n,a,b,c,out ); input clk,rst_n,a,b,c; output reg [1:0]out;//out=a+b+c>2,所以out应该为2位 reg [1:0] d;//定义一个中间变量 //d=a+b; //out=d+c always@(posedge clk or negedge rst_n) if(!rst_n) out=2'b0; else begin d=a+b; out=d+c; end endmodule
从RTL 视图可以看出整个运行过程是顺序执行的。
改变
d=a+b;
out=d+c;的赋值顺序,再观察RTL视图,会发现两者之间是有区别的,这说明顺序决定着输出的结果。
将赋值语句改为非阻塞赋值。
观察RTL视图,会发现两者是有明显不同。
同样的改变
d<=a+b;
out<=d+c;的赋值顺序,再观察RTL视图,会发现两者之间是没有区别的,这说明顺序并没有决定输出的结果。
由RTL图理论上可以看出两种赋值语句的不同,下面编写testbench进行仿真。
`timescale 1ns/1ns module block_nonblock_tb; `define clock_period 20 reg clock,rst_n,a,b,c; wire [1:0] out ; block_nonblock block_nonblock0(clock, rst_n, a, b, c, out );//这里采用一一对应的调用方式 initial clock=1'b0; always#(`clock_period/2) clock=~clock;//产生时钟信号 initial begin rst_n=1'b0; a=0;b=0;c=0; //赋值激励信号 #(`clock_period*200+1) rst_n=1'b1; a=0;b=0;c=1; #(`clock_period*200) a=0;b=1;c=0; #(`clock_period*200) a=0;b=1;c=1; #(`clock_period*200) a=1;b=0;c=0; #(`clock_period*200) a=1;b=0;c=1; #(`clock_period*200) a=1;b=1;c=0; #(`clock_period*200) a=1;b=1;c=1; #(`clock_period*200) #(`clock_period*200) $stop; end endmodule
设定文件路径后按前面的四个程序进行前仿。
d=a+b;
out=d+c;
out=d+c;
d=a+b;
d<=a+b;
out<=d+c;
out<=d+c;
d<=a+b;
通过仿真可以看出四种情况的异同,至此阻塞赋值和非阻塞赋值就可以通过实际例子来理解了。