跨时钟域的几种基本处理方法-FPGA常见问题社区-FPGA CPLD-ChipDebug

跨时钟域的几种基本处理方法

导言

在很久之前便陆续谈过亚稳态FIFO复位的设计。本次亦安做一个简单的总结,从宏观上给大家展示跨时钟域的解决方案。

什么是亚稳态?

对大多数工程师来讲,亚稳态是非常难以追踪的,因为它具有不确定性,在相对规范的设计下,如果仍然发生这个问题,那么可能非常难以复现异常。简单来讲,当触发器不满足建立时间和保持时间要求时,就会导致亚稳态。亚稳态出现时,触发器既不是高逻辑也不是低逻辑,后续电路则可能读取为0或者1(不确定状态),导致电路逻辑做出不符合当前事物逻辑的事情。

对于数字设计人员来讲,只要信号从一个时钟域跨越到另一个时钟域,那么就可能发生亚稳态。我们称为“跨时钟域”即“Clock Domain Crossing”,或CDC。

所以今天主要简单了解如何处理CDC这些基础问题。

同步跨时钟域信号

对大多数初学者来讲,当我们遇到CDCs时,有经验的工程师会告诉我们“打两拍”,即让信号通过两个 flip-flops,每个flip-flop都由新时钟域的时钟驱动(如下图)。第一个触发器出现亚稳态的概率很高,但第二个触发器的输出亚稳态的概率就会低得多。当然更多的flip-flops会让亚稳态的概率进一步下降,但一般下降的概率差距不会太大,如何选择取决于设计者自己。但并非多数CDC问题都能用这种简单方法,这种设计适用于旧时钟域比新时钟域慢的多的情况。经典的可配置的代码如下。

图片[1]-跨时钟域的几种基本处理方法-FPGA常见问题社区-FPGA CPLD-ChipDebug

// Language: Verilog-2001

`resetall
`timescale 1 ns / 1 ps
`default_nettype none

 

/*
* Synchronizes an asyncronous signal to a given clock by using a pipeline of
* two registers.
*/
module sync_signal #(
parameter WIDTH=1, // width of the input and output signals
parameter N=2 // depth of synchronizer
)(
input wire clk,
input wire [WIDTH-1:0] in,
output wire [WIDTH-1:0] out
);

 

reg [WIDTH-1:0] sync_reg[N-1:0];

 

/*
* The synchronized output is the last register in the pipeline.
*/
assign out = sync_reg[N-1];

 

integer k;

 

always @(posedge clk) begin
sync_reg[0] <= in;
for (k = 1; k < N; k = k + 1) begin
sync_reg[k] <= sync_reg[k-1];
end
end

 

endmodule

 

`resetall

 

异步复位同步释放

CDC中一个常见的例子就是异步复位,这个问题我在“FPGA复位信号设计讨论“一文中有详细的表述,这里简单讲解,对于初学者而言,不太建议用异步复位,尽管他们各有优劣,但至少对FPGA设计者而言,这个建议应该是有效的,即使是Xilinx官方也同样建议使用同步复位(UG949)。但有些状态下可能同步复位无效,比如在低功耗设计中,时钟可能在复位前就已经被“无效”,那么异步复位自然成为一个选择。异步复位关键点不在进入复位状态,而在于移除复位,因为移除状态可能会进入亚稳态,除了“FPGA复位信号设计讨论”一文,大家同样可以参阅我翻译的经典论文“同步复位与异步复位”。经典的代码如下所示,但请注意区别此处代码和“打两拍“的区别。

// Language: Verilog-2001

`resetall
`timescale 1ns / 1ps
`default_nettype none

 

/*
* Synchronizes an active-high asynchronous reset signal to a given clock by
* using a pipeline of N registers.
*/
module sync_reset #
(
// depth of synchronizer
parameter N = 2
)
(
input  wire clk,
input  wire rst,
output wire out
);

 

(* srl_style = “register” *)
reg [N-1:0] sync_reg = {N{1’b1}};

 

assign out = sync_reg[N-1];

 

always @(posedge clk or posedge rst) begin
if (rst) begin
sync_reg <= {N{1’b1}};
end else begin
sync_reg <= {sync_reg[N-2:0], 1’b0};
end
end

 

endmodule

 

`resetall

 

很多人争论高电平复位有效好还是低电平复位有效好,一般情况下我常见低电平复位有效,但高电平复位有效也不是没有,一般认为在FPGA中区别不是太大,但在ASIC中,低电平似乎更加常见。

跨时钟握手

有时需要一种方法来处理CDC,例如从慢时钟到快时钟,从快时钟到慢时钟,这时需要:握手信号,在“AXI4协议逻辑规范以及BUG处理”一文写过握手的注意点。握手的详细技术点感兴趣的朋友可以网上找相关资料。

FIFO

FIFO网上的文章特别多,具体的设计细节有很多需要讨论的地方,但对于跨时钟域而言,FIFO几乎是最方便的数据传输方式。这里不展开讲,之前我写过关于FIFO的相关文章,不是特别详细,接下来会有详细的关于FIFO的计划,在此之前,大家可以看Clifford E. Cummings经典论文,他的论文真的很推荐。

http://www.sunburst-design.com/papers/CummingsSNUG2008Boston_CDC.pdf
图片[2]-跨时钟域的几种基本处理方法-FPGA常见问题社区-FPGA CPLD-ChipDebug

请登录后发表评论

    没有回复内容