FPGA逻辑设计回顾(5)多比特信号的CDC处理方式之MUX同步器

前言

信号的跨时钟传输的方法很多,在上篇专栏中,就说了两种有关单比特脉冲信号的跨时钟域传输问题,FPGA逻辑设计回顾(4)亚稳态与单比特脉冲信号的CDC处理问题,建议大家看看,后面我还会扩展更多的方法。本篇承接上一篇文章,和单比特有点关系,但是是一种处理多比特信号的跨时钟域方法,MUX同步器!一起来看看吧。

多比特信号跨时钟域处理的场景与方案

多比特信号即位宽不为1的数据,对这种信号进行跨时钟域处理时,我们关注的重点就和单比特信号不太一样了,有的时候我们甚至不再关注源时钟与目的时钟之间的快慢,而是如何将数据传输到对面而不会出错。

有的人就说了,既然单比特信号我们可以直接两级寄存器同步,为什么对于多比特信号就不行了呢?原因有很多,因不同的使用用途侧重点也不同,例如最简单的考虑,如果数据位宽很大,那么全部使用寄存器同步,岂不是让电路面积很大?即使基于这个考虑我们也要改进下我们的设计。

还记得上一篇我们讲到的两级寄存器同步方案来解决从慢时钟域到快时钟域内传输单比特脉冲信号的方法吗? 链接如下:FPGA逻辑设计回顾(4)亚稳态与单比特脉冲信号的CDC处理问题

对,我要强调的是我们对每一种方案都有一种名字,这像是读我的文章的一种约定,说到某个方案的名字你就知道我指的是哪个设计。

  • 两级寄存器同步,即 two flip-flop synchronizer ;

下面介绍一种对多比特信号的跨时钟域处理方法,我们称之为MUX同步器,英文名叫:Mux synchronizer,它适用的场景理论上也得是让目的时钟域能检测到数据,也就是说要么数据持续时间够长(从快时钟域到慢时钟域),要么是从数据本身在较慢的时钟域内。

  • MUX 同步器:Mux synchronizer

如果这么说,在特定条件的限制下,MUX同步器也就是无所谓时钟域的快慢问题了。还有一点限制就是这种设计是单向的数据跨时钟域传输,也就是说,只能从源时钟域传输到目的时钟域,而不是反过来传输数据,这是设计本身决定的,单向设计。

正所谓,如果你选择了这种方式,你就得承担它的局限性呀。人生不如意者,常十之八九!可是这种设计方式也完全可以作为你的武器库(储存库),或者说十八般武器中的一种嘛,有实力才有选择权,多么通透的道理呀。

MUX同步器

MUX同步器这种方式,要求被同步的数据,跟随一个使能信号,如下图类型:

数据伴随数据有效信号

这在特定的场景下是不难实现的,下面具体讲它的实现方式:

我们今天想要跨时域的是图中的Data bus,想要将 data bus 从 clkA 转到 clkB(不论谁的频率快慢都一样),我们通过 data bus 的 valid 信号(属于clkA),也就是图中的 data enable A,将 data enable A 使用 two flip-flop synchronizer 跨到 clkB,也就是 data enable B,并且使用 data enable B当作最右边DFF的 flip-flop enable 信号(在图中使用mux来示意),由于data enable A 的时序等同于 data bus,跨到data enable B 时也就保证了 data bus 穿过 DFF 的信号已经稳定,即可拿来锁入最后一级DFF,最后一级的DFF的Q即是data bus存在于clk B domain的稳定信号。

MUX synchronizer

我们将中间信号标注一下,以便于波形图分析使用:

Mux synchronizer

如上图,我们假设时钟A是慢时钟,我们的数据仅持续一个时钟即可被同步到B时钟域。根据电路,得到的波形图如下:

数据同步过程

根据电路框图,我们使用Verilog语言进行描述,然后仿真,看其效果:

module mux_synchronizer(

    input   wire        clka            ,
    input   wire        clkb            ,
    input   wire        rst             ,
    input   wire [3:0]  data_bus        ,
    input   wire        data_enable_a   ,
    output  reg         data_bus_b

    );

    reg             reg_data_enable_a       ;
    reg             data_enable_b_mid       ;
    reg             data_enable_b           ;
    reg     [3:0]   reg1_data_bus_a         ;

    wire    [3:0]   data_bus_mux            ;

    //时钟域a下同步本地数据及其有效标志信号,改善时序
    always@(posedge clka or posedge rst) begin
        if(rst) begin
            reg_data_enable_a <= 1'b0           ;
            reg1_data_bus_a   <= 4'd0           ;      
        end
        else begin
            reg_data_enable_a <= data_enable_a  ;
            reg1_data_bus_a   <= data_bus       ;
        end
    end

    //将数据有效标志信号同步到b时钟域,两级同步器
    always@(posedge clkb or posedge rst) begin
        if(rst) begin
            data_enable_b_mid <= 1'b0   ;
            data_enable_b <= 1'b0;
        end
        else begin
            data_enable_b_mid <= reg_data_enable_a;
            data_enable_b <= data_enable_b_mid;
        end
    end

    //写法1:
    assign data_bus_mux = data_enable_b ? reg1_data_bus_a : data_bus_b;

    always@(posedge clkb or posedge rst) begin
        if(rst) begin
            data_bus_b <= 4'b0;
        end
        else begin
            data_bus_b <= data_bus_mux;
        end
    end

    // //写法2:
    // always@(posedge clkb or posedge rst) begin
    //     if(rst) begin
    //         data_bus_b <= 4'b0;
    //     end
    //     else if(data_enable_b) begin
    //         data_bus_b <= reg1_data_bus_a;
    //     end
    //     else begin
    //         data_bus_b <= data_bus_b;
    //     end
    // end

