FPGA跨时钟域处理

 

一、电平同步,主要解决单比特信号从慢速时钟域同步到快速时钟域的问题

以串口接收数据作为例子,如下图所示。

 

 

 

  首先是三个输入信号用绿色标注,时钟、复位和单比特串行输入的数据rx信号。一般在FPGA的设计中,会对单比特串行输入的数据rx信号进行打了两拍的操作(如果不做该处理会偶尔出现接收到的数据出现错误的现象),也就是让rx信号先经过两级寄存器。理论上,应该按照串口的时序找到rx的下降沿开始接收起始位的数据,为什么先将rx信号做打两拍处理呢?那就要先从跨时钟域会导致“亚稳态”的问题上说起。

  如果系统时钟刚好采集到上升沿的中间附近位置(按照概率来讲,如果数据传输量足够大或传输速度足够快时一定会产生这种情况),即数据传输过程中不满足触发器的建立时间Tsu(指触发器的时钟信号上升沿到来以前,数据稳定不变的最小时间)和保持时间Th(指触发器的时钟信号上升沿到来以后,数据稳定不变的最小时间),此时触发器输出端rx_reg1在有效时钟沿之后比较长的一段时间处于不确定状态,在这段时间里rx_reg1端在0和1之间处于振荡状态,而不是等于串口输入的稳定的rx值。

 

 

 

上图所示为产生亚稳态的波形示意图,rx信号经过FPGA中的第一级寄存器后输出的rx_reg1信号在时钟上升沿Tco时间后会有Tmet(决断时间)的振荡时段,当第一个寄存器发生亚稳态后,经过Tmet的振荡稳定后,第二级寄存器就能采集到一个稳定的值。但由于振荡时间Tmet是受到很多因素影响的,所以Tmet时间有长有短,如下图所示。

 

 

 

  当Tmet1时间长到大于一个采样周期后,那第二级寄存器就会采集到亚稳态,但是从第二级寄存器输出的信号就是稳定的了。当然会人会问到第二级寄存器的Tmet2的持续时间会不会继续长到大于一个采样周期?这种情况虽然会存在,但是其概率是极小的,寄存器本身就有减小Tmet时间让数据快速稳定的作用。

  由于在上位机中波特率和rx是同步关系,而rx信号和FPGA的系统时钟sys_clk是异步的关系,我们此时要做的是将慢速时钟域系统中的数据rx同步到快速时钟域系统中,所使用的方法叫电平同步法,俗称“打两拍法”。所以rx信号进入FPGA后会首先经过一级寄存器,出现亚稳态现象,导致rx_reg1信号的状态不确定是0还是1,就会使受其影响的将其他信号做出不同的判断,有的判断到“0”有的判断到“1”,有的也进入了亚稳态,产生连锁反应,导致逻辑混乱。为了避免这种情况,数据进来后首先进行打一拍的处理,打一拍后产生rx_reg1信号。但 rx_reg1可能还存在低概率的亚稳态问题,为了进一步降低出现亚稳态的概率,我们将从rx_reg1信号再打一拍后产生rx_reg2信号,使之能够较大概率保证 rx_reg2信号是0或者1中的一种确定情况,这样rx_reg2所影响的后级信号就都是相对稳定的了,就不会对后级电路造成过大影响。

 

 

 

