使用多路选择器(MUX)完成跨时钟域传输(含代码)

1. MUX完成多bit信号跨时钟域传输的原理

这里简单的说明一下,因为网上这方面资料比较多。简单来说,对于多bit信号,不能直接使用打两拍的方式来进行跨时钟域传输,因为各个信号的延时可能不同,打两拍可能会因为延时不同造成错误。
在这里插入图片描述
而使用MUX来进行跨时钟域传输,实际是通过一个使能信号去选通数据传输的通路,然后该选通信号是通过跨时钟域单bit信号处理方式进行处理的,首先对使能信号data enable和数据信号data bus在原时钟域打一拍,这是为了保持数据和使能信号的时序尽量一致。再在新时钟域对选通信号打两拍,这一方面是为了完成选通信号的同步,另一方面个人认为是完成使能信号的两拍延迟,从而保证使能信号完成同步后,数据信号已处于稳定,因而能保证数据传输的正确。

个人认为,MUX同步方式更适用于慢时钟域到快时钟域的传输,且数据信号最好持续几个时钟。因为使能信号需在新时钟域打两拍,如果新的时钟域更慢的话,这个两拍的时间对数据的影响可能太大了,甚至使能信号同步完成时数据已经结束了。

2.Verilog实现

时钟:1MHz和10MHz(系统时钟sys_clk为50MHz)。为了更好的仿真,将clk_cnt_1初始值设定为20,来实现时钟相位的不同。

module clk_gen(
    input sys_clk, 
    input sys_rst_n,
    
    output reg clk_1, //1MHz
    output reg clk_2 //10MHz
    );

reg [5:0] clk_cnt_1;
reg [5:0] clk_cnt_2;

always @(posedge sys_clk or negedge sys_rst_n) begin
    if(sys_rst_n==1'b0)begin
        clk_cnt_1<=5'd20;
        clk_cnt_2<=5'd0;
        clk_1<=1'b1;
        clk_2<=1'b0;
    end    
    else begin
        clk_cnt_1<=(clk_cnt_1==6'd50)?6'd0:clk_cnt_1+1'b1;
        clk_cnt_2<=(clk_cnt_2==6'd5)?6'd0:clk_cnt_2+1'b1;
        clk_1<=(clk_cnt_1==6'd50)?~clk_1:clk_1;
        clk_2<=(clk_cnt_2==6'd5)?~clk_2:clk_2;
    end
end
 
endmodule

数据和使能信号的产生,数据每次加111(随便定的数字),使能信号在数据的第一个时钟产生:

module data_gen(
    input clk_1,
    input rst_n,
    output reg [15:0] data,
    output valid
    );
    reg [3:0] cnt;
    assign valid=(cnt==4'd0)?1'b1:1'b0;
    always@(posedge clk_1 or negedge rst_n)begin
        if(!rst_n)begin
            data<=16'd0;
            cnt<=4'd0;
        end
        else begin
            cnt<=(cnt==4'd10)?4'd0:cnt+1'b1;
            data<=(cnt==4'd10)?data+16'd111:data;
        end
    end
    
endmodule

跨时钟域实现代码:

module cdc(
    input clk_2,
    input clk_1,
    input [15:0] data,
    input valid,
    input rst_n,
    output reg [15:0] data_out
    );
    reg valid_reg0,valid_reg1,valid_reg;
    reg [15:0] data_reg;
    always@(posedge clk_1 or negedge rst_n)begin
        if(!rst_n)begin
            data_reg<=16'd0;
            valid_reg<=1'b0;
        end
        else begin
            data_reg<=data;
            valid_reg<=valid;
        end  
    end
    always@(posedge clk_2 or negedge  rst_n)begin
        if(!rst_n)
            {valid_reg1,valid_reg0}<=2'd0;
        else
            {valid_reg1,valid_reg0}<={valid_reg0,valid_reg};
    end
    
    always@(posedge clk_2 or negedge rst_n)begin
        if(!rst_n)
            data_out<=16'd0;
        else
            data_out<=valid_reg1?data_reg:data_out;
    end
endmodule

顶层例化:

module top(
    input sys_clk,
    input rst_n
    );
    wire clk_1,clk_2;
    wire [15:0] data;
    wire valid;
    wire [15:0] cdc_data;
cdc u1(
    .clk_2(clk_2),
    .clk_1(clk_1),
    .data(data),
    .valid(valid),
    .rst_n(rst_n),
    .data_out(cdc_data)
    );
data_gen u2(
    .clk_1(clk_1),
    .rst_n(rst_n),
    .data(data),
    .valid(valid)
    );
clk_gen u3(
    .sys_clk(sys_clk),
    .sys_rst_n(rst_n),
    
    .clk_1(clk_1),
    .clk_2(clk_2)
    );
endmodule

3.仿真

testbench:
module tb(

    );
    reg rst_n;
    reg sys_clk;
    initial begin
        rst_n=1'b0;
        sys_clk=1'b0;
        #100
        rst_n=1'b1;
    end
    always #10 sys_clk=~sys_clk;
top u(
    .sys_clk(sys_clk),
    .rst_n(rst_n)
    );
endmodule

仿真结果,数据被同步到clk_2时钟域:
在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值