从处理单bit跨时钟域信号同步问题来入手

EDA/IC设计

1063人已加入

描述

在数字电路中,跨时钟域处理是个很庞大的问题,因此将会作为一个专题来陆续分享。今天先来从处理单bit跨时钟域信号同步问题来入手。

01

握手(handshake)是用来处理单bit信号的跨时钟域传递的一个有效的方法。在从快时钟向慢时钟传递时,由于输入信号变化较快,输出一侧可能跟不上输入的变化,从而导致“漏采“现象。

FPGA设计

图1 “漏采”现象演示

在图1中,由于两个时钟的速度差距,来自快时钟域的脉冲信号还未到达慢时钟的采样边沿便消失了,导致了“漏采”。

在这种情况下,如何让脉冲信号准确无误地传递过去呢?一个方法是将脉冲信号展宽,待输出一侧检测到信号并将其解析为脉冲信号后,再向输入一侧发送应答信号,表明接收到信号并且传输完成。这个过程被称之为“握手”。

02

先来看一个最基本的握手协议,下边是它的电路图。

FPGA设计

图2 “握手”电路演示

在上图中 src_clk和dst_clk分别为输入和输出侧的时钟,src_pulse为输入的脉冲信号,dst_pulse为同步到输出端的脉冲信号。可以看到,脉冲信号被同步到输出端后,输出端便立即向输入端发送了应答信号,表示收到信号。而当输出端的应答信号同步到输入端后,输入端才可以同步下一个信号。以下为这个电路的verilog描述。

