前文整理了GTX IP,完成了自定义PHY协议的收发模块设计,本文将通过光纤回环,对这些模块上板测试,首先需要编写一个用于生成测试数据的用户模块。
1、测试数据生成模块
本模块用于生成自定义PHY协议的测试数据,通过axi_stream接口向PHY发送模块生成测试数据。
对应代码如下所示,通过计数器发送每字节递增的数据,代码比较简单。
always@(posedge clk)begin
if(rst)begin//初始值为0;
cnt <= 6'd0;
end
else if(axi_s_ready)begin
cnt <= cnt + 1;
end
end
//生成输出数据;
always@(posedge clk)begin
if(rst)begin//初始值为0;
axi_s_data <= 0;
end
else begin
axi_s_data <= {{cnt[5:0],2'd0},{cnt[5:0],2'd1},{cnt[5:0],2'd2},{cnt[5:0],2'd3}};
end
end
//生成数据掩码信号,高电平表示对应数据有效;
always@(posedge clk)begin
if(rst)begin//初始值为0;
axi_s_keep <= 4'hf;
end
else if(cnt == DATA_NUM - 1)begin
axi_s_keep <= KEEP;
end
else begin
axi_s_keep <= 4'b1111;
end
end
//生成一帧数据最后一个数据的指示信号;
always@(posedge clk)begin
if(rst)begin//初始值为0;
axi_s_last <= 1'b0;
end
else if((cnt == DATA_NUM - 1) && axi_s_ready)begin
axi_s_last <= 1'b1;
end
else begin
axi_s_last <= 1'b0;
end
end
//生成数据有效指示信号,高电平有效。
always@(posedge clk)begin
if(rst)begin//初始值为0;
axi_s_valid <= 1'b0;
end
else if(axi_s_last)begin
axi_s_valid <= 1'b0;
end
else if((cnt == 0) && axi_s_ready)begin
axi_s_valid <= 1'b1;
end
end
endmodule
该模块对应的RTL结构如下所示,输出数据直接与自定义的PHY芯片连接。
2、顶层模块生成
本次自定义PHY包含两个高手收发器通道,因此需要将前文的GT收发器模块和PHY顶层模块,测试数据模块例化。
最后顶层模块的RTL结构如下所示,包含两个自定义的PHY模块、测试数据生成模块。
在例化的时候需要设置收发器的加重参数和幅度参数,具体参数可以通过眼图扫描获取,前文讲解眼图时已经讲解过具体扫描方法。
此处直接使用眼图扫描的参数设置两个收发器通道的gt0_txpostcursor_in、gt0_txprecursor_in、gt0_txdiffctrl_in参数。
仿真激励文件如下所示,但是由于前文已经对各个模块仿真过了,此处就不再留仿真细节了,直接进行上板测试,读者可自行仿真。
//--###############################################################################################
//--#
//--# File Name : tb_top
//--# Designer : 数字站
//--# Tool : Vivado 2021.1
//--# Design Date : 20
//--# Description : top的testbench文件
//--# Version : 0.0
//--# Coding scheme : GBK(If the Chinese comment of the file is garbled, please do not save it and check whether the file is opened in GBK encoding mode)
//--#
//--###############################################################################################
`timescale 1ns / 1ps
module tp_top ();
parameter CYCLE = 10 ;//系统时钟周期,单位ns,默认10ns;
parameter RST_TIME = 10 ;//系统复位持续时间,默认10个系统时钟周期;
parameter STOP_TIME = 1000 ;//仿真运行时间,复位完成后运行1000个系统时钟后停止;
reg clk ;//系统时钟,默认100MHz;
reg rst_n ;//系统复位,默认低电平有效;
reg gt_refclk_p ;//GT差分参考时钟信号;
wire gt_refclk_n ;//GT差分参考时钟信号;
wire [1 : 0] gt_tx_p ;//gt发送差分信号;
wire [1 : 0] gt_tx_n ;//gt发送差分信号;
wire [1 : 0] gt_rx_p ;//GT接收差分信号;
wire [1 : 0] gt_rx_n ;//GT接收差分信号;
wire [1 : 0] sfp_disable ;//光纤失能信号,高电平有效;
assign gt_rx_p = gt_tx_p;
assign gt_rx_n = gt_tx_n;
top u_top(
.clk ( clk ),//系统时钟信号;
.rst_n ( rst_n ),//系统复位信号,低电平有效;
.gt_refclk_p ( gt_refclk_p ),//GT差分参考时钟信号;
.gt_refclk_n ( gt_refclk_n ),//GT差分参考时钟信号;
.gt_tx_p ( gt_tx_p ),//gt发送差分信号;
.gt_tx_n ( gt_tx_n ),//gt发送差分信号;
.gt_rx_p ( gt_rx_p ),//GT接收差分信号;
.gt_rx_n ( gt_rx_n ),//GT接收差分信号;
.sfp_disable ( sfp_disable ) //光纤失能信号,高电平有效;
);
//生成周期为CYCLE数值的系统时钟;
initial begin
clk = 0;
forever #(CYCLE/2) clk = ~clk;
end
//生成差分时钟信号;
initial begin
gt_refclk_p = 1;
forever #3.2 gt_refclk_p = ~gt_refclk_p;
end
assign gt_refclk_n = ~gt_refclk_p;
//生成复位信号;
initial begin
rst_n = 1;
#2;
rst_n = 0;//开始时复位10个时钟;
#(RST_TIME*CYCLE);
rst_n = 1;
#(STOP_TIME*CYCLE);
$stop;//停止仿真;
end
endmodule
3、上板测试
对上述工程进行综合,然后光纤的引脚,本次使用双通道,可以直接使用光纤将数据回环,通过ILA抓取回环前后的数据,确定自定义PHY协议的设计是否正确。
注意差分引脚只需要分配引脚P就行了,因为差分对是绑定关系,分配一个引脚之后,另一个引脚的位置就已经确定了。
至于vivado怎么知道这两个信号就是差分信号?
这是因为差分信号输入FPGA后,都需要经过IBUFDS或者IBUFGDS这种差分转单端器件,这个器件的两个输入信号当然是差分信号,猜测vivado就是通过IBUFDS的输入或者OBUFDS的输出确定差分信号的。
可以直接在需要通过ILA观测的信号前面加上下面的约束,之后可以在vivaodo中将这些信号添加到ILA进行观测。
当工程综合完毕之后,进行如下操作,点击“SYNTHE SIS”下的“Open Synthesized Design”,然后点击“Set Up Debug”,进入界面后点击下一步即可。
然后进入信号添加界面,如下所示,这种方式添加的信号只能是变化的信号,如果该信号位固定电平,则该界面会报错。如下图所示,数据掩码的最高位始终为高电平,此时就会报错,需要删除这一位信号,后面再分析时,默认其为高电平即可。
然后需要设置ILA的数据深度,深度可以尽量设置大一点,方便一次抓取更多数据,另外2处可以启用其余的模式,如果需要对某个条件的信号需要特别关注,则可以勾选,本文随机抓取发送和接收数据即可,因此可以不勾选这两个选项。
最后时汇总界面,如下图所示,点击完成即可。
之后生成比特流文件,然后下载到开发板中进行测试,用光纤连接FPGA的两个光口,如下所示。
注意这种方式连接,高速收发器0的发送通道与高速收发器1的接收通道连接,高速收发器0的接收通道连接高速收发器1的发送通道,在后续波形查看时需要注意。
为加快测试速度,模块0与模块1发送数据时,一帧数据的最后一个数据掩码设置成不同数值,如下图所示。
将比特流下载到FPGA中,然后抓取高速收发器0的发送时序。如下图所示,用户发送一帧数据的开始位置。注意用户数据w_axi_s0_data是大端对齐,先发高字节数据,而GTX IP的输入数据gt_0_tx_data是小端对齐,先发低字节数据。
在一帧数据开始前会先发送逗号和起始位,之后跟数据。
下图是一帧数据的结尾部分,用户发送最后一个数据只有高三字节有效,及最后一个有效数据为8’hc6。组帧完成的信号在发送停止位前,最后一个数据为8’hc6,由此证明自定义PHY发送模块功能正常。
下图时高速收发器1接收通道,接收一帧数据的开始时序,首先检测到逗号和起始位组成的帧头,然后向用户输出第一个数据为32’h00010203,与图11用户发送的第一个数据一致。
下图是接收一帧数据末尾时序,停止位8’hfd之前最后一个数据为8’hc6,与图12发送的数据一致,证明GTX IP的发送和接收均正确。
其次,输出给用户的最后一个数据高三位有效(最高位在添加ILA时,表示为高电平,被删除了,没有在下图体现),最后一个数据为8’hc6,与图12用户发送的最后一个数据保持一致,证明PHY收发和GTX收发均没有问题。
前面验证了用户发送最后一个数据高三位有效时,接收和发送的时序正确性,后续还需要对另外三种情况进行验证,发送端的起始位只能在固定位置,因此起始位的位置就不进行验证了,前文也仿真了所有情况,不再赘述了。
下图是高速收发器1的发送通道,发送一帧数据的开始时序。与图11基本一致。
下图是发送一帧数据的结尾时序,由图可知,用户发送的最后一个数据所有位均有效,最后一个数据为8’hc7。
然后在组帧结束时,停止位8’hfd之前的最后一个数据是8’hc7,证明这种情况下,自定义PHY的发送模块代码也没有问题。
下图是高速收发器0接收通道,开始接收数据的起始时序,发送给用户的第一个数据为32’h00010203,与图15用户发送的数据一致。
下图是接收一帧数据的结束时序,接收数据的停止位8’hfd之前的最后一个数据为8’hc7,发送给用户的最后一个数据为8’hc7,与图16用户发送的最后一个数据保持一致。由此证明这种情况下,自定义PH收发模块和GTX IP收发功能均正常。
由两个收发器测试完两种结尾格式,之后更改顶层模块生成一帧测试数据,最后一个数据的有效字节,分别设置为一个字节和两个字节有效,如下图所示。
下图是高速收发器1的发送通道,发送一帧数据的结束时序(因为开始时序与图11和图15基本一致,后续不再抓取)。用户发送最后一个的高两字节有效,最后一个数据为8’hc5。
组帧完成之后,在停止位8’hfd之前,最后一个数据为8’hc5。
下图是高速收发器0接收一帧数据的开始时序,检测到帧头数据之后,向用户数据第一个数据为32’h00010203,与发送数据一致。
下图是接收一帧数据的结束位置,接收停止位8’hfd之前最后一字节数据为8’hc5,之后向用户输出的最后一个数据也为8’hc5。证明在这种情况下自定义PHY收发模块、GTX收发均正确。
下图开始验证最后一种情况,首先是高速收发器0的发送通道,发送一帧数据的结束时序,用户最后一个数据只有最高字节有效,最后一字节数据为8’hc4。
组帧后停止位8’hfd前的最后一个数据也是8’hc4,因此证明发送数据没有问题。
下图是高速收发器1的接收通道,接收一帧数据的起始时序,由于起始位的位置一致,所以每次抓取接收通道的起始时序基本也是一致的,不再赘述含义。
下图是接收一帧数据结尾时序,检测到停止位8’hfd前的最后一个字节为8’hc4,最终输出给用户的最后一个字节数据为8’hc4。
注意下图中框中输出给用户的掩码信号低3位为0,但是最高位固定为高电平,没有被添加到ILA中,因此最后一个字节数据是8’hc4,与图23发送的最后一个数据保持一致。
4、总结
本文算是对整理的GTX IP、自定义的PHY收发模块进行了验证,结合前文的接收数据仿真,可以认为收发数据模块没有问题了。
当自己写过这种自定义PHY协议,并且在板子上验证之后,会对GTX IP的认识清晰很多。
如果对文章内容理解有疑惑或者对代码不理解,可以在评论区或者后台留言,看到后均会回复!
如果本文对您有帮助,还请多多点赞👍、评论💬和收藏⭐!您的支持是我更新的最大动力!将持续更新工程!