Xilinx LVDS接收模块及自动训练设计仿真实操

本文详细介绍了使用Xilinx SelectIO IP核创建LVDS接收模块的过程,包括配置参数、代码解析和LVDS训练的重要性。重点讨论了IDELAYE2和ISERDESE2原语在LVDS接收中的作用,以及LVDS训练的原因和自动训练的思想。通过自动训练,确保数据传输的稳定性和位对齐。此外,提供了LVDS_RX工程的代码框架,包括LVDS_RX模块、LVDS_RX训练模块和仿真代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

系列文章目录

LVDS学习笔记之 IDELAYE2应用及仿真
LVDS学习笔记之 ISERDESE2应用及仿真



前言

  这期的文章内容是手把手使用Xlinx SelectIO IP核创建LVDS接收模块,如果对IDELAYE2和ISERDESE2不懂的同学建议先看笔者的LVDS系列文章。

一、使用SelectIO IP核创建LVDS接收模块

  使用软件版本Vivado 18.3

  1. 打开Vivado并创建名为LVDS_RX的工程,芯片型号可以选择你使用的芯片,笔者使用的是正点原子领航者开发板,这里选择xc7z020clg400-2芯片。

  2. 在IP Catalog中搜索selectIO,双击并打开配置页面。
    在这里插入图片描述

  3. 本次仿真只对一对LVDS信号以SDR进行仿真,在Data Bus Setup设置如下。
    在这里插入图片描述
    本次是LVDS的读操作,因此Data Bus Direction要选择Input类型;
    仿真的是单倍速率LVDS读信号,因此Data Rate选择SDR;
    串行数据选择8;
    I/O信号类型选择差分,标准选择LVDS25;

  4. 在Clock Setup设置如下。
    在这里插入图片描述

  5. 在Data And Clock Delay页面作如下设置。
    在这里插入图片描述
    该页面主要是设置数据延时类型,本次选择的是可加载可变延迟模式,这种方式使用最为灵活。
    该页面设置会使IP核内部产生一个IDELAYE2原语,后面会介绍到,因此include DELAYCTRL复选框需要打勾
    选择使能DELAY的高性能模式,减少输出抖动。

  6. 最后点击OK生成此IP。

二、生成的LVDS_RX模块代码详解

1.IP代码

在生成的IP中点击下三角,并打开selectio_lvds_rx_selectio_wiz代码。
在这里插入图片描述

代码如下:


// file: selectio_lvds_rx_selectio_wiz.v
// (c) Copyright 2009 - 2013 Xilinx, Inc. All rights reserved.
// 
// This file contains confidential and proprietary information
// of Xilinx, Inc. and is protected under U.S. and
// international copyright and other intellectual property
// laws.
// 
// DISCLAIMER
// This disclaimer is not a license and does not grant any
// rights to the materials distributed herewith. Except as
// otherwise provided in a valid license issued to you by
// Xilinx, and to the maximum extent permitted by applicable
// law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND
// WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES
// AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING
// BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-
// INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and
// (2) Xilinx shall not be liable (whether in contract or tort,
// including negligence, or under any other theory of
// liability) for any loss or damage of any kind or nature
// related to, arising under or in connection with these
// materials, including for any direct, or any indirect,
// special, incidental, or consequential loss or damage
// (including loss of data, profits, goodwill, or any type of
// loss or damage suffered as a result of any action brought
// by a third party) even if such damage or loss was
// reasonably foreseeable or Xilinx had been advised of the
// possibility of the same.
// 
// CRITICAL APPLICATIONS
// Xilinx products are not designed or intended to be fail-
// safe, or for use in any application requiring fail-safe
// performance, such as life-support or safety devices or
// systems, Class III medical devices, nuclear facilities,
// applications related to the deployment of airbags, or any
// other applications that could lead to death, personal
// injury, or severe property or environmental damage
// (individually and collectively, "Critical
// Applications"). Customer assumes the sole risk and
// liability of any use of Xilinx products in Critical
// Applications, subject only to applicable laws and
// regulations governing limitations on product liability.
// 
// THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS
// PART OF THIS FILE AT ALL TIMES.
//----------------------------------------------------------------------------
// User entered comments
//----------------------------------------------------------------------------
// None
//----------------------------------------------------------------------------