二、脉冲同步,主要解决单比特信号从快速时钟域同步到慢速时钟域的问题

     对于亚稳态的问题同样也会在将单比特信号从快速时钟域同步到慢速时钟域时出现,但我们却不能仅仅用“打两拍法”。如下图所示,慢速时钟域的时钟clkb根本就采集不到快速时钟域clka下的Signal_a脉冲信号,会漏掉数据,所以我们需要先解决这个问题,再做单比特数据的跨时钟域工作。

 

 

   就要想办法转换思路,如果能够让同步于快速时钟域clka下的脉冲信号Signal_a变长到可以让慢速时钟域clkb检测到这个问题,就可以完美解决了。所以先将快速时钟域clka下的脉冲信号Signal_a,在快速时钟域clka的作用下,变为沿信号,产生一个名为src_state的中间变量来作为脉冲信号Signal_a变成的沿信号。如下图所示,每当快速时钟域clka检测到Signal_a脉冲信号为高时,让src_state信号取反,使得Signal_a的第一个脉冲变为src_state信号的上升沿,Signal_a的第二个脉变为src_state信号的下降沿,后面如果Signal_a信号还有脉冲依然是变为src_state信号的上升沿和下降沿。

 

 

 

  巧妙的利用将“脉冲信号”转化为“沿信号”的思想就可以使慢速时钟域的时钟clkb检测到同步于快速时钟域clka且将脉冲信号Signal_a转化为沿信号的src_state,相当于是把同步于快速时钟域clka的脉冲信号Signal_a进行了展宽处理,这样我们就把快速时钟域clka的脉冲信号Signal_a通过src_state信号“沿”的形式在慢速时钟域clkb中得到了保留。如下图所示,src_state信号是同步与快速时钟域clka的信号,state_dly1信号是同步于慢速时钟域clkb的信号。

 

 

 现在只是将快速时钟域clka的脉冲信号Signal_a以src_state信号沿的形式保留住了,还没有做同步处理。如下图所示,此时我们再对src_state信号做打两拍的处理就可以将src_state信号同步到慢速时钟域clkb中了。其中,state_dly1信号是src_state信号在慢速时钟域clkb下打的第一拍,state_dly2信号是src_state信号在慢速时钟域clkb下打第二拍,state_dly2就是同步于慢速时钟域clkb的稳定信号。

 到此为止,还有最后一部需要做的那就是将变为state_dly2信号的“沿”再转化为脉冲信号。这就需要引入了另一个新的知识——边沿检测。边沿检测主要作用是能够准确的识别出信号的上升沿或下降沿,也就是希望当上升沿或下降沿来临时,能够产生一个唯一标识上升沿或下降沿的脉冲信号来告诉上升沿或下降沿来了,然后再根据这个脉冲信号启动下一级操作。如下图所示,对同一信号打一拍后在①位置处检测到上升沿,拉高一个时钟的脉冲;在②位置处检测到下降沿,拉高一个时钟的脉冲。要满足这种特殊的要求,我们该如何编写代码呢?

 

 

 

 

  由前面的分析知道src_dtate信号在慢速时钟域clkb的采样下得到的state_dly1信号可能是不稳定的,而state_dly2信号是稳定的,不能直接用state_dly1和state_dly2来产生沿标志信号,因为state_dly1的不稳定性可能会导致产生的下降沿标志信号也不稳定,所以需要将state_dly2信号再打一拍,产生dst_state信号,用state_dly2信号和dst_state信号产生上升沿和下降沿标志信号还原脉冲,这就是为什么看到一共打了三拍的原因。还原后的波形如下所示,看到Signal_b信号的波形我们就知道要用assign语句来实现。此时就成功的实现了将快速时钟域clka的脉冲信号Signal_a同步到慢速时钟域clkb的脉冲信号Signal_b。如果大家做过百兆以太网的设计,就会用到这种方法来同步脉冲信号。

 

 

 

 

 

三、握手信号

所谓握手,即通信双方使用了专用控制信号进行状态指示。
这个控制信号既有发送域给接收域的,也有接收域给发送域的,使用握手协议方式处理跨时钟域数据传输,只需要对双方的握手信号(req和ack)分别使用打两拍进行同步。
在具体实现中,假设req、ack总线在初始化时都处于无效状态,当发送域检测到脉冲信号pulse_a,使req信号有效,随后发送有效的req信号给接收域。
接收域在检测到有效的req信号后锁存数据总线,然后回送一个有效的ack信号表示读取完成应答。
发送域在检测到有效ack信号后撤销当前的req信号,接收域在检测到req撤销后也相应撤销ack信号,此时完成一次正常握手通信。

 

 

 

