拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

FPGA实现千兆以太网:TEMAC+裕太微YT8531SH的RGMII调试实战

FPGA实现千兆以太网:TEMAC+裕太微YT8531SH的RGMII调试实战

这块板子上一代用的还是进口PHY,国产化之后换成了裕太微的YT8531SH,FPGA侧依旧是Vivado 2018.3里现成的Tri-Mode Ethernet MAC IP核。当时想得比较简单:PHY芯片换一换,改一下复位引脚和PHY地址,最多个别寄存器不兼容,微调一下驱动就行。实际动手之后才发现,问题远不止“换芯片”这么简单,从MDIO读不到ID,到RGMII时序怎么都不收敛,再到百兆通千兆不通,一路排下来我前后折腾了将近两周。

我把这个组合下踩过的坑、排查思路和最终能跑通的完整配置流程整理出来。这个方案的适用场景很明确:FPGA是Xilinx 7系列(Artix-7/Kintex-7等),Vivado版本2018.3,MAC层用PL侧的TEMAC IP核,PHY用YT8531SH的RGMII接口,网口速率要求千兆。整个流程可以拆成硬件摸底、IP核配置、XDC时序约束、上板调试四个阶段,每个阶段都有几个特别容易翻车的地方,这篇就当是给准备做国产PHY替代的同行提个醒。

1. 方案定位:TEMAC IP核与YT8531SH的配合逻辑

先用一句话讲清楚这个方案在做什么:以太网通信链路里,FPGA内部跑的是MAC层逻辑,PHY芯片负责物理层的编码、并串转换和线缆驱动。Xilinx在Vivado里提供了现成的MAC软核,也就是Tri-Mode Ethernet MAC,简称TEMAC,在纯PL逻辑里实现MAC层功能,对外支持GMII、RGMII、MII这些物理接口。板子上的YT8531SH就是接到TEMAC的RGMMI接口上,完成物理层收发。

TEMAC这个IP核的定位和Zynq里的PS端GEM还不一样。Zynq PS自带GEM控制器,那是ARM侧在做MAC,而这个方案用的是PL可编程逻辑实现MAC,不依赖PS,所以它更适合纯FPGA平台,或者是Zynq里不想占PS MIO的场合。Vivado 2018.3这个版本对应的TEMAC IP核界面和后续版本不太一样,如果拿新版Vivado里Ethernet Subsystem的习惯去操作会发现对不上号,这也是我建议先看清IP版本的原因。

再来看YT8531SH本身。这是裕太微电子的一款单端口千兆以太网PHY,支持10/100/1000Mbps三种速率,接口有RGMII和MII等版本。它和常见的进口PHY比如88E1512、DP83867在功能上是同一类东西,也提供标准的MDIO管理接口,所以从MAC侧看,寄存器读写、PHY地址配置、自动协商这些流程都是通用的。但“功能兼容”不等于“寄存器兼容”,YT8531SH的寄存器映射、上电自举配置引脚、复位时序要求都有自己的细节,直接用进口PHY那套初始化代码大概率不灵。

我在方案选型时还踩过一个方向性的坑:如果应用对接口带宽要求更高或者板上PCB走线限制了并行信号线数量,更合适的可能是SGMII接口的PHY,配合Vivado里的1G/2.5G Ethernet PCS/PMA IP核。但SGMII需要高速串行收发器,对FPGA资源占用、参考时钟要求都不一样,不是简单替换RGMII方案就能解决的。所以做选型评估时,先想清楚板上是RGMII还是SGMII,再决定IP核和PHY型号,这一步千万别省。

2. 上电之前的硬件细节:时钟、复位与PHY地址

很多人拿到板子第一件事就是打开Vivado开搞IP核,我建议反过来,先把硬件上的几个关键点确认清楚。RGMII接口在千兆模式下对时钟方向、电压域和复位时序极其敏感,硬件细节没理顺,后面所有调试都会变成猜谜。

2.1 千兆模式下的GTX_CLK到底从哪里来

RGMII在1000Mbps模式下,TXC也就是发送时钟,由MAC侧输出给PHY,频率125MHz。这个125MHz不是PHY自己产生的,而是由FPGA内部的时钟源产生后,通过IO输出到TXC引脚。很多参考设计里把这个时钟叫作GTX_CLK或者TXC,概念上指的是同一条路径。

