异步bus交互(二)— 握手协议(2)

跨时钟域处理 & 亚稳态处理

1.概述

常见的跨时钟域信号处理方法都有哪些呢?有如下的三种:

(1)两级DFF同步器
(2)握手协议
(3)异步FIFO

.
.

2.一个复杂的握手信号

如果频率较高的时钟域A中的信号D1 要传到频率较低的时钟域B,但是D1只有一个时钟脉冲宽度(1T),clkb 就有几率采不到D1了,如图1。
在这里插入图片描述
因此只有当D1 在很长一段时间内为1或0,确保一定可以被clkb采样到,才能用两级DFF同步器处理。

如果信号D1 只有1T或几个T的脉宽,又需要传到时钟频率较低甚至或快或慢不确定的时钟域B,这种情况该怎么如何处理呢?

握手协议(handshake)异步信号处理是一种常见的异步信号处理方法。
.
.

常见的握手协议异步信号处理行为波形图大致如下图2:
在这里插入图片描述
信号d_in 所处时钟域是clk_in,且d_in只有1T 的宽度,想要传送到clk_out 时钟域(clk_out 跟clk_in不相关)。

(1)因为clk_out 和 clk_in相位关系不确定,时钟周期大小关系不确定,无法保证一定能采样到d_in。因此需要把d_in展宽,产生d_req 信号;

(2)d_req 信号一直拉高,经过clk_out时钟域两级DFF 同步器后,得到d_reg_sync;取d_req_sync 上升沿1T,即可得到传送到clk_out 时钟域的d_out。此时,d_in 从clk_in 传送到clk_out 的任务就算是结束了。

(3)但对于handshake 电路来说,任务还没结束,因为d_req 还一直是高电平。因此,需要把d_req_sync 信号再用两级DFF同步器,传回clk_in 时钟域,得到d_ack信号;当clk_in 看到d_ack拉高后,就可以把d_req 信号拉低,到这里一个handshake电路行为才算是结束了。
.

根据上面的波形图,可以看到握手协议异步信号处理并不复杂,但是细心的朋友应该会注意到,这个处理方法信号传递的速度相对较慢。
从图2 的波形来看,至少需要3个clk_in 和2个clk_out时钟周期。根据不同的应用需求,人们会对图2的波形做不同的改造。但万变不离其宗,原理都是一样的,电路也大同小异。

RTL代码
该代码基于以上的原理实现,改动之处:
din做成了多位的输入,通过临时寄存器将输入间接传入dout,代码可能有冗余之处,还请多多指正。

module AsynBus_shakehand(
din,clk_in,clk_out,dout,rst
    );
    input [31:0] din;                 //din为32bit
    input clk_in,clk_out;             //clk_in 100M  clk_out 10M
    input rst;
    
    output dout;                     
    
    reg [31:0]dout;                 //dout为32bit
    reg [31:0]dstore;               //储存input信号  
    reg d_req;
    reg d_req_syn_1;                //第一级同步
    reg d_req_syn_2;                //第二级同步                 
    reg d_ask;
    reg d_ask_syn;
   
    always@(posedge clk_in or negedge rst)  //输入数据存放在dstore
    if(!rst)
    dstore<=32'b0;
    else if(!d_req)
    dstore<=din;
    
    always@(posedge clk_in or negedge rst)   //拉高和拉低d_req
    if(!rst)
    d_req<=1'b0;
    else if(din!=dstore)
    d_req<=1'b1;
    else if(d_ask)
    d_req<=1'b0;
    
    always@(posedge clk_out or negedge rst)  //经过两级DFF
       if(!rst)
       begin
       d_req_syn_1<=1'b0;
       d_req_syn_2<=1'b0;
       end
       else
       begin
       d_req_syn_1<=d_req;
       d_req_syn_2<=d_req_syn_1;
       end
       
     always@(posedge clk_out or negedge rst)     //输出改变
       if(!rst)
       dout<=32'b0;
       else if(d_req_syn_2)
       dout<=dstore;
       
   always@(posedge clk_in or negedge rst)       //拉高和拉低d_ask
    if(!rst)
    begin
    d_ask_syn<=1'b0;
    d_ask<=1'b0;
    end 
   else 
   begin
    d_ask_syn<=d_req_syn_2;
    d_ask<=d_ask_syn;
   end   
    
endmodule

版权声明:本文为qq_44816673原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。