zl程序教程

您现在的位置是:首页 >  工具

当前栏目

LVDS学习笔记之lvds_transceiver设计及仿真

笔记学习 设计 仿真
2023-09-11 14:20:39 时间

系列文章目录

LVDS学习笔记之 IDELAYE2应用及仿真
LVDS学习笔记之 ISERDESE2应用及仿真
LVDS学习笔记之 RX模块设计及自动训练仿真
LVDS学习笔记之 OSERDESE2应用及仿真
LVDS学习笔记之 TX模块设计



前言

  经过前面几章的学习,我们已经有了LVDS的接收模块和发送模块例程,本章节会将两者结合成一个工程,设计一个LVDS收发模块。


一、设计顶层

请添加图片描述
  设计的顶层如上图所示。

  1. clk_manager模块

用于产生系统时钟,LVDS所需时钟,系统复位等信号;

  1. lvds_rx_top模块

与上期发布的LVDS学习笔记之 RX模块设计及自动训练仿真基本相同,做了简单的修改,一方面用于上电后的上电,另一方面用于接收LVDS数据;

  1. lvds_tx_top模块

同样与LVDS学习笔记之 TX模块设计类似,做了简单的修改,用于产生上电后的训练字,以及训练完成后产生所欲要的控制字。
不懂的同学可看下前面的文章。

二、工程代码

工程代码只放了工程顶层,和部分模块的顶层程序,如需需要其他工程代码程序,可下载本章的工程源代码。

代码说明:
顶层包含了lvds接口,以及准备下发数据的缓存fifo接口,接收的数据会由recv_fifo_dout输出。
训练完成后,仅需控制fifo,将需要传输的数据写入到fifo中,lvds就会启动发送模块,完成发送功能。

1.lvds_top顶层

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/08/17 22:48:43
// Design Name: 
// Module Name: lvds_top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module lvds_top(

        input           sys_clk_in,//50M,	
    //lvds rx
        input 			lvds_rx_p,
        input 			lvds_rx_n,
    //lvds tx
		output 			lvds_clk_p,
		output 			lvds_clk_n,
		output  		lvds_tx_p,
		output  		lvds_tx_n,	

        input			fifo_wren,
        input [7:0] 	fifo_din,
        output 			fifo_full,

        output[7:0]	    recv_fifo_dout
    );

    wire                clk_100m;
    wire                clk_200m;
    wire                clk_25m;
    wire                reset;

    wire                training_finish;

    

    clk_manager clk_manager_inst(
        .clk_in             ( sys_clk_in        ), //50M,		
        .clk_100m           ( clk_100m          ),
        .clk_200m           ( clk_200m          ),
        .clk_25m            ( clk_25m           ),
        .sys_reset          ( reset             )
    );

    lvds_rx_top u_lvds_rx_top(
        .clk_100m           ( clk_100m          ),
        .clk_200m           ( clk_200m          ),
        .clk_25m            ( clk_25m           ),
        .reset              ( reset             ),
        .lvds_rx_p          ( lvds_rx_p         ),
        .lvds_rx_n          ( lvds_rx_n         ),
        .training_finish    ( training_finish   ),
        .recv_fifo_dout     ( recv_fifo_dout    )
    );


    lvds_tx_top u_lvds_tx_top(
        .clk_100m           ( clk_100m          ),
        .clk_25m            ( clk_25m           ),
        .clk_200m           ( clk_200m          ),
        .reset              ( reset             ),
        .fifo_wren          ( fifo_wren         ),
        .fifo_din           ( fifo_din          ),
        .fifo_full          ( fifo_full         ),
        .training_finish    ( training_finish   ),
        .lvds_clk_p         ( lvds_clk_p        ),
        .lvds_clk_n         ( lvds_clk_n        ),
        .lvds_tx_p          ( lvds_tx_p         ),
        .lvds_tx_n          ( lvds_tx_n         )
    );

endmodule

2.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

