方法1 双触发器(打2拍)
该方法只用于慢到快时钟域的1bit信号传递。在Xilinx器件中,可以使用(* ASYNC_REG = "TRUE" *)标记,将两个寄存器尽量靠近综合,降低 亚稳态因导线延迟太大而传播到第二个寄存器的可能性。
moduleff2( input clk0,//10Minput din, input clk1,//100Moutputdout ); regdin_r=1'd0; (* ASYNC_REG ="TRUE"*) regr0=1'd0; (* ASYNC_REG ="TRUE"*) regr1=1'd0; assigndout = r1; always@(posedgeclk0) din_r <= din;//由于不确定前级是否有触发器,这里默认加一级寄存,防止出现毛刺always@(posedge clk1)begin r0 <= din_r; r1 <= r0; endendmodule
方法2 双向握手 传递信号
双向握手方法使用了5个触发器,右边3个与方法1一致。左边两个用于将确认信号同步到写时钟域。用户必须将信号保持到确认信号置高。
该方法适用于慢到快、快到慢、1bit信号传递,一般而言,信号有效值为高电平。

可以用一个非门、一个异或门、一个与门,和一个触发器(下图中的reg-2)实现信号保持,输入低速单脉冲,但是不保证输出单脉冲,可以在输出端加一个寄存器,用于上升沿判断.
在verilog可以用一些简单的判断来保持信号。下面是一个单向信号异步桥,支持单脉冲输入,单脉冲输出,时钟速率无限制
使用时,确保在active置低时,将s_vld给异步桥。
/* * Name : 单向信号异步桥 * Origin: 230406 * EE : hel */modulesignalbridge_async( inputwire s_clk , inputwire s_rstn , inputwire s_vld ,// pulse inputinputwire d_clk , inputwire d_rstn , outputreg d_vld ,// pulse outputoutputwire active );reg[1 :0] s_syncer ;reg[1 :0] d_syncer ;wire ack_d2 = s_syncer[1];wire req_d2 = d_syncer[1]; reg req_d3;reg async_req;wire async_ack = req_d2;assign active = async_req | ack_d2 ;// Source clock domainalways@(posedges_clkornegedges_rstn)beginif(!s_rstn)begin s_syncer <= 2'd0; endelsebegin s_syncer <= {s_syncer[0],async_ack}; endendalways @(posedge s_clk ornegedge s_rstn) beginif (!s_rstn) begin async_req <= 1'd0; endelseif(ack_d2)begin async_req <= 1'd0; endelseif(s_vld)begin async_req <= 1'd1; endend// Destination clock domainalways @(posedge d_clk ornegedge d_rstn) beginif (!d_rstn) begin d_syncer <= 2'd0; endelsebegin d_syncer <= {d_syncer[0],async_req}; endendalways @(posedge d_clk ornegedge d_rstn) beginif (!d_rstn) begin req_d3 <= 1'd0; d_vld <= 1'd0; endelsebegin req_d3 <= req_d2; d_vld <= req_d2 & (~req_d3); endendendmodule
方法3 双向握手 传递数据
可以使用请求信号采样跨时钟域过来的多比特数据,就可以做到数据的跨时钟域了(数据跨时钟域,频率低到高,高到低)如下图:

图里没有画反馈信号,数据需要在目的时钟采样时保持稳定。这种单个数据跨时钟域传输的编写流程如下:
原时钟域置高req,置好data
目的时钟域将req打两拍变成req_d2
目的时钟域检测到req_d2置高,将data打到寄存器内,并将ack置高
原时钟域检测到ack_d2被置高,拉低req
目的时钟域检测到req_d2被拉低,将ack拉低
通过上述流程编写的数据异步桥如下:
支持单个数据输入输出,时钟速率无限制。使用时,确保在active置低时,将s_din和s_vld给异步桥,s_vld必须为单脉冲。
/* * Name : 单向数据异步桥 * Origin: 230404 * EE : hel */moduledatabridge_async #( parameterDW =8)( inputwire s_clk , inputwire s_rstn , inputwire [DW-1:0] s_din , inputwire s_vld ,// pulse inputinputwire d_clk , inputwire d_rstn , outputreg [DW-1:0] d_dout , outputreg d_vld ,// pulse outputoutputwire active );reg[1 :0] s_syncer ;reg[1 :0] d_syncer ;wire ack_d2 = s_syncer[1];wire req_d2 = d_syncer[1]; reg req_d3;reg[DW-1:0] async_dat;reg async_req;wire async_ack = req_d2;assign active = async_req | ack_d2 ;// Source clock domainalways@(posedges_clkornegedges_rstn)beginif(!s_rstn)begin s_syncer <= 2'd0; endelsebegin s_syncer <= {s_syncer[0],async_ack}; endendalways @(posedge s_clk ornegedge s_rstn) beginif (!s_rstn) begin async_dat <= {DW{1'd0}}; endelseif(s_vld && (!active))begin async_dat <= s_din; endendalways @(posedge s_clk ornegedge s_rstn) beginif (!s_rstn) begin async_req <= 1'd0; endelseif(ack_d2)begin async_req <= 1'd0; endelseif(s_vld)begin async_req <= 1'd1; endend// Destination clock domainalways @(posedge d_clk ornegedge d_rstn) beginif (!d_rstn) begin d_syncer <= 2'd0; endelsebegin d_syncer <= {d_syncer[0],async_req}; endendalways @(posedge d_clk ornegedge d_rstn) beginif (!d_rstn) begin d_dout <= {DW{1'd0}}; endelseif(req_d2)begin d_dout <= async_dat; endendalways @(posedge d_clk ornegedge d_rstn) beginif (!d_rstn) begin req_d3 <= 1'd0; d_vld <= 1'd0; endelsebegin req_d3 <= req_d2; d_vld <= req_d2 & (~req_d3); endendendmodule
testbench:
`timescale1ns/1psmoduledatabridge_async_tb ();parameterDW =8;regs_clk =0;regs_rstn =0;reg[DW-1:0] s_din =0;regs_vld =0;regd_clk =0;regd_rstn =0;wire[DW-1:0] d_dout;wired_vld;wireactive;reg[DW-1:0]buffer ;realts;realtd;reala;realb;realclkstp;always#(ts) s_clk <= ~s_clk;always#(td) d_clk <= ~d_clk; databridge_async #(DW) databridge_async ( .s_clk ( s_clk ), .s_rstn ( s_rstn ), .s_din ( s_din ), .s_vld ( s_vld ), .d_clk ( d_clk ), .d_rstn ( d_rstn ), .d_dout ( d_dout ), .d_vld ( d_vld ), .active ( active ) );taskautomatic gen_trans;input [DW-1:0]data;begin #0 s_vld = 1; s_din = data; @(posedge s_clk); #0 s_vld = 0; s_din = 0;endendtask//automatictaskautomatic wait_busy;begin @(posedge s_clk);#0; while (active) begin @(posedge s_clk);#0; endendendtask//automatictaskautomatic loop_test;inputinteger n;integer i;reg [63:0]random_dat;beginfor (i = 0;i
方法4 转为独热码或格雷码
如果多bit信号是简单连续变化(连续加一或减一),可以将其转为格雷码,再打2拍到目标时钟域,最终再解码为二进制编码。
如果多bit信号没有变化规律,可以将其转为独热码,再打2拍到目标时钟域,最终再解码为二进制编码。该方法适用于慢到快、多bit信号传递。
为什么data不能直接通过同步器过域:
多比特数据由于存在传输路径延迟,到达同步器有先后顺序,导致同步器采样到不同电平,输出错误数据。
根本原因是源端数据有多个bit发生跳变。如果每次只有一个bit跳变(连续变化格雷码、独热码),就一定能采样到正确的数据。
假如目的时钟非常慢,比原时钟慢很多,在慢时钟上升沿时还是能采到正确数据,可能采到跳变之前的也可能是之后的,但总之都是source的数据。如果采到跳变之前的数据,就会产生异步FIFO的虚空虚满。从这个角度看,异步FIFO是绝对安全的(忽略同步器失效),异步FIFO两边的时钟频率没有任何要求。方法5 深度为2的ASYNC-FIFO
在传输信号,不需要较高带宽时,可以将普通格雷码异步FIFO特化为2-deep ASYNC-FIFO,该方法适用于慢到快、快到慢、多bit信号传递。一般很少使用2deep-FIFO,而是使用双向握手来传递单个多比特数据。
方法6 ASYNC-FIFO
该方法适用于慢到快、快到慢、多bit数据传递。
moduleasync_fifo//(First_Word_Fall_Through FIFO,数据提前输出,可能不利于时序收敛)#( parameterintegerDATA_WIDTH =16, parameterintegerADDR_WIDTH =8//深度只能是2**ADDR_WIDTH) ( inputwire wr_rst , inputwire wr_clk , inputwire wr_ena , inputwire[DATA_WIDTH-1:0] wr_dat , outputwire wr_full , inputwire rd_rst , inputwire rd_clk , inputwire rd_ena , outputwire[DATA_WIDTH-1:0] rd_dat , outputwire rd_empty );reg [DATA_WIDTH-1:0]mem[0:2**ADDR_WIDTH-1];reg [ADDR_WIDTH :0]wrptr;reg [ADDR_WIDTH :0]rdptr;wire[ADDR_WIDTH-1:0]wraddr = wrptr[ADDR_WIDTH-1:0];wire[ADDR_WIDTH-1:0]rdaddr = rdptr[ADDR_WIDTH-1:0];wire[ADDR_WIDTH :0]wrptr_gray = (wrptr>>1)^wrptr;wire[ADDR_WIDTH :0]rdptr_gray = (rdptr>>1)^rdptr;reg [ADDR_WIDTH :0]wrptr_gray_r0;reg [ADDR_WIDTH :0]wrptr_gray_r1;reg [ADDR_WIDTH :0]rdptr_gray_r0;reg [ADDR_WIDTH :0]rdptr_gray_r1;assignwr_full = (wrptr_gray == {~rdptr_gray_r1[ADDR_WIDTH:ADDR_WIDTH-1],rdptr_gray_r1[ADDR_WIDTH-2:0]});assignrd_empty = (rdptr_gray == wrptr_gray_r1);//在有些设计中,full和empty提早一个时钟出现,并加了一级寄存,这有助于收敛always@(posedgewr_clkorposedgewr_rst)beginif(wr_rst)begin rdptr_gray_r0 <= 'd0; rdptr_gray_r1 <= 'd0; endelsebegin rdptr_gray_r0 <= rdptr_gray; rdptr_gray_r1 <= rdptr_gray_r0; endendalways @(posedge rd_clk orposedge rd_rst) beginif (rd_rst) begin wrptr_gray_r0 <= 'd0; wrptr_gray_r1 <= 'd0; endelsebegin wrptr_gray_r0 <= wrptr_gray; wrptr_gray_r1 <= wrptr_gray_r0; endendalways @(posedge wr_clk orposedge wr_rst) beginif (wr_rst) begin mem[0] <= 'd0; wrptr <= 'd0; endelseif (wr_ena&&(!wr_full)) begin mem[wraddr] <= wr_dat; wrptr <= wrptr + 1'd1; endelsebegin mem[wraddr] <= mem[wraddr]; wrptr <= wrptr; endendalways @(posedge rd_clk orposedge rd_rst) beginif (rd_rst) begin rdptr <= 'd0; endelseif (rd_ena&&(!rd_empty)) begin rdptr <= rdptr + 1'd1; endelsebegin rdptr <= rdptr; endendassign rd_dat = mem[rdaddr];endmodule
-
寄存器
+关注
关注
31文章
5588浏览量
129035 -
Xilinx
+关注
关注
73文章
2192浏览量
129881 -
触发器
+关注
关注
14文章
2050浏览量
63033 -
时钟域
+关注
关注
0文章
53浏览量
9986
原文标题:跨异步时钟域的6种方法
文章出处:【微信号:gh_9d70b445f494,微信公众号:FPGA设计论坛】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录

跨异步时钟域处理方法大全


评论