跨时钟域同步2---单bit信号同步实战(快到慢+慢到快)——学习笔记

原文见耐心的小黑https://zhuanlan.zhihu.com/p/452183878

一、快时钟域>>>慢时钟域
我们假定有两个时钟,CLK1 和 CLK2,还有一个信号叫 READ,CLK1 时钟频率快于 CLK2,现在我们需要将READ 信号同步到CLK2时钟域下。

1、方法一:展宽+打拍同步
READ_DLY1 信号是 READ 信号相对于 CLK1 时钟打一拍产生的,READ_DLY2 信号是 READ 信号相对于 CLK1 时钟打两拍产生的,由于单纯的 READ 信号宽度根本不够 CLK2 采样,所以需要展宽 READ 的信号宽度, READ_OR 信号是由 READ 和 READ_DLY1 以及READ_DLY2 相或产生的。或之后 READ_OR 信号宽度以及够 CLK2 采样。同步原理如下,直接使用 CLK2 采样 READ_OR 信号得到 READ_ SYNC,然后再对READ_SYNC 打 2 拍,第一拍得到 READ_ SYNC_DLY1,第二拍得到 READ SYNC_DLY2,然后READ_SYNC_DLY1 和 READ_OR_SYNC_DLY2 的取反信号相与得到 READ SYNC_PULSE,即已经同步到 CLK2 时钟域的 READ 信号上升沿指示信号。

对 READ_SYNC 打 2 拍目的是消除亚稳态,打两拍之后的亚稳态概率已经非常非常小了,由于有电路噪声,所以寄存器会恢复到固定电平。我们画出如下的时序图。
在这里插入图片描述

① 程序设计

