赞
踩
[参考文献]
[1] https://blog.csdn.net/yijingjijng/article/details/48024059
CDR全称为Clock and Data Recovery,即时钟数据恢复。顾名思义,CDR就是接收端根据接收到的数据信号恢复出时钟,以便于接收端对数据信号进行恢复和处理。
那为什么需要CDR呢?CDR一般应用于串行数据的恢复,那为什么不像SPI一样多传输一条数据线呢?我们知道SPI的最大传输速度也就几Mbps,这对图像等实时传输是不可能的。而如果速度传输加快,信道对传输信号的影响变大,会导致时钟和数据信号产生不同程度的影响,导致误码。因此后面演变出了SerDes这类高速传输方式,其采用LVDS差分信号进行数据传输,同时省去了时钟线,接收端再根据接收到的差分数据信号进行CDR处理,恢复出时钟和数据。
本文是基于FPGA全数字电路实现的,重在实现的逻辑,没有物理层的优化,也就意味着无法对太高速的数据进行恢复,仅供参考。
目前基于FPGA的全数字CDR设计基本上都是采取了过采样的方式实现,具体可以使用同频多相时钟采样和数据延迟链采样两种方式实现。核心都是采用Nxf的高速时钟,对信号进行N倍的过采样,通过对采样后数据进行判断分析,得到数据跳变沿的位置,再用最佳的采样点进行恢复数据。但是对于A7系列FPGA来说,PLL最大只能生成600MHz左右的时钟,因此对于高速信号直接使用Nxf的高频时钟采样是很困难的。
本文采用同频多相时钟采样的方式实现CDR,利用PLL产生0°和90°相位差的采样时钟,两时钟利用双沿采样,如此起到了4倍上采样的效果。最后通过一个判决器对四个采样点进行判决,得到信号跳变沿的位置,再选取最佳的采样点恢复出数据。
如下图所示,数据信号发生跳变时存在四种情况,跳变沿前后两个采样点采样得到的值不一样,如此便能通过四个采样数值得到跳变沿所在的位置,再选取合适的采样点进行数据的恢复,比如说情况1,信号的上升沿在第一个和第二个采样点之间,可选择相对稳定的第三个采样点作为数据恢复点,以此类推。
代码实现
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实现。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。