endmodule

注意到,我们的注释处,有写法2:

    //写法2:
    always@(posedge clkb or posedge rst) begin
        if(rst) begin
            data_bus_b <= 4'b0;
        end
        else if(data_enable_b) begin
            data_bus_b <= reg1_data_bus_a;
        end
        else begin
            data_bus_b <= data_bus_b;
        end
    end

这种描述,和电路图中的MUX写法是一致的:

    //写法1:
    assign data_bus_mux = data_enable_b ? reg1_data_bus_a : data_bus_b;

    always@(posedge clkb or posedge rst) begin
        if(rst) begin
            data_bus_b <= 4'b0;
        end
        else begin
            data_bus_b <= data_bus_mux;
        end
    end

其实MUX充当的作用就是触发器的一个使能信号,如果有带有使能的触发器,那就直接使用无疑了。 而Xilinx的FPGA中,就有这样的触发器呀,如下为上述电路的RTL图:

RTL原理图

综合后的原理图:

综合后的原理图

实现后的原理图:

实现后的原理图

将触发器放大了看:

FPGA中的触发器

可见,这类寄存器在FPGA中太常见了。

下面对其功能进行仿真:

首先我们仿真从慢时钟到快时钟的情况,仿真平台如下:

module sim_mux_synchronizer(

    );

    reg             clka            ;
    reg             clkb            ;
    reg             rst             ;
    reg     [3:0]   data_bus        ;
    reg             data_enable_a   ;
    wire    [3:0]   data_bus_b      ;

    initial begin
        clka = 1'b0;
        forever begin
            #5 clka = ~clka;
        end
    end

    initial begin
        clkb = 1'b0;
        forever begin
            #2 clkb = ~clkb;
        end
    end

    initial begin
        rst = 1'b1;
        data_bus = 4'b0;
        data_enable_a = 1'b0;
        #15
        rst = 1'b0;

        #20
        @(posedge clka) begin
           data_bus = #0.5 4'b1101;
           data_enable_a = #0.5 1'b1;
        end
        @(posedge clka) begin
            data_bus =  #0.5 4'b0;
            data_enable_a = #0.5 1'b0;
        end

    end

    mux_synchronizer u_mux_synchronizer(
        .clka           ( clka           ),
        .clkb           ( clkb           ),
        .rst            ( rst            ),
        .data_bus       ( data_bus       ),
        .data_enable_a  ( data_enable_a  ),
        .data_bus_b     ( data_bus_b     )
    );

endmodule

由此得到仿真波形:

功能仿真

对比上面我们自己画的:

数据同步过程

可见,实现了我们想要的功能。

下面提一个问题:如果是从快时钟跨时钟域到慢时钟域呢?我们不妨简单仿真一下:

假设数据在快时钟域内持续时间大概是慢时钟域的三个时钟那么长,如下稍微修改即可得到仿真平台:

module sim_mux_synchronizer(

    );

    reg             clka            ;
    reg             clkb            ;
    reg             rst             ;
    reg     [3:0]   data_bus        ;
    reg             data_enable_a   ;
    wire    [3:0]   data_bus_b      ;

    initial begin
        clka = 1'b0;
        forever begin
            #2 clka = ~clka;
        end
    end

    initial begin
        clkb = 1'b0;
        forever begin
            #5 clkb = ~clkb;
        end
    end

    initial begin
        rst = 1'b1;
        data_bus = 4'b0;
        data_enable_a = 1'b0;
        #15
        rst = 1'b0;

        #20
        @(posedge clka) begin
           data_bus = #0.5 4'b1101;
           data_enable_a = #0.5 1'b1;
        end
        repeat(7) begin
            @(posedge clka);
        end
        @(posedge clka) begin
            data_bus =  #0.5 4'b0;
            data_enable_a = #0.5 1'b0;
        end

    end

    mux_synchronizer u_mux_synchronizer(
        .clka           ( clka           ),
        .clkb           ( clkb           ),
        .rst            ( rst            ),
        .data_bus       ( data_bus       ),
        .data_enable_a  ( data_enable_a  ),
        .data_bus_b     ( data_bus_b     )
    );

endmodule

得到仿真图:

从快到慢时钟的仿真

可见也是没问题的。

好了,这篇文章就到这里,更多的多比特信号的CDC问题,我们下篇见!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值