这里有个典型的错误做法:有的板子设计想省一个FPGA时钟源,直接用PHY的CLKOUT_125M引脚输出作为MAC侧的工作时钟。听起来很合理,PHY反正要出125MHz,FPGA直接借用一下不就行了?但实际调试时会发现一个类似“鸡生蛋”的问题:PHY没有完成初始化和自动协商之前,这个CLKOUT引脚可能是高阻或者无输出,而MAC侧要收发数据就必须先有稳定时钟,结果就是整个链路起不来。就算PHY进入了正常状态,这个时钟的相位噪声和抖动也很难保证MAC侧逻辑的时序余量。

我的建议是TXC的125MHz时钟由FPGA内部MMCM/PLL产生,经过BUFG之后一方面送给TEMAC IP核作为参考时钟,另一方面通过ODDR原语输出到TXC引脚。板子上PHY的25MHz参考晶振是PHY自己用的,和这个126MHz MAC侧时钟互不替代。25MHz管脚如果没有正常起振,PHY完全无法工作,这一点上电之后可以先拿示波器量一下。

2.2 复位信号:低电平时间不够就是link不起来的根源

YT8531SH的复位引脚RESET_N低有效,手册一般会明确最小复位脉冲宽度,常见的是10ms级别,有些PHY是1ms。保险起见,我用的是上电后至少拉低100ms再释放的做法,因为复位期间PHY会采样strap引脚配置,如果复位释放时strap电平还没稳定,PHY地址、延迟模式这些东西就会随机,后面调试完全没法做。

另一个容易忽略的点是FPGA配置期间这个复位引脚的电平状态。如果复位信号由FPGA的普通GPIO控制,在FPGA未配置完成时,这个引脚默认可能是高电平,也就是PHY提前开始工作。这时候PHY采样的strap可能不对,后面就算FPGA配置完了再拉一次复位,时序也不够干净。我一般把PHY复位信号接到FPGA BANK上的一个普通IO,并且在FPGA设计里用一个上电延时计数器,至少在配置完成后等待100ms再释放PHY复位。有条件的话,在复位信号上加一个RC延迟电路做硬件兜底更稳妥。

2.3 PHY地址不是猜的,是strap引脚配出来的

PHY地址这个坑可以说是老生常谈,但每次都有人踩。YT8531SH的PHY地址由PHYAD相关strap引脚的上下拉状态决定,常见地址范围0x0到0x7。调试时先用万用表量一下strap引脚的上下拉电阻,确认板上配置出来的是几号地址,再把这个地址写进MDIO访问代码或者TEMAC的PHY地址寄存器。

我遇到的情况是板子上strap默认配出来的PHY地址是0x3,而习惯性代码里写的是0x1,结果MDIO读回来全是0xFFFF。后面核对了原理图才发现问题。另外,YT8531SH的strap引脚通常还同时控制RXDLY、TXDLY、LED工作模式等,这些会影响RGMII时序和状态指示,建议在硬件检查阶段就一并记录清楚。

MDIO总线本身的硬件细节也要注意。MDC是推挽输出,不需要上拉,而MDIO是双向数据线,必须要有上拉电阻,否则读操作会一直读到高电平。这个上拉电阻放在PHY侧还是FPGA侧都行,但不能没有。

3. Vivado 2018.3中的IP核配置:TEMAC的创建与参数选择

硬件细节确定了,接下来才轮到Vivado操作。IP核配置本身不复杂,但参数选错会导致后面一系列连锁问题,比如用户接口选了一个不确定怎么接的、时钟域选项理解错了,最终综合出来的设计根本跑不起来。

3.1 在IP Catalog里定位正确的IP核

在Vivado 2018.3的IP Catalog里搜索“ethernet”,会出现几个容易混淆的IP核,包括Tri-Mode Ethernet MAC、AXI Ethernet、1G/2.5G Ethernet PCS/PMA or SGMII。这里要选的就是第一个Tri-Mode Ethernet MAC,在7系列器件下它是软核实现MAC功能。AXI Ethernet更像是给嵌入式处理器集成用的,内部其实也是TEMAC加AXI接口封装,但直接定制时用TEMAC更灵活。SGMII那一个是配合高速串行收发器用的,RGMII场景不适用。

