-
阻塞/非阻塞赋值 概念
https://www.cnblogs.com/xgcl-wei/p/9059789.html -
本节主要内容
FPGA实现变量赋值时,会由于器件原因造成延迟。举例来讲:
我们要计算 o u t = a + b + c out=a+b+c out=a+b+c
为了体现本节的效果,这里增加计算过程,首先定义一个中间变量 d d d,使得 d = a + b d=a+b d=a+b
o u t out out就可以写成 o u t = d + c out=d+c out=d+c
其中
a
、
b
、
c
a、b、c
a、b、c都是1位的数据,out最大值是3,因此设为2位的。tb仿真结果如下:
通过添加子模块,可以查看变量
d
d
d(方法:在sim窗口中找到子模块名,右键add wave,然后restar一下modelsim,对他们分组即可,分组教程见上一课)。由于大家可以自己查看对应的波形图,我就不再贴很多中间过程的图了,这里只贴出来一个最后门级仿真的效果图。
图上可见,很奇怪的是
o
u
t
out
out有一段时间出现了0!发生在
c
c
c跳转成0,
b
b
b跳转成1的时候。
这是因为
o
u
t
out
out依赖于d,而这个过程由于电路延迟(此处以时钟上升沿为参照),d出现了0。导致输出也出现了0。这就是非阻塞赋值的一个问题。也就是计算结果和书写顺序没关系,导致中间会出现错误结果,宏观上看的确是正确的,但我们把信号拉到和时钟频率一个数量级的时候,就能发现错误了!
- 还是贴出代码
block_and_noneblock.v
// 时序逻辑 输入必须有clk
// always@ 里面赋值的 必须为reg型
`timescale 1ns/1ns
`define tp 1
module block_and_noneblock(clk, rst_n, a, b, c, out);
input clk;
input rst_n;
input a, b, c;
output reg[1:0]out; // out = a+b+c 最大是3 a:0/1
// 定义一个中间变量d
reg [1:0]d; // 公式:d = a+b; // out = d+c;
always@(posedge clk or negedge rst_n)
if(!rst_n) // rst_n == 0 复位
out = 2'b0;
else begin // 这里面是并行执行的,但是顺序调换就不行
// d = a + b; // 阻塞赋值 与编写顺序相关
// out = d + c;
// out = d + c; // 阻塞赋值 与编写顺序相关
// d = a + b;
// out <= d + c; // 非阻塞赋值 与编写顺序无关 查看rtl 发现中间多一个寄存器
// d <= a + b;
d <= a + b; // #`tp 不会作用到硬件电路,只适用于仿真,模拟电路延迟
out <= d + c; // 非阻塞赋值 与编写顺序无关
end
endmodule
block_and_noneblock_tb.v
`timescale 1ns/1ns
`define clock_period 20
module block_and_noneblock_tb;
reg clk;
reg rst_n;
reg a, b, c;
wire [1:0]out;
block_and_noneblock block0(
.clk(clk),
.rst_n(rst_n),
.a(a),
.b(b),
.c(c),
.out(out)
);
// 产生时钟
initial clk = 1; // 初始化变量
always#(`clock_period/2)
clk = ~clk;
// 开始
initial begin
rst_n = 1'b0;
a = 0;
b = 0;
c = 0;
#(`clock_period*200 + 1);
rst_n = 1'b1; // 松开复位键
#(`clock_period*200);
a = 0; b = 0; c = 0;
#(`clock_period*100);
a = 0; b = 0; c = 1;
#(`clock_period*100);
a = 0; b = 1; c = 0;
#(`clock_period*100);
a = 0; b = 1; c = 1;
#(`clock_period*100);
a = 1; b = 0; c = 0;
#(`clock_period*100);
a = 1; b = 0; c = 1;
#(`clock_period*100);
a = 1; b = 1; c = 0;
#(`clock_period*100);
a = 1; b = 1; c = 1;
#(`clock_period*100)
#(`clock_period*200);
$stop;
end
endmodule
-
总结与反思
这一节弄了好长时间,遇到过以前的报错,同样的原因却没有第一时间发现。难受。最后,本菜鸟还有一点疑问:
上面程序中,最后一图可以看出来out变量是在d变量更新后一段时间更新的,看上去不是依赖的clk上升沿,大佬们如果知道真相还望告知!
参考&致谢
小梅哥FPGA