1、概述
前文对官方示例工程进行了仿真和上板测试,初步了解了如何使用该IP收发数据,但是官方的示例工程写的比较繁琐,如果想要使用多通道收发器该怎么办?多次例化示例工程?显然无法达到效果,因为示例工程中包含多个收发器的共享逻辑,多次例化后在综合时会报错。
整理前的IP模块RTL视图如下所示,包括GT_COMMON等共享逻辑,不方便扩展收发器通道。
整理后的模块如下图所示,共享逻辑部分位于收发器的顶层模块中,需要例化多个高速收发器时,只需要多次例化通道部分即可,多个高速收发器共用同一个QPLL。
因此后文先整理该部分内容,将一些需要用到的信号引出,不会的输入信号接固定电平,输出信号悬空。然后本文调用两个收发器,然后对工程仿真和上板测试。这种整理思路适用于后续所有高速收发器相关IP,通过整理官方示例工程得到用户自己的工程。
2、整理示例工程
首先整理每个通道的独立逻辑,然后再整理共享逻辑部分。如下图所示,单个通道包含以下三部分,首先是将IP输出的参考时钟信号通过BUFG生成用户时钟。
然后例化复位同步模块,将两个异步复位通道到相应时钟域之下,作为IP的复位信号,最后例化aurora_8b10b IP。
aurora_8b10b IP的例化如下图所示,把必须使用和可能会使用到的信号引到上层模块,不会使用的输出信号悬空处理,不会使用的输入接无效的固定电平。
Axi_steram、状态信号、部分时钟和复位信号、回环控制等信号必须引出,QPLL相关信号需要引到上层连接共享逻辑部分,而DRP信号后续开发可能会用到,因此也引出到上层模块。一般不会使用掉电模式,掉电信号接地即可。
高速收发器的顶层模块如下所示,首先通过IBUFDS_GTE2把差分时钟转换为单端时钟信号,之后例化GT_COMMON模块,将各个收发器的QPLL相关信号与该模块相连接。
整理后的高速收发器顶层RTL视图如下所示,例化了两个高速收发器模块,两个高速收发器共用同一个参考时钟信号和QPLL共享逻辑。需要注意只有一个高速收发器才能复位QPLL,其他收发器的QPLL复位信号悬空。
经过上述处理,完成了封装aurora_8b10b IP,两个收发器的顶层模块端口信号如下图所示。用户只需要通过axi_stream接口收发数据即可,一些状态信号可以判断高速收发器的工作状态,便于调试。
为了测试整理后的模块,需要一个用于生成测试信号的模块,相关代码如下所示,每经过一段时间就会生成一组axi_stream数据用于测试,可以通过参数KEEP控制最后一个数据的有效字节。
//时钟计数器,初始值为0,之后对时钟计数;
always@(posedge clk)begin
if(rst)begin//初始值为0;
cnt <= 'd0;
end
else if(m_axi_tx_last && m_axi_tx_valid)begin
cnt <= 'd0;//发送完一帧数据清零。
end
else if((~m_axi_tx_valid) || (m_axi_tx_valid && m_axi_tx_ready))begin
cnt <= cnt + 'd1;//计数空闲时间或者发送数据的时钟。
end
end
//发送数据有效指示信号,初始值为0;
always@(posedge clk)begin
if(rst)begin//初始值为0;
m_axi_tx_valid <= 1'b0;
end
else if(m_axi_tx_last && m_axi_tx_ready && m_axi_tx_valid)begin//发送最后一个后拉低;
m_axi_tx_valid <= 1'b0;
end
else if(cnt == 2)begin//当计数器计数到127时发送一帧数据,此时拉高;
m_axi_tx_valid <= 1'b1;
end
end
//生成测试数据;
always@(posedge clk)begin
if(rst)begin//初始值为0;
m_axi_tx_data <= 32'd0;
end
else if(m_axi_tx_last && m_axi_tx_valid)begin//当发送完最后一个数据时清零;
m_axi_tx_data <= 32'd0;
end
else if(m_axi_tx_ready && m_axi_tx_valid)begin
m_axi_tx_data <= {{cnt[5:0],2'd0},{{cnt[5:0],2'd0}+8'd1},{{cnt[5:0],2'd0}+8'd2},{{cnt[5:0],2'd0}+8'd3}};
end
end
//一帧数据最后一个指示信号,初始值为0;
always@(posedge clk)begin
if(rst)begin//初始值为0;
m_axi_tx_last <= 1'b0;
m_axi_tx_keep <= 4'b1111;
end
else if(m_axi_tx_last && m_axi_tx_ready)begin
m_axi_tx_last <= 1'b0;
m_axi_tx_keep <= 4'b1111;//最后一个数据发送完成之后回到设置值。
end
else if(m_axi_tx_valid && m_axi_tx_ready && (cnt == DATA_LEN))begin//当发送最后一个数据时拉高,其余时间保持不变;
m_axi_tx_last <= 1'b1;
m_axi_tx_keep <= KEEP;//最后一个数据输出对应掩码信号。
end
end
最后顶层模块的RTL视图如下所示,两个数据生成模块分别给两个高速收发器生成数据。
3、工程仿真
对应的仿真激励文件如下所示:
assign gt_0_rx_p = gt_1_tx_p;
assign gt_0_rx_n = gt_1_tx_n;
assign gt_1_rx_p = gt_0_tx_p;
assign gt_1_rx_n = gt_0_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_0_rx_p ( gt_0_rx_p ),//gt接收差分数据线;
.gt_0_rx_n ( gt_0_rx_n ),//gt接收差分数据线;
.gt_0_tx_p ( gt_0_tx_p ),//gt发送差分数据线;
.gt_0_tx_n ( gt_0_tx_n ),//gt发送差分数据线;
.gt_1_rx_p ( gt_1_rx_p ),//gt接收差分数据线;
.gt_1_rx_n ( gt_1_rx_n ),//gt接收差分数据线;
.gt_1_tx_p ( gt_1_tx_p ),//gt发送差分数据线;
.gt_1_tx_n ( gt_1_tx_n ),//gt发送差分数据线;
.dislabe ( dislabe ) //光电转换模块失能信号;
);
//生成周期为CYCLE数值的系统时钟;
initial begin
clk = 0;
forever #(CYCLE/2) clk = ~clk;
end
//生成165.25MHz的差分时钟信号;
initial begin
gt_refclk_p = 1;
forever #3.2 gt_refclk_p = ~gt_refclk_p;
end
initial begin
gt_refclk_n = 0;
forever #3.2 gt_refclk_n = ~gt_refclk_n;
end
//生成复位信号;
initial begin
rst_n = 1;
#2;
rst_n = 0;//开始时复位10个时钟;
#(RST_TIME*CYCLE);
rst_n = 1;
end
仿真需要运行大约40us后IP才能初始化完成,才能正常收发数据,如下所示。
两个数据生成模块发送的数据长度是不一样的,通过下图进行设置,可以对多种可能性进行仿真测试。
仿真结果如下所示,通道0和通道1相互发送数据,其中通道1接收的数据与通道0发送的数据一致,通道1发送的数据也与通道0接收的数据一致,由此证明该IP仿真正确。
4、上板测试
上述模块仿真完成之后,分配工程的端口信号,打开ILA模块的注释,然后综合工程。开发板有两个光口,对应的原理图如下所示。
开发板使用光纤短接两个光口,如下图所示。
工程综合完成之后,下载到开发板,使用ILA抓取相关信号。如下图所示lane_up和channel_up均为高电平,表示IP已经初始化完成的。
下图是高速收发器0的相关信号,红色框中的信号是发送通道需要发送的数据,总共发送了4个数据,分别为32’h0000、32’h0c0d0e0f、32’h10111213、16’h1415。
下图是高速收发器1接收数据的时序,接收一帧4个数据,分别为32’h0000、32’h0c0d0e0f、32’h10111213、16’h1415,与高速收发器0发送的数据一样。
下图是高速收发器1发送数据的时序,一帧发送5个数据,分别为32’h0000、32’h0c0d0e0f、32’h10111213、32’h114151617、8’h18。
下图是高速收发器0接收数据的时序,接收的一帧长度为5的数据,分别为32’h0000、32’h0c0d0e0f、32’h10111213、32’h114151617、8’h18,与高速收发器1发送的数据保持一致。
上述ILA抓取的时序证明高速收发器的接收功能和发送功能都没有问题,本文封装的双通道收发器功能正常。
本文讲解到此结束,后续只要涉及到高速收发器相关IP都可以采用类似的方式进行封装,便于扩展高速收发器。
如果对文章内容理解有疑惑或者对代码不理解,可以在评论区或者后台留言,看到后均会回复!
如果本文对您有帮助,还请多多点赞👍、评论💬和收藏⭐!您的支持是我更新的最大动力!将持续更新工程!