跨时钟域处理

参考:

跨时钟域

菜鸟教程

跨时钟域处理之全握手

为什么要进行跨时钟域处理?

避免亚稳态

亚稳态:数据无法在规定时间内达到一个可确认的状态。
产生亚稳态现象的原因:数据传输不满足触发器的建立时间和保持时间

建立时间(Tsu):时钟上升沿之前数据必须稳定的最短时间。
保持时间(Th):时钟上升沿之后数据必须稳定的最短时间。

触发器进入亚稳态后,输出端在有效时钟沿后会经历一个较长的不确定状态。这段时间内输出端可能会出现毛刺、振荡或某一固定电压值等无用的输出电平,而不会稳定在输入数据的期望值上,这段时间称为决断时间。在决断时间后输出端的状态将会稳定在0或1,但此时具体是哪一个值却与输入端无关。

触发器进入亚稳态后,无法预测会输出何种电平,也无法预测何时会输出某个正确的电平。

img

信号从一个时钟域传递到另一个异步时钟域时,由于多个时钟域中频率和相位不同,在数据采样时,有可能违背建立时间或保持时间,从而产生亚稳态问题,而且可能在整个周期内都保持在亚稳态。把这个不稳定的信号采样后,会传递给后级电路,会导致整个电路功能的错误。

在这里插入图片描述

保证数据完整性

  1. 防止数据丢失:在跨时钟域传输数据时,如果处理不当,可能会导致数据丢失。例如,当快时钟域的时钟频率远高于慢时钟域时,快时钟域的数据可能在慢时钟域的有效采样窗口内多次变化,从而导致接收端无法准确捕获数据。

  2. 确保数据同步:跨时钟域处理还可以确保不同时钟域之间的数据同步。在分布式系统或复杂电路中,不同部分可能由不同的时钟源驱动,导致数据在传输过程中存在时间差。通过跨时钟域处理,可以消除这种时间差,确保数据在接收端能够正确同步。

跨时钟域处理的方法

单比特信号

慢时钟域到快时钟域

两级触发器同步法

两或三级触发器对异步信号缓存,主要针对单Bit信号。

(1)单bit电平信号