3.lvds_tx_top模块

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/01/18 22:24:08
// Design Name: 
// Module Name: lvds_tx_top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module lvds_tx_top(
		input 			clk_100m,
		input 			clk_25m,
		input 			clk_200m,
		input 			reset,
		
		input			fifo_wren,
		input [7:0] 	fifo_din,
		output 			fifo_full,
		
		input 			training_finish,
		
		output 			lvds_clk_p,
		output 			lvds_clk_n,
		output  		lvds_tx_p,
		output  		lvds_tx_n		
    );
	reg 			io_reset;
	reg				training_flag;
	reg [19:0] 		timer_count;
	wire			fifo_rden;
	wire [7:0] 		fifo_dout;
	wire			fifo_empty;
	wire [7:0]		tx_dout;
	wire 			data_mux;
	
				
	assign fifo_rden = ((~fifo_empty) & (~training_flag));
			
	
	fifo_generator_0
	lvds_send_fifo
   (
		.wr_clk         (clk_100m			),
		.rd_clk         (clk_25m			),
		.din            (fifo_din			),
		.wr_en          (fifo_wren			),
		.rd_en          (fifo_rden			),
		.dout           (fifo_dout			),
		.full           (fifo_full			),
		.empty          (fifo_empty			)
	);
	
	always @(posedge clk_25m)
		if(reset)
			timer_count <= 20'd0;
        else if (~training_finish)
			timer_count <= 20'd0;
		else if (timer_count < 20'hFFFF0)
			timer_count <= timer_count + 1'b1;
			
	`define	SIM					//仿真时为了加快仿真速度定义SIM,正式使用时注释掉
	always @(posedge clk_25m)
		if(reset)
			training_flag <= 1'b0;	
		`ifdef SIM
        else if (timer_count < 20'd100)
            training_flag <= 1'b1;
		`else
			else if (timer_count < 20'd100000)
            training_flag <= 1'b1;
		`endif
        else
            training_flag <= 1'b0;	
		
		
    assign data_mux = fifo_rden & (~fifo_empty);
	assign tx_dout = training_flag ? 8'h93 : (data_mux ? fifo_dout : 8'h0000);
	
    always @(posedge clk_25m)
        if (reset)
            io_reset <= 1'b1;
        else
            io_reset <= 1'b0;
	
	lvds_tx 
	lvds_tx_inst
	(
		.data_out_from_device   (tx_dout		),
		.data_out_to_pins_p     (lvds_tx_p		),
		.data_out_to_pins_n     (lvds_tx_n		),
		.clk_to_pins_p          (lvds_clk_p		),
		.clk_to_pins_n          (lvds_clk_n		),
		.clk_in                 (clk_200m		),
		.clk_div_in             (clk_25m		),
		.clk_reset              (io_reset		),
		.io_reset               (io_reset		)
	);
endmodule

4.lvds_rx_top模块

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


module lvds_rx_top(
        input 	    clk_100m,
		input 	    clk_200m,
		input 	    clk_25m,
		input 	    reset,
		input		lvds_rx_p,
		input		lvds_rx_n,
		
        output      training_finish,

		output[7:0]	recv_fifo_dout
    );
	
	wire		recv_fifo_empty;
	wire		delay_locked;
	wire[7:0]	rx_data;
	wire		delay_ld;
	wire[4:0]	delay_tap;
	wire		bitslip;
	
	reg			recv_fifo_rden;
		
    lvds_rx_recv 
	lvds_rx_recv_inst(
        .clk_100m(clk_100m),
        .clk_200m(clk_200m),
        .reset(reset),
        
        .clk_ser(clk_200m),    //200m
        .clk_div(clk_25m),    //25m
        .fifo_rden(recv_fifo_rden),
        .fifo_dout(recv_fifo_dout),
        .fifo_empty(recv_fifo_empty),
        
        .delay_locked(delay_locked),
        .rx_dout(rx_data),
        .delay_ld(delay_ld),
        .delay_tap(delay_tap),
        .bitslip(bitslip),
        .training_finish(training_finish),
        
        .lvds_rx_p(lvds_rx_p),
        .lvds_rx_n(lvds_rx_n)
        );
        
        
    lvds_rx_training 
	lvds_rx_training_inst
        (
        .clk_div(clk_25m),
        .reset(reset),
        
        .delay_locked(delay_locked),
        .rx_din(rx_data),
        .delay_ld(delay_ld),
        .delay_tap(delay_tap),
        .bitslip(bitslip),
        .training_finish(training_finish)
        );
	
	always@(posedge clk_100m)begin
		if(!recv_fifo_empty )begin
			recv_fifo_rden <= 1'b1;
		end
		else begin
			recv_fifo_rden <= 1'b0;
		end
	end
	
endmodule

三、仿真tb代码

代码说明:
等待训练完成后,控制fifo写入带发送的数据,观察接收端口的数据。

`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_top_tb();


	reg           	sys_clk_in = 0;//50M;	
//lvds rx
	reg 			lvds_rx_p;
	reg 			lvds_rx_n;
//lvds tx
	wire 			lvds_clk_p;
	wire 			lvds_clk_n;
	wire  			lvds_tx_p;
	wire  			lvds_tx_n;	

	reg				fifo_wren;
	reg [7:0] 		fifo_din;
	wire 			fifo_full;

	wire[7:0]	    recv_fifo_dout;

lvds_top u_lvds_top(
    .sys_clk_in ( sys_clk_in ),
    .lvds_rx_p  ( lvds_rx_p  ),
    .lvds_rx_n  ( lvds_rx_n  ),
    .lvds_clk_p ( lvds_clk_p ),
    .lvds_clk_n ( lvds_clk_n ),
    .lvds_tx_p  ( lvds_tx_p  ),
    .lvds_tx_n  ( lvds_tx_n  ),
    .fifo_wren  ( fifo_wren  ),
    .fifo_din   ( fifo_din   ),
    .fifo_full  ( fifo_full  ),
    .recv_fifo_dout  ( recv_fifo_dout  )
);

	always @(posedge lvds_clk_p ) begin
		lvds_rx_p <= lvds_tx_p;
		lvds_rx_n <= lvds_tx_n;
	end

	always #(`clk_period/2) sys_clk_in = ~sys_clk_in;
	
	initial begin
		fifo_wren = 0;
		fifo_din = 0;

		@(posedge u_lvds_top.training_finish);

		#200;
		lvds_wr(8'h01);
		lvds_wr(8'h02);
		lvds_wr(8'h03);
		lvds_wr(8'h04);
		lvds_wr(8'h05);
		lvds_wr(8'h06);
		lvds_wr(8'h07);
		lvds_wr(8'h08);

		#200;
		lvds_wr(8'h09);
		$stop;
	end
	
	task lvds_wr;
		input [7:0] data;
		begin
			fifo_wren = 1;
			fifo_din = data;
			#10;
			fifo_wren = 0;
			#10;
		end
	endtask
	
endmodule

四、仿真截图

1.训练过程

上电完成后会启动训练,tx模块持续发送8'h93,待接收数据为8'h93后训练完成
请添加图片描述

2.写入待发送的数据

训练完成后分别往fifo中写入8'h01、8'h02、8'h03、8'h04、8'h05、8'h06、8'h07、8'h08
请添加图片描述

3.接收到的数据

接收到的数据与写入的数据相同
请添加图片描述

4.耗时

发送到接收传输耗时

请添加图片描述
单个字节传输耗时
由lvds的特性,单个字节耗时为25m时钟耗时,即40ns,可见是非常快。


总结

本文将前期编写的接收模块和发送模块进行整合,形成了lvds的收发器,经过仿真能够正确的接收发送端传输来的数据。但可发现并没有传输完成的标志信号,数据只是在lvds总线上进行传输而已。因此还需要进行发送接收协议的设计,在这里作者大致的讲解一下设计方法。

设计思想:
需要设计一个校验方式,如CRC校验
需要设计一个数据帧头,如接收到0x55aa即为数据帧头
接收到帧头后接收需要传输的数据
也可接收数据帧头后,接收数据长度,然后根据数据长度进行接收数据
lvds的发送可能会受到外界干扰,因此还需要设计重发机制

一帧数据的发送:
数据的发送帧大致为{帧头,数据长度,数据,校验},或者{帧头,数据,校验};
校验不通过后启动重发机制,若连续3次还为正常接收到数据,认定传输错误。

最后附上文章工程文件地址https://download.csdn.net/download/weixin_45372778/86405548
下期将会对本工程上板调试
欢迎各位同学,老师进行批评指正,共同进步,谢谢。