ARTICLE DETAIL

资讯详情

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

FPGA与RTL8211F的RGMII接口调试:时序约束与眼图实战

FPGA与RTL8211F的RGMII接口调试:时序约束与眼图实战 1. 项目缘起与整体设计思路搞FPGA网络通信的兄弟大多有过这种体验板子焊好了PHY芯片也贴上了代码里MAC层跑得飞起结果网口死活不通抓包工具里连个ARP包都看不到。十有八九问题就出在FPGA和PHY之间那条RGMII链路上。我这次拿RTL8211F这块千兆以太网PHY开刀把从引脚约束到时序收敛再到眼图调试的完整过程捋一遍尽量把踩过的坑和验证过的参数都摊开讲。RTL8211F是瑞昱的一款千兆以太网PHY芯片支持10/100/1000Mbps自适应RGMII接口是它的标配。RGMII全称Reduced Gigabit Media Independent Interface是GMII的精简版把数据位宽从8位砍到4位时钟频率翻倍到125MHz在双沿DDR采样下实现千兆速率。这个设计思路很讨巧引脚数从GMII的24根降到12根左右PCB布线压力小了很多代价是时序窗口变窄对FPGA侧的约束和PHY侧的延迟补偿要求更精细。为什么选RTL8211F而不是DP83867或者KSZ9031一方面是这块片子在国内开发板上用得极多参考原理图好找另一方面它支持RGMII内部延迟模式可以通过寄存器配置TX和RX的延迟省掉外部走线绕等长的麻烦。对于FPGA项目实战来说这意味着你可以在不改变PCB的前提下通过软件调延迟来适配不同的走线长度灵活性很高。整个项目的核心目标很明确让FPGA通过RGMII接口和RTL8211F正常收发数据跑通ping和UDP回环。适合谁看如果你正在做FPGA入门项目或者手头有个带RTL8211F的开发板想跑网络通信又或者你被RGMII时序约束搞得头大这篇内容应该能帮你省下不少查文档的时间。我假设你有基本的Verilog基础和Vivado使用经验但RGMII这块我会从零讲起小白也能跟上。2. RGMII接口核心细节与时序约束解析2.1 RGMII的信号组成与工作模式RGMII接口一共12根信号线分成发送和接收两组。发送方向从MAC到PHYTXC是发送时钟TXD[3:0]是4位数据TX_CTL是控制信号。接收方向从PHY到MACRXC是接收时钟RXD[3:0]是数据RX_CTL是控制。注意这里的数据位宽是4位不是8位所以每个时钟周期要传两次数据才能凑齐一个字节这就是DDR双沿采样的由来。TX_CTL和RX_CTL这两个控制信号是复用引脚在时钟上升沿它们承载的是TXEN和RXDV数据有效指示在下降沿承载的是TXER和RXER错误指示。这种复用方式在GMII里是不存在的GMII用独立的引脚分别传这些信号。RGMII这么设计就是为了省引脚但代价是控制逻辑要区分上升沿和下降沿FPGA侧的代码得用IDDR和ODDR原语来处理。时钟频率方面千兆模式下TXC和RXC都是125MHz双沿采样后数据率是125M×2×4bit1000Mbps。百兆模式下时钟降到25MHz十兆模式降到2.5MHz。RTL8211F会自动协商速率但FPGA侧的时钟管理模块需要根据协商结果切换时钟频率或者干脆用PLL生成125MHz然后靠PHY内部的分频来处理低速模式。我实测下来如果只跑千兆直接固定125MHz最省事。2.2 RTL8211F的延迟模式与寄存器配置RTL8211F最让人又爱又恨的地方就是它的RGMII延迟配置。PHY芯片内部有两个延迟模块TX延迟和RX延迟分别对应发送和接收方向。延迟的作用是让时钟和数据之间的相位关系满足采样要求。如果延迟配置不对要么数据采不到要么采到的是亚稳态。延迟模式通过PHY的扩展寄存器配置具体是Page 0xA43的寄存器0x11和0x15。0x11的bit1和bit2控制TX延迟0x15的bit3和bit4控制RX延迟。每个延迟模块可以设置成0ns、1.5ns、2.0ns或者由引脚电平决定。我一般推荐TX延迟设2.0nsRX延迟设1.5ns这个组合在大多数走线长度下都能工作。但如果你PCB走线特别长或者特别短可能需要微调。这里有个坑很多开发板的原理图里会把延迟配置引脚比如TXDLY和RXDLY直接上拉或下拉这时候PHY上电后会读取引脚状态来设置延迟寄存器配置可能被覆盖。所以你在调延迟之前先确认原理图上这两个引脚是怎么接的。如果是固定电平那就只能改硬件或者用寄存器强制覆盖。我手头这块板子是把TXDLY拉高、RXDLY拉低对应TX延迟2.0ns、RX延迟0ns结果RX方向死活不通后来用MDIO把RX延迟改成1.5ns才正常。2.3 FPGA侧的时序约束怎么写时序约束是RGMII调试的重头戏。Xilinx的IDDR和ODDR原语负责双沿采样但约束写不对的话Vivado会报一堆时序违例或者更坑的是不报违例但实际跑不通。我以Artix-7为例讲一下约束的写法。首先TXC和RXC需要创建时钟约束。假设系统时钟是200MHz通过MMCM生成125MHz的TXC和RXCcreate_clock -name sys_clk -period 5.000 [get_ports sys_clk_p] create_generated_clock -name tx_clk -source [get_pins mmcm_inst/CLKIN1] -divide_by 1 -multiply_by 5 -divide_by 8 [get_pins mmcm_inst/CLKOUT0] create_generated_clock -name rx_clk -source [get_pins mmcm_inst/CLKIN1] -divide_by 1 -multiply_by 5 -divide_by 8 [get_pins mmcm_inst/CLKOUT1]然后对TXD和TX_CTL设置output delay。RGMII的时序要求是数据在时钟边沿附近保持稳定具体来说发送方向数据要在TXC上升沿和下降沿之前稳定接收方向数据要在RXC边沿之后稳定。Xilinx的UG471文档里给了参考值但实际约束要根据PHY的延迟配置来调。set_output_delay -clock tx_clk -max 1.5 [get_ports {txd[*] tx_ctl}] set_output_delay -clock tx_clk -min -0.5 [get_ports {txd[*] tx_ctl}] set_input_delay -clock rx_clk -max 2.0 [get_ports {rxd[*] rx_ctl}] set_input_delay -clock rx_clk -min 0.5 [get_ports {rxd[*] rx_ctl}]这些数值不是拍脑袋来的。output delay的max值对应数据到达PHY的最晚时间min值对应最早时间。如果PHY的TX延迟是2.0ns那么数据在TXC边沿后2.0ns才到达采样点所以output delay的max可以设1.5ns左右给PCB走线留0.5ns余量。input delay则相反PHY的RX延迟是1.5ns数据在RXC边沿后1.5ns稳定所以input delay的max设2.0nsmin设0.5ns。注意这些约束值需要根据实际PCB走线延迟调整。走线每英寸约0.15ns延迟如果你的TXC和TXD走线长度差超过0.5英寸约束值就要相应修改。2.4 IDDR和ODDR原语的使用要点Xilinx的IDDR和ODDR原语是RGMII接口的标配。ODDR用于发送方向把FPGA内部的单沿数据转成双沿输出IDDR用于接收方向把双沿输入转成单沿数据。这两个原语的模式选择很关键。ODDR有OPPOSITE_EDGE和SAME_EDGE两种模式。RGMII发送要用SAME_EDGE模式这样数据在时钟上升沿和下降沿都能对齐输出。IDDR也有OPPOSITE_EDGE、SAME_EDGE和SAME_EDGE_PIPELINED三种模式。RGMII接收推荐用SAME_EDGE_PIPELINED这样上升沿和下降沿的数据会同时出现在Q1和Q2端口方便后续逻辑处理。ODDR #( .DDR_CLK_EDGE(SAME_EDGE), .INIT(1b0), .SRTYPE(SYNC) ) ODDR_tx_ctl ( .Q(tx_ctl_out), .C(tx_clk), .CE(1b1), .D1(tx_ctl_rise), .D2(tx_ctl_fall), .R(1b0), .S(1b0) );IDDR的例化类似但要注意Q1和Q2的对应关系。在SAME_EDGE_PIPELINED模式下Q1是上升沿数据Q2是下降沿数据两个数据在同一个时钟周期内有效。这样你就能用一个125MHz的时钟处理250Mbps的数据流后续的8位数据拼接和CRC校验都在这个时钟域下完成。3. 实操过程与核心环节实现3.1 硬件连接检查与上电测试在写代码之前先把硬件检查一遍。RTL8211F的供电有3.3V和1.05V两路1.05V是内核电压3.3V是IO电压。用万用表量一下这两个电压是否正常特别是1.05V很多板子用LDO生成如果LDO虚焊或者负载不够PHY根本不工作。然后检查晶振。RTL8211F需要25MHz的参考时钟这个时钟可以由外部晶振提供也可以由FPGA输出。我手头这块板子是用外部25MHz晶振所以FPGA侧不需要管。如果你的板子是用FPGA给PHY提供时钟那要确保FPGA上电后先输出25MHz否则PHY不启动。复位信号也很关键。RTL8211F的复位引脚是低有效复位时间至少10ms。我见过有人复位只给了1ms结果PHY内部寄存器没初始化完MDIO读出来全是0xFF。所以复位逻辑里加个计数器确保复位脉冲宽度足够。上电后先别急着跑数据用MDIO读一下PHY的ID寄存器。RTL8211F的ID是0x001CC916读出来对得上说明MDIO通信正常。如果读出来是0xFFFF或者0x0000检查MDIO的时钟频率。MDIO时钟不能超过2.5MHz我一般用2.5MHz分频系数根据系统时钟算。3.2 MDIO控制器设计与寄存器配置MDIO是管理接口用来配置PHY的寄存器。它只有两根线MDC是时钟MDIO是双向数据线。MDIO的帧格式是32位前导码2位操作码5位PHY地址5位寄存器地址2位 turnaround16位数据。前导码全是1操作码01表示读10表示写。写一个MDIO控制器不难但有几个细节要注意。首先是MDIO数据线的方向控制读操作时FPGA要释放数据线让PHY驱动写操作时FPGA驱动数据线。这个方向切换要在turnaround阶段完成否则会冲突。其次是MDC的时钟频率前面说了不能超过2.5MHz但也不能太低太低会影响配置速度。我一般用2.5MHz一个完整的MDIO帧大概需要20us左右。配置RTL8211F的流程是这样的先软复位然后配置延迟寄存器再配置自动协商广告寄存器最后启动自动协商。自动协商完成后读状态寄存器确认链路速度和双工模式。如果自动协商失败可以强制设置成1000M全双工模式但这样对端也必须强制否则可能协商不上。// MDIO写操作示例 task mdio_write; input [4:0] phy_addr; input [4:0] reg_addr; input [15:0] data; begin // 发送32位前导码 for (i 0; i 32; i i 1) begin mdc 0; mdio_out 1; mdc 1; end // 发送操作码01 // ... 省略具体位操作 end endtask3.3 发送通路实现与数据拼接发送通路的逻辑比较直接上层MAC送来8位数据FPGA把它拆成两个4位半字节分别在TXC的上升沿和下降沿输出。TX_CTL同理上升沿输出TXEN下降沿输出TXER。这里要注意数据顺序RGMII规定先发低4位还是高4位答案是先发低4位LSB first也就是TXD[3:0]在上升沿输出数据的bit[3:0]下降沿输出bit[7:4]。ODDR的D1和D2端口分别对应上升沿和下降沿的数据。所以D1接数据的低4位D2接高4位。TX_CTL的D1接TXEND2接TXER。这样ODDR就会在TXC的上升沿输出D1下降沿输出D2正好符合RGMII的要求。发送时钟TXC由FPGA的MMCM生成相位要调整到和数据对齐。我一般把TXC的相位设成0度然后通过output delay约束来保证建立保持时间。如果时序紧张可以把TXC相位调成90度或者180度让时钟边沿落在数据窗口中间。这个需要配合Vivado的时序报告来调没有万能值。3.4 接收通路实现与数据恢复接收通路比发送复杂因为RXC是PHY送过来的FPGA要用它来采样RXD和RX_CTL。这里有个关键问题RXC和RXD之间的相位关系是不确定的取决于PHY的RX延迟配置和PCB走线。所以IDDR的时钟要用RXC但RXC需要先经过BUFG或者BUFIO进入全局时钟网络否则时钟偏斜会很大。IDDR的输出Q1和Q2分别是上升沿和下降沿的数据把它们拼成一个8位数据{Q2, Q1}。注意顺序Q1是上升沿数据对应低4位Q2是下降沿数据对应高4位。所以拼接结果是{Q2, Q1}不是{Q1, Q2}。这个顺序搞反了的话数据会变成字节交换ping不通但抓包能看到ARP请求很容易误判。RX_CTL的恢复同理上升沿是RXDV下降沿是RXER。如果RXDV为高且RXER为低说明接收数据有效。如果RXER为高说明有错误要丢弃当前数据。接收通路还需要一个跨时钟域处理因为RXC是125MHz而FPGA内部逻辑可能跑在另一个时钟域。我一般用异步FIFO把数据从RXC域转到系统时钟域FIFO深度设512就够了。3.5 时序收敛与眼图测试代码写完后跑Vivado的时序分析。重点看RGMII相关的路径有没有违例。如果setup违例说明数据到达太晚可以减小output delay的max值或者调整TXC相位。如果hold违例说明数据变化太早可以增大output delay的min值。我遇到过最坑的情况是时序报告全绿但实际跑不通后来用示波器看眼图才发现TXC和TXD的相位差太大数据采样点落在跳变沿上。眼图测试需要示波器如果没有示波器可以用Vivado的IBERT或者ChipScope看内部信号。我一般先用ChipScope抓RXD和RXC看数据是否稳定。如果RXD在RXC边沿附近跳变说明采样点不对需要调RX延迟。调延迟的方法有两种改PHY寄存器或者改FPGA的IDDR时钟相位。我优先改PHY寄存器因为不动FPGA代码重新综合一次太耗时。实操心得调RX延迟时每次改0.5ns然后跑ping测试。如果ping通但丢包率高说明延迟接近临界值继续微调。如果完全不通可能是延迟方向反了试试把延迟从1.5ns改成0ns或者2.0ns。4. 常见问题与排查技巧实录4.1 链路不通的排查流程链路不通是最常见的问题排查要按顺序来不要东一榔头西一棒子。我的排查流程是这样的第一步查电源和时钟。用万用表量PHY的1.05V和3.3V用示波器量25MHz晶振是否起振。如果晶振不起振检查负载电容是否匹配RTL8211F要求晶振负载电容是12pF左右。第二步查MDIO通信。读PHY ID如果读不到检查MDC频率和MDIO上拉电阻。MDIO需要4.7kΩ上拉很多板子漏了或者用了10kΩ导致通信不稳定。第三步查自动协商状态。读BMSR寄存器地址0x01看bit2link status和bit5auto-negotiation complete。如果link status是0说明物理链路没建立检查网线和对端设备。如果auto-negotiation complete是0说明协商没完成检查广告寄存器配置。第四步查RGMII时序。如果MDIO正常、协商完成但数据不通基本就是RGMII时序问题。先用ChipScope抓TXC和TXD看发送数据是否有波形。如果没有波形检查ODDR的例化和时钟连接。如果有波形但PHY收不到调TX延迟。接收方向同理抓RXC和RXD调RX延迟。4.2 常见问题速查表现象可能原因排查方法解决方案MDIO读不到IDMDC频率过高示波器量MDC频率分频到2.5MHz以下MDIO读不到IDMDIO上拉电阻缺失万用表量MDIO对3.3V电阻补4.7kΩ上拉链路不通复位时间不足示波器量复位脉冲宽度延长复位到10ms以上链路不通晶振不起振示波器量晶振引脚换12pF负载电容ping不通但能抓包RXD数据顺序反了ChipScope抓RXD交换Q1和Q2拼接顺序ping丢包严重RX延迟临界逐步调RX延迟每次改0.5ns找最佳值时序违例output delay约束过紧看Vivado时序报告放宽max值或调TXC相位千兆不通百兆通TX延迟配置错误读PHY扩展寄存器设TX延迟2.0ns4.3 独家避坑技巧第一个坑RTL8211F的扩展寄存器需要先写Page寄存器才能访问。Page寄存器是0x1F写0xA43切换到扩展页写完后再切回Page 0。我见过有人直接读写0x11和0x15结果读出来全是0就是因为没切Page。第二个坑自动协商完成后PHY的链路状态需要时间稳定。我一般等2秒再读BMSR如果立即读可能link status还是0。这个等待时间在代码里用计数器实现别用延时函数FPGA里没有阻塞延时。第三个坑RGMII的TXC和RXC不能直接连到FPGA的普通IO必须走时钟专用引脚。Artix-7的时钟引脚是MRCC和SRCC查引脚约束文件确认。如果接错引脚Vivado会报错或者时钟偏斜过大。第四个坑PCB走线长度差要控制。TXC和TXD的走线长度差不要超过0.5英寸RXC和RXD同理。如果走线差太大延迟配置怎么调都调不好。我见过一块板子TXC比TXD长2英寸结果TX延迟调到最大都不通最后只能飞线解决。第五个坑Vivado的IDDR和ODDR原语在不同系列FPGA上名称可能不同。Artix-7是IDDR和ODDRKintex-7也一样但UltraScale是IDDRE1和ODDRE1。移植代码时要注意替换原语否则综合报错。4.4 性能优化与稳定性提升链路通了之后下一步是优化性能。千兆以太网的理论带宽是1000Mbps实际能跑到940Mbps左右就不错了。影响性能的因素有几个FIFO深度、跨时钟域处理、CRC校验延迟。FIFO深度我建议至少512字节太小的话突发数据会丢包。跨时钟域处理用异步FIFO读写时钟分别是RXC和系统时钟FIFO的读写指针用格雷码同步避免亚稳态。CRC校验可以在接收通路上做也可以在MAC层做我一般放在MAC层因为PHY送过来的数据已经去掉了前导码和SFDCRC校验更简单。稳定性方面建议加一个链路状态监控模块定期读BMSR寄存器如果link status从1变0说明网线被拔了或者对端重启了这时候要复位MAC层重新开始自动协商。这个监控模块用状态机实现每100ms读一次BMSR状态变化时触发复位。最后分享一个小技巧如果调试时没有示波器可以用FPGA的IOB内部上拉和下拉来粗略判断信号是否翻转。把RXD引脚设成上拉如果读出来是0说明PHY在驱动低电平如果读出来是1说明PHY没驱动或者驱动高电平。这个方法不能替代示波器但能快速判断PHY是否工作。这个项目后续还可以扩展成UDP协议栈在FPGA里实现ARP、IP、UDP的硬件解析做成一个完整的网络通信方案。我目前跑通了UDP回环下一步打算加个DDR缓存实现视频流传输。RGMII这块调通之后后面的协议栈就是纯逻辑活了难度不大但工作量不小。
返回列表