A7 100光纤光口闭环数据测试

  • Post author:
  • Post category:其他



全部设计参考达芬奇pro教程五十三和五十四章



GTP IP核



第一步:找到GTP的IP核


第二步:配置IP核第一页

我用的是A7的板子,选GTP,GTP被用于A7系列(GTZ被用于少数V7系列,V7系列大多用GTH,K7常用GTX),它所带载的最大带宽为6.6Gb/s


Include Shared Logic in example design:

包含收发器的QPLL、时钟和复位逻辑等在示例设计中,称为从核


Include Shared Logic in example design:

包含收发器的QPLL、时钟和复位逻辑在内核中,称为主核


从核和主核的区别:可以在示例设计中修改共享逻辑,在实际的设计中,可以使用主核也可以使用从核,但若设计中使用了一个主核后,则其内部便使用了该Quad上的QPLL资源,在使用该Quad上的其他GTP接口时,不能再使用主核,也无需再给从核添加共享逻辑(教程写的有点绕哈哈,我的理解:主核若被使用,主核内部就使用了主核Quad上的QPLL资源,主核的GTP接口使用顺位就靠后)



第三步:配置IP核第二页


我选择Quad0上的一个GTP的tx和另一个GTP上的rx来实现数据传输,在一个Quad下成为数据传输的闭环


初始默认只有一个GTP channel被选择,实现两个channel的闭环传输,需要再自行选择一个GTP的channel,图中画圈处即再选一个GTP channel的操作步骤

基于我所选择的闭环用到了Quad中的两个GTP channel,所以Protocol处选择 aurora 8b10b multi lane 4byte  (Aurora 8b10b single lane 4byte可用于一个GTP channel,它的rx接收数据,tx发送数据,数据收发和接收相互独立)

可以选择不同的线速率和编码格式,此处均选择一个常见的较低速率5Gbps用于功能验证,参考时钟为125M(收发器输入时钟,频率必须与开发板上为GTP提供时钟的晶振一致)

GTP的两个参考输入时钟都能连到PLL0和PLL1, 所以在ip核中可以任意选择用PLL0还是PLL1,本次实验收发速率一致,所以RX和TX可以共用一个锁相环,否则必须用两个锁相环,本次开发板的外部时钟是接在GTPREFCLK1,故在ip核中将参考时钟设为GTPREFCLK1,本次开发板用了两个SFP接口,这两个接口分别连接到GTP_X0Y4和GTP_X0Y5

第四步:配置IP核第三页

选的4byte,所以外部数据位宽设置为32bit,编码方式选8b/10b

DRP/System Clock Frequency是动态配置或系统工作时钟,通过DRP可以让设计者根据所选线速率和定义的协议实时调整收发器参数,该时钟可以通过锁相环来配置,本次实验将该时钟设为50M

enable TX Buffer和enable RX Buffer是因为数据不管从TSUSRCLK到XCLK还是反过来,都经过了时钟域的转换,所以TX和RX都要一个缓存区缓存下

第五步:配置IP核第四页

8b/10b编码表中有12个控制字符,以K开头,用于一些控制功能,K码中的comma码用于接收端时钟校准和数据对齐,K28.5(对应用户数据为16‘hbc)最为常见,数据在链路中以串行方式进行传输,所以接收端必须对其进行串并转换

第五页和第六页保持默认设置

GTP IP核的Example design更改

注释掉这部分,因为例程中DRP的输入时钟为差分时钟,A7开发板只带载了一个50M时钟的单端晶振,所以要将DRP的输入时钟改为单端时钟输入,我直接将drp_clk输入,注释掉IBUFDS和BUFG

PL部分

moni数据

moni数据模块用的是之前ddr的模拟数据,将640*480的数据传输到GTP的rx端,然后通过GTP的tx端发送出去

只是改动了时钟,改成了clk_200,200MHz

module moni_data(
    input clk_200,  //例程里clkout的一个,不用白不用,用在此处
   
    output  vs,
    output  hs,
    output  de_en ,
	output  [15:0] data_in
    );

    
    reg [11:0] x_cnt=12'd1;//每行传输
    reg [11:0] y_cnt=12'd1;//行数

    reg [1:0]data_en =1'b0;
	reg [15:0] de_cnt =12'd0;
