0,引言
最近在学习GTH,尝试在GHT外部套用一个phy层实现64b/66b,从官方例程开始学起:
参考资料:ug576-ultrascale-gth-transceivers (详细阅读)
pg182-gtwizard-ultrascale (根据example例程去找对应端口连接的通道,去ug576找详细解释)
以太网扫盲——64b/66b编码https://blog.chinaaet.com/justlxy/p/5100064750
例如:tx_sequence_in(外部序列计数器)
在pg182中:
可以看到与TXSEQUENCE端口连接,去ug576寻找如下:
ug576提供对应端口的详细描述与例子,可以帮助学习。
开发平台:vivado2020.2
开发板卡:axck060(黑金板卡)
FPGA芯片:xcku060ffva_1156_2_i
1.GTH IP核的创建
ip核的具体配置参考pg182-c4:design flow steps (从第81页开始,详细介绍了IP配置信息。)
以下为我自己要用的配置。
一、Basic
System:
- 传输配置:白身配置,自己编写;
- 只有GTH;
Transmitter:
1,Line_Rate:线速率:12.5Gb/s。因为FPGA板卡链接的参考时钟为156.25Mhz,线速率与参考时钟一般为倍数关系
2,PLL_type:GTH的一个quad包含两个qpll(qpll0,qpll1),以及四个cpll。
在ug576-p49可以看到两个qpll支持的频率范围:
cpll速率支持如下:(ug576-p45)
因为我们的线速率为12.5Gb/s,选择使用qpll0(qpll1也可以)
3,Actual Reference:根据自身板卡,我是156.25Mhz
4,Encoding:编码方式,实际上编码还是用GTH做,
我是实现64B/66B,有4种组合,根据Sync,Async,以及CAUI来区分。选择Sync:gearbox for 64B/66B。
Sync,Async的区别是sync要求在序列计数器32时暂停发送数据,而async允许每个txusrclk2周期连续应用数据。
因为gearbox64b/66b变速箱的输入带宽为66b(多了两个bit做数据包和控制包区分),输出带宽为64b,每次会少发两个bit,经过32个周期,会累积少发64个,刚好是一整个数据,这时外部序列计数器会使得外部发送数据暂停一个周期,将前面累积的64bit数据发送出去,这样不会造成数据丢失,且gearbox两端的整体带宽匹配。但会使得外部发送模块有周期性暂停。具体gearbox64b/66b参考ug576-C3:TC synchronous Gearbox:
5,User data width:64
6,internal data width :32
user data width 与internal data width的关系会导致 userclk与userclk2的倍数关系改变,具体查看手册。
7,Buffer:使能,如果不使能,需要自己编写相位同步电路(我一直使用buffer。buffer与相位对齐电路的主要区别:数据的latency!buffer的latency大,相位对齐电路的latency小。如果工程对延迟敏感,使用相位对齐电路)。
8,TXOUTCLK source:使用CLKPMA(个人习惯,这个没什么具体的区别,若要详细划分可以参考 ug576-p155,rx有对应的RX Fabric Clock)
9,后面的Advance模块,基本不用动 。
二、Physical Resources
物理配置,虽然这里可以配置,但是最后XDC中的物理物理约束具有最高优先级。这里可以随便点一点。DRP_CLK随便用一个时钟(基本不咋用),在范围内就行(这个范围测试过前面的配置会影响,根据自己的情况自行选择时钟)
三,Optional Features
这里是一些选项:
1,receiver comma detection and alignment(接收端逗号检测与对齐)
该模块负责逗号检测与逗号对齐。因为我们本次编写的为64b/66b,是通过hear来区分数据与控制信号的。并不需要逗号。comma一般用于8b/10b中,所以这里是灰色。
2,received channel bonding
接受通道对齐,一般是在多通道GT中使用。
3,received clock correction
接受段时钟校正。
在数据中每隔一段固定时间插入一个K码或者D码。在8b/10b开发中会使用。64b/66b中为灰色。因为使用gearbox。
4,buffer control
buffer控制,没用过
5,advanced clocking
qpll之类相关的,没用过
6,SATA
sata开发中使用。
四,structural options
1, helper_block
上半部分是控制生成example_prj时是否将COMMON_CLK,usesr_clocking,reset_controller等share_module放出来,这会改变ip的一些接口。推荐放在外面,可以更好的帮助我们学习控制这个IP。同时也可以更加快速的去搭建一个Quard中1-4个channel_module(因为common_clk被引出来了)。
2,interface(接口模块)
根据自身需求去选择对应接口是否开启。(xilinx非常贴心的在这个界面中,将相关的port在手册中的那一个部分进行了标注,根据自己所需去学习对应模块)
一般常用的: 接受,发送的极性控制,发送端的摆幅,预加重,后加重。一般来说就是在测试眼图中的参数。如下图
以上,ip配置完毕。
2.example_prj学习
生成例子如下:
主要包含userclk模块,reset模块,去历程中跑仿真,链接对应端口。大概耗费了三天,最总搭建了gth_channel。主要是复位,common_clk之间的相互连接。
module gt_channel_64b66b
(
input i_sys_clk ,
input i_sys_rst ,
input i_gtref_clk ,
input i_rx_rst ,
input i_tx_rst ,
output o_tx_done ,
output o_rx_done ,
input [ 2:0] i_gt_loopback ,
input [ 3:0] i_txdiffctrl_in ,//摆幅
input [ 0:0] i_txpolarity_in ,//极性
input [ 4:0] i_txpostcursor_in ,//后加重
input [ 4:0] i_txprecursor_in ,//预加重
input [ 0:0] i_rxpolarity_in ,//极性
//DRP接口
input wire [ 8:0] i_drpaddr_in ,
input wire [ 0:0] i_drpclk_in ,
input wire [ 15:0] i_drpdi_in ,
input wire [ 0:0] i_drpen_in ,
input wire [ 0:0] i_drpwe_in ,
output wire [ 15:0] o_drpdo_out ,
output wire [ 0:0] o_drprdy_out ,
//qpll 外部公用common模块传入
input i_rx_qpll_lock ,
input i_tx_qpll_lock ,
input qpll0clk_in ,
input qpll0refclk_in ,
input qpll1clk_in ,
input qpll1refclk_in ,
output o_rx_clk ,
output [ 63:0] o_rx_data ,
output o_rx_data_valid ,
output [ 1:0] o_rx_header ,
output o_rx_header_valid ,
input i_rxgearboxslip ,//手动对齐
output o_tx_clk ,
input [ 63:0] i_tx_data ,
input [ 1:0] i_tx_header ,
input [ 6:0] i_tx_seq ,//外部序列计数器
input i_rx_gt_p ,
input i_rx_gt_n ,
output o_tx_gt_p ,
output o_tx_gt_n
);
这样构建channel的好处就是之后再构建一个gt_module模块,根据quad的原则,一个module模块最多可以例化四个channel,从而实现了快速开发,比如我就建立两通道进行回环测试。
module gt_module_64b_66b
(
input i_sys_clk ,
input i_sys_rst ,
input i_mgtref_clk_p ,
input i_mgtref_clk_n ,
//channel_0
input i_c0_rx_rst ,
input i_c0_tx_rst ,
output o_c0_tx_done ,
output o_c0_rx_done ,
input [ 2:0] i_c0_gt_loopback ,
input [ 3:0] i_c0_txdiffctrl_in ,
input [ 0:0] i_c0_txpolarity_in ,
input [ 4:0] i_c0_txpostcursor_in ,
input [ 4:0] i_c0_txprecursor_in ,
input [ 0:0] i_c0_rxpolarity_in ,
input wire [ 8:0] i_c0_drpaddr_in ,
input wire [ 0:0] i_c0_drpclk_in ,
input wire [ 15:0] i_c0_drpdi_in ,
input wire [ 0:0] i_c0_drpen_in ,
input wire [ 0:0] i_c0_drpwe_in ,
output wire [ 15:0] o_c0_drpdo_out ,
output wire [ 0:0] o_c0_drprdy_out ,
output o_c0_rx_clk ,
output [ 63:0] o_c0_rx_data ,
output o_c0_rx_data_valid ,
output [ 1:0] o_c0_rx_header ,
output o_c0_rx_header_valid ,
input i_c0_rxgearboxslip ,//手动对齐
output o_c0_tx_clk ,
input [ 63:0] i_c0_tx_data ,
input [ 1:0] i_c0_tx_header ,
input [ 6:0] i_c0_tx_seq ,//外部序列计数器
input i_c0_rx_gt_p ,
input i_c0_rx_gt_n ,
output o_c0_tx_gt_p ,
output o_c0_tx_gt_n ,
//channel_1
input i_c1_rx_rst ,
input i_c1_tx_rst ,
output o_c1_tx_done ,
output o_c1_rx_done ,
input [ 2:0] i_c1_gt_loopback ,
input [ 3:0] i_c1_txdiffctrl_in ,
input [ 0:0] i_c1_txpolarity_in ,
input [ 4:0] i_c1_txpostcursor_in ,
input [ 4:0] i_c1_txprecursor_in ,
input [ 0:0] i_c1_rxpolarity_in ,
input wire [ 8:0] i_c1_drpaddr_in ,
input wire [ 0:0] i_c1_drpclk_in ,
input wire [ 15:0] i_c1_drpdi_in ,
input wire [ 0:0] i_c1_drpen_in ,
input wire [ 0:0] i_c1_drpwe_in ,
output wire [ 15:0] o_c1_drpdo_out ,
output wire [ 0:0] o_c1_drprdy_out ,
output o_c1_rx_clk ,
output [ 63:0] o_c1_rx_data ,
output o_c1_rx_data_valid ,
output [ 1:0] o_c1_rx_header ,
output o_c1_rx_header_valid ,
input i_c1_rxgearboxslip ,//手动对齐
output o_c1_tx_clk ,
input [ 63:0] i_c1_tx_data ,
input [ 1:0] i_c1_tx_header ,
input [ 6:0] i_c1_tx_seq ,//外部序列计数器
input i_c1_rx_gt_p ,
input i_c1_rx_gt_n ,
output o_c1_tx_gt_p ,
output o_c1_tx_gt_n
);
3.整体工程搭建
我们需要自己实现64/66的phy层,进行phy的编写。
包含phy_rx,phy_tx模块,对gt收发器接受到的数据进行解包
phy_rx如下:
module phy_rx
(
input i_rx_clk ,
input i_rx_rst ,
input [ 63:0] i_rx_data ,
input i_rx_valid ,
input [ 1:0] i_rx_header ,
input i_rx_header_valid ,
output [ 63:0] m_axis_data ,
output [ 7:0] m_axis_keep ,
output m_axis_last ,
output m_axis_valid
);
解包还需要控制rxgearboxslip,该信号用于控制gt收发器的接受端口的数据对齐,
module PHY_rx_bitsync#(
parameter P_HEARER_VALID_NUMNER = 64 ,
parameter P_SLIPBIT_GAP = 40
)(
input i_clk ,
input i_rst ,
(* MARK_DEBUG="true" *)input [1:0] i_header ,
(* MARK_DEBUG="true" *)input i_headr_valid ,
(* MARK_DEBUG="true" *)output o_slipbit ,
(* MARK_DEBUG="true" *)output o_sync
);
参考了example_prj中的工程。
核心就是,检验头部header,在64/66b中:
控制头说明:00:帧错误;01:纯数据帧;10:控制帧;11:帧错误
检测header是否一直为01或10。没有其他两种错误,即可认为链接建立完成。
若为满足预定时间的要求,即偶尔出现了00或者11,任然有错,发起一次o_slipbit。对拼接数据的位置进行移动。直到满足稳定要求。且o_slipbit的移动间隔必须大于32个周期,任然是使用了gearbox的原因。
last:上板验证
虽然说是AXI接口,但是gearbox64b\66b的每次间隔32个周期传输数据需要暂停一个周期的要求依然存在,通过m_axi_ready来体现。在传递时注意编写条件,不然会少传递数据。如果恰好在m_axi_last拉高时,aurora64/66_ip返回来的ready恰好拉低,就会出错!!!