一、GT简介
SerDes是(Serializer/Deserializer)的缩写,即串行器和解串器也叫串行收发器,是一种将并行数据转换成串行数据发送,将接收的串行数据转换成并行数据的模块。Xilinx的GT系列是在高速SerDes的基础上,增加了其他模块,如8b/10b编解码、预加载、PCIE协议控制等(具体可以看Xilinx相关文档,如ug476、ug482)形成的一个高速串行收发器,GT是Gigabit Transceiver的意思,它是实现当下一些高速串行接口的基础:如PCIe、RapidIO等。GTP , GTX , GTH,GTZ都是Xilinx的串行收发器,区别在速率不一样,支持的一些功能不一样。
- A7系列支持GTP:最高速率可到6.6Gb/s
- K7系列支持GTX:最高速率可到12.5Gb/s
- 部分V7系列支持GTH:最高速率可到13.1Gb/s
- 部分V7系列支持GTZ:最高速率可到28.05Gb/s
二、GTP内部结构
2.1 整体结构
由GTP在a7100t内部结构图可以看出:GTP在A7系列是上下分布,接下来我们解释这些名词什么意思。
- 每个 GTPE2_CHANNEL 原语由一个发射器和一个接收器组成。
- GTPE2_COMMON 原语包含两个环形振荡器 PLL(PLL0 和 PLL1)。
- 四个 GTPE2_CHANNEL 通道与一个 GTPE2_COMMON 原语聚集在一起称为 Quad 。
2.2 Quad的内部结构
一个Quad的内部结构图如下:
一个Quad外部有两个参考差分时钟REFCLK,参考时钟是两个PLL的输入时钟,可以用用户选择两个PLL的时钟源是哪个参考时钟,从而来产生其它生成时钟。
2.3 一对收发器的内部结构
一个GTPE2_CHANNEL 的内部结构图如下:
由图可看出,一个GTPE2_CHANNEL是由上面一个发送器和下面一个接收器组成。无论是接收器还是发送器都有PMA和PCS两部分组成。
- PCS:(Physical Coding Sublayer,物理编码子层):含8B/10B编解码、缓冲区、通道绑定和时钟修正等电路。
- PMA:(Physical Media Attachment,物理媒介适配层):包含高速串并转换(PISO)、预/后加重、接收均衡、时钟发生器及时钟恢复等电路。
PMA是最基本的单位,负责模拟部分,而PCS主要负责并行的数字电路处理,理论上来说PCS完全可以由逻辑设计完成;但是硬核专用电路实现肯定功能更强大更稳定。
2.3.1 发送器的内部结构
其中TX部分各模块的含义如下:
- 用户将待发送的数据流以并行是方式送至此处
- 全称为Physical Interface for PCI Express,PCIe物理层接口。如果使能PCIE接口,则该通道是PCIE的数据通道,否则将不会被使用。
- 发送数据变速箱:实现任意数据位宽之间转换,数据除了使用8b10b编码,还可以使用64b66b等编码方式,如果使用后面这种编码方式,那么就需要使用此模块。
- 完成对输入数据的8B/10B编码(目的与TMDS编码原理以及Verilog实现HDMI接口一致,保持直流均衡)。常用于速率低于8G的场景。
- 伪随机序列(Pseudo-random bit sequences,PRBS),一般用于高速串行通信通道传输的误码率测试。
- 这是TX相位插值器控制器模块,支持动态控制TX相位插值器(TX PI),TX PMA中的数据进行微调。
- 在发送端的PCS子层里面有两个并行时钟域,分别是PMA相连的并行时钟(XCLK)和TXUSRCLK时钟。要传输数据,XCLK速率必须与TXUSRCLK速率匹配,并且必须解决两个域之间的所有相位差。GTP的发送数据通道包括一个FIFO缓冲器和一个TX相位插值器,用于解决XCLK和TXUSRCLK域之间的相位差。缺点是使用此FIFO后的数据延迟比较大,导致用户把数据输入GTP到GTP输出数据的时间比较长,如果不考虑这部分延迟,推荐直接使用FIFO即可。
- SATA是硬盘接口,建立通信主要是通过检测OOB(Out Of Band)实现的,并且向上层传输连接情况,因此只有在使用SATA协议时,才会使用该模块,否则不用考虑。
- PICE唤醒功能,只有在使用PCIE协议时才会用到,否则不考虑。
- 极性控制,主要时针对PCB设计时将差分对设计反了,模块内部可以将极性反转,从而解决PCB设计问题。
- 并串转换模块(parallel input Serial output):实现数据的串并转换功能。
- 光标前和光标后传输预加重:高速信号在长距离、线速率较高的传输时,损耗是很严重的。经过损耗衰减之后,为了让接收端还能检测到数据,在发送端会做预加重和去加重处理,增大差分输出信号的摆幅等等,让其经过传输损耗之后差分信号的幅值变为零。
- 使用PCIE、SATA协议时才会使用的功能,否则不会用到。
- 发送驱动器,主要是数模转换。
- 发送时钟分频模块,来自PLL0或者PLL1。
2.3.2 接收器的内部结构
其中RX部分各模块的含义如下:
- RX_EQ:自适应连续时间线性均衡器(CTLE),以补偿由于物理信道中的高频衰减引起的信号失真;RX_OOB:这个与SATA、PCIE这些协议有关,如果不使用这些协议,可以忽略。
- 接收参考时钟,时钟进入PMA之后,需要先经过CDR调整时钟相位,然后再经过分频作为串并转换模块的串行时钟和并行时钟,保证时钟与数据的相位关系正确。
- 串并转换模块,将接收的串行数据转换为并行数据,然后输出到PCS侧。
- 极性控制,主要时针对PCB设计时将差分对设计反了,模块内部可以将极性反转,从而解决PCB设计问题。
- GTP接收器包括一个内置的PRBS检查器,与前文发送端生成的四种PRBS序列保持一致。
- 字节对齐,用来寻找K码来对齐数据。为了对齐,发送端发送一个可识别的序列,即Comma,通常称为逗号。接收方在传入数据中搜索逗号,当发现逗号时,将该逗号移动到一个字节边界,以便接收的并行字与发送的并行字匹配。
- 8b10b解码模块,将接收到的10b并行数据转换成8b并行数据。
- 这个与发送通道的 PIPE Control功能是一样的,与PCIE协议有关,不使用该协议时可以忽略。
- RX Status Control对弹性buffer的一些状态进行检测,包括溢出等等,然后去做一个时钟纠正的处理。
- 弹性buffer主要用于解决PMA并行时钟域(XCLK)和PCS内部并行时钟域RXUSRCLK的跨时钟域问题。
- 和发送一样,如果使用了64B66B这入需要通过这个模块来解码
- 用户接收到的并行数据模块。
三、时钟和复位
3.1 输入的参考时钟结构
参考时钟输入会先进入一个缓冲器,上图为缓冲器的内部结构。参考时钟输入在内部以每条支路上的 50Ω 电阻端接至 4/5 MGTAVCC。参考时钟在软件中使用 IBUFDS_GTE2 软件原语实例化。
3.1.1 IBUFDS_GTE2原语
IBUFDS_GTE2是对于高速接口差分转单端的原语,例化代码如下:
IBUFDS_GTE2 #(
.CLKCM_CFG("TRUE"), // Refer to Transceiver User Guide
.CLKRCV_TRST("TRUE"), // Refer to Transceiver User Guide
.CLKSWING_CFG(2'b11) // Refer to Transceiver User Guide
)
IBUFDS_GTE2_inst (
.O(O), // 1-bit output: Refer to Transceiver User Guide
.ODIV2(ODIV2), // 1-bit output: Refer to Transceiver User Guide
.CEB(CEB), // 1-bit input: Refer to Transceiver User Guide
.I(I), // 1-bit input: Refer to Transceiver User Guide
.IB(IB) // 1-bit input: Refer to Transceiver User Guide
);
3.1.2 IBUFDS_GTE2原语端口属性说明
端口属性说明如下所示:
信号名称 | 方向 | 描述 |
I,IB | in | 外部输入的差分时钟 |
CEB | in | 时钟缓冲器的低电平有效异步时钟使能信号。将此信号设置为高电平可关闭时钟缓冲器 |
O | out | 转换后的单端时钟信号 |
ODIV2 | out | 输出时钟信号的二分频 |
CLK_RCV_TRST | 保留。此属性将 50Ω 终端电阻切换至信号路径。此属性必须始终设置为 TRUE | |
CLKCM_CFG | 保留。此属性切换 50Ω 终端的终端电压。此属性必须始终设置为 TRUE | |
CLKSWING_CFG | 保留。此属性控制时钟的内部摆幅。此属性必须始终设置为 2'b11 |
3.2 参考时钟选择和分配
提供给指定Quad 中的 PLL 的参考时钟可以来自本地的参考时钟CLK0或CLK1,也可以来自设备同一半中的相邻 Quad。给GTPE2_COMMON 中PLL0和PLL1输入时钟可以来自于以下:
- 两个本地参考时钟引脚对,GTREFCLK0 或 GTREFCLK1
- 位于设备同一半部分的另一个 Quad 的两个参考时钟引脚对
3.2.1 GTPE2_COMMON 部分时钟端口信号
信号名称 | 方向 | 描述 |
PLL0REFCLKSEL[2:0] | in | 输入以动态选择 PLL0 的输入参考时钟;更改参考时钟输入后必须对 PLL0 进行复位 |
PLL1REFCLKSEL[2:0] | in | 输入以动态选择 PLL1 的输入参考时钟;更改参考时钟输入后必须对 PLL1 进行复位 |
RXSYSCLKSEL[1:0] | in | 选择 PLL 时钟源来驱动 RX 数据路径: RXSYSCLKSEL[0] = 1'b0 (PLL0);RXSYSCLKSEL[0] = 1'b1 (PLL1) 选择参考时钟源来驱动 RXOUTCLK: RXSYSCLKSEL[1] = 1'b0(来自PLL0的参考时钟);RXSYSCLKSEL[1] = 1'b1 (来自PLL1的参考时钟) |
TXSYSCLKSEL[1:0] | in | 选择 PLL 时钟源来驱动 TX 数据路径: TXSYSCLKSEL[0] = 1'b0 (PLL0);TXSYSCLKSEL[0] = 1'b1 (PLL1) 选择参考时钟源来驱动 TXOUTCLK: TXSYSCLKSEL[1] = 1'b0(来自 PLL0 的参考时钟);TXSYSCLKSEL[1] = 1'b1 (参考时钟来自 PLL1) |
3.3 复位和初始化
GTP 收发器必须在 FPGA 器件上电和配置后初始化,然后才能使用。GTP 收发器的发射器 (TX) 和接收器 (RX) 可以独立且并行地初始化,如图 2-11 所示。GTP 收发器的 TX 和 RX 初始化包括两个步骤:
- 初始化驱动 TX/RX 的相关 PLL
- 初始化 TX 和 RX 数据路径 (PMA + PCS)
GTP 收发器的 TX 和 RX 可以从 PLL0 或 PLL1 接收时钟。TX 和 RX 使用的相关 PLL(PLL0/PLL1)必须先初始化,然后才能初始化 TX 和 RX。TX 和 RX 使用的任何 PLL 都单独复位,其复位操作完全独立于所有 TX 和 RX 复位。TX 和 RX 数据路径必须仅在相关 PLL 锁定后初始化,初始化流程如下:
这一部分有官方的复位逻辑代码,感兴趣的朋友可以仔细阅读一下官方代码。
四、环回模式
环回模式是收发器数据路径的专门配置,其中流量被折回源。通常,会传输特定的流量模式,然后进行比较以检查错误。一共有四种环回模式,如下如所示:
控制接口如下:
//----------------------------- Loopback Ports -----------------------------
.gt0_loopback_in (gt0_loopback_in), // input wire [2:0] gt0_loopback_in
环回测试模式分为两大类:
- 近端环回模式将数据回传到最靠近流量生成器的收发器中
-
①Near-end PCS Loopback:必须启用 RX 弹性缓冲区,并将 RX_XCLK_SEL 设置为 RXREC,以使近端 PCS 环回正常工作。在近端 PCS 环回中,RX XCLK 域由 TX PMA 并行时钟 (TX XCLK) 提供时钟。如果在正常运行期间使用 RXOUTCLK 为 FPGA 逻辑提供时钟,并将 RXOUTCLKSEL 设置为 RXOUTCLKPMA,则在将 GTP 收发器置于近端 PCS 环回中时,必须更改以下两项之一:
1、Set RXOUTCLKSEL to select RXOUTCLKPCS
2、Set RXCDRHOLD = 1’b1 -
②Near-end PMA Loopback :进入和退出近端 PMA 环回后需要 GTRXRESET
- 远端环回模式将接收到的数据环回到链路远端的收发器中
- ③Far-end PMA Loopback:必须启用 TX 缓冲区,并且必须将 TX_XCLK_SEL 设置为 TXOUT,以使远端 PMA 环回正常工作。 在远端 PMA 环回中,TX 缓冲区的写入侧由 RX PMA 并行时钟 (RX XCLK) 计时。 进入和退出远端 PMA 环回后,需要 GTTXRESET。
- ④ Far-end PCS Loopback:如果未使用时钟校正,远端 PCS 环回中的收发器必须使用与作为环回数据源的收发器相同的参考时钟。无论是否使用时钟校正,端口 TXUSRCLK 和 RXUSRCLK 都必须由相同的时钟资源 (BUFG、BUFH) 驱动。 当通道中的两个或一个变速箱均启用时,不支持远端 PCS 环回。
4.1 环回选择端口属性
信号名称 | 方向 | 描述 |
LOOPBACK[2:0] | in | 000: Normal operation 001: Near-End PCS Loopback 010: Near-End PMA Loopback 011: Reserved 100: Far-End PMA Loopback 101: Reserved 110: Far-End PCS Loopback |
五、发送模块讲解
对于用户来说,发送数据只需使用以下三个信号即可,其它的根据GUI配置后,GTP就会自动将数据发送出去:
//---------------- Transmit Ports - FPGA TX Interface Ports ----------------
.gt0_txdata_in (gt0_txdata_in), // 代发送的数据
.gt0_txusrclk_in (gt0_txusrclk_i), // 发送时钟
.gt0_txusrclk2_in (gt0_txusrclk2_i), // 发送时钟的二分频
5.1 TX 接口介绍
FPGA TX 接口是 FPGA 到 GTP 收发器 TX 数据路径的网关。 用户通过在 TXUSRCLK2 的上升沿将数据写入 TXDATA 端口来通过 GTP 收发器传输数据。端口的宽度可以配置为两个或四个字节宽。端口的实际宽度取决于 TX_DATA_WIDTH 属性和 TX8B10BEN 端口设置。端口宽度可以是 16、20、32 和 40 位。接口处的并行时钟 (TXUSRCLK2) 的速率由 TX 线路速率、TXDATA 端口的宽度以及是否启用 8B/10B 编码决定。必须为发射器中的内部 PCS 逻辑提供第二个并行时钟 (TXUSRCLK)
GTP 收发器包含 2 字节内部数据路径。FPGA 接口宽度可通过设置 TX_DATA_WIDTH 属性进行配置。当启用 8B/10B 编码器时,必须将 TX_DATA_WIDTH 属性配置为 20 位或 40 位,在这种情况下,FPGA TX 接口仅使用 TXDATA 端口。例如,当 FPGA 接口宽度为 16 时,使用 TXDATA[15:0]。当绕过 8B/10B 编码器时,TX_DATA_WIDTH 属性可以配置为任何可用宽度:16、20、32 或 40 位。
当启动8b10b编码后,对于用户来说,发送端口模块的信号就变成了6个,新增三个信号代码如下:
//---------------- Transmit Ports - TX 8B/10B Encoder Ports ----------------
.gt0_txchardispmode_in (gt0_txchardispmode_in), // input wire [3:0] gt0_txchardispmode_in
.gt0_txchardispval_in (gt0_txchardispval_in), // input wire [3:0] gt0_txchardispval_in
.gt0_txcharisk_in (gt0_txcharisk_in), // 置1表示发送K码,否则就是发送数据
当禁止8b10b编码时
信号名称 | 方向 | 描述 |
TXCHARDISPMODE[3:0] | in | 当 8B/10B 编码被禁用时,TXCHARDISPMODE 用于扩展 20 位和 40 位 TX 接口的数据总线。 |
TXCHARDISPVAL[3:0] | in | 当 8B/10B 编码被禁用时,TXCHARDISPVAL 用于扩展 20 位和 40 位 TX 接口的数据总线。 |
当启动8b10b编码后,有了极性控制的作用
TXCHARDISPMODE | TXCHARDISPVAL | 描述 |
0 | 0 | 保留 |
0 | 1 | 反转RD极性 |
1 | 0 | 设置RD为负 |
1 | 1 | 设置RD为正 |
5.2 发送时钟结构
5.2.1 TXUSRCLK 和 TXUSRCLK2 关系
FPGA TX 接口包括两个并行时钟:TXUSRCLK 和 TXUSRCLK2。 TXUSRCLK 是 GTP 收发器发射器中 PCS 逻辑的内部时钟。TXUSRCLK 所需的速率取决于 GTPE2_CHANNEL 原语的内部数据路径宽度和 GTP 收发器发射器的 TX 线速率。
TXUSRCLK2 频率与 TXUSRCLK 的关系:
FPGA Interface Width | TX_DATA_WIDTH | TXUSRCLK2 Frequency |
2-Byte | 16, 20 | TXUSRCLK2 = TXUSRCLK |
4-Byte | 32, 40 | TXUSRCLK2 = TXUSRCLK/2 |
5.2.2 使用 TXOUTCLK 驱动 TX 接口
上图展示了发送模块的详细时钟结构,其中TXOUTCLK 来自 MGTREFCLK0[P/N] 或 MGTREFCLK1[P/N],还有TXOUTCLKPCS、TXOUTCLKPMA;通过TXOUTCLKSEL端口来选择。TXOUTCLK可以提供TXUSRCLK和TXUSRCLK2,具体如下所示:
上图中TXOUTCLK 用于在一个通道配置中驱动 2 字节模式(TX_DATA_WIDTH = 16 或 20)的 TXUSRCLK 和 TXUSRCLK2。TXUSRCLK2 的频率等于 TXUSRCLK
上图中TXOUTCLK 用于驱动 4 字节模式 (TX_DATA_WIDTH = 32 或 40) 的 TXUSRCLK2。TXUSRCLK2 的频率等于 TXUSRCLK 频率的一半。 MMCM 或 PLL 是位于器件上半部分的时钟管理块 (CMT) 的一部分,只能驱动器件上半部分的 BUFG。同样,位于下半部分的 MMCM 或 PLL 只能驱动下半部分的 BUFG。
&emspTXSYSCLKSEL是一个2bit数据,用来选择TXOUTCLK源和TXPMA时钟源,高位[1]选择TXOUTCLK源来自PLL0或者PLL1的参考时钟,低位[0]选择TXPMA来自PLL0或者PLL1的输出。“/D”分频器为我们提供了每通道实现不一样速率的方式,但只能实现1、2、4、8分频。所以每个发送器PMA模块都有一个D分频器,它将PLL的时钟分频,以降低线路速率,通过TXRATE来控制。
5.3 TX BUFFER
发送缓冲fifo可以选择不使用,但是如果没有特别的速率要求还是打开好一点。
还有一个信号来指示缓冲FIFO的状态,TXBUFSTATUS [1:0]:
端口名 | 方向 | 时钟域 | 说明 |
TXBUFSTATUS[1:0] | out | TXUSRCLK2 | TX 缓冲区状态。 TXBUFSTATUS[1]:TX 缓冲区溢出或下溢状态。当 TXBUFSTATUS[1] 设置为高电平时,它将保持高电平直到 TX 缓冲区复位。 1:TX FIFO 有溢出或下溢。 0:无 TX FIFO 溢出或下溢错误。 TXBUFSTATUS[0]:TX 缓冲区已满。 1:TX FIFO 至少为半满。 0:TX FIFO 未满半满。 |
六、接收模块讲解
6.1 接收器结构框图
每个 GTP 收发器都包含一个独立的接收器,由 PCS 和 PMA 组成。上图显示了 GTP 收发器 RX 的模块。高速串行数据从电路板上的走线流入 GTP 收发器 RX 的 PMA,再流入 PCS,最后流入 FPGA 逻辑。
6.2 模拟前端
模拟信号进入GT收发器后,该部分需要对其进行数字化分析处理。例如信号放大,电压均衡等,也就是上图的RX_EQ模块,具体更详细的内部结构图如下所示:
对于用户来说,需要配置的有两点:
- 配置接收端电压模式
- 校准终端电阻
配置终端电阻来选择接收的电压摆幅范围。
6.3 RX_EQ均衡器
GTP 收发器接收器具有节能的自适应连续时间线性均衡器 (CTLE),可补偿物理信道中高频衰减导致的信号失真。CTLE 被称为低功耗模式 (LPM)
6.4 CDR时钟恢复
每个 GTPE2_CHANNEL 收发器中的 RX 时钟数据恢复 (CDR) 电路从传入数据流中提取恢复的时钟和数据。PLL0 或 PLL1 为相位插值器提供基准时钟。相位插值器依次产生精细、均匀间隔的采样相位,以允许 CDR 状态机进行精细相位控制。CDR 状态机可以跟踪可能与本地 PLL 参考时钟存在频率偏移的传入数据流。
6.5 接收时钟结构
接收器的时钟结构和发送器的时钟结构几乎一模一样,因此这里不过多介绍。
6.6 极性控制
如果 PCB 上的 RXP 和 RXN 差分走线意外接反了,GTP 收发器 RX 接收的差分数据将被反转。GTP 收发器 RX 允许在 SIPO 之后对 PCS 中的并行字节进行反转,以抵消差分对上的反转极性。 极性控制功能使用 RXPOLARITY 输入,该输入由结构用户界面驱动为高电平以反转极性。
端口名 | 方向 | 时钟域 | 说明 |
RXPOLARITY | in | RXUSRCLK2 | RXPOLARITY 端口可以反转输入数据的极性:0:不反转。RXP 为正,RXN 为负。 1:反转。RXP 为负,RXN 为正 |
6.7 接收字节对齐
串行数据必须与符号边界对齐,然后才能用作并行数据。为了实现对齐,发送器发送一个可识别的序列,通常称为逗号。接收器在传入数据中搜索逗号。当它找到逗号时,它会将逗号移动到字节边界,以便接收的并行字与发送的并行字匹配。
下图左侧显示TX为并行数据,右侧显示RX接收到逗号对齐后可识别的并行数据。
要启用逗号对齐块,RXCOMMADETEN 端口将被驱动为高电平。 RXCOMMADETEN 将被驱动为低电平,以完全绕过该块,从而实现最短延迟
这些逗号码都是8B10B里面的内容,后续会单独出一篇文章来了解并实现8B10B,这里就先大致了解一下即可。
6.8 接收弹性缓冲区
GTP收发器RX数据通路的PCS部分有两个内部并行时钟域:PMA并行时钟域(XCLK)和RXUSRCLK域。要保证数据的正确接收,不仅要求速率一致,也要解决两个时钟域之间的相位差异,如下图所示:
为解决该问题,Xilinx提供了两种解决方案,一是利用弹性缓冲区,二是用相位对齐。
- RX 相位对齐电路用于在绕过 RX 弹性缓冲器时调整 PMA 并行时钟域 (XCLK) 和 RXUSRCLK 域之间的相位差。它还通过调整 RXUSRCLK 来执行 RX 延迟对齐,以补偿温度和电压变化。组合的 RX 相位和延迟对齐可以由 GTP 收发器自动执行,也可以由用户手动控制。
- RX 缓冲区是建议尽可能使用的默认缓冲区。它功能强大且更易于操作;可以使用 RX 恢复时钟或本地时钟(带时钟校正);时钟校正和通道绑定所需
6.9 时钟矫正
RX 弹性缓冲器旨在桥接两个不同的时钟域,即 RXUSRCLK 和 XCLK,后者是从 CDR 恢复的时钟。即使 RXUSRCLK 和 XCLK 以相同的时钟频率运行,也总是存在微小的频率差异。由于 XCLK 和 RXUSRCLK 并不完全相同,因此除非纠正,否则差异可能会累积,最终导致 RX 弹性缓冲器溢出或下溢。为了允许纠正,每个 GTP 收发器 TX 都会定期传输一个或多个特殊字符,GTP 收发器 RX 可以根据需要在 RX 弹性缓冲器中移除或复制这些字符。通过在 RX 弹性缓冲器太满时移除字符并在 RX 弹性缓冲器太空时复制字符,接收器可以防止溢出或下溢。
6.10 用户接口
上面介绍的大部分是接收器的功能配置,实际上对于用户来说,需要使用的端口只有几个:
//---------------- Receive Ports - FPGA RX Interface Ports -----------------
.gt0_rxdata_out (gt0_rxdata_out), // output wire [31:0] gt0_rxdata_out
.gt0_rxusrclk_in (gt0_rxusrclk_i), // input wire gt0_rxusrclk_i
.gt0_rxusrclk2_in (gt0_rxusrclk2_i), // input wire gt0_rxusrclk2_i
如果启用了8B10B解码,则还增加两个:
//---------------- Receive Ports - RX 8B/10B Decoder Ports -----------------
.gt0_rxcharisk_out (gt0_rxcharisk_out), // output wire [3:0] gt0_rxcharisk_out
.gt0_rxdisperr_out (gt0_rxdisperr_out), // output wire [3:0] gt0_rxdisperr_out
端口信号功能和发送端一样。
七、GTP眼图下板测试
7.1 IBERT核例化
在配置GTP之前,我们可以使用Xilinx提供的IBERT核来测试一下我们的GTP功能是否正常,通过收发器由外部回环进行自收自发而实现。就是将同一组收发器的 TX 和 RX 进行短接,TX 发送端通过发送某种特定序列的数据流,在 RX 接收端接收后,通过比对发送和接收的数据,从而得出误码率和眼图信息,来验证开发板 GTP 部分工作的稳定性和可靠性。
打开IP库搜索IBERT,然后打开。
7.2 连接光纤
配置好后直接打开设计例程,然后分配管脚直接下板;
首先插入一个SFP,然后用光纤把发送和接收端短接在一起,然后下载程序。
7.3 下载程序
下载后会自动弹出响应的收发器链接的通道以及速率。
选择7bit伪随机序列,然后复位后跑一段时间观察误码率一直为0,说明GTP运行没问题,我们更改一下伪随机序列为31bit,然后点击复位。
更换了31bit的伪随机序列误码率依然为0。
更换另一组GTP收发器测试依然误码率一直为0
7.4 创建眼图扫描
点击加号创建一个眼图扫描
中间蓝色部分区域越大,表示GTP 所对应的 PCB 高速电路的信号完整性越好。
八、配置GTP
既然板级上的GTP和连线没有问题,那我们就用官方例程实现GTP通信验证一下,开始例化IP核。
九、分析官方例程
9.1 打开设计例程
GTP的IP核例化成功后,我们打开设计例程:
- gtwizard_0_support :实例化GTP的顶层模块
- gtwizard_0_GT_USRCLK_SOURCE:产生用户使用时钟和发送时钟
- gtwizard_0_cpll_railing:收发器的断电复位和初始化模块
- gtwizard_0_common:例化收发器的原语
- gtwizard_0_common_reset:产生CPLL复位
- GT收发器
- 发送数据产生模块
- 接收数据校验模块
- 接收数据文件
- 发送数据文件
9.2 gtwizard_0_GT_USRCLK_SOURCE模块分析
源码如下:
`timescale 1ns / 1ps
//***********************************Entity Declaration*******************************
module gtwizard_0_GT_USRCLK_SOURCE
(
output GT0_TXUSRCLK_OUT, //用户发送时钟
output GT0_TXUSRCLK2_OUT, //用户发送时钟2分频
input GT0_TXOUTCLK_IN, //用户发送时钟来源
output GT0_RXUSRCLK_OUT, //用户接受使用时钟
output GT0_RXUSRCLK2_OUT, //用户接受使用时钟2分频
input GT0_RXOUTCLK_IN, //用户接受时钟来源,GUI导向里选择
input wire Q0_CLK1_GTREFCLK_PAD_N_IN, //外部输入差分参考时钟N端
input wire Q0_CLK1_GTREFCLK_PAD_P_IN, //外部输入差分参考时钟P端
output wire Q0_CLK1_GTREFCLK_OUT
);
`define DLY #1
//*********************************Wire Declarations**********************************
wire tied_to_ground_i;
wire tied_to_vcc_i;
wire gt0_txoutclk_i;
wire gt0_rxoutclk_i;
wire q0_clk1_gtrefclk /*synthesis syn_noclockbuf=1*/;
wire gt0_txusrclk_i;
wire gt0_rxusrclk_i;
//*********************************** Beginning of Code *******************************
// Static signal Assigments
assign tied_to_ground_i = 1'b0;
assign tied_to_vcc_i = 1'b1;
assign gt0_txoutclk_i = GT0_TXOUTCLK_IN;
assign gt0_rxoutclk_i = GT0_RXOUTCLK_IN;
assign Q0_CLK1_GTREFCLK_OUT = q0_clk1_gtrefclk;
//IBUFDS_GTE2:外部差分参考时钟进入缓冲器
IBUFDS_GTE2 ibufds_instQ0_CLK1
(
.O (q0_clk1_gtrefclk),
.ODIV2 (),
.CEB (tied_to_ground_i),
.I (Q0_CLK1_GTREFCLK_PAD_P_IN),
.IB (Q0_CLK1_GTREFCLK_PAD_N_IN)
);
// Instantiate a MMCM module to divide the reference clock. Uses internal feedback
// for improved jitter performance, and to avoid consuming an additional BUFG
BUFG txoutclk_bufg0_i
(
.I (gt0_txoutclk_i),
.O (gt0_txusrclk_i)
);
BUFG rxoutclk_bufg1_i
(
.I (gt0_rxoutclk_i),
.O (gt0_rxusrclk_i)
);
assign GT0_TXUSRCLK_OUT = gt0_txusrclk_i;
assign GT0_TXUSRCLK2_OUT = gt0_txusrclk_i;
assign GT0_RXUSRCLK_OUT = gt0_rxusrclk_i;
assign GT0_RXUSRCLK2_OUT = gt0_rxusrclk_i;
endmodule
正如前面3.1节所说,外部输入的参考差分时钟进入GT前会先进入一个缓冲器转变成单端时钟后给收发器使用,具体IBUFDS_GTE2原语的使用方法,看本文的第3.1.1节。
除此之外,输入给用户的TX时钟以及RX时钟的来源是自己通过GUI界面选的如下所示:
其中TXUSRCLK 和 TXUSRCLK2 关系以及RXUSRCLK 和 RXUSRCLK2 关系请看本文的第5.2.1节。
9.3 gtwizard_0_cpll_railing 模块分析
源码分析:
`timescale 1ns / 1ps
`define DLY #1
module gtwizard_0_cpll_railing #
(
parameter USE_BUFG = 0 //set it to 1 if you want to use BUFG
)
(
output cpll_reset_out,
output cpll_pd_out,
output refclk_out,
input refclk_in
);
(* equivalent_register_removal="no" *) reg [95:0] cpllpd_wait = 96'hFFFFFFFFFFFFFFFFFFFFFFFF;
(* equivalent_register_removal="no" *) reg [127:0] cpllreset_wait = 128'h000000000000000000000000000000FF;
wire refclk_i;
generate
if(USE_BUFG == 1)
begin
BUFG refclk_buf
(.O (refclk_i),
.I (refclk_in));
end
else
begin
BUFH refclk_buf
(.O (refclk_i),
.I (refclk_in));
end
endgenerate
assign refclk_out = refclk_i;
always @(posedge refclk_i)
begin
cpllpd_wait <= {cpllpd_wait[94:0], 1'b0};
cpllreset_wait <= {cpllreset_wait[126:0], 1'b0};
end
assign cpll_pd_out = cpllpd_wait[95];
assign cpll_reset_out = cpllreset_wait[127];
endmodule
- 本模块输出的cpll_pd_out信号是对 CPLL进行断电操作,高电平有效
上电后对CPLL进行断电96个周期然后拉低。 - cpll_reset_out信号是对CPLL进行高电平复位的信号,上电后,等待120个周期后,拉高8个时钟周期的cpll 复位信号,然后再拉低。
- refclk_out:在上面差分参考时钟进入IBUFDS_GTE2后转成单端信号,再进入BUFG或者BUFH,然后输出给收发器。整个模块仿真如下图所示:
9.4 gtwizard_0_common 模块分析
这个模块就是例化出硬核GT收发器,看不到源码,只能看到信号接入端口:
`default_nettype wire
`timescale 1ns / 1ps
`define DLY #1
//***************************** Entity Declaration ****************************
module gtwizard_0_common #
(
// Simulation attributes
parameter WRAPPER_SIM_GTRESET_SPEEDUP = "TRUE", // Set to "true" to speed up sim reset
parameter SIM_PLL0REFCLK_SEL = 3'b001,
parameter SIM_PLL1REFCLK_SEL = 3'b001
)
(
output PLL0OUTCLK_OUT,
output PLL0OUTREFCLK_OUT,
output PLL0LOCK_OUT,
input PLL0LOCKDETCLK_IN,
output PLL0REFCLKLOST_OUT,
input PLL0RESET_IN,
input [2:0] PLL0REFCLKSEL_IN,
input PLL0PD_IN,
output PLL1OUTCLK_OUT,
output PLL1OUTREFCLK_OUT,
input GTREFCLK1_IN,
input GTREFCLK0_IN
);
//***************************** Parameter Declarations ************************
parameter PLL0_FBDIV_IN = 4;
parameter PLL1_FBDIV_IN = 1;
parameter PLL0_FBDIV_45_IN = 5;
parameter PLL1_FBDIV_45_IN = 4;
parameter PLL0_REFCLK_DIV_IN = 1;
parameter PLL1_REFCLK_DIV_IN = 1;
// ground and vcc signals
wire tied_to_ground_i;
wire [63:0] tied_to_ground_vec_i;
wire tied_to_vcc_i;
wire [63:0] tied_to_vcc_vec_i;
//********************************* Main Body of Code**************************
assign tied_to_ground_i = 1'b0;
assign tied_to_ground_vec_i = 64'h0000000000000000;
assign tied_to_vcc_i = 1'b1;
assign tied_to_vcc_vec_i = 64'hffffffffffffffff;
//_________________________________________________________________________
//_________________________________________________________________________
//_________________________GTPE2_COMMON____________________________________
GTPE2_COMMON #
(
// Simulation attributes
.SIM_RESET_SPEEDUP (WRAPPER_SIM_GTRESET_SPEEDUP),
.SIM_PLL0REFCLK_SEL (SIM_PLL0REFCLK_SEL),
.SIM_PLL1REFCLK_SEL (SIM_PLL1REFCLK_SEL),
.SIM_VERSION ( "2.0"),
.PLL0_FBDIV (PLL0_FBDIV_IN ),
.PLL0_FBDIV_45 (PLL0_FBDIV_45_IN ),
.PLL0_REFCLK_DIV (PLL0_REFCLK_DIV_IN),
.PLL1_FBDIV (PLL1_FBDIV_IN ),
.PLL1_FBDIV_45 (PLL1_FBDIV_45_IN ),
.PLL1_REFCLK_DIV (PLL1_REFCLK_DIV_IN),
//----------------COMMON BLOCK Attributes---------------
.BIAS_CFG (64'h0000000000050001),
.COMMON_CFG (32'h00000000),
//--------------------------PLL Attributes----------------------------
.PLL0_CFG (27'h01F03DC),
.PLL0_DMON_CFG (1'b0),
.PLL0_INIT_CFG (24'h00001E),
.PLL0_LOCK_CFG (9'h1E8),
.PLL1_CFG (27'h01F03DC),
.PLL1_DMON_CFG (1'b0),
.PLL1_INIT_CFG (24'h00001E),
.PLL1_LOCK_CFG (9'h1E8),
.PLL_CLKOUT_CFG (8'h00),
//--------------------------Reserved Attributes----------------------------
.RSVD_ATTR0 (16'h0000),
.RSVD_ATTR1 (16'h0000)
)
gtpe2_common_i
(
.DMONITOROUT (),
//----------- Common Block - Dynamic Reconfiguration Port (DRP) -----------
.DRPADDR (tied_to_ground_vec_i[7:0]),
.DRPCLK (tied_to_ground_i),
.DRPDI (tied_to_ground_vec_i[15:0]),
.DRPDO (),
.DRPEN (tied_to_ground_i),
.DRPRDY (),
.DRPWE (tied_to_ground_i),
//--------------- Common Block - GTPE2_COMMON Clocking Ports ---------------
.GTEASTREFCLK0 (tied_to_ground_i),
.GTEASTREFCLK1 (tied_to_ground_i),
.GTGREFCLK1 (tied_to_ground_i),
.GTREFCLK0 (GTREFCLK0_IN),
.GTREFCLK1 (GTREFCLK1_IN),
.GTWESTREFCLK0 (tied_to_ground_i),
.GTWESTREFCLK1 (tied_to_ground_i),
.PLL0OUTCLK (PLL0OUTCLK_OUT),
.PLL0OUTREFCLK (PLL0OUTREFCLK_OUT),
.PLL1OUTCLK (PLL1OUTCLK_OUT),
.PLL1OUTREFCLK (PLL1OUTREFCLK_OUT),
//------------------------ Common Block - PLL Ports ------------------------
.PLL0FBCLKLOST (),
.PLL0LOCK (PLL0LOCK_OUT),
.PLL0LOCKDETCLK (PLL0LOCKDETCLK_IN),
.PLL0LOCKEN (tied_to_vcc_i),
.PLL0PD (PLL0PD_IN),
.PLL0REFCLKLOST (PLL0REFCLKLOST_OUT),
.PLL0REFCLKSEL (PLL0REFCLKSEL_IN),
.PLL0RESET (PLL0RESET_IN),
.PLL1FBCLKLOST (),
.PLL1LOCK (),
.PLL1LOCKDETCLK (tied_to_ground_i),
.PLL1LOCKEN (tied_to_vcc_i),
.PLL1PD (1'b1),
.PLL1REFCLKLOST (),
.PLL1REFCLKSEL (3'b001),
.PLL1RESET (tied_to_ground_i),
//-------------------------- Common Block - Ports --------------------------
.BGRCALOVRDENB (tied_to_vcc_i),
.GTGREFCLK0 (tied_to_ground_i),
.PLLRSVD1 (16'b0000000000000000),
.PLLRSVD2 (5'b00000),
.REFCLKOUTMONITOR0 (),
.REFCLKOUTMONITOR1 (),
//---------------------- Common Block - RX AFE Ports -----------------------
.PMARSVDOUT (),
//------------------------------- QPLL Ports -------------------------------
.BGBYPASSB (tied_to_vcc_i),
.BGMONITORENB (tied_to_vcc_i),
.BGPDB (tied_to_vcc_i),
.BGRCALOVRD (5'b11111),
.PMARSVD (8'b00000000),
.RCALENB (tied_to_vcc_i)
);
endmodule
9.5 gtwizard_0_common_reset模块分析
这个模块主要是用户给的系统时钟以及用户控制CPLL的复位,复位信号高电平有效,复位信号下降沿时,给CPLL一个周期的高电平复位
`timescale 1ns / 1ps
`define DLY #1
module gtwizard_0_common_reset #
(
parameter STABLE_CLOCK_PERIOD = 8 // Period of the stable clock driving this state-machine, unit is [ns]
)
(
input wire STABLE_CLOCK, //Stable Clock, either a stable clock from the PCB
input wire SOFT_RESET, //User Reset, can be pulled any time
output reg COMMON_RESET = 1'b0 //Reset QPLL
);
localparam integer STARTUP_DELAY = 500;//AR43482: Transceiver needs to wait for 500 ns after configuration
localparam integer WAIT_CYCLES = STARTUP_DELAY / STABLE_CLOCK_PERIOD; // Number of Clock-Cycles to wait after configuration
localparam integer WAIT_MAX = WAIT_CYCLES + 10; // 500 ns plus some additional margin
reg [7:0] init_wait_count = 0;
reg init_wait_done = 1'b0;
wire common_reset_i;
reg common_reset_asserted = 1'b0;
localparam INIT = 1'b0;
localparam ASSERT_COMMON_RESET = 1'b1;
reg state = INIT;
always @(posedge STABLE_CLOCK)
begin
// The counter starts running when configuration has finished and
// the clock is stable. When its maximum count-value has been reached,
// the 500 ns from Answer Record 43482 have been passed.
if (init_wait_count == WAIT_MAX)
init_wait_done <= `DLY 1'b1;
else
init_wait_count <= `DLY init_wait_count + 1;
end
always @(posedge STABLE_CLOCK)
begin
if (SOFT_RESET == 1'b1)
begin
state <= INIT;
COMMON_RESET <= 1'b0;
common_reset_asserted <= 1'b0;
end
else
begin
case (state)
INIT :
begin
if (init_wait_done == 1'b1) state <= ASSERT_COMMON_RESET;
end
ASSERT_COMMON_RESET :
begin
if(common_reset_asserted == 1'b0)
begin
COMMON_RESET <= 1'b1;
common_reset_asserted <= 1'b1;
end
else
COMMON_RESET <= 1'b0;
end
default:
state <= INIT;
endcase
end
end
endmodule
模块仿真如下所示:
9.6 gtwizard_0_GT_FRAME_GEN 模块分析
这个模块主要是产生发送数据,在tx_user_clk上升沿时从tx_data.dat文件中读一个数出来。
`timescale 1ns / 1ps
`define DLY #1
//***********************************Entity Declaration*******************************
(* DowngradeIPIdentifiedWarnings="yes" *)
module gtwizard_0_GT_FRAME_GEN #
(
// tx_data.dat里数据的数量
parameter WORDS_IN_BRAM = 512
)
(
// User Interface
output reg [79:0] TX_DATA_OUT, //发送的数据,位宽80是为了适应其它位宽的数据,实际上只有20bit接到GT收发器
output reg [7:0] TXCTRL_OUT, //控制符,表示现在输入的数据还是对齐码,这次没用到
// System Interface
input wire USER_CLK, //用户使用的发送时钟
input wire SYSTEM_RESET //GT发送初始化完成信号
);
//********************************* Wire Declarations*********************************
wire tied_to_ground_i;
wire tied_to_vcc_i;
wire [31:0] tied_to_ground_vec_i;
wire [63:0] tx_data_bram_i;
wire [7:0] tx_ctrl_i;
//***************************Internal Register Declarations***************************
reg [8:0] read_counter_i;
reg [79:0] rom [0:511];
reg [79:0] tx_data_ram_r;
(* ASYNC_REG = "TRUE" *) (* keep = "true" *) reg system_reset_r;
(* ASYNC_REG = "TRUE" *) (* keep = "true" *) reg system_reset_r2;
//*********************************Main Body of Code**********************************
assign tied_to_ground_vec_i = 32'h00000000; //都未使用的信号
assign tied_to_ground_i = 1'b0; //都未使用的信号
assign tied_to_vcc_i = 1'b1; //都未使用的信号
//___________ synchronizing the async reset for ease of timing simulation ________
always@(posedge USER_CLK)
begin
system_reset_r <= `DLY SYSTEM_RESET;
system_reset_r2 <= `DLY system_reset_r;
end
//____________________________ Counter to read from BRAM __________________________
always @(posedge USER_CLK)
if(system_reset_r2 || (read_counter_i == "111111111")) //从dat文件读出的读数量计数器,读完512个后清零
begin
read_counter_i <= `DLY 9'd0;
end
else read_counter_i <= `DLY read_counter_i + 9'd1;
// Assign TX_DATA_OUT to BRAM output
always @(posedge USER_CLK)
if(system_reset_r2) TX_DATA_OUT <= `DLY 80'h0000000000;
else TX_DATA_OUT <= `DLY {tx_data_bram_i,tx_data_ram_r[15:0]}; //每个时钟上升沿,发送一个数据
// Assign TXCTRL_OUT to BRAM output
always @(posedge USER_CLK)
if(system_reset_r2) TXCTRL_OUT <= `DLY 8'h0;
else TXCTRL_OUT <= `DLY tx_ctrl_i;
//________________________________ BRAM Inference Logic _____________________________
assign tx_data_bram_i = tx_data_ram_r[79:16];
assign tx_ctrl_i = tx_data_ram_r[15:8];
initial
begin
$readmemh("gt_rom_init_tx.dat",rom,0,511);
end
always @(posedge USER_CLK)
tx_data_ram_r <= `DLY rom[read_counter_i];
endmodule
打开gt_rom_init_tx.dat文件,我们观察发送的数据如下:
rx_data.dat文件内容和这一模一样。
9.7 gtwizard_0_GT_FRAME_CHECK 模块分析
该模块主要就是检查接受到的数据,然后找到K码对齐从而接收到K码之后的数据与rx_data.dat文件里的数据是否一直,如果不一致然后就累加ERROR_COUNT_OUT
always @(posedge USER_CLK)
begin
if( (system_reset_r2 == 1'b1) | (rxdata_or == 1'b0) ) begin
bit_align_r <= 1'b0;
end else begin
if( ({rx_data_r[9:0],rx_data_r2[19:10]} == START_OF_PACKET_CHAR) || (rx_data_r[19:0] == START_OF_PACKET_CHAR) )
begin
bit_align_r <= 1'b1;
end
end
end
always @(posedge USER_CLK)
begin
if(system_reset_r2) begin
sel <= 1'b0;
end else begin
if({rx_data_r[9:0],rx_data_r2[19:10]} == START_OF_PACKET_CHAR)
begin
sel <= 1'b1;
end
else if(rx_data_r[19:0] == START_OF_PACKET_CHAR)
begin
sel <= 1'b0;
end
end
end
always @(posedge USER_CLK)
begin
if(system_reset_r2) rx_data_r3 <= 'h0;
else
begin
if(sel == 1'b1)
begin
rx_data_r3 <= `DLY {rx_data_r[9:0],rx_data_r2[19:10]};
end
else
begin
rx_data_r3 <= `DLY rx_data_r2;
end
end
end
assign rx_data_has_start_char_c = (rx_data_aligned == START_OF_PACKET_CHAR) ;
assign error_detected_c = track_data_r3 && (rx_data_r_track != bram_data_r);
//We register the error_detected signal for use with the error counter logic
always @(posedge USER_CLK)
if(!track_data_r)
error_detected_r <= `DLY 1'b0;
else
error_detected_r <= `DLY error_detected_c;
//We count the total number of errors we detect. By keeping a count we make it less likely that we will miss
//errors we did not directly observe.
always @(posedge USER_CLK)
if(system_reset_r2)
error_count_r <= `DLY 9'd0;
else if(error_detected_r)
error_count_r <= `DLY error_count_r + 1;
//Here we connect the lower 8 bits of the count (the MSbit is used only to check when the counter reaches
//max value) to the module output
assign ERROR_COUNT_OUT = error_count_r[7:0];
十、仿真验证
然后直接开始仿真,先来观察数据发送模块
复位结束后,就每个时钟上升沿发送ROM里面的数,从00100、0027C、00403等开始,和tx_data.dat文件里的一样。接下来看接收端:
复位结束后,接收对接收到的数据进行打拍处理,这里可以看到,此时接收到的数据并不是我们发送过来的数据,因为此时的数据是未经过字节对齐的数据,因此需要有个模块来进行K码检测,源码如9.7所示,
再找到K码27C后,对齐后的数据就和我们发送数据一模一样了。
再找到K码后,延迟67个周期表示整个接收链路以及建立链接,传输正常。
十一、下板验证
我们配置一个aurora协议,启用8b10b编码,配置如下:
后面的配置同样默认就好,打开例程,添加ila核观察发送和接受数据,以及K码,分配好管脚后直接下板,先观察接收端:
我们找到K码的位置,K码为BC(K28.5),在低位,K码后面的数据为a0 09 08 07 等等。我们打开发送的数据rom看:
从数据文件可以看出,bc后面的数据和ila接受的数据一致,我们再来看发送端:
发送端也一致,至此整个GTP的通信没有问题。
参考
《UG482》