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

时间:2016-03-09 14:03:45

标签: verilog system-verilog

我有以下代码。

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。

为什么会发生这种情况以及如何进行编码以使其不发生?

2 个答案:

答案 0 :(得分:2)

通常,顺序块中的分配使用非阻塞(<=)分配。值得注意的除了用于生成派生时钟之外,其应该使用阻塞(=)分配。阻止顺序块中的分配是合法的;但你需要知道自己在做什么,否则你会在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

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

答案 1 :(得分:0)

问题在于这一行:

clk_2 <= ~clk_2;

您正在使用非阻止分配,或许您想要阻止分配:

clk_2 = ~clk_2;

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

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

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