如何处理从一个时钟域到另一个时钟域的数据,该时钟域的时钟除以第一个时钟的 2 个版本?

How to handle data going from a clock domain to another clock domain whose clock is divide by 2 version of the first clock?

我有以下代码。

module tb;

reg clk;
reg clk_2;
reg [15:0] from_flop;
reg [15:0] to_flop;

initial begin
        clk = 0; 
        clk_2 = 0;
        from_flop = 1;
end

always #10 clk = ~clk;


always @(posedge clk) begin
        clk_2 <= ~clk_2;
end


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

always @(posedge clk_2) begin
        to_flop <= from_flop; 
end

endmodule

然而,在 10ns 时刻,from_flop 和 to_flop 都得到值 = 2。这与我的预期相反。我期待 from_flop 在 10ns 时从 1 变为 2,而 to_flop 在 10ns 时从 x 变为 1。

为什么会发生这种情况以及如何编写代码以防止这种情况发生?

问题出在这一行:

clk_2 <= ~clk_2;

您正在使用非阻塞赋值,而您可能需要阻塞赋值:

clk_2 = ~clk_2;

非阻塞分配在阻塞分配之后安排,因此 always @(posedge clk) begin 将始终在 always @(posedge clk_2) begin 之前计时。

显然,这不是可综合的代码。所以,这是一个模拟(调度)问题。如果您打算使用此功能综合一些东西,请非常仔细地考虑如何生成分频时钟。

http://www.edaplayground.com/x/AyQ

通常顺序块中的赋值是使用非阻塞(<=)赋值。一个值得注意的例外是生成派生时钟,它应该使用阻塞 (=) 分配。顺序块中的阻塞赋值是合法的;但是您需要知道自己在做什么,否则 RTL 仿真和电路之间会出现功能不匹配。也就是说,在使用这种方法时,您仍然需要谨慎行事。

触发器有时钟到 Q 延迟,因此任何派生时钟都会与其父时钟有相位偏移。一些合成工具可能会为您补偿偏移量,但是您需要指定合成器应该发生这种情况的所有情况;它不会为你解决。这意味着您不应在整个设计中零星地创建派生时钟。所有派生时钟信号都需要从一个专用模块生成;这使它易于管理。

在大多数情况下,您不应创建任何派生时钟。相反,通过添加一个额外的触发器对每隔一个时钟进行采样。

always @(posedge clk) begin
  from_flop <= from_flop + 1; 
  if (transfer_n==1'b0) begin
    to_flop <= from_flop;
  end
  transfer_n <= ~transfer_n;
end

此策略将设计保持在一个时钟域中,这通常可以通过更好的时序使综合更容易。最小的额外触发器对面积的影响可以很容易地小于保持派生时钟对齐所需的添加缓冲区的面积损失。