使用触发器的时钟分频器的 Verilog 竞赛

Verilog race with clock divider using flops

我在 eda playground 上针对我遇到的问题做了一个基本示例。 假设我有两个时钟 1x 和 2x。使用触发器除法器将 2x 从 1x 中分离出来。

我有两个寄存器a和b。 a 以 1x 计时,b 以 2x 计时。

b为a的采样值

当我们有 1x 和 2x 时钟的上升沿时,b 没有取 a 的预期值,而是取下一个周期值。

这是因为这个时钟分频器方案,如果我们使用 icgs 和 en 进行分频,它工作正常。 但是有没有办法使用这种带有触发器的时钟分频器方案使其工作?

EDA 游乐场 link : https://www.edaplayground.com/x/map#

module race_test;

  logic clk1x = 0;
  logic clk2x = 0;

  always
    #5ns clk1x = !clk1x;

  int a, b;


  always @(posedge clk1x) begin
    a <= a+1;
    clk2x <= !clk2x;
  end

  // Problem here is that b will sample postpone value of a
  // clk2x is not triggering at the same time than clk1x but a bit later
  // This can be workaround by putting blocking assignment for clock divider
  always @(posedge clk2x) begin
    b <= a;
  end

  initial begin
    $dumpfile("test.vcd");
    $dumpvars;
    #1us
    $stop;
  end
endmodule

数字时钟分频器在模拟和物理时序方面都存在问题。

Verilog 的非阻塞赋值运算符假定读取和写入相同变量的每个人都同步到相同的时钟事件。通过使用 NBA 写入 clk2x,您已将 a 的读数转移到另一个 delta 时间*,正如您发现的那样,a已经更新了。

在实际硬件中,存在相当大的传播延迟,通常可以避免这种情况。但是,您使用相同的 D-flop 分配给 clk2x,因此那里也会有传播延迟。您最后一个 always 块现在代表一个时钟域交叉问题。因此,根据两个时钟之间的偏差,您仍然可能会遇到竞争条件。

纠正此问题的一种方法是使用时钟频率更高的时钟发生器模块

always #2.5ns clk = !clk;

always @(posedge clk) begin
       clk1x <= !clk1x;
       if (clk1x == 1)
         clk2x = !clk2x;

当然你已经解决了这个问题,但我认为还有更好的方法。 这本书说可以使用阻塞分配来避免竞争。但是阻止 assignemnt 会导致 synopsys lint 检查出错。因此,在没有 lint 错误的情况下避免竞争问题的一种方法是使用虚拟逻辑,就像这样

wire [31:0] a_logic;
wire dummy_sel;
assign dummy_sel = 1'b0;
assign a_logic = dummy_sel ? ~a : a;
always @(posedge clk2x) begin
  b <= a_logic;
end