前文分析了xilinx官方提供的GTX IP示例工程,该代码的结构比较混乱,本文将该代码进行梳理,形成一个便于使用的模块,后续如果要使用多通道的收发器,多次例化某个模块就行了。
下图是官方例程中GTX IP相关模块的RTL视图,根据前文时钟资源的讲解,同一bank的四个通道共用一个QPLL,因此需要将GT_COMMON放到上层模块,同时IBUFGDS_GTE2也应该放到上层模块中。
之后如果需要使用多个通道,只需要将剩余的部分多次例化即可,多个通道共用一个QPLL。
1、整理gt_channel模块
首先整理每个通道相关模块,包含复位同步模块、MMCM模块、GTX IP,需要把GTX IP一些不常用的输入端口置为固定电平,输出端口悬空,其余端口引出到上层模块中。
首先把GTX IP输出的复位信号和复位同步模块输出的复位信号合成为GT_COMMON的复位信号输出,将发送通道和接收通道的用户时钟输出,对应代码如下所示。
assign o_qpllreset = w_commonreset || w_gt_qpll_reset;//生成QPLL复位信号;
assign o_rx_clk = gt_rxusrclk2;//将用户时钟信号引出;
assign o_tx_clk = gt_txusrclk2;//将用户时钟信号引出;
之后整理时钟生成模块,GTX IP输出的时钟信号TXOUTCLK经过内部的MMCM生成TXUSRCLK等四个时钟信号。由于IBUFGDS_GTE2放到上层模块中,因此可以去除对应的端口和内部信号。
//例化生成用户时钟的MMCM模块。
gtwizard_0_GT_USRCLK_SOURCE u_gt_usrclk_source (
.GT0_TXUSRCLK_OUT ( gt_txusrclk ),//;
.GT0_TXUSRCLK2_OUT ( gt_txusrclk2 ),//;
.GT0_TXOUTCLK_IN ( gt_txoutclk ),//;
.GT0_TXCLK_LOCK_OUT ( gt_txmmcm_lock ),//;
.GT0_TX_MMCM_RESET_IN ( gt_txmmcm_reset ),//;
.GT0_RXUSRCLK_OUT ( gt_rxusrclk ),//;
.GT0_RXUSRCLK2_OUT ( gt_rxusrclk2 ),//;
.GT0_RXCLK_LOCK_OUT ( gt_rxmmcm_lock ),//;
.GT0_RX_MMCM_RESET_IN ( gt_rxmmcm_reset ) //;
);
然后例化复位同步模块,该模块将外部的异步复位信号同步到系统时钟域下,进而可以作为QPLL复位信号之一。
//例化GT_COMMON复位模块。
gtwizard_0_common_reset u_common_reset (
.STABLE_CLOCK ( sys_clk ),//Stable Clock, either a stable clock from the PCB
.SOFT_RESET ( i_tx_rst ),//User Reset, can be pulled any time
.COMMON_RESET ( w_commonreset ) //Reset QPLL
);
之后例化GTX IP模块,该模块信号比较多,将DRP、复位完成指示信号、回环模式控制、加重、极性控制、字节对齐指示信号、QPLL和MMCM相关信号引出。眼图测试、txoutclkpcs这些不常用引脚直接置为固定电平或者悬空即可,整理后的代码如下所示。
//例化GTX IP。
gtwizard_0 u_gtwizard_0 (
.sysclk_in ( sys_clk ),//系统时钟输入,与DRP时钟频率保持一致。
.soft_reset_tx_in ( i_tx_rst ),//发送端口的软复位信号。
.soft_reset_rx_in ( i_rx_rst ),//接收端口的软复位信号。
.dont_reset_on_data_error_in ( 0 ),//输入数据错误指示信号,高电平有效。
.gt0_tx_fsm_reset_done_out ( o_tx_done ),//发送数据的状态机复位完成指示信号,高电平有效;
.gt0_rx_fsm_reset_done_out ( ),//接收数据的状态机复位完成指示信号,高电平有效;
.gt0_data_valid_in ( 1 ),//输入GT数据有效指示信号。
.gt0_tx_mmcm_lock_in ( gt_txmmcm_lock ),//用户输出数据时钟信号锁定指示信号。
.gt0_tx_mmcm_reset_out ( gt_txmmcm_reset ),//用户输出数据时钟信号复位信号。
.gt0_rx_mmcm_lock_in ( gt_rxmmcm_lock ),//用户输入数据时钟信号锁定指示信号。
.gt0_rx_mmcm_reset_out ( gt_rxmmcm_reset ),//用户输入数据时钟信号复位信号。
.gt0_drpaddr_in ( i_drpaddr ),//input wire [8:0] gt0_drpaddr_in
.gt0_drpclk_in ( i_drpclk ),//input wire gt0_drpclk_in
.gt0_drpdi_in ( i_drpdi ),//input wire [15:0] gt0_drpdi_in
.gt0_drpdo_out ( o_drpdo ),//output wire [15:0] gt0_drpdo_out
.gt0_drpen_in ( i_drpen ),//input wire gt0_drpen_in
.gt0_drprdy_out ( o_drprdy ),//output wire gt0_drprdy_out
.gt0_drpwe_in ( i_drpwe ),//input wire gt0_drpwe_in
.gt0_dmonitorout_out ( ),//监测端口输出。
.gt0_loopback_in ( i_loopback ),//回环模式输入端口。
.gt0_eyescanreset_in ( 0 ),//眼图复位输入。
.gt0_rxuserrdy_in ( 1 ),//接收数据准备接口,使用IP接收数据时输入高电平。
.gt0_eyescandataerror_out ( ),//眼图扫描相关信号。
.gt0_eyescantrigger_in ( 0 ),//眼图扫描相关信号。
.gt0_rxclkcorcnt_out ( ),//时钟状态监测。
.gt0_rxusrclk_in ( gt_rxusrclk ),//接收端用户时钟信号。
.gt0_rxusrclk2_in ( gt_rxusrclk2 ),//接收端用户时钟信号。
.gt0_rxdata_out ( o_rx_data ),//接收数据信号,位宽与IP配置用户数据位宽保持一致。
.gt0_rxdisperr_out ( ),//接收数据极性错误指示信号。
.gt0_rxnotintable_out ( ),//output wire [3:0] gt0_rxnotintable_out
.gt0_gtxrxp_in ( i_gt_rx_p ),//接收数据的差分引脚。
.gt0_gtxrxn_in ( i_gt_rx_n ),//接收数据的差分引脚。
.gt0_rxbyteisaligned_out ( o_rx_bytealign ),//接收数据的字节对齐指示信号。
.gt0_rxdfelpmreset_in ( 0 ),//LPM复位引脚。
.gt0_rxmonitorout_out ( ),//output wire [6:0] gt0_rxmonitorout_out
.gt0_rxmonitorsel_in ( 0 ),//input wire [1:0] gt0_rxmonitorsel_in
.gt0_rxoutclkfabric_out ( ),//output wire gt0_rxoutclkfabric_out
.gt0_gtrxreset_in ( i_rx_rst ),//RX端复位信号。
.gt0_rxpmareset_in ( i_rx_rst ),//PMA复位信号。
.gt0_rxpolarity_in ( i_rx_polarity ),//极性控制。
.gt0_rxcharisk_out ( o_rx_char ),//接收数据的K码指示信号,高电平表示K码。
.gt0_rxresetdone_out ( o_rx_done ),//接收器的复位完成指示信号。
.gt0_txpostcursor_in ( i_txpostcursor ),//发送端的预加重。
.gt0_txprecursor_in ( i_txpercursor ),//发送端的后加重。
.gt0_gttxreset_in ( i_tx_rst ),//发送端的复位信号。
.gt0_txuserrdy_in ( 1 ),//input wire gt0_txuserrdy_in
.gt0_txusrclk_in ( gt_txusrclk ),//input wire gt0_txusrclk_in
.gt0_txusrclk2_in ( gt_txusrclk2 ),//input wire gt0_txusrclk2_in
.gt0_txdiffctrl_in ( i_tx_diffctrl ),//差分的幅值控制引脚。
.gt0_txdata_in ( i_tx_data ),//用户发送数据输入端口。
.gt0_gtxtxn_out ( o_gt_tx_n ),//接收数据的差分引脚。
.gt0_gtxtxp_out ( o_gt_tx_p ),//接收数据的差分引脚。
.gt0_txoutclk_out ( gt_txoutclk ),//output wire gt0_txoutclk_out
.gt0_txoutclkfabric_out ( ),//output wire gt0_txoutclkfabric_out
.gt0_txoutclkpcs_out ( ),//PCS时钟输出。
.gt0_txcharisk_in ( i_tx_char ),//发送数据的K码指示信号。
.gt0_txresetdone_out ( ),//发送端的复位完成指示信号。
.gt0_txpolarity_in ( i_tx_polarity ),//发送端的极性控制。
.gt0_qplllock_in ( i_qplllock ),//input wire gt0_qplllock_in
.gt0_qpllrefclklost_in ( i_qpllrefclklost ),//input wire gt0_qpllrefclklost_in
.gt0_qpllreset_out ( w_gt_qpll_reset ),//output wire gt0_qpllreset_out
.gt0_qplloutclk_in ( i_qplloutclk ),//input wire gt0_qplloutclk_in
.gt0_qplloutrefclk_in ( i_qplloutrefclk ) //input wire gt0_qplloutrefclk_in
);
最终gt_channel模块的RTL视图如下所示。
2、整理gt_module模块
本次需要使用两个高速收发器,则只需要在该模块内,将gt_channel例化两次,两个模块共用同一个QPLL输出时钟即可。
首先整理IBUFGDS_GTE2和GT_COMMON,如下所示,差分时钟先经过IBUFGDS_GTE2转化为单端时钟,之后通过GT_COMMON生成GTX IP需要的时钟信号,注意QPLL的复位信号只能通过一个gt_channel模块提供。
//例化IBUFDS,将差分时钟输入转换为单端时钟。
IBUFDS_GTE2 u_IBUFDS_GTE2(
.O ( gtrefclk ),//输出单端时钟信号;
.ODIV2 ( ),//输出二分频单端时钟信号;
.CEB ( 0 ),//输出时钟使能信号,低电平有效;
.I ( i_gtrefclk_p ),//输入的差分时钟对;
.IB ( i_gtrefclk_n ) //输入的差分时钟对;
);
//例化QPLL
gtwizard_0_common #(
.SIM_QPLLREFCLK_SEL ( 3'b001 ) //时钟选择。
)
u_common (
.QPLLREFCLKSEL_IN ( 3'b001 ),//选择BANK的时钟0作为QPLL时钟输入。
.GTREFCLK0_IN ( gtrefclk ),//BANK的时钟0;
.GTREFCLK1_IN ( 1'b0 ),//BANK的时钟1;
.QPLLLOCKDETCLK_IN ( sys_clk ),
.QPLLLOCK_OUT ( w_qplllock ),//QPLL时钟锁定端口;
.QPLLOUTCLK_OUT ( w_qplloutclk ),//QPLL时钟输出端口;
.QPLLOUTREFCLK_OUT ( w_qplloutrefclk ),//QPLL参考时钟端口;
.QPLLREFCLKLOST_OUT ( w_qpllrefclklost ),//QPLL时钟;
.QPLLRESET_IN ( w_qpllreset ) //QPLL时钟复位端口;
);
之后只需要例化两个gt_channel模块,就可以使用两通道的高速收发器,整个模块的例化如下所示。
//例化通道0的GT收发器模块;
gt_channel u_gt_channel_0(
.sys_clk ( sys_clk ),//系统时钟输入,与DRP时钟频率保持一致。
.i_rx_rst ( i_0_rx_rst ),//接收数据端复位信号;
.i_tx_rst ( i_0_tx_rst ),//发送数据端复位信号;
.o_tx_done ( o_0_tx_done ),//发送端口复位完成指示信号;
.o_rx_done ( o_0_rx_done ),//接收数据端口复位完成指示信号;
.i_tx_polarity ( i_0_tx_polarity ),//发送数据极性控制引脚;
.i_tx_diffctrl ( i_0_tx_diffctrl ),//差分幅值控制引脚;
.i_txpostcursor ( i_0_txpostcursor ),//发送数据前加重调节信号;
.i_txpercursor ( i_0_txpercursor ),//发送数据后加重信号;
.i_rx_polarity ( i_0_rx_polarity ),//接收数据极性控制引脚;
.i_loopback ( i_0_loopback ),//回环模式信号;
.i_drpaddr ( i_0_drpaddr ),//动态设置参数端口;
.i_drpclk ( i_0_drpclk ),//动态设置参数端口;
.i_drpdi ( i_0_drpdi ),//动态设置参数端口;
.o_drpdo ( o_0_drpdo ),//动态设置参数端口;
.i_drpen ( i_0_drpen ),//动态设置参数端口;
.o_drprdy ( o_0_drprdy ),//动态设置参数端口;
.i_drpwe ( i_0_drpwe ),//动态设置参数端口;
.i_qplllock ( w_qplllock ),//QPLL时钟锁定端口;
.i_qpllrefclklost ( w_qpllrefclklost ),//QPLL时钟端口信号;
.o_qpllreset ( w_qpllreset ),//QPLL时钟复位端口;
.i_qplloutclk ( w_qplloutclk ),//QPLL时钟输出端口;
.i_qplloutrefclk ( w_qplloutrefclk ),//QPLL参考时钟端口;
.o_rx_bytealign ( o_0_rx_bytealign ),//接收数据字节对齐指示信号;
.o_rx_clk ( o_0_rx_clk ),//接收数据时钟信号;
.o_rx_data ( o_0_rx_data ),//接收数据,位宽与IP设置保持一致;
.o_rx_char ( o_0_rx_char ),//接收数据K码指示信号,高点破表示对应字节数据时K码;
.o_tx_clk ( o_0_tx_clk ),//发送数据时钟信号;
.i_tx_data ( i_0_tx_data ),//发送数据,位宽与IP设置保持一致;
.i_tx_char ( i_0_tx_char ),//发送数据K码指示信号,高点破表示对应字节数据时K码;
.o_gt_tx_p ( o_0_gt_tx_p ),//GT发送数据的差分信号对;
.o_gt_tx_n ( o_0_gt_tx_n ),//GT发送数据的差分信号对;
.i_gt_rx_p ( i_0_gt_rx_p ),//GT接收数据的差分信号对;
.i_gt_rx_n ( i_0_gt_rx_n ) //GT接收数据的差分信号对;
);
//例化通道1的GT收发器模块;
gt_channel u_gt_channel_1(
.sys_clk ( sys_clk ),//系统时钟输入,与DRP时钟频率保持一致;
.i_rx_rst ( i_1_rx_rst ),//接收数据端复位信号;
.i_tx_rst ( i_1_tx_rst ),//发送数据端复位信号;
.o_tx_done ( o_1_tx_done ),//发送端口复位完成指示信号;
.o_rx_done ( o_1_rx_done ),//接收数据端口复位完成指示信号;
.i_tx_polarity ( i_1_tx_polarity ),//发送数据极性控制引脚;
.i_tx_diffctrl ( i_1_tx_diffctrl ),//差分幅值控制引脚;
.i_txpostcursor ( i_1_txpostcursor ),//发送数据前加重调节信号;
.i_txpercursor ( i_1_txpercursor ),//发送数据后加重信号;
.i_rx_polarity ( i_1_rx_polarity ),//接收数据极性控制引脚;
.i_loopback ( i_1_loopback ),//回环模式信号;
.i_drpaddr ( i_1_drpaddr ),//动态设置参数端口;
.i_drpclk ( i_1_drpclk ),//动态设置参数端口;
.i_drpdi ( i_1_drpdi ),//动态设置参数端口;
.o_drpdo ( o_1_drpdo ),//动态设置参数端口;
.i_drpen ( i_1_drpen ),//动态设置参数端口;
.o_drprdy ( o_1_drprdy ),//动态设置参数端口;
.i_drpwe ( i_1_drpwe ),//动态设置参数端口;
.i_qplllock ( w_qplllock ),//QPLL时钟锁定端口;
.i_qpllrefclklost ( w_qpllrefclklost ),//QPLL时钟端口信号;
.o_qpllreset ( ),//QPLL时钟复位端口,一个bank只需要一个通道输出QPLL复位即可。
.i_qplloutclk ( w_qplloutclk ),//QPLL时钟输出端口;
.i_qplloutrefclk ( w_qplloutrefclk ),//QPLL参考时钟端口;
.o_rx_bytealign ( o_1_rx_bytealign ),//接收数据字节对齐指示信号;
.o_rx_clk ( o_1_rx_clk ),//接收数据时钟信号;
.o_rx_data ( o_1_rx_data ),//接收数据,位宽与IP设置保持一致;
.o_rx_char ( o_1_rx_char ),//接收数据K码指示信号,高点破表示对应字节数据时K码;
.o_tx_clk ( o_1_tx_clk ),//发送数据时钟信号;
.i_tx_data ( i_1_tx_data ),//发送数据,位宽与IP设置保持一致;
.i_tx_char ( i_1_tx_char ),//发送数据K码指示信号,高点破表示对应字节数据时K码;
.o_gt_tx_p ( o_1_gt_tx_p ),//GT发送数据的差分信号对;
.o_gt_tx_n ( o_1_gt_tx_n ),//GT发送数据的差分信号对;
.i_gt_rx_p ( i_1_gt_rx_p ),//GT接收数据的差分信号对;
.i_gt_rx_n ( i_1_gt_rx_n ) //GT接收数据的差分信号对;
);
后续如果要使用多通道的收发器,只需要在该模块下,多次例化gt_channel模块即可。
gt_module的RTL视图如下所示。
本文对模块的整理到此结束,后文将使用该模块实现一个自定义收发格式的PHY设计。
如果对文章内容理解有疑惑或者对代码不理解,可以在评论区或者后台留言,看到后均会回复!
如果本文对您有帮助,还请多多点赞👍、评论💬和收藏⭐!您的支持是我更新的最大动力!将持续更新工程!