跨时钟处理详解

跨时钟域处理

参考视频

为什么跨时钟域可能出现亚稳态?
采样时可能采到上升沿和下降沿

单 bit 信号

电平信号检测
电平检测

通过寄存器打两拍进行同步,也就是电平同步器。等同亚稳态处理
问题

  • 输入信号必须保持两个接收时钟周期,每次同步完,输入信号要恢复到无效状态。如果是从快到慢,信号很有可能被滤除。

适用于慢时钟域向快时钟域,或者差异不大的时钟

脉冲信号检测
问题

窄脉冲跨到慢速时钟里,可能会被当毛刺过滤

image.pngl200

慢速时钟到快速时钟,脉冲变长

image.pngl200

解决方式

握手方式:

  • A 在 clk1 下构建 a_latch
  • B_latch 在 clk2 下识别 a_latch
  • B_latch_r 再在 clk2 下识别 b_latch(消除亚稳态)
  • 利用 b_latch_r 再打一拍,形成异或逻辑,组成 b 脉冲信号
  • B_feedback_latch 在 clk2 下识别 b 脉冲锁住
  • C_latch 在 clk1 下识别 b_feedback_latch
  • C_latch_r 在 clk1 下识别 C_latch (消除亚稳态)
  • A 在 clk1 下识别 C_latch_r 反馈
  • 重复流程 a - b - c 逐步稳定

为什么使用 c 作为反馈,而不是 b 直接反馈?(b 的脉冲在 clk1 相当于电平)

  • 这种设计可以不需要管 clk1、clk2 的快慢问题

Busy 信号用来提示 clk1 上的 a 不要频繁发送

image.pngl200

代码
module cross_domain_clock(

    input           clk1,
    input           rst1_n,
    input           clk2,
    input           rst2_n,
    input              a,

	output            b,
    output reg      busy
);


    reg             a_latch;
    reg             b_latch;
    reg             b_latch_r;
    reg             b_latch_r_2;
    reg             b_feedback_latch;
    reg             c_latch;
    reg             c_latch_r;



    always @(posedge clk1 or negedge rst1_n) begin
        if (!rst1_n) begin
            a_latch <= 0;
        end else if(a)
            a_latch <= 1;
        else if(c_latch_r)
            a_latch <= 0;
    end

    always @(posedge clk2 or negedge rst2_n) begin
        if (!rst2_n) begin
            b_latch     <= 0;
            b_latch_r   <= 0;
            b_latch_r_2 <= 0;
        end else 
            b_latch     <= a_latch;
            b_latch_r   <= b_latch;
            b_latch_r_2 <= b_latch_r;
    end

    assign b = b_latch_r & !b_latch_r_2;

    always @(posedge clk2 or negedge rst2_n) begin
        if (!rst2_n) begin
            b_feedback_latch <= 0;
        end else if(b)
            b_feedback_latch <= 1;
        else if(!b_latch_r)
            b_feedback_latch <= 0;
    end

    always @(posedge clk1 or negedge rst1_n) begin
        if (!rst1_n) begin
            c_latch   <= 0;
            c_latch_r <= 0;
        end 
            c_latch   <= b_feedback_latch;
            c_latch_r <= c_latch;
    end


    assign busy = a_latch | c_latch_r;

endmodule
Testbech
module cross_domain_clock_tb;

  // Declare testbench signals
  reg clk1;
  reg rst1_n;
  reg clk2;
  reg rst2_n;
  reg a;
  wire b;
  wire busy;

  // Instantiate the DUT
  cross_domain_clock dut (
    .clk1(clk1),
    .rst1_n(rst1_n),
    .clk2(clk2),
    .rst2_n(rst2_n),
    .a(a),
	.b(b),
    .busy(busy)
  );

  // Clock generation for clk1 (100MHz)
  initial begin
    clk1 = 0;
    forever #5 clk1 = ~clk1;  // 10ns period
  end

  // Clock generation for clk2 (75MHz)
  initial begin
    clk2 = 0;
    forever #12.5 clk2 = ~clk2;  // 13.33ns period
  end

  // Test stimulus
  initial begin
    // Initialize signals
    rst1_n = 0;
    rst2_n = 0;
    a = 0;

    // Wait for 100ns and release reset
    #100;
    rst1_n = 1;
    rst2_n = 1;

    // Test case 1: Toggle input a
    #50;
    a = 1;
    #10;
    a = 0;

    // Test case 2: Rapid toggle of input a
    #200;


    // End simulation after some time
    $finish;
  end

  // Monitor changes
  initial begin
    $monitor("Time=%0t rst1_n=%b rst2_n=%b a=%b busy=%b",
             $time, rst1_n, rst2_n, a, busy);
  end


  initial begin
    $fsdbDumpfile("tb.fsdb");  // 波形文件名
    $fsdbDumpvars(0);  // 0表示dump所有层级
    $fsdbDumpMDA();  // 专门用于dump内存数组
    $fsdbDumpMemNow;  // 立即dump当前内存内容
    end