`timescale 1ps/1ps

module selectio_lvds_rx_selectio_wiz
   // width of the data for the system
 #(parameter SYS_W = 1,
   // width of the data for the device
   parameter DEV_W = 8)
 (
  // From the system into the device
  input  [SYS_W-1:0] data_in_from_pins_p,
  input  [SYS_W-1:0] data_in_from_pins_n,
  output [DEV_W-1:0] data_in_to_device,
  input              in_delay_reset,                       // Active high synchronous reset for input delay
  input  [SYS_W -1 :0]           in_delay_data_ce,                     // Enable signal for delay
  input  [SYS_W -1 :0]           in_delay_data_inc,                    // Delay increment (high), decrement (low) signal
  input  [5*SYS_W -1:0]       in_delay_tap_in,  // Dynamically loadable delay tap value for input delay
  output [5*SYS_W -1:0]       in_delay_tap_out, // Delay tap value for monitoring input delay
 
  output             delay_locked,   // Locked signal from IDELAYCTRL
  input              ref_clock,      // Reference clock for IDELAYCTRL. Has to come from BUFG.
  input  [SYS_W-1:0]             bitslip,       // Bitslip module is enabled in NETWORKING mode
                                    // User should tie it to '0' if not needed
  input              clk_in,        // Fast clock input from PLL/MMCM
  input              clk_div_in,    // Slow clock input from PLL/MMCM
  input              io_reset);
  localparam         num_serial_bits = DEV_W/SYS_W;
  wire clock_enable = 1'b1;
  // Signal declarations
  ------------------------------
  // After the buffer
  wire   [SYS_W-1:0] data_in_from_pins_int;
  // Between the delay and serdes
  wire [SYS_W-1:0]  data_in_from_pins_delay;
  wire [SYS_W-1:0] in_delay_ce;
  wire [SYS_W-1:0] in_delay_inc_dec;
  wire [4:0]  in_delay_tap_in_int[0:SYS_W - 1];   // fills in starting with 0
  wire [4:0]  in_delay_tap_out_int[0:SYS_W - 1];   // fills in starting with 0
  wire ref_clock_bufg;
  // Array to use intermediately from the serdes to the internal
  //  devices. bus "0" is the leftmost bus
  wire [SYS_W-1:0]  iserdes_q[0:13];   // fills in starting with 0
   assign in_delay_ce = {                      in_delay_data_ce[0]};
   assign in_delay_inc_dec = {                      in_delay_data_inc[0]};
   assign in_delay_tap_in_int[0] = in_delay_tap_in[5*(0 + 1) -1:5*(0)] ;
   assign in_delay_tap_out[5*(0 + 1) -1:5*(0)] = in_delay_tap_out_int[0];
  // Create the clock logic


  // We have multiple bits- step over every bit, instantiating the required elements
  genvar pin_count;
  genvar slice_count;
  generate for (pin_count = 0; pin_count < SYS_W; pin_count = pin_count + 1) begin: pins
    // Instantiate the buffers
    ------------------------------
    // Instantiate a buffer for every bit of the data bus
    IBUFDS
      #(.DIFF_TERM  ("FALSE"),             // Differential termination
        .IOSTANDARD ("LVDS_25"))
     ibufds_inst
       (.I          (data_in_from_pins_p  [pin_count]),
        .IB         (data_in_from_pins_n  [pin_count]),
        .O          (data_in_from_pins_int[pin_count]));

    // Instantiate the delay primitive
    -------------------------------

     (* IODELAY_GROUP = "selectio_lvds_rx_group" *)
     IDELAYE2
       # (
         .CINVCTRL_SEL           ("FALSE"),                            // TRUE, FALSE
         .DELAY_SRC              ("IDATAIN"),                          // IDATAIN, DATAIN
         .HIGH_PERFORMANCE_MODE  ("TRUE"),                            // TRUE, FALSE
         .IDELAY_TYPE            ("VAR_LOAD"),              // FIXED, VARIABLE, or VAR_LOADABLE
         .IDELAY_VALUE           (0),                  // 0 to 31
         .REFCLK_FREQUENCY       (200.0),
         .PIPE_SEL               ("FALSE"),
         .SIGNAL_PATTERN         ("DATA"))                             // CLOCK, DATA
       idelaye2_bus
           (
         .DATAOUT                (data_in_from_pins_delay[pin_count]),
         .DATAIN                 (1'b0),                               // Data from FPGA logic
         .C                      (clk_div_in),
         .CE                     (in_delay_ce[pin_count]), //(in_delay_data_ce),
         .INC                    (in_delay_inc_dec[pin_count]), //in_delay_data_inc),
         .IDATAIN                (data_in_from_pins_int  [pin_count]), // Driven by IOB
         .LD                     (in_delay_reset),
         .REGRST                 (io_reset),
         .LDPIPEEN               (1'b0),
         .CNTVALUEIN             (in_delay_tap_in_int[pin_count]), //in_delay_tap_in),
         .CNTVALUEOUT            (in_delay_tap_out_int[pin_count]), //in_delay_tap_out),
         .CINVCTRL               (1'b0)
         );


     // Instantiate the serdes primitive
     ------------------------------

     // local wire only for use in this generate loop
     wire cascade_shift;
     wire [SYS_W-1:0] icascade1;
     wire [SYS_W-1:0] icascade2;
     wire clk_in_int_inv;

     assign clk_in_int_inv = ~ clk_in;

     // declare the iserdes
     ISERDESE2
       # (
         .DATA_RATE         ("SDR"),
         .DATA_WIDTH        (8),
         .INTERFACE_TYPE    ("NETWORKING"), 
         .DYN_CLKDIV_INV_EN ("FALSE"),
         .DYN_CLK_INV_EN    ("FALSE"),
         .NUM_CE            (2),
         .OFB_USED          ("FALSE"),
         .IOBDELAY          ("IFD"),                                // Use input at DDLY to output the data on Q
         .SERDES_MODE       ("MASTER"))
       iserdese2_master (
         .Q1                (iserdes_q[0][pin_count]),
         .Q2                (iserdes_q[1][pin_count]),
         .Q3                (iserdes_q[2][pin_count]),
         .Q4                (iserdes_q[3][pin_count]),
         .Q5                (iserdes_q[4][pin_count]),
         .Q6                (iserdes_q[5][pin_count]),
         .Q7                (iserdes_q[6][pin_count]),
         .Q8                (iserdes_q[7][pin_count]),
         .SHIFTOUT1         (),
         .SHIFTOUT2         (),
         .BITSLIP           (bitslip[pin_count]),                             // 1-bit Invoke Bitslip. This can be used with any DATA_WIDTH, cascaded or not.
                                                                   // The amount of BITSLIP is fixed by the DATA_WIDTH selection.
         .CE1               (clock_enable),                        // 1-bit Clock enable input
         .CE2               (clock_enable),                        // 1-bit Clock enable input
         .CLK               (clk_in),                              // Fast clock driven by MMCM
         .CLKB              (clk_in_int_inv),                      // Locally inverted fast 
         .CLKDIV            (clk_div_in),                          // Slow clock from MMCM
         .CLKDIVP           (1'b0),
         .D                 (1'b0),                                // 1-bit Input signal from IOB
         .DDLY              (data_in_from_pins_delay[pin_count]),  // 1-bit Input from Input Delay component 
         .RST               (io_reset),                            // 1-bit Asynchronous reset only.
         .SHIFTIN1          (1'b0),
         .SHIFTIN2          (1'b0),
    // unused connections
         .DYNCLKDIVSEL      (1'b0),
         .DYNCLKSEL         (1'b0),
         .OFB               (1'b0),
         .OCLK              (1'b0),
         .OCLKB             (1'b0),
         .O                 ());                                   // unregistered output of ISERDESE1

     // Concatenate the serdes outputs together. Keep the timesliced
     //   bits together, and placing the earliest bits on the right
     //   ie, if data comes in 0, 1, 2, 3, 4, 5, 6, 7, ...
     //       the output will be 3210, 7654, ...
     ---------------------------------------------------------
     for (slice_count = 0; slice_count < num_serial_bits; slice_count = slice_count + 1) begin: in_slices
        // This places the first data in time on the right
        assign data_in_to_device[slice_count] =
          iserdes_q[num_serial_bits-slice_count-1];
        // To place the first data in time on the left, use the
        //   following code, instead
        // assign data_in_to_device[slice_count] =
        //   iserdes_q[slice_count];
     end
  end
  endgenerate

// IDELAYCTRL is needed for calibration
(* IODELAY_GROUP = "selectio_lvds_rx_group" *)
  IDELAYCTRL
    delayctrl (
     .RDY    (delay_locked),
     .REFCLK (ref_clock_bufg),
     .RST    (io_reset));

  BUFG
    ref_clk_bufg (
     .I (ref_clock),
     .O (ref_clock_bufg));
endmodule

2.代码详解

  可以看到整个IP中使用了IBUFDS、IDELAYE2、ISERDESE2、IDELAYCTRL及BUFG原语。这时大家应该明白为什么笔者前期要作IDELAYE2和ISERDESE2两个原语的学习了吧。
下们讲解以下各个原语的功能:
  IBUFDS:这个原语主要是将差分信号转成单端信号,为了降低外界的干扰,lvds采用差分传输,转换后的信号单端信号较为稳定,传给IDELAYE2进行数据延时操作。
  IDELAYE2:主要是将传过来的lvds信号进行延时,能够防止在数据不稳定时采集数据,如果要详细了解其中各个端口及属性的意义,请看博主的LVDS学习笔记之 IDELAYE2应用及仿真文章。
  ISERDESE2:实现串并转换,想更深入了解原语的端口及属性的意义,请看博主的LVDS学习笔记之 ISERDESE2应用及仿真文章。
  IDELAYCTRL:减少过程、电压和温度变化对参考时钟的影响。
BUFG:全局始终缓存。
  总的来说,该IP的运行过程为:首先通过IBUFDS将LVDS转换成单端信号,并通过IDELAYE2进行数据延时,整个延时过程由IDELAYCTRL提供参考时钟进行协助;然后将延时后稳定的数据传输给ISERDESE2进行串并转换,实现LVDS信号的输出。

三、LVDS训练的原因及自动训练思想

1.LVDS训练原因

  这个是整个LVDS乃至SelectIO的重点。数据高速传输过程中,由于线的延时可能会导致时钟信号和数据信号未能同时到达,中间多多少少存在一定的时间差,在高速传输模式下,时钟周期为ns甚至ps为单位,布线的线网的延时多多少少会存在一定的差异。
  大家可能会觉得,整个传输过程中LVDS信号明明就只有一对差分信号,哪里来的时钟信号。你有这个思想说明你看进去了,还记得ISERDESE2中的CLK和CLK_DIV吧,CLK_DIV就是采样的起始,CLK是采样信号。

2.LVDS训练思想

  下面我将以图像的形式IDELAYE2及ISERDESE2的作用:
在这里插入图片描述
  上图为一帧传输的过程,CLK_DIV的上升沿标志着一个采样的开始(其实是CLK_DIV上升沿后CLK的第三个上升沿才是第一个采样的开始,为了说明这里先这样讲)。然后来一个CLK,对应采集一次data的信号,这样就完成LVDS信号的采集。
  但上图CLK信号与data变化过程中对其,此时采集的信号并不稳定,由于CLK信号无法移动,因此通过IDELAYE2将data信号进行延时操作,使CLK采集到数据稳定的位置。
在这里插入图片描述

3.LVDS自动训练思想

  在训练初期,我们需要设置一个同步字,本文设置的同步字是8‘h93(8’b1001_0011)。用training_finish信号表示训练完成与否,0表示未完成训练,1表示以完成训练。训练初期,使用LVDS_TX模块往LVDS_RX模块一直发送同步字(发送8’b1001_0011),由于LVDS_TX模块尚未编写,在测试文件模拟LVDS_TX模块发送8’b1001_0011。
  因此,lvds_rx_p端信号会循环1001_0011_1001_0011_1001_0011_1001_0011_1001_0011…。由于线网延时不定,以及ISERDESE2采样位置跟信号起始位置不固定,因此ISERDESE2接收的数据只有8种可能:8’b1001_0011、8’b0010_0111、8’b0100_1110、8’b1001_1100、8’b0011_1001、8’b0111_0010、8’b1110_0100、8’b1100_1001
  我们训练的中心目的就两个,一个是保证数据输出稳定,另一个是保证数据位对齐。
  首先,我们得到的数据要为以上的任意一个,我们的训练才能开始。
  然后,控制tap的值,寻找一个输出稳定的tap值。这个是首要考虑的问题,这里也是最重要的部分,这里弄懂了,自动训练的第一个思想就懂了,下面我用图的方式解释怎么作,为什么这样做:
在这里插入图片描述
分别延时tap1和tap2,有一种可能跟上图一样,延时tap1时ISERDESE2输出值为一个,延时tap2时ISERDESE2的输出值为另一个,两者不相等。可见没有我们想要的tap值,因此需要继续增加tap的值。
在这里插入图片描述
还有一种可能是上图这样,延时tap1和延时tap2时ISERDESE2输出值一致,为了防止tap1或者tap2在数据变化的边界,因此我们取tap的中间值作为最终的tap值。
在第一种情况之下,随着继续增大tap的值,最终会到达第二种情况。
当然,tap的值不是可以随意增大的,它的最大值为31,若tap值增大到31时,两者数据还未稳定,这就有可能是干扰太大,或者其他原因,这样的话训练会失败,此时将training_fail信号置高,通知其他模块目前lvds训练失败,好采取重新训练或者检查原因。
  其次,找到合适的tap值之后,需要做的就是将位对齐,上面我也讲了,ISERDESE2输出的数据有以上8种,但只有一种8’b1001_0011是我们LVDS_TX模块发送的,因此,通过置高一个时钟的bitslip信号,使得ISERDESE2移为输出,直至输出数据与同步码相等,此时我们的训练才最终结束。
  值得一提的是,并不是两次tap值取得的输出数据相等时,位对齐一定相等。为了防止干扰导致误判,此时还需要对bitslip信号计数。这里的数据是8位,也就是8次bitslip脉冲数据输出一个循环。当bitslip计数值达到16时,输出数据还未与同步码相等,这里就有可能是因为误判了,此时训练失败。
  目前我是用这种方式来进行LVDS自动训练的,其思想大同小异,明白以上的过程,将对LVDS的自动训练有更深的认识。
  如果你看了这段还没明白什么意思,可以私信或者留言,共同进步。
  如果你看了这段对你了解LVDS自动训练思想有所启发,不妨点赞、关注、收藏一波,对博主支持下。
  创作不易,谢谢大家!

四、LVDS_RX工程代码及仿真代码

1.LVDS_RX工程代码

代码框架如下图所示
在这里插入图片描述
clk_manager模块代码

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2021/12/30 22:31:46
// Design Name: 
// Module Name: clk_manager
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//

module clk_manager(
	input			clk_in, //50M,	
	output			clk_100m,
	output			clk_200m,
	output          clk_25m,
	output	reg		sys_reset
    );
	
    wire			pll_locked;
	reg		[7:0]	locked_r = 8'b0;
    
   clk_wiz_0 
   clk_wiz_0_inst
   (
    .clk_100m(clk_100m),     	// output clk_100m
    .clk_200m(clk_200m),     	// output clk_200m
    .clk_25m(clk_25m),     		// output clk_25m
    .locked(pll_locked),       	// output locked
    .clk_in(clk_in));      		// input clk_in
    
    always @ (posedge clk_100m)
        begin
            locked_r <= {locked_r[6:0], pll_locked};
            if (&locked_r)
                sys_reset <= 1'b0;
            else
                sys_reset <= 1'b1;
        end
    
endmodule

lvds_rx_recv模块代码

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2021/12/30 22:32:54
// Design Name: 
// Module Name: lvds_rx_recv
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module lvds_rx_recv(
	input 				clk_100m,
    input 				clk_200m,
    input 				clk_25m,
    input 				reset,
    
    // control interface
    input 				fifo_rden,
    output [7:0] 		fifo_dout,
    output 				fifo_empty,
    output 				fifo_rd_rst_busy,
    
    // training inteface
    output 				delay_locked,
    output [7:0] 		rx_dout,
    input 				delay_ld,
    input [4:0] 		delay_tap,
    input  				bitslip,
    input 				training_finish,
    
    // LVDS interface
    input 				lvds_rx_p,
    input 				lvds_rx_n
    );
	
    reg fifo_wren = 1'b0;
    reg io_reset = 1'b1;
	
	wire [4:0] in_delay_tap_out;
    wire [7:0] fifo_din;
	wire fifo_wr_rst_busy, fifo_full;
	
    assign rx_dout = fifo_din;
    
    always @ (posedge clk_25m)
        if ((~reset) & (~fifo_wr_rst_busy) & (~fifo_full) & training_finish)
            fifo_wren <= 1'b1;
        else
            fifo_wren <= 1'b0;
    
    lvds_rx_fifo 
	lvds_rx_fifo_inst (
		.rst(reset),                  
		.wr_clk(clk_25m),            	
		.rd_clk(clk_100m),            
		.din(fifo_din),                
		.wr_en(fifo_wren),             
		.rd_en(fifo_rden),             
		.dout(fifo_dout),              
		.full(fifo_full),              
		.empty(fifo_empty),            
		.wr_rst_busy(fifo_wr_rst_busy),
		.rd_rst_busy(fifo_rd_rst_busy) 
    );
    
    
    always @ (posedge clk_25m)
        if (reset)
            io_reset <= 1'b1;
        else
            io_reset <= 1'b0;
    
   

	selectio_lvds_rx 
	LVDS_RX_inst(
		.data_in_from_pins_p(lvds_rx_p), 	
		.data_in_from_pins_n(lvds_rx_n), 	
		.data_in_to_device(fifo_din), 		
		.in_delay_reset(delay_ld), 			  
		.in_delay_data_ce(1'b0), 			
		.in_delay_data_inc(1'b0), 			
		.in_delay_tap_in(delay_tap), 		
		.in_delay_tap_out(in_delay_tap_out),  
		
		.delay_locked(delay_locked), 		   
		.ref_clock(clk_200m), 				  
		.bitslip(bitslip), 					  
		.clk_in(clk_200m), 					  
		.clk_div_in(clk_25m), 				  
		.io_reset(io_reset) 					
	); 

endmodule

lvds_rx_training模块代码

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2021/12/30 22:30:49
// Design Name: 
// Module Name: lvds_rx_training
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module lvds_rx_training(
    input clk_25m,
    input reset,

    input delay_locked,
    input [7:0] rx_din,
    output reg delay_ld,
    output reg [5:0] delay_tap,
    output reg bitslip,
    output reg training_finish
    );
    
    
    reg training_start = 1'b0;
    reg [7:0] rx_count = 8'b0;
    reg [7:0] rx_din_last = 8'b0;
    
    always @ (posedge clk_25m)
        begin
            rx_din_last <= rx_din;
            if ((~training_start) && (rx_count >= 8'd10))
                training_start <= 1'b1;
        end
        
    always @ (posedge clk_25m)
        case (rx_din)
            8'b1001_0011:
                if (rx_din_last == rx_din)
                    rx_count <= rx_count + 1'b1;
                else
                    rx_count <= 8'b0;
            8'b0010_0111:
                if (rx_din_last == rx_din)
                    rx_count <= rx_count + 1'b1;
                else
                    rx_count <= 8'b0;
            8'b0100_1110:
                if (rx_din_last == rx_din)
                    rx_count <= rx_count + 1'b1;
                else
                    rx_count <= 8'b0;
            8'b1001_1100:
                if (rx_din_last == rx_din)
                    rx_count <= rx_count + 1'b1;
                else
                    rx_count <= 8'b0;
            8'b0011_1001:
                if (rx_din_last == rx_din)
                    rx_count <= rx_count + 1'b1;
                else
                    rx_count <= 8'b0;
            8'b0111_0010:
                if (rx_din_last == rx_din)
                    rx_count <= rx_count + 1'b1;
                else
                    rx_count <= 8'b0;
            8'b1110_0100:
                if (rx_din_last == rx_din)
                    rx_count <= rx_count + 1'b1;
                else
                    rx_count <= 8'b0;
            8'b1100_1001:
                if (rx_din_last == rx_din)
                    rx_count <= rx_count + 1'b1;
                else
                    rx_count <= 8'b0;
            default:
                rx_count <= 8'b0;
        endcase
        
    
    
    localparam SYNC_CODE = 8'h93;
    
    
    localparam  STATE_IDLE        = 16'b0000_0000_0000_0001,
                STATE_DELAY_SET1  = 16'b0000_0000_0000_0010,
                STATE_WAIT1       = 16'b0000_0000_0000_0100,
                STATE_READ1       = 16'b0000_0000_0000_1000,
                STATE_DELAY_SET2  = 16'b0000_0000_0001_0000,
                STATE_WAIT2       = 16'b0000_0000_0010_0000,
                STATE_READ_CHK    = 16'b0000_0000_0100_0000,
                STATE_DELAY_SET3  = 16'b0000_0000_1000_0000,
                STATE_WAIT3       = 16'b0000_0001_0000_0000,
                STATE_READ2       = 16'b0000_0010_0000_0000,
                STATE_BITSLIP_EN  = 16'b0000_0100_0000_0000,
                STATE_FINISH      = 16'b0000_1000_0000_0000,
                STATE_FAIL        = 16'b0001_0000_0000_0000;
                
    
    
    (*mark_debug = "true"*)reg [15:0] cstate;
    reg [15:0] nstate;
    
    reg [3:0] wait_count;
    reg [7:0] bitslip_count;
    reg [4:0] delay_tap_temp;
    reg [7:0] rx_din_temp;
    (*mark_debug = "true"*)reg training_fail;
    
    always @ (posedge clk_25m)
        if (reset)
            cstate <= STATE_IDLE;
        else
            cstate <= nstate;
            
    
    always @ (*)
        case (cstate)
            STATE_IDLE:
                if (delay_locked & training_start)
                    nstate = STATE_DELAY_SET1;
                else
                    nstate = STATE_IDLE;
                    
            STATE_DELAY_SET1:
                nstate = STATE_WAIT1;
                
            STATE_WAIT1:
                if (wait_count >= 4'd10)
                    nstate = STATE_READ1;
                else
                    nstate = STATE_WAIT1;
                    
            STATE_READ1:
                nstate = STATE_DELAY_SET2;
                
            STATE_DELAY_SET2:
                nstate = STATE_WAIT2;
                
            STATE_WAIT2:
                if (wait_count >= 4'd10)
                    nstate = STATE_READ_CHK;
                else
                    nstate = STATE_WAIT2;
                
            STATE_READ_CHK:
                if (rx_din_temp == rx_din)
                    nstate = STATE_DELAY_SET3;
                else if(delay_tap_temp > 5'd20)
                    nstate = STATE_FAIL;
                else
                    nstate = STATE_DELAY_SET1;
                    
            STATE_DELAY_SET3:
                nstate = STATE_WAIT3;
                
            STATE_WAIT3:
                if (wait_count >= 4'd10)
                    nstate = STATE_READ2;
                else
                    nstate = STATE_WAIT3;
                
            STATE_READ2:
                if (rx_din == SYNC_CODE)
                    nstate = STATE_FINISH;
                else if(bitslip_count > 8'd16)
                    nstate = STATE_FAIL;
                else
                    nstate = STATE_BITSLIP_EN;
                    
            STATE_BITSLIP_EN:
                nstate = STATE_WAIT3;
            
            STATE_FINISH:
                nstate = STATE_FINISH;
                
            STATE_FAIL:
                nstate = STATE_IDLE;
                    
            default:
                nstate = STATE_IDLE;
        endcase
    
    
    always @ (posedge clk_25m)
        if (reset)
            begin
                training_finish <= 1'b0;
                bitslip <= 1'b0;
                delay_ld <= 1'b0;
                delay_tap <= 5'b0;
                wait_count <= 4'b0;
                bitslip_count <= 8'b0;
                delay_tap_temp <= 5'b0;
                rx_din_temp <= 8'b0;
                training_fail <= 1'b0;
            end
        else case (cstate)
            STATE_IDLE:
                begin
                    training_finish <= 1'b0;
                    bitslip <= 1'b0;
                    delay_ld <= 1'b0;
                    delay_tap <= 5'b0;
                    wait_count <= 4'b0;
                    bitslip_count <= 8'b0;
                    delay_tap_temp <= 5'b0;
                    rx_din_temp <= 8'b0;
                    training_fail <= 1'b0;
                end
                
            STATE_DELAY_SET1:
                begin
                    training_finish <= 1'b0;
                    bitslip <= 1'b0;
                    delay_ld <= 1'b1;
                    delay_tap <= delay_tap_temp;
                    wait_count <= 4'b0;
                    bitslip_count <= 8'b0;
                end
                
            STATE_WAIT1:
                begin
                    training_finish <= 1'b0;
                    bitslip <= 1'b0;
                    delay_ld <= 1'b0;
                    wait_count <= wait_count + 1'b1;
                    bitslip_count <= 8'b0;
                end
            
            STATE_READ1:
                begin
                    training_finish <= 1'b0;
                    bitslip <= 1'b0;
                    delay_ld <= 1'b0;
                    wait_count <= 4'b0;
                    bitslip_count <= 8'b0;
                    rx_din_temp <= rx_din;
                end      
            
            STATE_DELAY_SET2:
                begin
                    training_finish <= 1'b0;
                    bitslip <= 1'b0;
                    delay_ld <= 1'b1;
                    delay_tap <= delay_tap_temp + 5'd10;
                    wait_count <= 4'b0;
                    bitslip_count <= 8'b0;
                end
                
            STATE_WAIT2:
                begin
                    training_finish <= 1'b0;
                    bitslip <= 1'b0;
                    delay_ld <= 1'b0;
                    wait_count <= wait_count + 1'b1;
                    bitslip_count <= 8'b0;
                end
                
            STATE_READ_CHK:
                begin
                    training_finish <= 1'b0;
                    bitslip <= 1'b0;
                    delay_ld <= 1'b0;
                    wait_count <= 4'b0;
                    bitslip_count <= 8'b0;
                    delay_tap_temp <= delay_tap_temp + 1'b1;
                end
                
            STATE_DELAY_SET3:
                begin
                    training_finish <= 1'b0;
                    bitslip <= 1'b0;
                    delay_ld <= 1'b1;
                    wait_count <= 4'b0;
                    bitslip_count <= 8'b0;
                    delay_tap <= delay_tap_temp + 5'd4;
                end
                
            STATE_WAIT3:
                begin
                    training_finish <= 1'b0;
                    bitslip <= 1'b0;
                    delay_ld <= 1'b0;
                    wait_count <= wait_count + 1'b1;
                end
                
            STATE_READ2:
                begin
                    training_finish <= 1'b0;
                    bitslip <= 1'b0;
                    delay_ld <= 1'b0;
                    wait_count <= 4'b0;
                end
                
            STATE_BITSLIP_EN:
                begin
                    training_finish <= 1'b0;
                    bitslip <= 1'b1;
                    delay_ld <= 1'b0;
                    wait_count <= 4'b0;
                    bitslip_count <= bitslip_count + 1'b1;
                end
                
            STATE_FINISH:
                begin
                    training_finish <= 1'b1;
                    bitslip <= 1'b0;
                    delay_ld <= 1'b0;
                    wait_count <= 4'b0;
                end
                
            STATE_FAIL:
                begin
                    training_finish <= 1'b0;
                    bitslip <= 1'b0;
                    delay_ld <= 1'b0;
                    wait_count <= 4'b0;
                    training_fail <= 1'b1;
                end
            
        endcase
    
endmodule


2.LVDS_RX仿真代码

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2021/12/30 22:54:53
// Design Name: 
// Module Name: lvds_rx_top_tb
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//
`define	clk_period 20