module handshake_signal(
input aclk,
input arst_n,
input signal_a,
input bclk,
input brst_n,
output signal_b
);

reg req;
reg req_r0;
reg req_r1;
reg req_r2;

reg ack;
reg ack_r0;

always@(posedge aclk or negedge arst_n)
begin
if(arst_n==1'b0)
req<=1'b0;
else if(signal_a==1'b1)
req<=1'b1;
else if(ack_r0==1'b1)
req<=1'b0;
end

always@(posedge bclk or negedge brst_n)
begin
if(brst_n==1'b0)
begin
{req_r2,req_r1,req_r0}<=3'b0;
end
else
begin
{req_r2,req_r1,req_r0}<= {req_r1,req_r0,req};
end
end

always@(posedge aclk or negedge arst_n)
begin
if(arst_n==1'b0)
{ack_r0,ack}<=2'b0;
else
{ack_r0,ack}<= {ack,req_r1};
end
assign signal_b=req_r2^req_r1;
endmodule

 

 

 

多bit数据的跨时钟域问题

 1.如果跨时钟域多bit数据的传输,且传输数据间隔刚好为1,我们可以采用异步FIFO进行数据传输。为了消除亚稳态带来的影响,我们首先把传输的数据转换为格雷码,这样处理的好处是可以大大减小数据传输过程中多bit数据同时翻转的位数,因为格雷码相邻之间只有1位数据不同。然后将传输的数据进行同步化处理,这样做的目的是消除数据传输过程中亚稳态的出现带来的不良影响,即把转化后的格雷码数据先在源时钟域打一拍,然后再通过目的时钟域打两拍后完成一次数据的同步化; 

 

 

2.使用握手信号MUX,跨时钟域传输数据:既然有了将二进制数据转换为格雷码,然后通过异步FIFO进行多bit数据的传输的办法,为什么要需要另一种办法呢?因为转化为格雷码的办法是有局限性的,一般只能用于地址数据传输或者传输的两个数据之间递增1或者递减1这样的数据,因为只有这样的数据转化为格雷码的时候,才会仅仅只有一bit数据发生翻转;而采用握手信号的原理和上述提及到的单bit数据传输是一样的道理,即源时钟域发出一个1bit的请求信号(req),然后经过同步化处理后,将源时钟域的信号传输至目的时钟域,然后目的时钟域接受到数据后,反馈出一个完成信号(res),再进过一个同步化。这样一个过程称之为一次握手,表明完成一次跨时钟域数据的传输;之所以用1bit的请求信号,是因为多bit数据传输不可能每bit数据后都经过一次同步化,这样会使得电路面积大大增加,也不现实,所以就采用源时钟域发出的请求信号(res)和目的时钟域进行交互; 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
`timescale 1ns/1ns
 
module mux(
    input               clk_a   ,
    input               clk_b   ,  
    input               arstn   ,
    input               brstn   ,
    input       [3:0]   data_in ,
    input               data_en ,
 
    output reg  [3:0]   dataout
);
    reg[3:0]data_in1;
    reg data_ena1,data_enb1,data_enb2;
    always@(posedge clk_a or negedge arstn)begin
        if(!arstn)begin
            data_in1 <= 0;
            data_ena1 <= 0;
        end
        else begin
            data_in1 <= data_in;
            data_ena1 <= data_en;
        end
    end
     
    always@(posedge clk_b or negedge brstn)begin
        if(!brstn)begin
            data_enb1 <= 0;
            data_enb2 <= 0;
        end
        else begin
            data_enb1 <= data_ena1;
            data_enb2 <= data_enb1;
        end
    end
     
    always@(posedge clk_b or negedge brstn)begin
        if(~brstn)
            dataout <= 0;
        else
            dataout <= data_enb2? data_in1: dataout;
    end
                    
endmodule

 

posted @ 2022-07-26 13:28  220nf绿波电龙  阅读(1022)  评论(0编辑  收藏  举报