module asyn_process ( 
    input clk1 , //快时钟信号
    input read , //信号,快时钟阈的
    input clk2 , //慢时钟信号

    input sys_rst_n , //复位信号,低电平有效
    output wire read_sync_pulse //输出信号
);

 //reg define
 reg read_dly1 ;
 reg read_dly2 ;
 reg read_or ;

 reg read_sync ;
 reg read_sync_dly1 ;
 reg read_sync_dly2 ;

 //*****************************************************
 //**                   main code
 //*****************************************************
 always @(posedge clk1 or negedge sys_rst_n) begin
    if (sys_rst_n ==1'b0) 
        read_dly1 <= 1'b0;
    else 
        read_dly1 <=read;
 end

 always @(posedge clk1 or negedge sys_rst_n) begin
    if (sys_rst_n ==1'b0) 
        read_dly2 <= 1'b0;
    else 
        read_dly2 <= read_dly1 ;
 end

 always @(posedge clk1 or negedge sys_rst_n) begin
    if (sys_rst_n ==1'b0) 
        read_or <= 1'b0;
    else 
        read_or <= read | read_dly1 | read_dly2;
 end

 always @(posedge clk2 or negedge sys_rst_n) begin
    if (sys_rst_n ==1'b0) begin
        read_sync <= 1'b0;
    end
    else 
        read_sync <= read_or;
 end

 always @(posedge clk2 or negedge sys_rst_n) begin
    if (sys_rst_n ==1'b0) 
        read_sync_dly1 <= 1'b0;
    else
        read_sync_dly1 <= read_sync;
 end

 always @(posedge clk2 or negedge sys_rst_n) begin
    if (sys_rst_n ==1'b0) 
        read_sync_dly2<= 1'b0;
    else 
        read_sync_dly2 <= read_sync_dly1;
 end

 assign read_sync_pulse = read_sync_dly1 & ~read_sync_dly2;

 endmodule
② 测试代码
`timescale 1ns / 1ps

module TB();

reg sys_clk1; 
reg sys_clk2; 
reg sys_rst_n; 
reg read ;
initial begin
    sys_clk1 = 1'b0;
    sys_clk2 = 1'b0;
    sys_rst_n = 1'b0;

    read = 1'b0;

    #200
    sys_rst_n = 1'b1;

    #100
    read = 1'b1;

    #20
    read = 1'b0;
    #100
    read = 1'b1;
    #20
    read = 1'b0;

end

 always #10 sys_clk1 = ~sys_clk1;
 always #30 sys_clk2 = ~sys_clk2;

 asyn_process u_asyn_process(
    .clk1 (sys_clk1 ),
    .clk2 (sys_clk2 ),
    .sys_rst_n (sys_rst_n),
    .read (read ),
    .read_sync_pulse(read_sync_pulse )
 );
endmodule

1、展宽是为了让慢时钟也可以采集到该信号,展宽的倍数一般根据频率相差的倍数适当选择即可。
2、在展宽时,如果有效电平很宽,即使使得慢时钟能够采样到多次也没关系,因为后续在慢时钟域中进行的边沿检测就是为了最终使得该信号的有效电平只有一个慢时钟周期
③ 仿真结果
在这里插入图片描述
有效电平很宽时的情况
在这里插入图片描述
2、方法二:使用握手协议(handshake)

  1. 跨时钟域信号处理——握手协议(handshake)

用敲两级DFF的办法(两级DFF同步器)可以实现单比特信号跨时钟域处理。但你或许会有疑问,是所有的单比特信号跨时钟域都可以这么处理吗?
NO!两级DFF同步器,是对信号有一定的要求的。

想象一下,如果频率较高的时钟域A中的信号D1 要传到频率较低的时钟域B,但是D1只有一个时钟脉冲宽度(1T,CLKA),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不相关)。
因为clk_out 和 clk_in相位关系不确定,时钟周期大小关系不确定,无法保证一定能采样到d_in。
因此需要把d_in展宽,产生d_req 信号;
d_req 信号一直拉高,经过clk_out时钟域两级DFF 同步器后,得到d_reg_sync;
取d_req_sync 上升沿1T,即可得到传送到clk_out 时钟域的d_out。
此时,d_in 从clk_in 传送到clk_out 的任务就算是结束了。
但对于handshake 电路来说,任务还没结束,因为d_req 还一直是高电平。
因此,需要把d_req_sync 信号再用两级DFF同步器(此时是单比特信号从慢速时钟域同步到快速时钟域需要使用打两拍的方式消除亚稳态。和第一次打两拍不一样,第一次是展宽了),传回clk_in 时钟域,得到d_ack信号;
当clk_in 看到d_ack拉高后,就可以把d_req 信号拉低,到这里一个handshake电路行为才算是结束了。

① 程序设计

module handshake_sync ( 
    input clk1 , //快时钟信号
    input sys_rst_n , //复位信号,低电平有效
    input read , //信号,快时钟阈的
    input clk2 , //慢时钟信号

    output read_sync_pulse //输出信号
);

 //in1表示该信号在clk1时钟域
 reg req_in1 ;
 reg ack_in1 ;
 reg ack_in1_dly1 ;
 //in2表示该信号在clk2时钟域
 reg req_in2 ;
 reg req_in2_dly1 ;
reg req_in2_dly2 ;
 //*****************************************************
 //**                   main code
 //*****************************************************

 //1、clk1时钟域下req信号的生成
 always @(posedge clk1 or negedge sys_rst_n) begin
    if (sys_rst_n == 1'b0) 
        req_in1 <= 1'b0;
    else if(read == 1'b1)
        req_in1 <= 1'b1;
    else if(ack_in1_dly1 == 1'b1)
        req_in1 <= 1'b0;
    else
        req_in1 <= 1'b0;
 end

 //2、clk2时钟域下req信号的采样
 always @(posedge clk2 or negedge sys_rst_n) begin
    if (sys_rst_n == 1'b0) begin
        req_in2 <= 1'b0;
        req_in2_dly1 <= 1'b0;
        req_in2_dly2 <= 1'b0;
    end
    else begin
        req_in2 <= req_in1;
        req_in2_dly1 <= req_in2;
        req_in2_dly2 <= req_in2_dly1;
    end
 end

 //3、clk1时钟域下ack信号的采样 直接采样req_in2_dly1作为ack信号即可
 //这是因为有了req_in2和req_in2_dly1之后我们就可以生成dout,所以此时就可以返回ack信号了
 always @(posedge clk1 or negedge sys_rst_n) begin
    if (sys_rst_n == 1'b0) begin
        ack_in1 <= 1'b0;
        ack_in1_dly1 <= 1'b0;
    end

    else begin
        ack_in1 <= req_in2_dly1 ;
        ack_in1_dly1 <= ack_in1;
    end

 end

 //4、dout信号的产生

 assign read_sync_pulse = req_in2_dly1 & ~req_in2_dly2;

 endmodule

② 测试代码

`timescale 1ns / 1ps

module tb_handshake_sync();

reg sys_clk1; 
reg sys_clk2; 
reg sys_rst_n; 
reg read ;
reg read1 ;
initial begin
    sys_clk1 = 1'b0;
    sys_clk2 = 1'b0;
    sys_rst_n = 1'b0;

    read = 1'b0;

    #200
    sys_rst_n = 1'b1;

    #110
    read = 1'b1;

    #20
    read = 1'b0;
    #400
    read = 1'b1;
    #20
    read = 1'b0;

end

 always #10 sys_clk1 = ~sys_clk1;
 always #30 sys_clk2 = ~sys_clk2;

always @(posedge sys_clk1 or negedge sys_rst_n)
        if (sys_rst_n ==1'b0) 
            read1 <= 1'b0;
        else 
            read1 <=read;
 handshake_sync u_handshake_sync(
    .clk1 (sys_clk1 ),
    .clk2 (sys_clk2 ),
    .sys_rst_n (sys_rst_n),
    .read (read1 ),
    .read_sync_pulse(read_sync_pulse )
 );

endmodule

③ 仿真结果
在这里插入图片描述

1,假设时钟域clka比clkb 频率高,如果输入信号的两个相邻脉冲D0和D1非常较近,如下图所示,如果使用握手协议处理,会发生怎样的事情?
在这里插入图片描述
2.问题1里面,如果要确保D1数据一定要被能传送到clkb,电路该如何实现?

二、慢时钟域>>>快时钟域
慢时钟域到快时钟域的同步很简单,直接打拍就可以。如果信号有效时间太短,为了更好的采样该信号,也可以适当展宽一下,步骤和上面代码里写的差不多。

  • 17
    点赞
  • 164
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值