module lvds_rx_top_tb();

	reg 		sys_clk_in        ;
	reg			lvds_rx_p         ;
	reg			lvds_rx_n         ;
	wire[7:0]	recv_fifo_dout    ;

	lvds_rx_top
	lvds_rx_top_inst(
		.sys_clk_in      (sys_clk_in),
		.lvds_rx_p       (lvds_rx_p),
		.lvds_rx_n       (lvds_rx_n),
		.recv_fifo_dout  (recv_fifo_dout)
    );
	
	initial sys_clk_in = 0;
	always #(`clk_period/2) sys_clk_in = ~sys_clk_in;
	
	initial begin
		lvds_rx_p = 1'b0;
		lvds_rx_n = ~lvds_rx_p;
		@(posedge lvds_rx_top_inst.clk_manager_inst.pll_locked);
		#(`clk_period*10);
		
		while(lvds_rx_top_inst.training_finish == 1'b0)begin
			lvds_data_gen;
		end
		#(`clk_period*10);
		$stop;
	end
	
	task lvds_data_gen;
		reg [7:0] data_send;
		begin
			data_send = 8'h93;
			repeat(8)begin
				lvds_rx_p = data_send[0];
				lvds_rx_n = ~lvds_rx_p;
				data_send = {1'b0,data_send[7:1]};
				#(5);
			end
		end
	endtask
	
endmodule

五、LVDS_RX仿真截图

1.lvds_rx_top模块仿真截图

在这里插入图片描述

2.lvds_rx_training模块仿真截图

在这里插入图片描述

六、工程文件

本工程打包后的工程文件连接:https://download.csdn.net/download/weixin_45372778/72415258

如果你觉得这篇文章对你有所帮助,给个三连支持下,创作不易。
有不懂的欢迎私信,留言。
下期开始LVDS_TX模块的学习。

评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郭郭的柳柳在学FPGA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值