//单比特电平信号
always @(posedge clk2 or negedge rst_n) begin
	if(!rst_n) begin
		signal_out_r  <= 1'b0;
		signal_out_rr <= 1'b0;
	end
	else if(signal_in == 1'b1) begin
		signal_out_r <= signal_in;
		signal_out_rr <= signal_out_r;
	end
	else begin
		signal_out_r <= 1'b0;
		signal_out_rr <= 1'b0;
	end
end

assign signal_out = signal_out_rr;

endmodule

(2)单bit脉冲信号

//单比特脉冲信号
always @(posedge clk2 or negedge rst_n) begin
	if(!rst_n) begin
		signal_out_r  <= 1'b0;
		signal_out_rr <= 1'b0;
	end
	else if(signal_in == 1'b1) begin
		signal_out_r <= signal_in;
		signal_out_rr <= signal_out_r;
		signal_out_rrr <= signal_out_rr;
	end
	else begin
		signal_out_r <= 1'b0;
		signal_out_rr <= 1'b0;
		signal_out_rrr <= 1'b0;
	end
end

//组合逻辑输出脉冲
assign signal_out = signal_out_rr && !signal_out_rrr;

endmodule

快时钟域到慢时钟域

两级触发器同步法

(1)单bit电平信号
只要快时钟域的信号保持高电平或低电平的时间足够长,以至于能被慢时钟在满足时序约束的条件下采集到,就可以认为该信号为电平信号,从快时钟域到慢时钟域的电平信号也采用两级触发器同步的方法。

脉宽扩展法

(2)单bit脉冲信号

这里的脉冲信号是指从快时钟域输出的有效宽度小于慢时钟周期的信号。如果慢时钟域直接去采集这种窄脉冲信号,有可能会漏掉。脉宽扩展法是通过相互握手的方式对窄脉冲信号进行脉宽扩展。

脉宽扩展法的基本原理:

(1) 快时钟域对脉冲信号进行检测,检测为高电平时输出高电平信号 pulse_fast_r。或者快时钟域输出高电平信号时,不要急于将信号拉低,先保持输出信号为高电平状态
(2) 慢时钟域对快时钟域的信号 pulse_fast_r 进行延迟打拍采样。因为此时的脉冲信号被快时钟域保持拉高状态,延迟打拍肯定会采集到该信号。
(3) 慢时钟域确认采样得到高电平信号 pulse_fast2s_r 后,再反馈给快时钟域。
(4) 快时钟域对反馈信号 pulse_fast2s_r 进行延迟打拍采样。如果检测到反馈信号为高电平,证明慢时钟域已经接收到有效的高电平信号。如果此时快时钟域自身逻辑不再要求脉冲信号为高电平状态,拉低快时钟域的脉冲信号即可。

//同步模块工作时钟大约为 25MHz 的模块
//异步数据对来自工作时钟为 100MHz 的模块
module test
  #( parameter          PULSE_INIT = 1'b0
   )
  (
   input                rstn,
   input                clk_fast,
   input                pulse_fast,
   input                clk_slow,
   output               pulse_slow);

   wire                 clear_n ;
   reg                  pulse_fast_r ;
   /**************** fast clk ***************/
   //(1) 快时钟域检测到脉冲信号时,不急于将脉冲信号拉低
   always@(posedge clk_fast or negedge rstn) begin
        if (!rstn)
           pulse_fast_r  <= PULSE_INIT ;
        else if (!clear_n)
           pulse_fast_r  <= 1'b0 ;
        else if (pulse_fast)
           pulse_fast_r  <= 1'b1 ;
   end

   reg  [1:0]           pulse_fast2s_r ;
   /************ slow clk *************/
   //(2) 慢时钟域对信号进行延迟打拍采样
   always@(posedge clk_slow or negedge rstn) begin
      if (!rstn)
        pulse_fast2s_r     <= 3'b0 ;
      else
        pulse_fast2s_r     <= {pulse_fast2s_r[0], pulse_fast_r} ;
   end
   

   reg [1:0]            pulse_slow2f_r ;
   /********* feedback for slow clk to fast clk *******/
   //(3) 对反馈信号进行延迟打拍采样
   always@(posedge clk_fast or negedge rstn) begin
      if (!rstn)
        pulse_slow2f_r  <= 1'b0 ;
      else
        pulse_slow2f_r  <= {pulse_slow2f_r[0], pulse_slow} ;
   end
   //控制快时钟域脉冲信号拉低
   assign clear_n = ~(!pulse_fast && pulse_slow2f_r[1]) ;

    reg pulse_fast2s_r_r;
    /************ slow clk *************/
    //(4) 得到脉冲信号
    always@(posedge clk_slow or negedge rstn)begin
        if(!rstn)begin
            pulse_fast2s_r_r<='d0;
        end
        else begin
            pulse_fast2s_r_r<=pulse_fast2s_r[1];
        end
    end
    
    assign pulse_slow = (!pulse_fast2s_r_r)& pulse_fast2s_r[1];
    
endmodule
`timescale 1ns/1ps

module test ;
   reg          clk_100mhz, clk_25mhz ;
   reg          rstn ;

   initial begin
      clk_100mhz = 0 ;
      clk_25mhz  = 0 ;
      rstn = 0 ;
      #11 rstn = 1 ;
   end
   always #(10/2)   clk_100mhz  = ~clk_100mhz ;
   always #(45/2)   clk_25mhz   = ~clk_25mhz ;

   reg [7:0]            cnt ;
   reg                  pulse_sig ;
   always @(posedge clk_100mhz or negedge rstn) begin
      if (!rstn) begin
         cnt     <= 'b0 ;
      end
      else begin
         cnt    <= cnt + 1'b1 ;
      end
   end

   //窄脉冲生成部分
   always @(posedge clk_100mhz or negedge rstn) begin
      if (!rstn) begin
         pulse_sig      <= 1'b0 ;
      end
      else if (cnt == 5 ||
               cnt == 40 || cnt == 42 ||
               cnt >= 75 && cnt <= 81 || cnt == 85 || cnt == 87 ) 
      begin
         pulse_sig      <= 1'b1 ;
      end
      else begin
         pulse_sig      <= 1'b0 ;
      end
   end

   test u_fast2s_pulse(
      .rstn             (rstn),
      .clk_fast         (clk_100mhz),
      .pulse_fast       (pulse_sig),
      .clk_slow         (clk_25mhz),
      .pulse_slow       ());

   initial begin
      forever begin
         #100;
         if ($time >= 10000)  $finish ;
      end
   end

endmodule // test

可见,当快时钟域的脉冲信号变化速率过快时,该方法不能分辨相邻的脉冲。

在这里插入图片描述

多比特信号

慢时钟域到快时钟域

数据使能选通法(DMUX)

基本思想是保证信号被安全采集的时刻,通过使能信号把多bitCDC问题转化为单bitCDC问题。

当使能信号周期变化时,可以对使能信号进行二级打拍缓存检测上升沿来确定采样时刻。

 //DMUX
module led(
	input clk_a,
	input clk_b,
	input rst_n,
	input a_en,
	input [3:0] data_in,
	
	output reg [3:0] data_out
);

//a时钟域使能信号同步到b时钟域,作为MUX的sel
reg a_en_r;
reg a_en_rr;

always @(posedge clk_b or negedge rst_n) begin
	if(!rst_n)
		{a_en_rr,a_en_r} <= {2{1'b0}};
	else 
		{a_en_rr,a_en_r} <= {a_en_r,a_en};
end


//二选一MUX
always @(posedge clk_b or negedge rst_n) begin
	if(!rst_n)
		data_out <= 'b0;
	else if(a_en_rr == 1'b1)//如果使能信号有效
		data_out <= data_in;
	else //如果使能信号无效
		data_out <= data_out;
		
end

endmodule

延迟计数法

当使能信号一直有效或无使能信号时,可以在快时钟域检测慢时钟的边沿。如果两个时钟频率相差较大,在检测到慢时钟上升沿后,可以通过计数定位到慢时钟周期中间时刻采样。

//clk1:999kHz   clk2:100MHz
//4级缓存:3级用于打拍同步,一级用于边沿检测
   reg [3:0]    edge_r ;
   always @(posedge clk2 or negedge rstn) begin
     if (!rstn) edge_r  <= 3'b0 ;
     else       edge_r  <= {edge_r[2:0], clk1} ;
   end
   wire edge_pos = edge_r[2] && !edge_r[3] ;

   //延迟计数器,检测到慢时钟上升沿时开始计数
   reg [5:0] cnt ;
   always @(posedge clk2 or negedge rstn) begin
      if (!rstn)                cnt <= 6'h3f ;
      else if (edge_pos && din_en)
                                cnt <= 6'h0 ;
      else if (cnt != 6'h3f)    cnt <= cnt + 1'b1 ;
   end

   //数据同步
   reg [31:0]           dout_r ;
   reg                  dout_en_r ;
   always @(posedge clk2 or negedge rstn) begin
      if (!rstn)
        dout_r         <= 'b0 ;
      else if (din_en && cnt == 47) //大约在慢时钟周期中间时刻采样
        dout_r         <= din ;
   end
   //数据使能信号较数据采样时刻延迟一个周期输出
   always @(posedge clk2 or negedge rstn) begin
      if (!rstn)        dout_en_r      <= 1'b0 ;
      else if (din_en && cnt==48)
                        dout_en_r      <= 1'b1 ;
      else              dout_en_r      <= 1'b0 ;
   end

通用方法

异步FIFO

多比特信号跨时钟域处理的常用方法是使用异步FIFO,有关异步FIFO可参考:
异步FIFO设计

全握手传输法

1.tx模块接收外部输入信号data_in和valid,在valid上升沿到来时输出data_out_t到写数据线上,并且同时发出请求信号req_t告知rx模块。

2.req_t在传递给rx后再经过打两拍处理得到req_dd,此时经过两拍之后数据已经稳定。rx认为tx的数据已经准备好了便接收写数据线上的数据,同时将要返回的数据data_out_r输出,并且返回一个应答信号ack_r给tx,表面rx已接收数据,至此已经完成了半握手。

3.应答信号ack_r传给tx后再经过打两拍处理得到ack_dd,tx检测到应答信号后拉低请求信号req_t。

4.rx在检测到req_dd拉低后,将应答信号ack_r也拉低。

5.tx检测到ack_dd拉低,本次传输过程结束。

发送模块:

module fullhs_tx(
input              tclk        ,
input              treset      ,
input      [31:0]  data_in     ,
input              valid       ,
input              ack_t       ,
output reg [31:0]  data_out_t  ,
output reg          req_t         
    );
reg           ack_d           ; 
reg           ack_dd          ; 
reg           valid_d         ; 
wire          valid_posflag   ; //valid上升沿信号。

//使用valid的上升沿作为判断条件可以防止在一个valid期间多次传输数据。
//上升沿检测。
always@(posedge tclk or negedge treset) begin
if(!treset) begin
    valid_d <= 'b0;
end
else begin
    valid_d <= valid;
end
end
assign  valid_posflag = valid & (~valid_d);
//应答信号同步到发端
always@(posedge tclk or negedge treset) begin
if(!treset) begin
    ack_d  <= 'b0;
    ack_dd <= 'b0;
end
else begin
    ack_d  <= ack_t;
    ack_dd <= ack_d;
end   
end
always@(posedge tclk or negedge treset) begin
if(!treset) begin
    req_t           <= 1'b0;
    data_out_t      <= 'b0;
end
else if(ack_dd == 1) begin  //收到返回的ack后将req置零。
     req_t           <= 1'b0; 
end   
else if(valid_posflag == 1) begin //数据有效时拉高请求信号
     req_t           <= 1'b1 ;
     data_out_t      <= data_in;
end      
end
endmodule

接收模块:

module fullhs_rx(
input                    rclk            ,
input                    rreset          ,
input          [31:0]    data_in_r       ,
input                    req_r           ,
output    reg            ack_r           ,
output    reg  [31:0]    data_out_r        
    );
reg            req_d           ;
reg            req_dd          ; 

wire rx_en;
assign rx_en=(!req_dd)&req_d; 
//将req信号同步到收端 
always@(posedge rclk or negedge rreset) begin
if(!rreset) begin
    req_d  <= 'b0;
    req_dd <= 'b0;
end
else begin
    req_d  <= req_r;
    req_dd <= req_d;
end   
end
 
always@(posedge rclk or negedge rreset) begin
if(!rreset) begin
    ack_r            <= 'b0;
    data_out_r      <= 'b0;
end
else if(rx_en) begin   
     ack_r           <= 1'b1; 
     data_out_r      <= data_in_r ;
end   
else if(req_dd == 0) begin //req信号拉低后,拉低ack信号,结束一次通信。 
     ack_r           <= 1'b0 ;    
end    
end
 
endmodule

顶层:

module fullhs_top(
input tclk,
input rclk,
input [31:0] data_in,
input valid ,
input reset,
output [31:0] data_out
    );
wire [31:0]	txdata;
wire req;
wire ack;
 
 fullhs_tx fullhs_tx_u(
.tclk      ( tclk       ),
.treset    ( reset      ),
.ack_t     ( ack        ),
.data_out_t( txdata     ),
. req_t    ( req        ),
.data_in   (data_in     ),
.valid     (valid       )
 
);
 
fullhs_rx fullhs_rx_u(
.rclk       (  rclk      ),
.rreset     (  reset     ),
.data_in_r  ( txdata    ),
.req_r      (  req       ),
.ack_r      (  ack       ),
.data_out_r (  data_out    )
 
 
);   
endmodule

仿真:

`timescale 1ns / 1ps
 
module tb_fullhs(    );
reg     tclk;
reg     rclk;
reg  [31:0]   data_in;
reg     valid;
reg     reset;
 
initial begin
tclk = 0;
rclk = 0;
reset = 0;
data_in = 0;
valid = 0;
#200 ;
reset = 1;
#300 ;
data_in = 32'hf0f0f0f0;
valid   = 1;
#200 ;
valid = 0;
#300 ;
data_in = 32'hffff0000;
valid = 1;
#200 ;
valid = 0;
# 300;
data_in = 32'hff00ff00;
valid =1;
end
always # 5 tclk = ~tclk;
always # 10  rclk = ~rclk;
fullhs_top fullhs_top_u(
.tclk (tclk  ),
.rclk (rclk  ),
.data_in(data_in),
.valid(valid),
.reset(reset )
);
endmodule

在这里插入图片描述

打拍通用原则

1.输入来自同步时钟域,需要跳变沿:寄存1次
2.输入来自异步时钟域、不需要跳变沿:寄存2次
3.输入来自异步时钟域,需要跳变沿:寄存3次

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hi小瑞同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值