这个IP核在Vivado 2018.3里还是老界面,和2019.2之后的Ethernet Subsystem完全不一样。如果之前只熟悉新版本,很可能在配置页面里找不到对应选项,这一点留意一下就好。

3.2 物理接口、速率和用户侧接口的搭配

双击Tri-Mode Ethernet MAC进入定制界面,第一个要选的是Physical Interface,这里必须选择RGMII。如果选成GMII或者MII,后面引脚数量、时钟方向全部都会变。速率建议直接选10/100/1000Mbps自适应,虽然只跑千兆可以选单一1000Mbps,但保留低速率模式对调试很有利,后面排查问题的时候可以降速验证。

用户侧接口我建议选AXI4-Lite,这是一个管理接口,通过它读写TEMAC内部的寄存器以及MDIO控制器。数据通路方面,如果不想额外接AXI DMA这类重量级模块,可以选GMII接口作为用户侧数据输出,自己写一个简单的MAC用户层逻辑来收发帧。这个方式结构最简单,但也意味着用户侧时序要自己处理。如果想直接用AXI4-Stream接DMA,数据通路会更规范,不过工作量会大不少。

MDIO控制器建议勾选Enable MDIO Master,让TEMAC内部自带MDIO主机功能,这样可以通过AXI4-Lite寄存器访问PHY,调试阶段会非常方便。很多一开始说“MDIO没有信号”的问题,往往不是接口问题,而是IP核根本没配MDIO主模式。

3.3 直接用IP的example design可以省掉一半工作量

IP配置完成后,在Vivado的Sources窗口右键点击生成的IP核,选择Open IP Example Design,会生成一个完整的参考设计工程。这个example design不只是拿来综合仿真看的,更重要的是里面有现成的XDC约束文件,针对RGMII接口的时钟、输入延迟、输出延迟约束都写好了。

我后来在这类项目里的固定做法是:先打开example design,把里面的约束文件复制到自己工程,改掉引脚位置和IOSTANDARD,再重新跑综合实现。直接自己从零写RGMII时序约束很容易漏掉某个关键约束,而Xilinx官方示例里的数值虽然是给标准RGMII场景准备的,但思路一定是正确的,基于它去调整远比从零开始高效。

4. RGMII时序与XDC约束:最容易翻车也最容易糊弄过去的部分

RGMII接口在FPGA设计里是一个很有意思的东西:表面上看只有8根数据线加上时钟和控制线,好像不复杂,但你如果只用顶层逻辑做数据缓冲,完全不做时序约束,仿真肯定一切正常,上板之后各种随机错误就冒出来了。这个设计的核心难点不在逻辑功能,而在时序,RGMII的时序如果没收敛,哪怕功能逻辑再正确也白搭。

4.1 RGMII的双沿采样机制

RGMII在千兆模式下,TXD[3:0]和TX_CTL在TXC的上升沿和下降沿都会被采样,等效数据率是125MHz乘2再乘4位,也就是1Gbps。发送方向,TXC、TXD、TX_CTL都从MAC输出,要求在FPGA的IOB里用ODDR把并行数据转成双沿输出。接收方向,RXC、RXD、RX_CTL都从PHY输入,FPGA要在RXC的上下升沿用IDDR采样。

Vivado的TEMAC IP核内部其实已经把ODDR和IDDR例化好了,不需要使用者自己写原语。但如果哪一天你决定不走TEMAC,自己用GMII逻辑加外部转换来实现RGMII,那就必须自己写ODDR和IDDR原语,而且要把它们放到IOB中。这也是很多人对RGMII实现疑惑的地方——到底要不要自己写IDDR、ODDR?答案是看IP核内部是否已经集成。

自己写IDDR原语大概是这个样子,用SAME_EDGE_PIPELINED模式可以让上下沿采到的数据在同一个时钟沿之后稳定输出,方便后续逻辑处理:

IDDR #( .DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), .INIT_Q1(1'b0), .INIT_Q2(1'b0), .SRTYPE("SYNC") ) u_iddr_rxd0 ( .Q1(rxd_r[0]), .Q2(rxd_f[0]), .C(rgmii_rxc), .CE(1'b1), .D(rgmii_rxd[0]), .R(1'b0), .S(1'b0) );