always @(posedge clk_200) 
begin
    if(x_cnt == 12'd800)
        x_cnt <= 12'd1;
    else
        x_cnt <= x_cnt + 12'd1;
end

always @(posedge clk_200)   
begin
    if(x_cnt==12'd800)//每行在800时,行数加1
        if(y_cnt==12'd525)
            y_cnt <= 12'd1;
        else
            y_cnt <= y_cnt + 12'd1;
    else;
end

wire  hs_en = ((x_cnt>(12'd160)) && (x_cnt<=(12'd800)))?1:0; //注意一句话不能同时<=和>=
wire  vs_en = ((y_cnt>(12'd45)) && (y_cnt<=(12'd525)))?1:0;

//一幅图读完 就重新计数x_cnt y_cnt
always @ (posedge clk_200)
begin 
    if(hs_en)
      if(vs_en)
//  if ((x_cnt>(  12'd160)) && (x_cnt<=(12'd800))) 
//    if((y_cnt>( 12'd45)) && (y_cnt<=(12'd525)))     
            begin
                data_en<= 1'b1;
            end    
        else
            begin
                data_en <=  1'b0;
            end    
    else
	   data_en   <=  1'b0;
end  

always @ (posedge clk_200)
begin
    if(de_en==1'b1)
        if(de_cnt==12'd639) 
            
                de_cnt<=12'd0;
            
        else 
            de_cnt<=de_cnt+12'd1;
    else
        de_cnt<=12'd0;
end
        
 assign vs = vs_en;
 assign hs = hs_en;
 assign de_en=data_en;  
 assign data_in=(de_en==1)?de_cnt:16'd0;
  
//自己加的ila测试用
 ila_5 ila_5
 (
       .clk    (clk_200)     , 
       .probe0 (de_en)     ,
       .probe1 (vs)     ,
       .probe2 (hs)     ,
       .probe3 (x_cnt)     , 
       .probe4 (y_cnt)     ,
	   .probe5 (de_cnt),
	   .probe6 (data_in)

       );               
 
    
endmodule

clk模块

根据达芬奇pro教程的sfp_hdmi生成的模块

输入50MHz

输出如下:

光口模块

光口模块将接收到的16bit数据转换为32bit数据,然后按照8b10b的通信编码格式将数据发送出去,然后将光口接收到的数据按照8b10b的通信解码格式将数据解析出来,最后将32bit的数据转化为16bit

GTP顶层模块(gt_exdes)

一方面负责与用户(FPGA)进行数据交互,一方面还产生控制光口传输的各种时序,并实现对光口的通信操作,用户可以将IP看作是FPGA与光口交流信息的桥梁,这个桥梁Xilinx已经写好,封装成了一个IP供我们使用,我们只需要根据实际需求对IP进行配置即可

光口编码模块(sfp_encoder)


K码(32’hbc),前24位由用户自定义(随意定义),最后的bc因为采用的K28.5,K28.5的格式就是’hbc(低8位是bc)

fifo阈值设为500,该设置的数小于一行有效数即可,例如640*480,640为一行像素数,设的阈值小于640即可,这样是为了最后一行有数可以读

module sfp_encoder#(
//k28.5是32'bc,最低八位是bc,其余随便设,当K是0001时代表数据有K
//码,k码为0000时代表发送的是原始数据
    parameter VS_POSE_DATA1	=	32'h55a101bc,  
    parameter VS_POSE_DATA2	=	32'h55a102bc,		
    parameter DATA_START1	    =	32'h55a105bc,
    parameter DATA_START2	    =	32'h55a106bc,
    parameter DATA_END1		=	32'h55a107bc,     
    parameter DATA_END2		=	32'h55a108bc,     
    parameter UNUSE_DATA	    =	32'h55a109bc  
    )(
      input clk_in,
      input rst_n,
      input vs_in,   //输入场信号
      input data_valid_in,  //输入数据有效信号
      input [15:0] data_in, //输入数据
      
      input tx_clk,    //光口tx端时钟
      input tx_rst_n,  //光口tx端复位
      output [3:0] gt_txcharisk,   //光口tx端K码发送信号
      output [31:0] gt_txdata      //光口tx端发送数据
    );
    
    //parameter define
    parameter  tx_unuse_data  = 8'b0000_0001;
    parameter  tx_vs_pose1    = 8'b0000_0010; 	
    parameter  tx_vs_pose2    = 8'b0000_0100; 	
    parameter  tx_data_start1 = 8'b0000_1000; 	
    parameter  tx_data_start2 = 8'b0001_0000; 	
    parameter  tx_send_data   = 8'b0010_0000; 	
    parameter  tx_data_end1   = 8'b0100_0000; 	
    parameter  tx_data_end2   = 8'b1000_0000; 	
    
    reg        vs_in_d0;
    reg        vs_in_d1;
    reg        data_valid_in_d0;
    reg [15:0] data_in_d0;
    reg        vs_pose;
    reg [3:0]  gt_txcharisk;
    reg [31:0] gt_txdata;
    reg [7:0]  state;
    reg [7:0]  cnt_data;
    reg        fifo_rd_en;
    reg [7:0]  cnt_fifo_rst;
    reg        fifo_rst;
    reg        data_start;
    reg        data_start_d0;
    reg [9:0]  data_cnt;
    
    //wire define
    wire [31:0] fifo_dout;
    wire        fifo_empty;
    wire        fifo_almost_empty;
    wire [9:0]  rd_data_count;
    wire [9:0]  fifo_threshold_value;
    
    //比如视频帧率为640*480(有效),那么fifo一行最好设置为小于640的数
    //这样便于读最后一行
    assign fifo_threshold_value = 10'd500;  //这个信号是给amlost_full1用的
    
    //对输入的数据进行寄存
    always@(posedge clk_in or negedge rst_n)begin
      if(!rst_n)begin
        data_valid_in_d0 <= 1'b0;
        data_in_d0 <= 16'd0;
      end
      else begin
        data_valid_in_d0 <= data_valid_in;
        data_in_d0 <= data_in;
      end
    end 
    
    //对输入的信号进行跨时钟域处理
    always@(posedge tx_clk or negedge tx_rst_n)begin
      if(!tx_rst_n)begin
        vs_in_d0 <= 1'b0;
        vs_in_d1 <= 1'b0;
      end
      else begin
        vs_in_d0 <= vs_in;
        vs_in_d1 <= vs_in_d0;
      end
    end
    
    always@(posedge tx_clk or negedge tx_rst_n)begin
      if(!tx_rst_n)begin
        data_start_d0 <= 1'b0;
      end
      else begin
        data_start_d0 <= data_start;
      end
    end
    
    //产生场信号上升沿
    //输入的场信号的信息传送给vs_pose
    always@(posedge tx_clk or negedge tx_rst_n)begin
      if(!tx_rst_n)      
        vs_pose <= 1'b0;
      else if(vs_in_d0 && ~vs_in_d1)
        vs_pose <= 1'b1;  
      else 
        vs_pose <= 1'b0;                      
      end
    
    //数据发送开始信号
    always@(posedge tx_clk or negedge tx_rst_n)begin
      if(!tx_rst_n)
        data_start <= 1'b0;
      else if(fifo_empty || vs_pose)//fifo空(无数据可读)或者vs高
        data_start <= 1'b0;
      else if(rd_data_count >= fifo_threshold_value)//读完一行数据后
      //数据开始发送
        data_start <= 1'b1;
      else 
        data_start <= data_start;
    end
     
    //产生tx端K码发送信号和发送数据
always@(posedge tx_clk or negedge tx_rst_n)begin
	if(!tx_rst_n)begin
		gt_txcharisk <= 4'd0;
		gt_txdata <= 32'd0;				
		state <= tx_unuse_data;	
		fifo_rd_en <= 1'b0;
		cnt_data <= 8'd0;
		data_cnt<=10'd0;
	end
	else begin
		if(vs_pose)begin
			gt_txcharisk <= 4'd0000;
			gt_txdata <= 32'ha151a252;	//只是随便设的一个标志,标志vs上升沿到来			
			state <= tx_vs_pose1;	
			cnt_data <= 0;
        end
        else if(data_start && ~data_start_d0)begin
            gt_txcharisk <= 4'd0000;
			gt_txdata <= 32'ha151a252;				
			state <= tx_data_start1;
			cnt_data <= 0; 
			data_cnt<=10'd0;
        end
        else begin
            case(state)
			    tx_unuse_data:	begin
									cnt_data <= cnt_data + 1;
									fifo_rd_en <= 1'b0;
									if(cnt_data == 255)begin
										gt_txcharisk <= 4'd0001;
										gt_txdata <= UNUSE_DATA;				
										state <= tx_unuse_data; 								
									end
									else begin
										gt_txcharisk <= 4'd0000;
										gt_txdata <= 32'ha151a252;				
										state <= tx_unuse_data; 																
									end							
								end
			    tx_vs_pose1:	begin
									gt_txcharisk <= 4'd0001;
									gt_txdata <= VS_POSE_DATA1;				
									state <= tx_vs_pose2;
									fifo_rd_en <= 1'b0; 																
								end
			    tx_vs_pose2:	begin
									gt_txcharisk <= 4'd0001;
									gt_txdata <= VS_POSE_DATA2;				
									state <= tx_unuse_data; 																
								end	
			    tx_data_start1:	begin
									gt_txcharisk <= 4'd0001;
									gt_txdata <= DATA_START1;				
									state <= tx_data_start2; 
                                    fifo_rd_en <= 1'b1;																										
								end
			    tx_data_start2:	begin
									gt_txcharisk <= 4'd0001;
									gt_txdata <= DATA_START2;				
									state <= tx_send_data; 
                                    fifo_rd_en <= 1'b1;									
								end
                //这个状态发送视频或者图像数据(来自fifo的数据)
			    tx_send_data:	begin
				                    if(!fifo_almost_empty)begin
									    gt_txcharisk <= 4'd0000;
									    gt_txdata <= fifo_dout;				
									    state <= tx_send_data; 
                                        fifo_rd_en <= 1'b1;
                                      
									end
                                   else if(fifo_almost_empty && !fifo_empty)begin
								    
 									    gt_txcharisk <= 4'd0000;
									    gt_txdata <= fifo_dout;				
									    state <= tx_send_data; 
                                        fifo_rd_en <= 1'b0;	                                  
                                    end	
                                    else begin
 									    gt_txcharisk <= 4'd0000;
									    gt_txdata <= fifo_dout;				
									    state <= tx_data_end1; 
                                        fifo_rd_en <= 1'b0;	
                                    end									
								end								
			    tx_data_end1:	begin
									gt_txcharisk <= 4'd0001;
									gt_txdata <= DATA_END1;				
									state <= tx_data_end2; 
                                    fifo_rd_en <= 1'b0;									
								end	
			    tx_data_end2:	begin
									gt_txcharisk <= 4'd0001;
									gt_txdata <= DATA_END2;				
									state <= tx_unuse_data; 
                                    fifo_rd_en <= 1'b0;									
								end												
				default : begin
								cnt_data <= cnt_data + 1;
								if(cnt_data == 255)begin//255就是UNUSE_DATA持续255个周期
									gt_txcharisk <= 4'd0001;
									gt_txdata <= UNUSE_DATA;				
									state <= tx_unuse_data; 								
								end
								else begin
									gt_txcharisk <= 4'd0000;
									gt_txdata <= 32'ha151a252;				
									state <= tx_unuse_data; 																
								end					
				          end
			endcase
        end 		
	end
 end	
	
  //产生fifo复位信号
  always@(posedge tx_clk or negedge tx_rst_n)begin
    if(!tx_rst_n)begin                          
	  cnt_fifo_rst <= 8'b0;
	  fifo_rst <= 1'b1;    //fifo_rst为高意味着fifo复位有效
	end
	else if(vs_pose)begin
	  cnt_fifo_rst <= 8'd0;
	  fifo_rst <= 1;  
	end
	else if(cnt_fifo_rst >= 100) begin  //100个tx_clk时间单位,为保证fifo完全复位成功
	  cnt_fifo_rst <= cnt_fifo_rst;
	  fifo_rst <= 0;
	end
	else begin
	  cnt_fifo_rst <= cnt_fifo_rst +1;  
	  fifo_rst <= fifo_rst;
	end
  end
	
  sfp_tx_2048x16 u_sfp_2048x16(
    .rst           (fifo_rst),
    .wr_clk        (clk_in),
    .rd_clk        (tx_clk),
    .din           (data_in_d0),
    .wr_en         (data_valid_in_d0),
    .rd_en         (fifo_rd_en),
    .dout          (fifo_dout),
    .full          (),
    .rd_data_count (rd_data_count),
    .empty         (fifo_empty),
    .almost_empty  (fifo_almost_empty)
  );
  ila_0 ila_0(
    .clk(clk_in),
    
    .probe0(vs_in),
    .probe1(data_valid_in),
    .probe2(data_in)
  );
  ila_1 ila_1(
    .clk(tx_clk),
    
    .probe0(gt_txcharisk),
    .probe1(gt_txdata)
  );
  
endmodule


更改之后的encoder模块

module sfp_encoder#(
//k28.5是32'bc,最低八位是bc,其余随便设,当K是0001时代表数据有K
//码,k码为0000时代表发送的是原始数据
    parameter VS_POSE_DATA1	=	32'h55a101bc,  
    parameter VS_POSE_DATA2	=	32'h55a102bc,		
    parameter DATA_START1	    =	32'h55a105bc,
    parameter DATA_START2	    =	32'h55a106bc,
    parameter DATA_END1		=	32'h55a107bc,     
    parameter DATA_END2		=	32'h55a108bc,     
    parameter UNUSE_DATA	    =	32'h55a109bc  
    )(
      input clk_in,
      input rst_n,
      input vs_in,   //输入场信号
      input data_valid_in,  //输入数据有效信号
      input [15:0] data_in, //输入数据
      
      input tx_clk,    //光口tx端时钟
      input tx_rst_n,  //光口tx端复位
      output [3:0] gt_txcharisk,   //光口tx端K码发送信号
      output [31:0] gt_txdata      //光口tx端发送数据
    );
    
    //parameter define
    parameter  tx_unuse_data  = 8'b0000_0001;
    parameter  tx_vs_pose1    = 8'b0000_0010; 	
    parameter  tx_vs_pose2    = 8'b0000_0100; 	
    parameter  tx_data_start1 = 8'b0000_1000; 	
    parameter  tx_data_start2 = 8'b0001_0000; 	
    parameter  tx_send_data   = 8'b0010_0000; 	
    parameter  tx_data_end1   = 8'b0100_0000; 	
    parameter  tx_data_end2   = 8'b1000_0000; 	
    
    reg        vs_in_d0;
    reg        vs_in_d1;
    reg        data_valid_in_d0;
    reg [15:0] data_in_d0;
    reg        vs_pose;
    reg [3:0]  gt_txcharisk;
    reg [31:0] gt_txdata;
    reg [7:0]  state;
    reg [7:0]  cnt_data;
    reg        fifo_rd_en;
    reg [7:0]  cnt_fifo_rst;
    reg        fifo_rst;
    reg        data_start;
    reg        data_start_d0;
    ///
    reg [9:0]  fifo_rd_cnt;
    
    //wire define
    wire [31:0] fifo_dout;
    wire        fifo_empty;
    wire        fifo_almost_empty;
    wire [9:0]  rd_data_count;  //fifo读计数
    wire [9:0]  fifo_threshold_value;
    
    //比如视频帧率为640*480(有效),那么fifo一行最好设置为小于640的数
    //这样便于读最后一行
    //640*16=320*32
    //异步fifo,一行写进去640个16bit数,读出是320个32bit数,此处读fifo阈值要略低于320,保证 
    //最后一行数据能达到阈值,触发读条件
    assign fifo_threshold_value = 10'd300;  
    
    //对输入的数据进行寄存
    always@(posedge clk_in or negedge rst_n)begin
      if(!rst_n)begin
        data_valid_in_d0 <= 1'b0;
        data_in_d0 <= 16'd0;
      end
      else begin
        data_valid_in_d0 <= data_valid_in;
        data_in_d0 <= data_in;
      end
    end 
    
    //对输入的信号进行跨时钟域处理
    always@(posedge tx_clk or negedge tx_rst_n)begin
      if(!tx_rst_n)begin
        vs_in_d0 <= 1'b0;
        vs_in_d1 <= 1'b0;
      end
      else begin
        vs_in_d0 <= vs_in;
        vs_in_d1 <= vs_in_d0;
      end
    end
    
    always@(posedge tx_clk or negedge tx_rst_n)begin
      if(!tx_rst_n)begin
        data_start_d0 <= 1'b0;
      end
      else begin
        data_start_d0 <= data_start;
      end
    end
    
    //产生场信号上升沿
    //输入的场信号的信息传送给vs_pose
    always@(posedge tx_clk or negedge tx_rst_n)begin
      if(!tx_rst_n)      
        vs_pose <= 1'b0;
      else if(vs_in_d0 && ~vs_in_d1)
        vs_pose <= 1'b1;  
      else 
        vs_pose <= 1'b0;                      
      end
    
    //数据发送开始信号
    always@(posedge tx_clk or negedge tx_rst_n)begin
      if(!tx_rst_n)
        data_start <= 1'b0;
      //将条件fifo_empty换成了fifo_rd_cnt == 319,更改的原因是我改了状态机发送数据状态的条 
      //件,换之前数据一帧只有一行的gt_txdata,
      //换之后一帧正常出480行数据
      else if((fifo_rd_cnt == 319) || vs_pose)//fifo空(无数据可读)或者vs上升沿    
//        else if(fifo_empty || vs_pose)
        data_start <= 1'b0;
      else if(rd_data_count >= fifo_threshold_value)//读完一行数据后
      //数据开始发送
        data_start <= 1'b1;
      else 
        data_start <= data_start;
    end
     
    //产生tx端K码发送信号和发送数据
always@(posedge tx_clk or negedge tx_rst_n)begin
	if(!tx_rst_n)begin
		gt_txcharisk <= 4'd0;
		gt_txdata <= 32'd0;				
		state <= tx_unuse_data;	
		fifo_rd_en <= 1'b0;
		cnt_data <= 8'd0;
		fifo_rd_cnt<=10'd0;
	end
	else begin
		if(vs_pose)begin
			gt_txcharisk <= 4'd0000;
			gt_txdata <= 32'ha151a252;	//只是随便设的一个标志,标志vs上升沿到来			
			state <= tx_vs_pose1;	
			cnt_data <= 0;
        end
        else if(data_start && ~data_start_d0)begin
            gt_txcharisk <= 4'd0000;
			gt_txdata <= 32'ha151a252;				
			state <= tx_data_start1;
			cnt_data <= 0; 
			fifo_rd_cnt<=10'd0;
        end
        else begin
            case(state)
			    tx_unuse_data:	begin
									cnt_data <= cnt_data + 1;
									fifo_rd_en <= 1'b0;
									if(cnt_data == 255)begin //255只是151a252持续的一个时 
                                                             //间,无具体意义
										gt_txcharisk <= 4'd0001;
										gt_txdata <= UNUSE_DATA;				
										state <= tx_unuse_data; 								
									end
									else begin
										gt_txcharisk <= 4'd0000;
										gt_txdata <= 32'ha151a252;				
										state <= tx_unuse_data; 																
									end							
								end
			    tx_vs_pose1:	begin
									gt_txcharisk <= 4'd0001;
									gt_txdata <= VS_POSE_DATA1;				
									state <= tx_vs_pose2;
									fifo_rd_en <= 1'b0; 																
								end
			    tx_vs_pose2:	begin
									gt_txcharisk <= 4'd0001;
									gt_txdata <= VS_POSE_DATA2;				
									state <= tx_unuse_data; 																
								end	
//			    tx_data_start1:	begin
//									gt_txcharisk <= 4'd0001;
//									gt_txdata <= DATA_START1;				
//									state <= tx_data_start2; 
//                                    fifo_rd_en <= 1'b1;																										
//								end
//			    tx_data_start2:	begin
//									gt_txcharisk <= 4'd0001;
//									gt_txdata <= DATA_START2;				
//									state <= tx_send_data; 
//                                    fifo_rd_en <= 1'b1;									
//								end
                //删去了start2,原因是由仿真图可知,start1时,fifo读使能就拉高,下一个读使能 
                //时钟(tx_clk)就要开始读数据,如果start1后面再有个strat2,那么开始读的数据
                //就会错乱,以至于一帧里面每一行后面的数据都会错乱(这个问题是师兄帮忙找的)
				tx_data_start1:	begin
									gt_txcharisk <= 4'd0001;
									gt_txdata <= DATA_START1;				
									state <= tx_send_data; 
                                    fifo_rd_en <= 1'b1;																										
								end				
			    tx_send_data:	begin
                                    //读到320个数,就结束一行读数
				                    if(fifo_rd_cnt == 10'd319)begin
									    gt_txcharisk <= 4'd0000;
									    gt_txdata <= fifo_dout;				
									    state <= tx_data_end1; 
                                        fifo_rd_en <= 1'b0; 
                                        fifo_rd_cnt <= 0;                                    
									end
                                    else begin
 									    gt_txcharisk <= 4'd0000;
									    gt_txdata <= fifo_dout;				
									    state <= tx_send_data;
									    fifo_rd_cnt <= fifo_rd_cnt + 1'b1;
                                        fifo_rd_en <= 1'b1;	
                                    end									
								end								
			    tx_data_end1:	begin
									gt_txcharisk <= 4'd0001;
									gt_txdata <= DATA_END1;				
									state <= tx_data_end2; 
                                    fifo_rd_en <= 1'b0;									
								end	
			    tx_data_end2:	begin
									gt_txcharisk <= 4'd0001;
									gt_txdata <= DATA_END2;				
									state <= tx_unuse_data; 
                                    fifo_rd_en <= 1'b0;									
								end												
				default:begin
								cnt_data <= cnt_data + 1;
								if(cnt_data == 255)begin
									gt_txcharisk <= 4'd0001;
									gt_txdata <= UNUSE_DATA;				
									state <= tx_unuse_data; 								
								end
								else begin
									gt_txcharisk <= 4'd0000;
									gt_txdata <= 32'ha151a252;				
									state <= tx_unuse_data; 																
								end					
				          end
			endcase
        end 		
	end
 end	
	
  //产生fifo复位信号
  always@(posedge tx_clk or negedge tx_rst_n)begin
    if(!tx_rst_n)begin                          
	  cnt_fifo_rst <= 8'b0;
	  fifo_rst <= 1'b1;    
	end
	else if(vs_pose)begin
	  cnt_fifo_rst <= 8'd0;
	  fifo_rst <= 1;  //vs下降沿时fifo清空开始准备读数据
	end
	else if(cnt_fifo_rst >= 15) begin  //之前fifo复位是100个时钟,太久了,导致数据从一开始 
                                       //就有一部分没有读出来,异步fifo复位大概十几个时钟就 
                                       //够了,我试了20,30,40,50,112,15,15是比较合
                                       //的 
	  cnt_fifo_rst <= cnt_fifo_rst;
	  fifo_rst <= 0;
	end
	else begin
	  cnt_fifo_rst <= cnt_fifo_rst +1;  
	  fifo_rst <= fifo_rst;
	end
  end
	
  sfp_tx_2048x16 u_sfp_2048x16(
    .rst           (fifo_rst),
    .wr_clk        (clk_in),
    .rd_clk        (tx_clk),
    .din           (data_in_d0),
    .wr_en         (data_valid_in_d0),
    .rd_en         (fifo_rd_en),
    .dout          (fifo_dout),
    .full          (),
    .rd_data_count (rd_data_count),
    .empty         (fifo_empty),
    .almost_empty  (fifo_almost_empty)
  );
  ila_0 ila_0(
    .clk(clk_in),
    
    .probe0(vs_in),
    .probe1(data_valid_in),
    .probe2(data_in)
  );
//  ila_1 ila_1(
//    .clk(tx_clk),
    
//    .probe0(gt_txcharisk),
//    .probe1(gt_txdata)
//  );
  
endmodule

encoder用到fifo是异步fifo

standard fifo读数据会延迟两拍

first word fall through会把数据先挂在传输口上,不会有延迟

彤告诉我说一般选第二种好些

光口字对齐模块(sfp_data_align)

将接收回来不对齐的数据转化为对齐的数据,然后再输出到光口解码模块

module sfp_data_align(
  input clk_in,   //rx端时钟
  input rst_n,
  input [31:0] rx_data_in,  //rx端未校正发送数据
  input [3:0] rx_charisk_in,//rx端K码未校正接收信号
  
  output [31:0] rx_data_out,//rx端校正后发送数据
  output [3:0] rx_charisk_out  //rx端K码校正后接收信号
    );
    
  (* keep="true" *)reg [31:0] rx_data_out;  
  (* keep="true" *)reg [3:0] rx_charisk_out;
  (* keep="true" *)reg [31:0] rx_data_in_d0;
  (* keep="true" *)reg [3:0] rx_charisk_in_d0;
  (* keep="true" *)reg [31:0] rx_data_in_d1;
  (* keep="true" *)reg [3:0] rx_charisk_in_d1;
  (* keep="true" *)reg [3:0] byte_ctrl;
  
  //main code//
  //对输入数据进行打拍
  always@(posedge clk_in or negedge rst_n)begin
    if(!rst_n)begin
      rx_data_in_d0 <= 1'b0;
      rx_charisk_in_d0 <= 1'b0;
      rx_data_in_d1 <= 1'b0;
      rx_charisk_in_d1 <= 1'b0;
    end
    else begin
      rx_data_in_d0 <= rx_data_in;
      rx_data_in_d1 <= rx_data_in_d0;
      rx_charisk_in_d0 <= rx_charisk_in;
      rx_charisk_in_d1 <= rx_charisk_in_d0;
    end
  end  
    
  //对rx端K码未校正接收信号进行寄存
  //把rx_charisk_in寄存给byte_ctrl
  always@(posedge clk_in or negedge rst_n)begin
    if(!rst_n)begin
      byte_ctrl <= 4'd0;
    end
    else begin
      if(rx_charisk_in_d0 > 0)
        byte_ctrl <= rx_charisk_in_d0;
      else 
        byte_ctrl <= byte_ctrl;
    end
  end 
    
  //对rx端的信号进行对齐
  always@(posedge clk_in or negedge rst_n)begin
    if(!rst_n)begin
      rx_data_out <= 32'd0;
      rx_charisk_out <= 4'd0;
    end
    else begin
    //实际过程中,发送的32位数据可能会出现16位数据的移位,若0001变为
    //0100,说明存在数据移位,需要重新组合
      if(byte_ctrl == 4'b0100)begin
        rx_data_out <= {rx_data_in_d0[15:0],rx_data_in_d1[31:16]};
        rx_charisk_out <= {rx_charisk_in_d0[1:0],rx_charisk_in_d1[3:2]};
      end
      else begin
        rx_data_out <= rx_data_in_d0;
        rx_charisk_out <= rx_charisk_in_d0;
      end
    end
  end 

  ila_4 ila_4(
    .clk(clk_in),
    
    .probe0(rx_charisk_in),
    .probe1(rx_data_out),
    .probe2(rx_charisk_out)
  );

endmodule

光口解码模块(sfp_decode)

将对齐后的数据按照8b10b的解码格式转化为32bit的像素数据,再通过fifo将32bit的数据转化为16bit

module sfp_decode#(
  parameter VS_POSE_DATA1	=	32'h55a101bc, 
  parameter VS_POSE_DATA2	=	32'h55a102bc,	
  parameter DATA_START1	    =	32'h55a105bc,
  parameter DATA_START2  	=	32'h55a106bc,
  parameter DATA_END1     	=	32'h55a107bc,
  parameter DATA_END2	    =	32'h55a108bc, 
  parameter UNUSE_DATA   	=	32'h55a109bc  
  )(
    input  clk_in,          //200M
    input  rst_n,
    output vs_out,          //场输出信号
    output data_valid_out,  //数据输出有效信号
    output [15:0] data_out, //数据输出信号
    
    input        rx_clk,
    input        rx_rst_n,
    input [3:0]  rx_charisk_align,  //rx端K码校正后接收信号
    input [31:0] rx_data_align      //rx端校正后发送数据
    );
    
    reg        sfp_line_end_t;
    reg        sfp_line_end_t1;
    //reg        fifo_rd_en;
    reg        data_valid_out;
    reg [7:0]  data_end_en_d;
    reg        sfp_line_end;
    reg [7:0]  cnt_vs;
    reg        vs_out;
    reg        data_start_en;
    reg        data_end_en;
    reg        fifo_wren;
    reg [31:0] fifo_datain;
    reg [31:0] fifo_rd_en;
    reg [3:0]  data_valid_out;
    reg [31:0] rx_data_align_d0;
    reg [3:0]	rx_charisk_align_d0;
    reg [31:0]	rx_data_align_d1;  
    reg [3:0]	rx_charisk_align_d1;
    reg [3:0]	data_start_en_d;    
    
    wire [15:0] data_out;
    wire        fifo_almost_empty;
    
    main code/
    //对输入的数据进行打拍
    always@(posedge rx_clk or negedge rx_rst_n)begin
      if(!rx_rst_n)begin
        rx_charisk_align_d0 <= 1'b0;
        rx_data_align_d0 <= 16'd0;
        rx_charisk_align_d1 <= 1'b0;
        rx_data_align_d1 <= 16'd0;
      end
      else begin
        rx_charisk_align_d0 <= rx_charisk_align;
        rx_charisk_align_d1 <= rx_charisk_align_d0;
        rx_data_align_d0 <= rx_data_align;
        rx_data_align_d1 <= rx_data_align_d0;
      end
    end
    
    //对数据接收开始和结束信号进行移位
    always@(posedge rx_clk or negedge rx_rst_n)begin
      if(!rx_rst_n)begin
        data_end_en_d <= 8'd0;
        data_start_en_d <= 4'd0;
      end
      else begin
      //这里是在打拍子,第一个信号打了7拍,第二个信号打了3拍,这俩信号实际有效位数就是一位
      //仔细观察,这样的信号被这样处理都是在打拍
        data_end_en_d <= {data_end_en_d[6:0],data_end_en};
        data_start_en_d <= {data_start_en_d[2:0],data_start_en};
      end
    end
    
    //产生一行数据接收结束信号
    always@(posedge rx_clk or negedge rx_rst_n)begin
      if(!rx_rst_n)
        sfp_line_end <= 1'd0;
      else if(data_end_en_d > 0)
        sfp_line_end <= 1'd1;
      else 
        sfp_line_end <= 1'd0;
    end
    
    //产生场输出信号
    always@(posedge rx_clk or negedge rx_rst_n)begin
      if(!rx_rst_n)begin
        vs_out <= 1'b0;
        cnt_vs <= 8'h0;
      end
      //当rx端K码校正后接收信号为1&&连续两次检测到场上升沿的特殊字符
      else if((rx_charisk_align == 4'd1)&&(rx_data_align == VS_POSE_DATA2)
             &&((rx_data_align_d0 == VS_POSE_DATA1)))begin
        vs_out <= 1'b1;  //拉高vs信号
        cnt_vs <= 8'h0;
      end
      else if(cnt_vs >= 100)begin  //场信号拉高持续一段时间
        vs_out <= 1'b0;
        cnt_vs <= cnt_vs +1;
      end
      else begin
        vs_out <= vs_out;
        cnt_vs <= cnt_vs + 1;
      end
    end
    
    //产生数据接收开始信号
    always@(posedge rx_clk or negedge rx_rst_n)begin
      if(!rx_rst_n)begin
        data_start_en <= 1'b0;
      end
      //当rx段K码校正后为1&&连续两次检测到数据发送开始的特殊字符
      else if((rx_charisk_align == 4'd1)&&(rx_data_align == DATA_START2)
              &&((rx_data_align_d0 == DATA_START1)))begin
        data_start_en <= 1'b1;  //将数据接收信号拉高
      end
      else begin
        data_start_en <= 1'b0;  //下一个时钟周期(包括其他情况)信号被拉低
      end
    end
    
    //产生数据接收结束信号
    always@(posedge rx_clk or negedge rx_rst_n)begin
      if(!rx_rst_n)begin
        data_end_en <= 1'b0;
      end
      else if((rx_charisk_align == 4'd1)&&(rx_data_align == DATA_END2)
              &&((rx_data_align_d0 == DATA_END1)))begin    
        data_end_en <= 1'b1; 
      end 
      else begin
        data_end_en <= 1'b0;
      end
    end
    
    //产生fifo写使能
    always@(posedge rx_clk or negedge rx_rst_n)begin
      if(!rx_rst_n)begin
        fifo_wren <= 1'b0;
      end
      else if(data_start_en_d[1])begin  //这是打了一拍
        fifo_wren <= 1'b1;
      end
      //数据接收结束信号拉高后,写使能拉低
      else if(data_end_en)begin
        fifo_wren <= 1'b0;
      end
      else begin
        fifo_wren <= fifo_wren;      
      end
    end
    
    //产生fifo写数据
    always@(posedge rx_clk or negedge rx_rst_n)begin
      if(!rx_rst_n)begin
        fifo_datain <= 32'b0;
      end
      else begin
        fifo_datain <= rx_data_align_d1; //rx端校正后发送的数据
      end
    end
    
    //读数据/
    //对一行数据接收结束信号进行时钟域切换
    //sfp_line_end,该信号是在125MHz时钟下产生的,所以要打拍
    always@(posedge clk_in or negedge rst_n)begin
      if(!rst_n)begin
        sfp_line_end_t <= 1'b0;
        sfp_line_end_t1 <= 1'b0;
      end
      else begin
        sfp_line_end_t <= sfp_line_end;
        sfp_line_end_t1 <= sfp_line_end_t;
      end
    end
    
   //产生fifo读使能
     //光口的rx端将一行数据接收完后立即拉高读使能,当fifo将要
     //读空时立即拉低读使能
   always@(posedge clk_in or negedge rst_n)begin
     if(!rst_n)begin
       fifo_rd_en <= 1'b0;
     end
     else begin
       if(sfp_line_end_t1)begin
         fifo_rd_en <= 1'b1;
       end
       else if(fifo_almost_empty)begin
         fifo_rd_en <= 1'b0;
       end
       else begin
         fifo_rd_en <= fifo_rd_en;
       end
     end
   end 
    
   //产生数据输出有效信号
   always@(posedge clk_in or negedge rst_n)begin
     if(!rst_n)
       data_valid_out <= 1'b0;
     else 
       data_valid_out <= fifo_rd_en;
   end
    
   sfp_rx_32x1024 u_sfp_rx_32x1024(
     .rst(vs_out),    //一个新的vs来的时候,hs的有效数据并不是同步的,有个后沿还是什么东西
                      //所以刚好在一个新的vs开始的时候对fifo进行复位,以便它读新一行的数据
     .wr_clk(rx_clk),
     .rd_clk(clk_in),
     .din(fifo_datain),
     .dout(data_out),
     .wr_en(fifo_wren),
     .rd_en(fifo_rd_en),
     .full(),
     .empty(),
     .almost_empty(fifo_almost_empty)
   ); 

   ila_2 ila_2(
     .clk(clk_in),
     
     .probe0(vs_out),
     .probe1(data_valid_out),
     .probe2(data_out)
   );
   
   ila_3 ial_3(
     .clk(rx_clk),
     
     .probe0(rx_charisk_align),
     .probe1(rx_data_align)
   );
endmodule



版权声明:本文为koki227原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。