【问题标题】:Non-blocking and blocking assignments don't work as expected非阻塞和阻塞分配不能按预期工作
【发布时间】:2015-07-19 11:19:49
【问题描述】:

我在理解这样一个看起来很简单的事情时遇到了问题:阻塞和非阻塞分配。

我创建了一个小型测试台来模拟这段代码的行为:

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 都设置为“不关心”?

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

【问题讨论】:

    标签: verilog simulation assign


    【解决方案1】:

    无需深入研究 Verilog Simulators 使用的模拟周期,您可以将非阻塞分配与阻塞分配简单地认为是这样的:

    阻塞赋值发生在给定赋值执行时内联,这意味着如果我有像A = A + 1这样的行,这意味着我们取A的当前值,加1并分配A那个新的价值。因此,赋值“阻塞”执行直到它完成。

    非阻塞赋值 (NBA) 发生的时间稍晚于行执行时。您可以将非阻塞分配视为告诉模拟器稍后安排此分配的行(注意,稍后仍具有相同的模拟时间步长,因此所有这些仍在 simtime t 中发生)。因此,如果您有类似A &lt;= A + 1 的内容,这意味着在执行此行时取A 的值,加1 并安排A 稍后更新为该值,但继续前进与该行之后的行。因此,如果下一行是out = (A == 1) ? 1 : 0,则此行将使用旧值A 执行,而不是增加的值。一旦模拟器完成了活动代码,它就可以继续执行所有非阻塞分配。现在,A 将获得递增的值,并且所有其他非阻塞分配都将生效。

    所以,对于你的例子。案例一,我们看到了NBA的延迟效应。在initial 块中,A 被赋值为 0,这意味着A 稍后会取 0 的值(记得还在 sim time 0 之内);即分配计划在所有阻塞分配都运行后进行(严格来说不正确,但在这种情况下有效)。此外,时钟的摆位发生了,所以always 块运行。这里,A 取值为A + 1,但请记住,A 的赋值尚未发生,因此A 仍具有其初始值8'bx。所以,A + 1 也是 8'bx。而且由于这是一个阻塞分配,它会立即发生。所以,A 不会从不在乎改变。继续下去,out 获得了A 的当前值,即8'bx。所以,我们在out 上得到了不关心。在完成这些和其他阻塞任务之后,现在我们完成了 NBA,在这种情况下,A 变为 0。所以,仍在模拟时间 0 内,A 变为 0,我们就完成了。在时钟的下一个 posedge 时,A 为 0,out 无关紧要,您的 always 块按预期运行,将 A 递增并将 out 赋值为相同的值。

    如果您将 always 块更改为使用 NBA(如果它是一个寄存器,则应该这样做),情况会略有变化。 initial 块仍然导致 NBA 计划为 A 变为 0。但是现在,always 块做了一些不同的事情。现在,A &lt;= A + 1 不是立即将A 分配给不关心,而是将A 安排为8'bx(请记住,要分配的值的右侧表达式是内联评估的,所以@987654361 @ 仍然像以前一样使用 A 不关心;改变的是当 A 采用这个新值时)并且这计划在 A 变为 0 之后。所以,A 的两个 NBA 都是设置,但是告诉 A 为 0 的那个首先发生,并被后来的 A 分配给 8'bx 所清除。 out 同样被安排在 8'bx 上,但现在,A 永远不会变成 0。因此,Aout 都被困在 8'bx

    您可以查看 Verilog 或 SystemVerilog LRM,以更好地了解 sim 周期以及实际情况,但我希望这有助于您更好地了解差异!

    【讨论】:

      【解决方案2】:

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

      造成这种情况的原因很可能是这两个分配的处理方式。 = 分配是增量完成的,任何新值都可用于后续分配。通过&lt;= 分配所做的更改只有在处理完所有分配后才可用。

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

      【讨论】:

        【解决方案3】:

        这些术语是等价的。 “阻塞”与“顺序”相同,因为“阻塞”意味着必须在模拟器移动到下一行(按顺序)之前完成分配。 “非阻塞”意味着可以一次完成所有行。与 Verilog 一样,它有助于想象预期的硬件,因此您有时可能会将其视为“并行”与“串行”。

        在您的模拟中,时间 0 是否存在正时钟边沿?

        【讨论】:

        • 是的,有。首先将其偏移或将其更改为负边缘有助于(?)。那并发代码呢?
        • 为什么在我的情况下它仍然是8'hxx
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-20
        • 2016-07-06
        • 1970-01-01
        • 1970-01-01
        • 2011-11-07
        • 1970-01-01
        相关资源
        最近更新 更多