输出侧用ODDR:

ODDR #( .DDR_CLK_EDGE("SAME_EDGE"), .INIT(1'b0), .SRTYPE("SYNC") ) u_oddr_txd0 ( .Q(rgmii_txd[0]), .C(gtx_clk), .CE(1'b1), .D1(txd_r[0]), .D2(txd_f[0]), .R(1'b0), .S(1'b0) );

4.2 时序约束的思路:数据要落在采样窗口的中心

RGMII时序约束的本质是告诉Vivado,数据相对于时钟在PCB和PHY内部经历了多少延迟,从而让布局布线工具知道要保证多大的建立时间和保持时间余量。

TX方向的约束:TEMAC把TXC、TXD、TX_CTL都作为ODDR输出,TXC是时钟输出,TXD是数据输出。对PHY来说,它在TXC沿采样TXD,所以FPGA需要保证TXD和TXC之间的相位关系满足PHY的建立保持时间要求。这就是set_output_delay要做的事情。

RX方向的约束:PHY输出的RXC和RXD是同步的,FPGA的IDDR在RXC沿采样RXD,所以需要告诉工具,RXD相对RXC的输入延迟是多少,也就是set_input_delay。

以下是一份可用的示意约束,具体数值要以YT8531SH数据手册里的时序参数为准。比如GTX_CLK周期8ns,如果PHY的建立时间要求1ns,PCB走线偏差约0.5ns,那output delay的max大概在8-1-0.5=6.5ns左右。用这种方式反推会比网上随便抄一组数靠谱得多:

# 时钟约束 create_clock -period 8.000 -name gtx_clk [get_ports gtx_clk] create_clock -period 8.000 -name rgmii_rxc [get_ports rgmii_rxc] # TX方向:数据在gtx_clk双沿输出 set_output_delay -clock [get_clocks gtx_clk] -max 2.0 [get_ports {rgmii_txd[*] rgmii_tx_ctl}] set_output_delay -clock [get_clocks gtx_clk] -min -1.0 [get_ports {rgmii_txd[*] rgmii_tx_ctl}] set_output_delay -clock [get_clocks gtx_clk] -clock_fall -max 2.0 [get_ports {rgmii_txd[*] rgmii_tx_ctl}] set_output_delay -clock [get_clocks gtx_clk] -clock_fall -min -1.0 [get_ports {rgmii_txd[*] rgmii_tx_ctl}] # RX方向:在rgmii_rxc双沿采样 set_input_delay -clock [get_clocks rgmii_rxc] -max 2.5 [get_ports {rgmii_rxd[*] rgmii_rx_ctl}] set_input_delay -clock [get_clocks rgmii_rxc] -min 0.0 [get_ports {rgmii_rxd[*] rgmii_rx_ctl}] set_input_delay -clock [get_clocks rgmii_rxc] -clock_fall -max 2.5 [get_ports {rgmii_rxd[*] rgmii_rx_ctl}] set_input_delay -clock [get_clocks rgmii_rxc] -clock_fall -min 0.0 [get_ports {rgmii_rxd[*] rgmii_rx_ctl}]

实际项目里我强烈建议把TEMAC的example design约束文件先考进来,再根据YT8531SH手册调整数值。不要嫌麻烦,RGMII的时序收敛在整个设计里是最容易出问题的一环,而且一旦出了问题,症状表现为随机丢包、CRC错误,排查起来非常痛苦。

4.3 RXDLY/TXDLY是YT8531SH上最关键的寄存器开关

这里必须单独讲一下PHY内部的延迟配置,因为这是国产PHY和不少进口PHY调试思路差异最大的地方。RGMII协议演进到2.0版本之后,允许MAC或者PHY在发送/接收路径上插入延迟来保证数据在时钟沿中心对齐。YT8531SH通过strap引脚或者MDIO寄存器控制RXDLY和TXDLY两个选项:

  • TXDLY开启时,PHY在发送路径上插入约2ns延迟,这样MAC侧看到的TXD相对TXC会有更好的对齐。
  • RXDLY开启时,PHY在接收路径上插入约2ns延迟,这样FPGA侧IDDR采样RXD时能采到稳定的数据窗口。