endmodule
仿真图

image.pngl200

多 bit 信号

握手

Clk1 下数据变化后稳定时,发出 vld1 信号,clk2 接到 vld2 时对数据采样
前提要求,data_in 在 vld2 采样之前不发生变化,如果可能发生变化,则进行锁存

image.pngl200

需要锁存的代码
module cross_domain_bits(

    input               clk1,
    input               rst1_n,
    input               clk2,
    input               rst2_n,
    input               valid,
    input  [3:0]        data_in,


    output              busy,
    output  reg [3:0]       data_out
);

    
    reg             a_latch;
    reg             b_latch;
    reg             b_latch_r;
    reg             b_latch_r_2;
    reg             b_feedback_latch;
    reg             c_latch;
    reg             c_latch_r;

    wire            valid_2;  

    always @(posedge clk1 or negedge rst1_n) begin
        if (!rst1_n) begin
            a_latch <= 0;
        end else if(valid)
            a_latch <= 1;
        else if(c_latch_r)
            a_latch <= 0;
    end

    always @(posedge clk2 or negedge rst2_n) begin
        if (!rst2_n) begin
            b_latch     <= 0;
            b_latch_r   <= 0;
            b_latch_r_2 <= 0;
        end else 
            b_latch     <= a_latch;
            b_latch_r   <= b_latch;
            b_latch_r_2 <= b_latch_r;
    end

    assign valid_2 = b_latch_r & !b_latch_r_2;

    always @(posedge clk2 or negedge rst2_n) begin
        if (!rst2_n) begin
            b_feedback_latch <= 0;
        end else if(valid)
            b_feedback_latch <= 1;
        else if(!b_latch_r)
            b_feedback_latch <= 0;
    end

    always @(posedge clk1 or negedge rst1_n) begin
        if (!rst1_n) begin
            c_latch   <= 0;
            c_latch_r <= 0;
        end 
            c_latch   <= b_feedback_latch;
            c_latch_r <= c_latch;
    end


    assign busy = a_latch | c_latch_r;

    always @(posedge clk2 or negedge rst2_n) begin
    if (!rst2_n) begin
        data_out <= 0;
    end if(valid_2)
        data_out <= data_in;
    end 

endmodule
Testbench
module cross_domain_bits_tb;

  // Declare testbench signals
  reg clk1;
  reg rst1_n;
  reg clk2;
  reg rst2_n;
  reg valid;
  reg [3:0] data_in;
  
  wire busy;
  reg [3:0] data_out;

  // Instantiate the DUT
  cross_domain_bits dut (
    .clk1(clk1),
    .rst1_n(rst1_n),
    .clk2(clk2),
    .rst2_n(rst2_n),
    .valid(valid),
    .data_in(data_in),
  
    .busy(busy),
    .data_out(data_out)
  );

  // Clock generation for clk1 (100MHz)
  initial begin
    clk1 = 0;
    forever #5 clk1 = ~clk1;  // 10ns period
  end

  // Clock generation for clk2 (75MHz)
  initial begin
    clk2 = 0;
    forever #12.5 clk2 = ~clk2;  // 13.33ns period
  end

  // Test stimulus
  initial begin
    // Initialize signals
    rst1_n = 0;
    rst2_n = 0;
    valid = 0;
    data_in = 4'h0;
    // Wait for 100ns and release reset
    #100;
    rst1_n = 1;
    rst2_n = 1;

    // Test case 1: Toggle input a
    #50;
    valid = 1;
    data_in = 4'hf;

    #10;
    valid = 0;

    // Test case 2: Rapid toggle of input a
    #200;


    // End simulation after some time
    $finish;
  end

  // Monitor changes


  initial begin
    $fsdbDumpfile("tb.fsdb");  // 波形文件名
    $fsdbDumpvars(0);  // 0表示dump所有层级
    $fsdbDumpMDA();  // 专门用于dump内存数组
    $fsdbDumpMemNow;  // 立即dump当前内存内容
    end

endmodule
波形图

image.pngl200

异步 FIFO

参考博文

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值