文章目录
前言
根据官方的example design设计一个自定义协议的高速PHY设计
一、设计框图
设计思路及代码思路参考FPGA奇哥系列网课
IP核解析参考xilinx文档PG168 7 Series FPGAs Transceiver Wizard v3.6
二、例化IP核端口
即框图当中的gtwizard_0模块
所有端口含义都可以在PG168当中进行了解
需要特别关注的在代码里进行了简单的注释
gtwizard_0 gtwizard_0_i
(
.sysclk_in (i_sysclk ), //SYSCLK是一个自由运行的系统/板载时钟,用于驱动示例设计中的FPGA逻辑。当启用DRP接口时,DRP_CLK连接到示例设计中的SYSCLK。需要在XDC中对此时钟进行约束。
.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 ),
.gt0_tx_mmcm_lock_in (gt0_txmmcm_lock_i ),
.gt0_tx_mmcm_reset_out (gt0_txmmcm_reset_i ),
.gt0_rx_mmcm_lock_in (gt0_rxmmcm_lock_i ),
.gt0_rx_mmcm_reset_out (gt0_rxmmcm_reset_i ),
.gt0_drpaddr_in (i_drpaddr ),
.gt0_drpclk_in (i_sysclk ),
.gt0_drpdi_in (i_drpdi ),
.gt0_drpdo_out (o_drpdo ),
.gt0_drpen_in (i_drpen ),
.gt0_drprdy_out (o_drprdy ),
.gt0_drpwe_in (i_drpwe ),
.gt0_dmonitorout_out (),
.gt0_loopback_in (i_loopback ),
.gt0_eyescanreset_in (0 ),
.gt0_rxuserrdy_in (1 ),
.gt0_eyescandataerror_out (),
.gt0_eyescantrigger_in (0 ),
.gt0_rxclkcorcnt_out ( ),
.gt0_rxusrclk_in (gt0_rxusrclk_i ),
.gt0_rxusrclk2_in (gt0_rxusrclk2_i ),
.gt0_rxdata_out (o_rx_data ),//接收数据,位宽为IP配置的用户位宽
.gt0_rxdisperr_out (),
.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 ),
.gt0_rxmonitorout_out (),
.gt0_rxmonitorsel_in (0 ),
.gt0_rxoutclkfabric_out (),
.gt0_gtrxreset_in (i_rx_rst ),
.gt0_rxpmareset_in (i_rx_rst ),
.gt0_rxpolarity_in (i_rx_polarity ),
.gt0_rxcharisk_out (o_rx_char ),//标记接收的有效的8B/10BK字符。高位比特对应数据路径的高位字节。
.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 ),
.gt0_txusrclk_in (gt0_txusrclk_i ),
.gt0_txusrclk2_in (gt0_txusrclk2_i ),
.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 (gt0_txoutclk_i ),
.gt0_txoutclkfabric_out (),
.gt0_txoutclkpcs_out (),
.gt0_txcharisk_in (i_tx_char ),//与接收同理
.gt0_txresetdone_out (),
.gt0_txpolarity_in (i_tx_polarity ),
.gt0_qplllock_in (i_qplllock ),
.gt0_qpllrefclklost_in (i_qpllrefclklost ),
.gt0_qpllreset_out (w_gt_qpll_reset ),
.gt0_qplloutclk_in (i_qplloutclk ),
.gt0_qplloutrefclk_in (i_qplloutrefclk )
);
endmodule
三、common_reset_i模块
用于产生QPLL的复位信号
assign w_qpll_reset = w_commonreset | w_gt_qpll_reset ;
gtwizard_0_common_reset #
(
.STABLE_CLOCK_PERIOD ( )
)
common_reset_i
(
.STABLE_CLOCK (i_sysclk ),
.SOFT_RESET (i_tx_rst ),
.COMMON_RESET (w_commonreset )
);
四、gt_usrclk_source模块
用户时钟的产生模块,发送端和接收端的逻辑需要通过gt0_txusrclk2_i和gt0_rxusrclk2_i驱动。这里对示例工程里面的gt_usrclk_source进行了简单的修改,在原本的example design里,IBUFDS_GTE2原语被放到了gt_usrclk_source模块里,该模块就是用来将外部差分参考时钟转为单端时钟信号。在原本的gt_usrclk_source里,它将输入的外部差分参考时钟信号转化为单端后又从该模块输出了出去,所以完全可以直接放到顶层去。
gtwizard_0_GT_USRCLK_SOURCE gt_usrclk_source
(
.GT0_TXUSRCLK_OUT (gt0_txusrclk_i ),
.GT0_TXUSRCLK2_OUT (gt0_txusrclk2_i ),
.GT0_TXOUTCLK_IN (gt0_txoutclk_i ),
.GT0_TXCLK_LOCK_OUT (gt0_txmmcm_lock_i ),
.GT0_TX_MMCM_RESET_IN (gt0_txmmcm_reset_i ),
.GT0_RXUSRCLK_OUT (gt0_rxusrclk_i ),
.GT0_RXUSRCLK2_OUT (gt0_rxusrclk2_i ),
.GT0_RXCLK_LOCK_OUT (gt0_rxmmcm_lock_i ),
.GT0_RX_MMCM_RESET_IN (gt0_rxmmcm_reset_i )
);
五、IBUFDS_GTE2和gtwizard_0_common模块
gtwizard_0_common 就是QPLL,里面例化了一个GTXE2_COMMON原语
gtwizard_0_common 只需要例化一次,因为一个QUAD只有一个QPLL
IBUFDS_GTE2 IBUFDS_GTE2_u0
(
.O (w_gtrefclk ),
.ODIV2 (),
.CEB (0),
.I (i_gtrefclk_p ),
.IB (i_gtrefclk_n )
);
gtwizard_0_common #
(
.WRAPPER_SIM_GTRESET_SPEEDUP(),
.SIM_QPLLREFCLK_SEL (3'b010)
)
common0_i
(
.QPLLREFCLKSEL_IN (3'b010 ),//参考时钟选择如下图所示,具体看自己的板卡接入了哪一路参考时钟
.GTREFCLK0_IN (0 ),
.GTREFCLK1_IN (w_gtrefclk ),
.QPLLLOCK_OUT (w_qplllock ),
.QPLLLOCKDETCLK_IN (i_sysclk ),
.QPLLOUTCLK_OUT (w_qplloutclk ),
.QPLLOUTREFCLK_OUT (w_qplloutrefclk ),
.QPLLREFCLKLOST_OUT (w_qpllrefclklost ),
.QPLLRESET_IN (w_qpllreset )
);
六、顶层模块gt_module
在该模块里我们可以例化多个gt_channel模块,需要注意的一点是w_qpllreset信号只需要一个gt_channel的信号即可。
module gt_module(
input i_sysclk ,
input i_gtrefclk_p ,
input i_gtrefclk_n ,
input i_rx0_rst ,
input i_tx0_rst ,
output o_tx0_done ,
output o_rx0_done ,
input i_tx0_polarity ,
input [3 :0] i_tx0_diffctrl ,
input [4 :0] i_tx0postcursor ,
input [4 :0] i_tx0percursor ,
input i_rx0_polarity ,
input [2 :0] i_loopback0 ,
input [8 :0] i_0_drpaddr ,
input i_0_drpclk ,
input [15:0] i_0_drpdi ,
output [15:0] o_0_drpdo ,
input i_0_drpen ,
output o_0_drprdy ,
input i_0_drpwe ,
output o_rx0_ByteAlign ,
output o_rx0_clk ,
output [31:0] o_rx0_data ,
output [3 :0] o_rx0_char ,
output o_tx0_clk ,
input [31:0] i_tx0_data ,
input [3 :0] i_tx0_char ,
input i_rx1_rst ,
input i_tx1_rst ,
output o_tx1_done ,
output o_rx1_done ,
input i_tx1_polarity ,
input [3 :0] i_tx1_diffctrl ,
input [4 :0] i_tx1postcursor ,
input [4 :0] i_tx1percursor ,
input i_rx1_polarity ,
input [2 :0] i_loopback1 ,
input [8 :0] i_1_drpaddr ,
input i_1_drpclk ,
input [15:0] i_1_drpdi ,
output [15:0] o_1_drpdo ,
input i_1_drpen ,
output o_1_drprdy ,
input i_1_drpwe ,
output o_rx1_ByteAlign ,
output o_rx1_clk ,
output [31:0] o_rx1_data ,
output [3 :0] o_rx1_char ,
output o_tx1_clk ,
input [31:0] i_tx1_data ,
input [3 :0] i_tx1_char ,
output o_gt_tx0_p ,
output o_gt_tx0_n ,
input i_gt_rx0_p ,
input i_gt_rx0_n ,
output o_gt_tx1_p ,
output o_gt_tx1_n ,
input i_gt_rx1_p ,
input i_gt_rx1_n
);
wire w_gtrefclk ;
wire w_qplllock ;
wire w_qpllrefclklost ;
wire w_qpllreset ;
wire w_qplloutclk ;
wire w_qplloutrefclk ;
IBUFDS_GTE2 IBUFDS_GTE2_u0
(
.O (w_gtrefclk ),
.ODIV2 (),
.CEB (0),
.I (i_gtrefclk_p ),
.IB (i_gtrefclk_n )
);
gtwizard_0_common #
(
.WRAPPER_SIM_GTRESET_SPEEDUP(),
.SIM_QPLLREFCLK_SEL (3'b010)
)
common0_i
(
.QPLLREFCLKSEL_IN (3'b010 ),//1:参考时钟0;2:参考时钟1 3:北时钟 4:南时钟
.GTREFCLK0_IN (0 ),
.GTREFCLK1_IN (w_gtrefclk ),
.QPLLLOCK_OUT (w_qplllock ),
.QPLLLOCKDETCLK_IN (i_sysclk ),
.QPLLOUTCLK_OUT (w_qplloutclk ),
.QPLLOUTREFCLK_OUT (w_qplloutrefclk ),
.QPLLREFCLKLOST_OUT (w_qpllrefclklost ),
.QPLLRESET_IN (w_qpllreset )
);
gt_channel gt_channel_u0(
.i_sysclk (i_sysclk ),
.i_gtrefclk (w_gtrefclk ),
.i_rx_rst (i_rx0_rst ),
.i_tx_rst (i_tx0_rst ),
.o_tx_done (o_tx0_done ),
.o_rx_done (o_rx0_done ),
.i_tx_polarity (i_tx0_polarity ),
.i_tx_diffctrl (i_tx0_diffctrl ),
.i_txpostcursor (i_tx0postcursor ),
.i_txpercursor (i_tx0percursor ),
.i_rx_polarity (i_rx0_polarity ),
.i_loopback (i_loopback0 ),
.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 ),
.i_qpllrefclklost (w_qpllrefclklost ),
.o_qpllreset (w_qpllreset ),
.i_qplloutclk (w_qplloutclk ),
.i_qplloutrefclk (w_qplloutrefclk ),
.o_rx_ByteAlign (o_rx0_ByteAlign ),
.o_rx_clk (o_rx0_clk ),
.o_rx_data (o_rx0_data ),
.o_rx_char (o_rx0_char ),
.o_tx_clk (o_tx0_clk ),
.i_tx_data (i_tx0_data ),
.i_tx_char (i_tx0_char ),
.o_gt_tx_p (o_gt_tx0_p ),
.o_gt_tx_n (o_gt_tx0_n ),
.i_gt_rx_p (i_gt_rx0_p ),
.i_gt_rx_n (i_gt_rx0_n )
);
gt_channel gt_channel_u1(
.i_sysclk (i_sysclk ),
.i_gtrefclk (w_gtrefclk ),
.i_rx_rst (i_rx1_rst ),
.i_tx_rst (i_tx1_rst ),
.o_tx_done (o_tx1_done ),
.o_rx_done (o_rx1_done ),
.i_tx_polarity (i_tx1_polarity ),
.i_tx_diffctrl (i_tx1_diffctrl ),
.i_txpostcursor (i_tx1postcursor ),
.i_txpercursor (i_tx1percursor ),
.i_rx_polarity (i_rx1_polarity ),
.i_loopback (i_loopback1 ),
.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 ),
.i_qpllrefclklost (w_qpllrefclklost ),
.o_qpllreset ( ),
.i_qplloutclk (w_qplloutclk ),
.i_qplloutrefclk (w_qplloutrefclk ),
.o_rx_ByteAlign (o_rx1_ByteAlign ),
.o_rx_clk (o_rx1_clk ),
.o_rx_data (o_rx1_data ),
.o_rx_char (o_rx1_char ),
.o_tx_clk (o_tx1_clk ),
.i_tx_data (i_tx1_data ),
.i_tx_char (i_tx1_char ),
.o_gt_tx_p (o_gt_tx1_p ),
.o_gt_tx_n (o_gt_tx1_n ),
.i_gt_rx_p (i_gt_rx1_p ),
.i_gt_rx_n (i_gt_rx1_n )
);
endmodule
总结
那么现在我们需要进行GT设计的时候,只需要正确配置时钟信号,然后控制输入输出数据以及K码指示信号等用户数据接口即可,需要例化多个GT channel的话就再gt_module当中例化多个gt_channel模块即可,不可以例化多次gt_module,因为gtwizard_0_common 包含在gt_module中,而gtwizard_0_common 只能例化一次