基于FPGA的CDR时钟恢复设计


[参考文献]

[1] https://blog.csdn.net/yijingjijng/article/details/48024059

前言

  CDR全称为Clock and Data Recovery,即时钟数据恢复。顾名思义,CDR就是接收端根据接收到的数据信号恢复出时钟,以便于接收端对数据信号进行恢复和处理。
那为什么需要CDR呢?CDR一般应用于串行数据的恢复,那为什么不像SPI一样多传输一条数据线呢?我们知道SPI的最大传输速度也就几Mbps,这对图像等实时传输是不可能的。而如果速度传输加快,信道对传输信号的影响变大,会导致时钟和数据信号产生不同程度的影响,导致误码。因此后面演变出了SerDes这类高速传输方式,其采用LVDS差分信号进行数据传输,同时省去了时钟线,接收端再根据接收到的差分数据信号进行CDR处理,恢复出时钟和数据。

  本文是基于FPGA全数字电路实现的,重在实现的逻辑,没有物理层的优化,也就意味着无法对太高速的数据进行恢复,仅供参考。

一、CDR原理

  目前基于FPGA的全数字CDR设计基本上都是采取了过采样的方式实现,具体可以使用同频多相时钟采样和数据延迟链采样两种方式实现。核心都是采用Nxf的高速时钟,对信号进行N倍的过采样,通过对采样后数据进行判断分析,得到数据跳变沿的位置,再用最佳的采样点进行恢复数据。但是对于A7系列FPGA来说,PLL最大只能生成600MHz左右的时钟,因此对于高速信号直接使用Nxf的高频时钟采样是很困难的。

  本文采用同频多相时钟采样的方式实现CDR,利用PLL产生0°和90°相位差的采样时钟,两时钟利用双沿采样,如此起到了4倍上采样的效果。最后通过一个判决器对四个采样点进行判决,得到信号跳变沿的位置,再选取最佳的采样点恢复出数据。

  如下图所示,数据信号发生跳变时存在四种情况,跳变沿前后两个采样点采样得到的值不一样,如此便能通过四个采样数值得到跳变沿所在的位置,再选取合适的采样点进行数据的恢复,比如说情况1,信号的上升沿在第一个和第二个采样点之间,可选择相对稳定的第三个采样点作为数据恢复点,以此类推。

在这里插入图片描述

二、CDR实现电路

在这里插入图片描述

代码实现

 module CDR(
    input clk_0,
    input clk_90,
    input rst_n,
    input ser_in,
    
    output ser_clk,
    output ser_out,
    output [3:0] debug
    );
    reg data00,data01,data02,data03;
    reg data10,data11,data12,data13;
    reg data20,data21,data22,data23;
    reg data30,data31,data32,data33;
    reg data04,data14,data24,data34;
    wire cdr_en ;
    reg [3:0] pos_neg;

    assign debug = {data34,data24,data14,data04};

    assign ser_out = (pos_neg[0])?data23:
                     (pos_neg[1])?data33:
                     (pos_neg[2])?data03:
                     (pos_neg[3])?data13:0 ;
                                  
    assign ser_clk = (pos_neg[0])?~clk_0 :
                     (pos_neg[1])?~clk_90:
                     (pos_neg[2])? clk_0 :
                     (pos_neg[3])? clk_90:0 ;
    //Determine which position is the edge
    always @(posedge clk_0  or negedge rst_n)begin
        if(!rst_n) pos_neg <= 4'd0;
        else if(!cdr_en)begin
            pos_neg[0] <= !data04 &  data14 &  data24 &  data34;        //the edge of data is in the first position
            pos_neg[1] <= !data04 & !data14 &  data24 &  data34;        //
            pos_neg[2] <= !data04 & !data14 & !data24 &  data34;        //
            pos_neg[3] <=  data04 &  data14 &  data24 &  data34;        //
        end
        else
            pos_neg <= pos_neg;
    end
    assign cdr_en = (|pos_neg)?1'b1:1'b0;

    always @(posedge clk_0 or negedge rst_n)begin
        if(!rst_n)begin
        data04 <= 1'b0;
        data14 <= 1'b0;
        data24 <= 1'b0;
        data34 <= 1'b0;
        end
        else begin
        data04 <= data03;
        data14 <= data13;
        data24 <= data23;
        data34 <= data33;
        end
    end
    always @(posedge clk_0 or negedge rst_n)begin
        if(!rst_n) begin
            data00 <= 1'b0;       data01 <= 1'b0;
            data02 <= 1'b0;       data03 <= 1'b0;
        end
        else begin
           data00 <= ser_in;      data01 <= data00; 
           data02 <= data01;      data03 <= data02; 
        end
    end
    always @(posedge clk_90 or negedge rst_n)begin
        if(!rst_n) begin
            data10 <= 1'b0;       data11 <= 1'b0;
            data12 <= 1'b0;       data13 <= 1'b0;
        end
        else begin
           data10 <= ser_in;      data11 <= data10; 
           data12 <= data11;      data13 <= data12; 
        end
    end
    always @(negedge clk_0 or negedge rst_n)begin
        if(!rst_n) begin
            data20 <= 1'b0;       data21 <= 1'b0;
            data22 <= 1'b0;       data23 <= 1'b0;
        end
        else begin
           data20 <= ser_in;      data21 <= data20; 
           data22 <= data21;      data23 <= data22; 
        end
    end
    always @(negedge clk_90 or negedge rst_n)begin
        if(!rst_n) begin
            data30 <= 1'b0;       data31 <= 1'b0;
            data32 <= 1'b0;       data33 <= 1'b0;
        end
        else begin
           data30 <= ser_in;      data31 <= data30; 
           data32 <= data31;      data33 <= data32; 
        end
    end

endmodule

测试文件

module tb_cdr( );
reg clk;
reg rst_n;
reg ser_in;
wire ser_out;
wire clk_90;
wire ser_clk;
initial begin
clk = 0;ser_in = 0; rst_n = 1;
#100 rst_n = 0;
#10 rst_n = 1;
#1000
#11
repeat(100)begin
    #10 ser_in = $random%2;
end
end

always #5 clk = ~clk;
assign #2.5 clk_90 = clk;

CDR CDR(
    .clk_0      (clk    ),
    .clk_90     (clk_90 ),
    .rst_n      (rst_n  ),
    .ser_in     (ser_in ),
    .ser_clk    (ser_clk),
    .ser_out    (ser_out)
    );


endmodule

三、仿真波形

在这里插入图片描述

总结

  本文基于FPGA全数字实现了CDR,实现结果有所局限,CDR的频率不能太高且不能自适应更正识别数据信号的相位,主要目的是实现时钟恢复的功能,仅供参考。后续将该CDR模块会应用于低速的serdes实现。

  • 29
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值