非阻塞和阻塞分配没有按预期工作

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

经过模拟,我得到了这一波:

我期望 Aout 下的值相同,因为我按顺序为它们分配了值。为什么 out 在第一个时钟期间“不关心”?

然后我尝试使用非阻塞赋值。我将一部分代码更改为:

always @(posedge clk) begin
        A <= A + 1;
        out <= A;
end

我得到了这一波:

我没想到这里有什么,因为非阻塞语句对我来说有点神秘。为什么 Aout 都设置为“不关心”?

此外,我在每个页面上都发现了不同的名称,所以请帮助我: 阻塞和非阻塞 是否可以与 顺序和并发 互换?哪个是正确的:非阻塞语句并发语句

这两个词是等价的。 "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'bxout 同样计划接受 8'bx 但现在,A 永远不会变为 0。因此,Aout 都会卡在 8'bx.

您可以查看 Verilog 或 SystemVerilog LRM,以更好地了解 sim 循环和实际情况,但我希望这能帮助您更好地理解其中的区别!

您的问题来自在 initial 块中使用非阻塞分配。请改用 initial A = 8'b0;

原因很可能是两个分配的处理方式。 = 分配是递增完成的,任何新值都可用于后续分配。通过 <= 分配所做的更改仅在处理完所有分配后才可用。

因为你的第一个边缘在 t = 0(处理初始块时),在第一个示例中 A 被分配 0,但是 0 不可用到 out 直到它被处理。那是虽然第一个周期看起来很奇怪,但其他一切都很好。在第二个中,A 被分配了 0A+1,因此模拟器使用 always 块而不是 initial,与 A+1,当 A 仍然是一个未知值时。因此,Aout 的值永远未知。