FPGA跨时钟域处理全解:同步器、FIFO与握手协议的场景选型指南
CDC核心不是“都会写”,而是按信号类型选型:单比特电平用同步器,窄脉冲用展宽/toggle,多比特低频配置用手握,持续数据流用异步FIFO。多比特总线直接打两拍最易出错,各bit走线延迟不同会采到中间态。
一、单比特:两级同步器
慢到快、持续电平(使能、模式、错误标志)用2-FF;高速或安全路径可3级。两级之间不加组合逻辑,Xilinx加ASYNC_REG让工具就近布局。
module sync_2ff #(parameter STG=2)(
input clk_dst,rst_n,input d,output reg q);
reg [STG-1:0] chain;
always @(posedge clk_dst or negedge rst_n) begin
if(!rst_n) chain<={STG{1'b0}};
else chain<={chain[STG-2:0],d};
end
assign q=chain[STG-1];
endmodule
快到慢的窄脉冲别直接同步:先转toggle,慢域打拍后边沿恢复,保证不漏。
reg tg; always @(posedge clk_fast) if(pulse) tg<=~tg;
sync_2ff s0(clk_slow,rst_n,tg,sync_tg);
always @(posedge clk_slow) begin s1<=sync_tg; s2<=s1; end
assign out_pulse=s1^s2;
二、多比特低频:握手协议
配置字、命令、寄存器组等非连续数据,用req/ack闭环。源端锁数据→拉req,目的端同步req→采样数据→拉ack,源端同步ack后撤req。只跨单比特控制线,数据线不跨时钟。
module cdc_handshake #(parameter W=32)(
input clk_s,clk_d,rst_n,input [W-1:0] din,input vld,
output reg [W-1:0] dout,output reg dvalid,output ackbusy);
reg req,ack; reg [W-1:0] hold;
reg r1,r2,a1,a2;
always @(posedge clk_s or negedge rst_n) begin
if(!rst_n) begin req<=0; hold<=0; end
else if(vld&&!req) begin hold<=din; req<=1; end
else if(a2) req<=0;
end
always @(posedge clk_d or negedge rst_n) begin
if(!rst_n) begin r1<=0;r2<=0;ack<=0;dout<=0;dvalid<=0; end
else begin
r1<=req; r2<=r1;
if(r2&&!ack) begin dout<=hold; dvalid<=1; ack<=1; end
else if(!r2) begin ack<=0; dvalid<=0; end
end
end
always @(posedge clk_s or negedge rst_n) begin
if(!rst_n) begin a1<=0;a2<=0; end else begin a1<=ack;a2<=a1; end
end
assign ackbusy=req;
endmodule
握手延迟大但绝对安全,适合上电配置、参数下发,不适合视频/ADC流。
三、持续数据流:异步FIFO
写读异频、突发包、DMA桥接用异步FIFO。指针扩一位,二进制转格雷,跨域两级同步,比较同步后格雷码判空满;存储用真双口RAM或厂家FIFO IP。
parameter W=8,AW=4,DEP=1<<AW;
reg [W-1:0] ram[0:DEP-1];
reg [AW:0] wptr,rptr,wg,rg;
reg [AW:0] wg_s1,wg_s2,rg_s1,rg_s2;
wire [AW:0] wgray=rptr^(rptr>>1), rgray=wptr^(wptr>>1);
always @(posedge wclk or negedge rn) begin
if(!rn) wptr<=0; else if(wen&&!full) begin ram[wptr[AW-1:0]]<=din; wptr<=wptr+1; end
end
always @(posedge rclk or negedge rn) begin
if(!rn) rptr<=0; else if(ren&&!empty) begin dout<=ram[rptr[AW-1:0]]; rptr<=rptr+1; end
end
always @(posedge wclk) begin rg_s1<=rgray; rg_s2<=rg_s1; end
always @(posedge rclk) begin wg_s1<=wgray; wg_s2<=wg_s1; end
assign full =(wptr=={~rg_s2[AW:AW-1],rg_s2[AW-2:0]});
assign empty=(rptr==wg_s2);
深度按最大突发/速率差算,写快读慢留余量;GT/以太网用IP更稳,自研用于低资源场景。
四、选型与约束
单比特电平:2-FF;窄脉冲:toggle或展宽;安全/GHz:3-FF。
多比特偶发:握手;多比特流:FIFO;计数器跨域可只同步格雷指针。
异步时钟组加set_clock_groups -asynchronous,同步链别让工具优化合并;复位用异步断言、同步释放。
验证重点:快到慢漏脉冲、FIFO空满边界、握手ack丢失、复位释放竞态。按“信号粒度+速率+允许延迟”定方案,比记住某个模块更重要。