我们板子上YT8531SH的strap默认把RXDLY配置为开启,TXDLY配置为关闭。这个组合在多数情况下是合理的,因为TEMAC IP核输出TXC时本身就能和TXD保持较好的对齐关系,而PHY接收方向如果不插入延迟,FPGA用IDDR采RXD很容易采到跳变沿。如果调试中发现1024字节大包不停CRC错误,先把RXDLY确认一遍,这是最常见的原因。

如果strap已经固定无法改,也可以通过MDIO寄存器动态改RXDLY/TXDLY。具体寄存器地址和位域需要对应到YT8531SH的数据手册,不同批次可能略有差异,调试时以手册为准,不要套用其他PHY的寄存器地址。

5. 管脚分配、电平标准与实现阶段的报错排查

IP核和时序约束搞定之后,进入实现阶段。这个阶段同样有坑,很多错误信息看起来莫名其妙,实际上都是管脚分配、电平标准、时钟专用引脚这些物理属性没有匹配好。

5.1 电平标准:VDDIO没对上一切白搭

YT8531SH的IO电源VDDIO常见有2.5V和3.3V两种配置。FPGA这边,7系列器件的管脚分成HR和HP两类bank,HR bank支持2.5V/3.3V这类电平,HP bank最高支持到1.8V。如果PHY的IO电压是3.3V,而RGMII信号接到了FPGA的HP bank上,那属于硬件设计问题,管脚约束里再怎么设IOSTANDARD也没用,因为电平根本不在一个域里。

正确的做法是在XDC里根据PHY实际IO电压设置IOSTANDARD,PHY侧VDDIO如果是2.5V就写LVCMOS25,如果是3.3V就写LVCMOS33。这个一定要和硬件原理图核对,不能想当然。

5.2 rgmii_rxc必须进时钟专用引脚

这是很多人在实现阶段第一次报错的地方。rgmii_rxc是PHY输出的时钟信号,FPGA内部要拿它作为IDDR的采样时钟,因此物理上必须连接到FPGA的MRCC或SRCC引脚。如果PCB布线时为了省事把rgmii_rxc接到了普通IO,实现阶段会报CLOCK_DEDICATED_ROUTE错误,提示时钟信号没有走专用时钟路径。

有一个临时规避手段是在XDC里加set_property CLOCK_DEDICATED_ROUTE ANY_CMT_BUFG,让工具绕过这个规则强行布线。但这个做法只适合临时调试,产品上不建议用,因为绕线时钟的skew和抖动不可控,时序余量会很差。如果遇到类似Drc rtstat-2的报错,本质上也和引脚分配、时钟树布局冲突相关,先检查是不是有信号被错误分配到了专用资源冲突的位置。

5.3 实现阶段的DRC和bitstream失败

如果实现了但迟迟出不了bitstream,或者报DRC错误,排查顺序一般是这样的:先看有没有未约束的输入输出端口,这是最常见的问题;再看有没有IOSTANDARD不一致,同一个bank里混用了不同电压等级的电平标准会直接报错;然后看时序报告,如果WNS是负数,说明时序约束没有满足,RGMII双向时序是最先需要怀疑的地方。

我记得有一次综合和实现都过了,但generate bitstream一直失败,查了半天发现只是有一个输出端口忘记加IOSTANDARD,Vivado默认给了个LVCMOS12,和物理bank上3.3V电平完全不匹配。这类低级错误,说多了都是泪,但排查时往往最费时间。

6. 上板调试:从MDIO回读到PING通

配置全部完成后进入上板阶段。调试必须按照从底层到上层的顺序来,如果一上来就拿着PING工具去试网口,一旦不通会根本不知道故障在哪一层。我的顺序是先验证MDIO链路,再验证PHY状态,接着做回环测试,最后才是真正的网络通信。

6.1 MDIO读PHY ID:一切验证的基础

上板后第一件事,通过TEMAC的AXI4-Lite寄存器或者直接写一个简易MDIO控制器,读取YT8531SH的PHY ID寄存器。读操作返回的数值应该和YT8531SH数据手册里的PHY ID一致。如果读到0xFFFF,先检查PHY地址和MDIO上拉;如果读到0x0000,先检查PHY供电和复位释放。只有MDIO通路正常,后面对PHY的寄存器配置才有意义。

