非阻塞和阻塞分配没有按预期工作
Non-blocking and blocking assignments don't work as expected
我在理解这样一个看起来很简单的东西时遇到了问题:阻塞和非阻塞赋值。
我创建了一个小型测试平台来模拟这段代码的行为:
module ATest(clk, out);
input wire clk;
output reg [7:0] out;
reg [7:0] A;
initial begin
A <= 8'b0;
end
always @(posedge clk) begin
A = A + 1;
out = A;
end
endmodule
经过模拟,我得到了这一波:
我期望 A
和 out
下的值相同,因为我按顺序为它们分配了值。为什么 out
在第一个时钟期间“不关心”?
然后我尝试使用非阻塞赋值。我将一部分代码更改为:
always @(posedge clk) begin
A <= A + 1;
out <= A;
end
我得到了这一波:
我没想到这里有什么,因为非阻塞语句对我来说有点神秘。为什么 A
和 out
都设置为“不关心”?
此外,我在每个页面上都发现了不同的名称,所以请帮助我:
阻塞和非阻塞 是否可以与 顺序和并发 互换?哪个是正确的:非阻塞语句或并发语句?
这两个词是等价的。 "Blocking" 与 "sequential" 相同,因为 "blocking" 意味着赋值必须在模拟器移动到下一行之前完成(按顺序)。 "Non-Blocking" 表示所有行可以一次完成。与 Verilog 的一切一样,它有助于想象预期的硬件,因此您有时可以将其视为 "parallel" 与 "serial"。
在您的仿真中,时间 0 是否有时钟上升沿?
无需深入研究 Verilog 模拟器使用的模拟周期,您可以简单地认为非阻塞与阻塞分配如下:
阻塞赋值在执行给定赋值时内联发生,所以这意味着如果我有一行 A = A + 1
,这意味着我们取 A
的当前值,加 1 和分配 A
该新值。所以,赋值"blocks"一直执行到完成。
非阻塞赋值 (NBA) 发生的时间略晚于该行的执行时间。您可以将非阻塞分配视为告诉模拟器稍后安排此分配的行(请注意,稍后仍具有相同的模拟时间步长,因此所有这些仍在 simtime t 中发生)。所以,如果你有类似 A <= A + 1
的东西,这意味着在执行此行时取 A
的值,加 1 并安排 A
稍后更新为该值位,但继续继续该行之后的行。因此,如果下一行是 out = (A == 1) ? 1 : 0
,则该行将使用 A
的旧值执行,而不是增加后的值。一旦模拟器完成了活动代码,它就可以继续执行所有非阻塞分配。现在,A
将获得增加的值,所有其他非阻塞赋值将生效。
所以,以你的例子为例。在案例一中,我们看到了 NBA 的延迟效应。在initial
块中,A
被赋值为0,这意味着A
将在稍后取值0(仍然在sim time 0 记住);即分配计划在所有阻塞分配具有 运行 之后进行(严格来说不正确,但在这种情况下有效)。此外,你有时钟的姿势发生,所以 always
块 运行s。这里,A
取值 A + 1
,但请记住,A
赋值给 0 并没有发生,所以 A
仍然有它的初始值 8'bx
.所以,A + 1
也是 8'bx
。由于这是一个阻塞任务,它会立即发生。所以,A
不会从不在乎改变。继续,out
获取 A
的当前值,即 8'bx
。所以,我们在 out
上得到了不关心。在完成这些和其他阻塞分配后,现在我们完成 NBA,在这种情况下,A
变为 0。因此,仍然在模拟时间 0 内,A
变为 0,我们就完成了。在时钟的下一个位置,A
为 0,out
无关紧要,您的 always
按预期阻止 运行s,递增 A
和将 out
分配给相同的值。
如果您将 always
块更改为使用 NBA(如果假定它是一个寄存器,则应该如此),情况会略有不同。 initial
块仍然导致 NBA 计划为 A
变为 0。但是现在,always
块做了一些不同的事情。现在,A <= A + 1
不是立即将 A
分配给 don't care,而是将 A
安排为 8'bx
(请记住,右侧表达式表示什么值assign 是在线评估的,所以 A + 1
仍然使用 A
就像以前一样不关心;改变的是当 A
接受这个新值时)并且这是在 [=11 之后安排的=] 变为 0。因此,A
的两个 NBA 都已设置,但告诉 A
为 0 的 NBA 最先发生,并被后来的 A
分配给消除8'bx
。 out
同样计划接受 8'bx
但现在,A
永远不会变为 0。因此,A
和 out
都会卡在 8'bx
.
您可以查看 Verilog 或 SystemVerilog LRM,以更好地了解 sim 循环和实际情况,但我希望这能帮助您更好地理解其中的区别!
您的问题来自在 initial
块中使用非阻塞分配。请改用 initial A = 8'b0;
。
原因很可能是两个分配的处理方式。 =
分配是递增完成的,任何新值都可用于后续分配。通过 <=
分配所做的更改仅在处理完所有分配后才可用。
因为你的第一个边缘在 t = 0(处理初始块时),在第一个示例中 A
被分配 0
,但是 0
不可用到 out
直到它被处理。那是虽然第一个周期看起来很奇怪,但其他一切都很好。在第二个中,A
被分配了 0
和 A+1
,因此模拟器使用 always
块而不是 initial
,与 A+1
,当 A
仍然是一个未知值时。因此,A
和 out
的值永远未知。
我在理解这样一个看起来很简单的东西时遇到了问题:阻塞和非阻塞赋值。
我创建了一个小型测试平台来模拟这段代码的行为:
module ATest(clk, out);
input wire clk;
output reg [7:0] out;
reg [7:0] A;
initial begin
A <= 8'b0;
end
always @(posedge clk) begin
A = A + 1;
out = A;
end
endmodule
经过模拟,我得到了这一波:
我期望 A
和 out
下的值相同,因为我按顺序为它们分配了值。为什么 out
在第一个时钟期间“不关心”?
然后我尝试使用非阻塞赋值。我将一部分代码更改为:
always @(posedge clk) begin
A <= A + 1;
out <= A;
end
我得到了这一波:
我没想到这里有什么,因为非阻塞语句对我来说有点神秘。为什么 A
和 out
都设置为“不关心”?
此外,我在每个页面上都发现了不同的名称,所以请帮助我: 阻塞和非阻塞 是否可以与 顺序和并发 互换?哪个是正确的:非阻塞语句或并发语句?
这两个词是等价的。 "Blocking" 与 "sequential" 相同,因为 "blocking" 意味着赋值必须在模拟器移动到下一行之前完成(按顺序)。 "Non-Blocking" 表示所有行可以一次完成。与 Verilog 的一切一样,它有助于想象预期的硬件,因此您有时可以将其视为 "parallel" 与 "serial"。
在您的仿真中,时间 0 是否有时钟上升沿?
无需深入研究 Verilog 模拟器使用的模拟周期,您可以简单地认为非阻塞与阻塞分配如下:
阻塞赋值在执行给定赋值时内联发生,所以这意味着如果我有一行 A = A + 1
,这意味着我们取 A
的当前值,加 1 和分配 A
该新值。所以,赋值"blocks"一直执行到完成。
非阻塞赋值 (NBA) 发生的时间略晚于该行的执行时间。您可以将非阻塞分配视为告诉模拟器稍后安排此分配的行(请注意,稍后仍具有相同的模拟时间步长,因此所有这些仍在 simtime t 中发生)。所以,如果你有类似 A <= A + 1
的东西,这意味着在执行此行时取 A
的值,加 1 并安排 A
稍后更新为该值位,但继续继续该行之后的行。因此,如果下一行是 out = (A == 1) ? 1 : 0
,则该行将使用 A
的旧值执行,而不是增加后的值。一旦模拟器完成了活动代码,它就可以继续执行所有非阻塞分配。现在,A
将获得增加的值,所有其他非阻塞赋值将生效。
所以,以你的例子为例。在案例一中,我们看到了 NBA 的延迟效应。在initial
块中,A
被赋值为0,这意味着A
将在稍后取值0(仍然在sim time 0 记住);即分配计划在所有阻塞分配具有 运行 之后进行(严格来说不正确,但在这种情况下有效)。此外,你有时钟的姿势发生,所以 always
块 运行s。这里,A
取值 A + 1
,但请记住,A
赋值给 0 并没有发生,所以 A
仍然有它的初始值 8'bx
.所以,A + 1
也是 8'bx
。由于这是一个阻塞任务,它会立即发生。所以,A
不会从不在乎改变。继续,out
获取 A
的当前值,即 8'bx
。所以,我们在 out
上得到了不关心。在完成这些和其他阻塞分配后,现在我们完成 NBA,在这种情况下,A
变为 0。因此,仍然在模拟时间 0 内,A
变为 0,我们就完成了。在时钟的下一个位置,A
为 0,out
无关紧要,您的 always
按预期阻止 运行s,递增 A
和将 out
分配给相同的值。
如果您将 always
块更改为使用 NBA(如果假定它是一个寄存器,则应该如此),情况会略有不同。 initial
块仍然导致 NBA 计划为 A
变为 0。但是现在,always
块做了一些不同的事情。现在,A <= A + 1
不是立即将 A
分配给 don't care,而是将 A
安排为 8'bx
(请记住,右侧表达式表示什么值assign 是在线评估的,所以 A + 1
仍然使用 A
就像以前一样不关心;改变的是当 A
接受这个新值时)并且这是在 [=11 之后安排的=] 变为 0。因此,A
的两个 NBA 都已设置,但告诉 A
为 0 的 NBA 最先发生,并被后来的 A
分配给消除8'bx
。 out
同样计划接受 8'bx
但现在,A
永远不会变为 0。因此,A
和 out
都会卡在 8'bx
.
您可以查看 Verilog 或 SystemVerilog LRM,以更好地了解 sim 循环和实际情况,但我希望这能帮助您更好地理解其中的区别!
您的问题来自在 initial
块中使用非阻塞分配。请改用 initial A = 8'b0;
。
原因很可能是两个分配的处理方式。 =
分配是递增完成的,任何新值都可用于后续分配。通过 <=
分配所做的更改仅在处理完所有分配后才可用。
因为你的第一个边缘在 t = 0(处理初始块时),在第一个示例中 A
被分配 0
,但是 0
不可用到 out
直到它被处理。那是虽然第一个周期看起来很奇怪,但其他一切都很好。在第二个中,A
被分配了 0
和 A+1
,因此模拟器使用 always
块而不是 initial
,与 A+1
,当 A
仍然是一个未知值时。因此,A
和 out
的值永远未知。