当前位置:   article > 正文

基于FPGA的CDR时钟恢复设计_cdr fpga 工程

cdr fpga 工程


[参考文献]

[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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99

测试文件

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

三、仿真波形

在这里插入图片描述

总结

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/132746
推荐阅读
  

闽ICP备14008679号