为什么阻塞赋值和非阻塞赋值在测试台中的行为不同?

Why don't blocking and non-blocking assignments behave the same in a testbench?

我写了一个D触发器模块如下:

module mod1(
    input clk,
    input d,
    output reg q
);

    always @(posedge clk)begin
        q <= d;
    end

endmodule

这是我用来测试我的模块的两个代码片段mod1

// code 1
module mod1_tb;
    reg clk;
    reg d;
    wire q;
    
    mod1 dut(
        .clk(clk),
        .d(d),
        .q(q)
    );
    
    always #10 clk = ~clk;
    
    initial begin
        clk = 0;

        @(posedge clk)
            d <= 0;
        @(posedge clk)
            d <= 1;
        @(posedge clk)
            d <= 0;
        @(posedge clk)
            d <= 1;
    
    end

endmodule

波形可以这样看:

// code 2
module mod1_tb;
    reg clk;
    reg d;
    wire q;
    
    mod1 dut(
        .clk(clk),
        .d(d),
        .q(q)
    );
    
    always #10 clk = ~clk;
    
    initial begin
        clk = 0;
        
        @(posedge clk);
        d = 0;
        @(posedge clk);
        d = 1;
        @(posedge clk);
        d = 0;
        @(posedge clk);
        d = 1;
    
    end

endmodule

波形可以这样看:

在我看来,它们应该都可以正常工作,但结果表明只有第一种方法有效。 我有点困惑为什么第二个不起作用。

假设您的输入信号 (d) 与时钟同步。您应该像在设计中驱动该信号一样在测试台中驱动该信号。使用 @(posedge clk),就像您已经在做的那样,并使用非阻塞分配 (<=) 而不是阻塞 (=)。

您的 code 1 在测试台中正确使用了 nonblocking,这就是它按您预期进行模拟的原因。

您的 code 2 在测试台中使用 阻塞 ,这就是它没有按预期模拟的原因。

非阻塞分配保证模拟器将在您期望的时间对同步输入进行采样。

推荐的良好编码习惯是对时序逻辑使用非阻塞,例如触发器。


有大量文献讨论了 Verilog 事件调度的细节。 Here is one paper.