1. 概念:
1)阻塞赋值(用“=”)和非阻塞赋值(用“<=”)都是过程性赋值语句,出现在initial语句和always语句中;区别于出现在模块module中,使用assign关键词的连续赋值语句(用“=”)
2)阻塞赋值(用“=”)可以用于组合逻辑,也可以用于时序逻辑中,只不过用于时序逻辑中时,如果语句顺序没有使用恰当,得到的结果可能不是所期待的,所以建议时序逻辑中使用非阻塞赋值(用“<=”)(后文例子中解释)
3)非阻塞赋值(用“<=”)除了能描述时序逻辑电路外,也可描述组合逻辑电路,只是这样的代码风格不好
4)阻塞赋值是串行的,按照顺序执行,排列在前面的语句完成赋值后,再执行下面的赋值语句;阻塞赋值(如b=a)立即执行,也就是说执行下一条语句时,b已等于a
5)非阻塞赋值是并行的,允许块中的其他语句同时执行(非阻塞赋值语句把想要赋给左式的值安排在未来时刻,然后继续执行,换言之,它并不等到赋值执行完成之后才执行下一句);非阻塞赋值在语句块中,上面语句所赋的变量值不能立即就为下面的语句所用,块结束后才能完成这次赋值操作,而所赋的变量值是上一次赋值得到的(不立即执行)。(如always @(posedge clk or negedge rst_n) begin b<=a;c<=b end)在赋值开始时计算表达式右边的值,在过程块结束(本次clk周期结束)才更新被赋值变量,因为同时进行赋值,后面的语句会比上一条语句慢一个clk周期(也就是时钟clk上升沿来了之后,a的值赋给b,上一clk周期的b的值赋给c)(可能理解有误,欢迎指正~)
| 阻塞过程性赋值 | 非阻塞过程性赋值 |
| 使用“=”赋值符号 | 使用“<=”赋值符号 |
| 出现在initial语句和always语句中 | 出现在initial语句和always语句中 |
| 驱动变量 | 驱动变量 |
| 可用于组合逻辑(推荐),也可用于时序逻辑(不推荐,语句书写顺序不恰当可能产生意想不到的结果) | 可用于组合逻辑(不推荐,代码风格不好),也可用于时序逻辑(推荐) |
| 串行,立即执行,赋值语句执行完后块才结束 | 并行,不立即执行,块结束后才能完成这次赋值 |
2.实例:
设计代码:
情况1:非阻塞赋值
module block_nonblock1 (
input clk,
input rst_n,
input a,b,c,
output reg [1:0] out
);
reg [1:0] d;
always @(posedge clk or negedge rst_n)
if (!rst_n)begin
out<=2'b0;
d<=2'b0;
end
else begin
d<=a+b;
out<=d+c;
end
//等同于上面的写法
// always @(posedge clk or negedge rst_n)
// if (!rst_n)begin
// out<=2'b0;
// end
// else begin
// out<=d+c;
// end
// always @(posedge clk or negedge rst_n)
// if (!rst_n)begin
// d<=2'b0;
// end
// else begin
// d<=a+b;
// end
endmodule
情况2:将非阻塞赋值功能错误理解成阻塞赋值功能后,的非阻塞赋值
module block_nonblock2 (
input clk,
input rst_n,
input a,b,c,
output reg [1:0] out
);
reg [1:0] d;
always @(posedge clk or negedge rst_n)
if (!rst_n)begin
out<=2'b0;
d<=2'b0;
end
else begin
out<=a+b+c;
end
endmodule
情况3:能够实现正确目标设计的阻塞赋值
module block_nonblock3 (
input clk,
input rst_n,
input a,b,c,
output reg [1:0] out
);
reg [1:0] d;
always @(posedge clk or negedge rst_n)
if (!rst_n)begin
out=2'b0;
d=2'b0;
end
else begin
out=d+c;
d=a+b;
end
endmodule
情况4:因书写顺序不同,导致结果与预设不同的,阻塞赋值
module block_nonblock2 (
input clk,
input rst_n,
input a,b,c,
output reg [1:0] out
);
reg [1:0] d;
always @(posedge clk or negedge rst_n)
if (!rst_n)begin
out=2'b0;
d=2'b0;
end
else begin
d=a+b;
out=d+c;
end
endmodule
我们从底层电路角度进行分析:

从图中可看出,情况1和情况3的结果一致;情况2和情况4的结果一致。
下面展示出情况1和情况4(只存在非阻塞赋值和阻塞赋值的区别)的电路结构图:

情况1 非阻塞赋值,两层D触发器

情况4 阻塞赋值,一层D触发器
测试代码:
给出情况1的
//对情况1,非阻塞,d<=a+b;out<=d+c;
`timescale 1ns / 1ns
`define clk_period 20
module block_nonblock2_tb();
reg clk;
reg rst_n;
reg a,b,c;
wire [1:0] out;
block_nonblock1 block_num(.clk(clk),.rst_n(rst_n),.a(a),.b(b),.c(c),.out(out));
initial clk=1;
always #(`clk_period/2) clk=~clk;
initial begin
rst_n=1'b0;
a=0;
b=0;
c=0;
#(`clk_period*200+1);
rst_n=1'b1;
#(`clk_period*200);
a=0;b=0;c=0;
#(`clk_period*200);
a=0;b=0;c=1;
#(`clk_period*200);
a=0;b=1;c=0;
#(`clk_period*200);
a=0;b=1;c=1;
#(`clk_period*200);
a=1;b=0;c=0;
#(`clk_period*200);
a=1;b=0;c=1;
#(`clk_period*200);
a=1;b=1;c=0;
#(`clk_period*200);
a=1;b=1;c=1;
#(`clk_period*200);
#(`clk_period*200);
$stop;
end
endmodule
仿真波形:
情况一的仿真波形

Tip:
有个疑问,时钟上升沿的时候,d和out的值到底是0还是1?

解决方法:
代码更改

模拟D触发器电路延时的设计代码
仿真结果:

out<#2 d+c
这与实际的电路中是一样的。
理想D触发器中,clk上升沿一到,D的值就赋给Q。但实际电路中,是有延时的。所以"out<#2 d+c"这样的写法可以模拟实际的电路的传输延迟,也可以方便看仿真波形。而#1没有实际的电路与之对应,软件在进行比特流生成布局布线的时候,直接忽视它,所以也不影响板级调试。
参考
夏闻宇《Verilog数字系统设计教程》
本文详细比较了Verilog语言中的阻塞赋值和非阻塞赋值在initial和always语句中的使用,强调了非阻塞赋值在时序逻辑中的优势,通过实例展示了它们在电路设计中的不同效果。还讨论了阻塞赋值可能导致的问题以及如何模拟实际电路的延迟。
1221

被折叠的 条评论
为什么被折叠?