module Sync_Pulse (
                input  wire src_clk,
                input wire dst_clk,
                input wire rst_n,
                input wire src_pulse,

                output wire dst_pulse
                  );


    reg                 req_state_dly1, req_state_dly2,dst_req_state,src_sync_req;
    reg                 ack_state_dly1,src_sync_ack;
    wire               dst_sync_ack;

    always @ (posedge src_clk or negedge rst_n)
        begin
            if (rst_n == 1'b0)
                src_sync_req <= 1'b0;
            else if (src_pulse)           
                src_sync_req <= 1'b1;
            else if (src_sync_ack)           
                src_sync_req <= 1'b0;
            else;
        end


    always @ (posedge dst_clk or negedge rst_n)
        begin
            if (rst_n == 1'b0)
            begin
                req_state_dly1 <= 1'b0;
                req_state_dly2 <= 1'b0;
                dst_req_state <= 1'b0;
            end else begin
                req_state_dly1 <= src_sync_req;
                req_state_dly2 <= req_state_dly1;        
                dst_req_state <= req_state_dly2;
             end
        end

    assign dst_sync_ack = req_state_dly2;

    always @ (posedge src_clk or negedge rst_n)
        begin
            if (rst_n == 1'b0) 
                begin
                    ack_state_dly1 <= 1'b0;
                    src_sync_ack <= 1'b0;
                end
            else 
                begin
                     ack_state_dly1 <= dst_sync_ack;
                     src_sync_ack <= ack_state_dly1;
                end
        end

    assign  dst_pulse =   dst_req_state & (~req_state_dly2);

endmodule

03

上述电路虽然可以完整的同步信号,但是若在同步一个脉冲的过程中,输入端又接收到一个输入进来的脉冲,那么此时刚刚输入进来的脉冲将会同步失败。更糟糕的是该电路没有同步失败的反馈,导致使用者误以为正确同步了信号。鉴于此,将上述电路进行改进,当同步失败后将输出src_sync_fail信号来指示同步失败。以下为该电路的verilog描述(此代码来源于网络):

module handshake_pulse_sync
(
        src_clk , //source clock
        src_rst_n , //source clock reset (0: reset)
        src_pulse , //source clock pulse in
        src_sync_fail , //source clock sync state: 1 clock pulse if sync fail.
        dst_clk , //destination clock
        dst_rst_n , //destination clock reset (0:reset)
        dst_pulse //destination pulse out
);
        //PARA DECLARATION
        //INPUT DECLARATION
        input src_clk ; //source clock
        input src_rst_n ; //source clock reset (0: reset)
        input src_pulse ; //source clock pulse in
        input dst_clk ; //destination clock
        input dst_rst_n ; //destination clock reset (0:reset)
        //OUTPUT DECLARATION
        output src_sync_fail ; //source clock sync state: 1 clock pulse if sync fail.
        output dst_pulse ; //destination pulse out
        //INTER DECLARATION


        wire dst_pulse ;
        wire src_sync_idle ;
        reg src_sync_fail ;
        reg src_sync_req ;
        reg src_sync_ack ;
        reg ack_state_dly1 ;
        reg ack_state_dly2 ;
        reg req_state_dly1 ;
        reg req_state_dly2 ;
        reg dst_req_state ;
        reg dst_sync_ack ;


        //--========================MODULE SOURCE CODE==========================--
        //--=========================================--
        // DST Clock :
        // 1. generate src_sync_fail;
        // 2. generate sync req
        // 3. sync dst_sync_ack
        //--=========================================--
        assign src_sync_idle = ~(src_sync_req | src_sync_ack );

        //report an error if src_pulse when sync busy ;
        always @(posedge src_clk or negedge src_rst_n)
        begin
            if(src_rst_n == 1'b0)
                    src_sync_fail <= 1'b0 ;
            else if (src_pulse & (~src_sync_idle))
                    src_sync_fail <= 1'b1 ;
            else
                    src_sync_fail <= 1'b0 ;
        end


        //set sync req if src_pulse when sync idle ;
        always @(posedge src_clk or negedge src_rst_n)
        begin
                if(src_rst_n == 1'b0)
                        src_sync_req <= 1'b0 ;
                else if (src_pulse & src_sync_idle)
                        src_sync_req <= 1'b1 ;
                else if (src_sync_ack)
                        src_sync_req <= 1'b0 ;
        end


        always @(posedge src_clk or negedge src_rst_n)
        begin
                if(src_rst_n == 1'b0)
                begin
                        ack_state_dly1 <= 1'b0 ;
                        ack_state_dly2 <= 1'b0 ;
                        src_sync_ack <= 1'b0 ;
                end
                else
                begin
                        ack_state_dly1 <= dst_sync_ack ;
                        ack_state_dly2 <= ack_state_dly1 ;
                        src_sync_ack <= ack_state_dly2 ;
                end
        end


        //--=========================================--
        // DST Clock :
        // 1. sync src sync req
        // 2. generate dst pulse
        // 3. generate sync ack
        //--=========================================--
        always @(posedge dst_clk or negedge dst_rst_n)
        begin
                if(dst_rst_n == 1'b0)
                begin
                        req_state_dly1 <= 1'b0 ;
                        req_state_dly2 <= 1'b0 ;
                        dst_req_state <= 1'b0 ;
                end
                else
                begin
                        req_state_dly1 <= src_sync_req ;
                        req_state_dly2 <= req_state_dly1 ;
                        dst_req_state <= req_state_dly2 ;
                end
        end

        //Rising Edge of dst_state generate a dst_pulse;
        assign dst_pulse = (~dst_req_state) & req_state_dly2 ;
        //set sync ack when src_req = 1 , clear it when src_req = 0 ;


        always @(posedge dst_clk or negedge dst_rst_n)
        begin
                if(dst_rst_n == 1'b0)
                        dst_sync_ack <= 1'b0;
                else if (req_state_dly2)
                        dst_sync_ack <= 1'b1;
                else
                        dst_sync_ack <= 1'b0;
        end
endmodule

从代码可以得知,在同步信号的过程中,src_sync_idle会拉低,此时若再有输入脉冲,则会输出同步失败信号。

仿真结果如下

FPGA设计

图3 电路仿真结果

从仿真结果可以看到,该设计达到了同步信号要求。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分