MDC时钟频率也值得注意。MDIO标准规定MDC最高一般不超过2.5MHz,如果TEMAC内部配置MDC太快,或者是通过软件模拟MDIO时序时延时不够,PHY会表现出时好时坏、偶尔读不到的现象。

6.2 link状态、PHY回环和MAC回环

MDIO通了之后,插上网线,观察PHY的link状态寄存器。YT8531SH的BMSR寄存器(地址0x01)bit2表示link状态,link up时这一位应该是1。如果link一直起不来,除了检查网线和对端设备,还要看PHY的自动协商状态、速率协商结果是不是千兆。

然后做回环测试。PHY内部digital loopback是把PHY接收通路直接转发到发送通路,这样不依赖外部网线就能验证MAC侧收发通路是否正常。操作方式是修改PHY寄存器0的bit14为1,进入digital loopback模式。在这个模式下,FPGA发出的数据经过PHY后直接回到FPGA的接收侧。如果数据能正常收到且CRC校验通过,说明MAC到PHY之间的RGMII接口基本没问题。再恢复bit14为0,进入正常模式。

TEMAC IP内部也支持MAC级回环,通过TEMAC寄存器配置,可以在不经过PHY的情况下把发送数据回路接收出来。这个测试用来区分问题是出在MAC逻辑还是PHY/RGMII物理层。如果MAC回环通过但PHY回环失败,问题多半在RGMII接口时序或者PHY配置上。

6.3 从ARP到PING:网络层验证和CRC排查

当回环测试全部通过,就可以接真实网络设备验证了。先用PC配置一个和板卡同一网段的静态IP,板卡通过ARP请求获得PC的MAC地址,然后PING板卡的IP,确认双向链路正常。如果PING不通,优先抓包看有没有ARP请求和响应。抓包工具推荐Wireshark,把PC网口抓包和板上TX/RX统计寄存器对应起来,很快能定位是哪一侧的帧没发出去。

PING通之后建议再做一次大流量测试,不要只测几个ICMP包。实际项目里很多问题在小包场景下不暴露,一旦持续跑数据,CRC错误、丢包率就开始显现。如果对端收到大量CRC错误,说明本端TX路径上有问题;如果本端rx_crc_error计数持续增长,说明RX路径采样有问题。排查RX方向的首选操作是调整IODELAY或者修改PHY的RXDLY寄存器,找到错误计数最低的配置点。这个调整过程本质上是在找数据采样窗口的位置,多花一点时间耐心试,值得。

我在调试中还试过一次“PHY芯片背靠背”的方式:用两个板卡,A板的TXD直接连到B板的RXD,绕过真实的网络变压器和网线,用来验证两侧FPGA的RGMII逻辑是否兼容。这个方式对于摸底两端设计是否一致非常直观。如果两个板卡配置的是同一套逻辑但出现奇偶字节序不一致的问题,排查起来效率会高很多。

7. 最后再分享一点个人经验

这套TEMAC加YT8531SH的方案,经过完整调试之后其实非常稳定,但前提是每一步都要按流程来。我给还没有开始的同行一个建议:先把Vivado生成的example design完整跑一遍,确认官方IP在这个板子上工作正常,再往里面集成自己的业务逻辑。很多问题其实是出在自己的逻辑和IP交接上,用example design做隔离能帮你划分责任边界。

调试过程中记录好YT8531SH每个关键寄存器的默认值和最终配置值。特别是RXDLY/TXDLY、PHY地址、LED工作模式这些,不同批次芯片的strap差异可能导致表现不一致,有一份寄存器配置记录在手,换板子或者换芯片批次后能省下大量重新排查的时间。

这个项目做完之后,我的体会是国产PHY芯片本身没有什么不靠谱的地方,真正的问题是我们在开发时容易沿用旧硬件环境下的调试经验和思维定式。电阻位置、复位时序、PHY延迟开关,这些细节一个不对都会带来看似玄学的网络故障。希望这篇踩坑记录能帮你少走点弯路。

返回列表