【问题标题】:How does Verilog decide when events happen "simultaneously"?Verilog 如何决定事件何时“同时”发生?
【发布时间】:2015-04-10 22:40:04
【问题描述】:

我只知道 Verilog 2 个月,最多 3 个月。我对这件事感到非常沮丧,如果它发生在我所知道的其他非 HD 语言中,我将其称为 竞态条件。以下是描述案例的最简化形式的文件:

circuit.v

module circuit(f, x, clk, rst);

    output f;
    input x, clk, rst;

    wire d, q;

    d_flip_flop flipper (q, d, clk, rst);

    assign f = q;
    assign d = x;

endmodule

d_flip_flop.v

module d_flip_flop(q, d, clk, rst);

    output reg q;
    input d, clk, rst;

    always @(posedge clk, posedge rst)
    begin
        if (rst)
            q <= 1'b0;
        else
            q <= d;
    end

endmodule

我用这个tester.v测试这些

module tester;

    reg x, clk, rst;
    wire f;

    circuit uut (f, x, clk, rst);

    initial clk <= 0;
    always #10 clk <= ~clk;

    initial
    begin
        x <= 0;
        rst <= 0;

        rst <= #2 1;
        rst <= #4 0;

        #10 x <= 1;
    end

endmodule

这是我得到的波形:

我从该波形中看到的是 Verilog 已决定它应该首先处理 clk 中的更改,因此执行 q &lt;= d;;然后才处理x中的更改,因此执行assign d = x;

我所说的可能在硬件中甚至没有意义,但无论如何。

现在:我周围有一些人,所谓的讲座助理,更有经验,可能更博学,声称​​正确编写的 Verilog 顺序模块总是认为发生了变化,因为时钟敲响了。然而,即使在这个极其简单的例子中,我也认为情况并非如此(可能也不一定)。

所以,问题是,我应该怎么做才能把它变成一个正确编写 Verilog 顺序模块?我怎样才能让我的坏代码像其他代码一样好?还是根本没有这样的事情,而 Verilog 在这种情况下只是自豪地是非确定性的?

【问题讨论】:

  • 在顶级模块中不要使用d_flip_flop flipper (q, d, clk, rst);,而是使用d_flip_flop flipper (q, x, clk, rst);

标签: verilog race-condition simultaneous


【解决方案1】:

在这种情况下我经常看到的行为是:首先时钟从低到高上升,并产生一个 posedge 事件。发生这种情况时,非阻塞赋值中的右侧表达式会以其当前值读取。

对此我不能更具体,但我的假设是,如果右侧表达式在时钟生成 posedge 事件的同时发生变化,则读取的值是未确定的。现实世界的触发器需要一定的时间让输入信号稳定下来,然后才能被认为是有效的(建立时间)。如果不满足这个建立时间,则触发器存储的值将是输入信号的先前值。因此,模拟器试图模仿这种行为。

一个适当设计的时序系统应该只允许在活动时钟转换尚未发生时更改输入信号。例如:SPI 协议规定数据信号必须在与用于接受当前数据值的时钟转换相反的时钟转换中发生变化。

数据(MOSI 和 MISO 信号)在 SCLK 的下降沿发生变化。在 SCLK 的上升沿读取或写入数据。请注意,此时,两条数据线中的数据都是稳定的,因此满足了建立和保持时间限制,并且当前值有效地存储在 SPI 使用的移位寄存器中。

这种描述的行为应该是一致且完全确定的,因为它是许多已知硬件结构(例如移位寄存器和计数器)的基础:

module shiftreg2bits (
  input wire clk,
  input wire i,
  output wire [1:0] o
  );

  reg ff1 = 1'b0;
  reg ff2 = 1'b0;
  always @(posedge clk) begin
    ff1 <= i;
    ff2 <= ff1;
  end
  assign o = {ff1,ff2};
endmodule

一开始,时间#0o = 00

假设i = 1 当第一个clk 事件发生时:

  • ff1iCURRENT值,即1
  • ff2ff1CURRENT 值,即0ff2 不需要 1,因为 ff1 存储刚刚读取的值 1 的机制需要一些时间(模拟中的一个时间单位,没有给出延迟,并且特定的非零时间是依赖于真正触发器的硬件,即所谓的“propagation delay”),所以当clk 事件发生时,ff1 的输出仍然是0
  • 所以,o = 10

【讨论】:

    【解决方案2】:

    要创建正确编写的模型并避免竞争条件,您应该首先将非阻塞分配更改为阻塞分配。阻塞分配将按照您的意图立即更新信号。

    注意所有的 '

    Verilog 的阻塞赋值会阻塞(即停止)并立即更新信号。

    您最初编写的非阻塞分配将在增量延迟后更新左侧 (LHS)。非阻塞分配最初旨在模拟时序逻辑(触发器)的时钟到 Q 延迟。

    module tester;
    
    reg x, clk, rst;
    wire f;
    
    circuit uut (f, x, clk, rst);
    
    initial clk = 0;
    always #10 clk = ~clk;
    
    initial
    begin
        x = 0;
        rst = 0;
    
        rst = #2 1;
        rst = #4 0;
    
        #10 x = 1;
    end
    

    Verilog Gotcha!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-01
      • 2018-08-26
      • 2016-05-08
      • 2016-10-19
      • 1970-01-01
      相关资源
      最近更新 更多