ARTICLE DETAIL

资讯详情

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

FPGA实战:从ILA抓包到PC Ping通,RGMII接口调试全记录

FPGA实战:从ILA抓包到PC Ping通,RGMII接口调试全记录 我从一个反直觉的现象开始说RGMII 这个接口看 Xilinx 官方文档和 PHY 芯片手册时总觉得无非就是 4 根数据线加一对时钟逻辑也不复杂DDR 双沿采样而已。但真到了在 Artix-7 上动手从 ILA 抓信号开始到 PC 端ping通第一个包中间踩过的坑比预想的多一个量级。这篇文章就是完整记录这条链路——从 ILA 抓包思路、RGMII 时序约束、MAC 层帧处理、ARP/ICMP 最小协议栈实现到最终 PC 与 FPGA 互通的全过程。适合正在调 RGMII 或者准备接触以太网接口的 FPGA 开发者我把每个环节的波形、寄存器、代码片段和排查逻辑都展开讲。1. RGMII 不是看文档就能跑通的接口——先弄清它的时序本质1.1 接口信号与工作模式比 GMII 少了一半线代价是时序敏感RGMIIReduced Gigabit Media Independent Interface是 Reduced 版的 GMII把 8 位数据总线压缩到 4 位利用时钟的双沿DDR来传输完整字节。信号组很固定方向信号名说明TXTX_CLK125MHz 吉比特速率或 25/2.5MHz 十百兆速率TXTXD[3:0]发送数据上升沿发低 4 位下降沿发高 4 位TXTX_CTL上升沿发 TX_EN下降沿发 TX_EN XOR TX_ERRXRX_CLK由 PHY 提供与 RX 数据同步RXRXD[3:0]接收数据同样 DDR 双沿RXRX_CTL指示数据有效/错误状态MDIOMDC / MDIO管理接口配置 PHY 寄存器很多初次接触的人会问直接照着手册写个always (posedge clk)不就行了吗问题就出在“双沿”和“时钟相位”上。在 1G 模式下TX_CLK 是 125MHz但每个沿都有 4 位数据2.5ns 就要完成一组信号的建立保持。FPGA 内部资源可以处理这种速率但 IO 路径上的延迟、PCB 走线长度差异、PHY 内部的延迟都会破坏时序。这也是为什么 RGMII 调试中 90% 的问题都集中在时序而不是逻辑功能上。1.2 时钟相位与 TSKEWRGMII 调试里最大的坑RGMII 规范定义中PHY 输出的 RX_CLK 与 RXD/RX_CTL 是“中心对齐”的也就是说数据的跳变沿在时钟沿附近这对 FPGA 内部 IDDR 采样极其不友好——你无法直接在时钟沿采到稳定的数据。解决思路有两类。第一类是用 FPGA 的 IDELAY 对数据线做延迟把数据往中间推等效转换成“边沿对齐”让时钟沿正好落在数据稳定窗口内。第二类更常见也很巧妙在采样时同时使用时钟的上升沿和下降沿或者把时钟反向用IDDR原语的SAME_EDGE_PIPELINED模式对齐到同一个时钟沿做处理。我实际验证下来比较稳妥的方案是在 RX 方向加 IDELAY。Artix-7 的 IDELAY 每个 tap 约 78ps具体以硅片实测为准一个数据窗口 2.5ns理论上需要至少 32 个 tap 的覆盖范围通常建议从中间值开始扫描调试。具体怎么扫描后面第 2 节结合 ILA 实测讲。2. 硬件准备Artix-7 开发板上那些容易被忽略的细节2.1 PHY 芯片选型与复位、时钟配置我用的板子上 PHY 是 Marvell 88E1512和常见的 RTL8211、KSZ9031 大同小异MDIO 寄存器布局基本兼容。选 PHY 时重点留意三件事默认模式上电时 PHY 通过引脚电平选择接口模式很多 PHY 默认是 GMII/MII而不是 RGMII需要确认 strap 引脚配置或者通过 MDIO 写寄存器切换。时钟源PHY 参考时钟可以是 25MHz 晶振配合内部 PLL 倍频也可以由 FPGA 提供 125MHz。无论哪种PHY 输出的 RX_CLK 和 TX_CLK 必须干净最好测量一下实际频率。复位时序PHY 复位后需要等待一段时间通常至少 1ms有的 PHY 要 10ms才能访问 MDIO。这个等待如果放到 FPGA 逻辑里做记得上电后把所有 PHY 寄存器读取都放在延时之后。我当时第一次上电MDIO 读回来全是0xFFFF排查了很久发现是复位引脚被 FPGA 拉低之后逻辑里只等了 100us 就开始读寄存器。后来把延时加到 5ms问题直接消失。这里强烈建议MDIO 驱动初始化时先跑一个“读 PHY ID 寄存器0x02/0x03”的自检流程能读到0x0141这类 ID 就说明通路正常。2.2 管脚约束与时钟约束实操管脚约束属于“写了不一定对但不写肯定不行”的部分。RGMII 数据线和控制线要约束在 HRHigh Range或 HPHigh Performance bank 的合适电平标准上。我用的是 1.8V LVCMOS如果你板子是 2.5V/3.3V电平标准要对应改否则时序收敛会很吃力。时钟约束上TX_CLK 如果是从 FPGA 内部分频/倍频产生的要创建create_generated_clockRX_CLK 是 PHY 送进来的必须create_clock并且set_input_delay要按 RGMII 中心对齐的特性来设置。我最初犯过一个典型错误只约束了系统时钟没管 RX_CLK结果 ILA 抓波形时看到的数据全是乱的时序报告也从来没检查过这条路径。约束文件的参考骨架set_property -dict {PACKAGE_PIN T25 IOSTANDARD LVCMOS18} [get_ports {eth_rxc}] set_property -dict {PACKAGE_PIN R25 IOSTANDARD LVCMOS18} [get_ports {eth_rx_ctl}] set_property -dict {PACKAGE_PIN P25 IOSTANDARD LVCMOS18} [get_ports {eth_rxd[3]}] # ... 其他数据引脚 create_clock -name eth_rx_clk -period 8.000 [get_ports eth_rxc] set_input_delay -clock eth_rx_clk -max 2.4 [get_ports {eth_rxd[*] eth_rx_ctl}] set_input_delay -clock eth_rx_clk -min 0.6 [get_ports {eth_rxd[*] eth_rx_ctl}]这里的 input delay 值需要微调不能照抄。我见过不同 PHY、不同走线长度下最优延迟窗口差异超过 1ns。3. ILA 抓包的完整流程从“抓不到信号”到“看懂波形”3.1 抓不到信号先查这些别急着怀疑 ILA 配置网上搜“ILA 抓信号没有反应”能出来一大片帖子说明这是个普适问题。我自己也折腾过一晚上最后发现原因根本不是 ILA 没配置对而是信号被综合器优化掉了。FPGA 综合工具会把没有扇出的中间信号直接优化掉ILA 自然什么都抓不到。两个解决方法一是在 RTL 里给信号加(* MARK_DEBUG TRUE *)属性二是在综合设置里把-keep_equivalent_registers加上。我习惯两种都用。(* MARK_DEBUG TRUE *) reg rx_axis_tvalid; (* MARK_DEBUG TRUE *) reg [51:0] rx_axis_tdata;另一个常见原因是 ILA 的工作时钟。ILA 的采样时钟必须是你想抓信号的真实时钟域不能图省事直接挂在系统时钟上。比如你要抓 RX 方向的数据PHY 给的 RX_CLK 是 125MHzILA 就应该用 RX_CLK抓 TX 方向再用 TX_CLK。两个方向分开两个 ILA或者用异步 FIFO 把数据同步到一个统一时钟再抓都行。但要注意跨时钟域信号直接抓波形在 ILA 里看起来会有亚稳态干扰判断。3.2 ILA 触发条件和采样深度的设置心得触发条件设计是调试效率的分水岭。第一次调 RGMII 时我用rx_ctl的上升沿做触发结果抓到的全是空闲态和以太网帧头后续的数据因为采样深度不够被截断了一半。更合理的触发策略是先用rx_axis_tvalid 1接收端 AXIS 接口的数据有效信号做触发同时把采样深度拉到 4096 或 8192这样能完整看到一整包以太网帧的前半部分确认链路通畅后再缩小触发范围去抓特定特征比如 ARP 请求的以太网类型字段0x0806。ILA 的采样深度不是随便选大的。深度越大片上 BRAM 占用越高而且在嵌入式逻辑分析仪里看波形也更卡。1024 深度适合抓短时序2048~4096 适合抓以太网帧再深就意义不大了因为你可以通过触发条件精确命中想要的事件。3.3 看懂 RGMII 波形高低 nibble 拼接才是关键RGMII 波形最初看起来非常劝退一个时钟周期内数据线在上升沿采到的是一字节的低 4 位下降沿采到高 4 位。直接看rxd[3:0]波形会看到每个时钟周期出现两个不同的 nibble完全没有以太网帧的直观样子。我当时的处理办法是写一个小的“nibble 拼接模块”// RGMII RX nibble-to-byte assembly always (posedge eth_rxc) begin // IDDR for each data line, using SAME_EDGE_PIPELINED mode end wire [7:0] rx_data_byte {rx_data_high_nibble[3:0], rx_data_low_nibble[3:0]};rx_data_low_nibble是上升沿采到的低 4 位rx_data_high_nibble是下降沿采到的高 4 位。按{high, low}拼起来才是一个完整的字节。如果用IDDR的SAME_EDGE_PIPELINED模式两个 nibble 会在同一个时钟沿一起送到逻辑侧省去手动打拍对齐的麻烦。拼接完成后ILA 里显示的rx_axis_tdata应该能直接看到类似55 55 55 55 55 55 55 5D前导码SFD的开头。如果你看到的却是D5开头或者55和5D错位说明 nibble 顺序或者字节序拼接错了这是 RGMII 调试最典型的失败模式之一。4. 收发通路设计从 MAC 帧到 ARP、ICMP 的最小可用的协议栈4.1 TX 方向发送链路为什么先回 ARP 再谈 PingPC 要 ping 通 FPGA第一步不是发 ICMP Echo Request而是先发 ARP 请求询问“谁有 192.168.1.10 的 MAC 地址”。我一开始没意识到这个细节代码里只实现了 ICMP 回显结果 PC 端一直显示“请求超时”因为 ARP 根本没回应。所以 FPGA 端做以太网第一步是完整解析 ARP 请求并回复 ARP 响应。ARP 请求的帧结构是目标 MAC 为广播地址FF:FF:FF:FF:FF:FF以太网类型0x0806ARP 头里操作码0x0001表示请求0x0002表示响应。FPGA 收到 ARP 请求后需要把自己的 MAC 地址填入 Sender Hardware Address把目标 IP 和请求方的 IP 对调操作码改成0x0002再把目标 MAC 改成请求方的 MAC重新计算 FCS发送回去。发 ARP 响应时我犯过一个抓狂的错误源 MAC 地址写反了。正常情况下帧头里的源 MAC 是 FPGA 自己的 MAC但我直接复用了接收帧的源 MAC导致 PC 收到 ARP 响应后根本不缓存。这个错误在 ILA 里很难发现因为波形看着完全正常帧结构也合法直到用 Wireshark 在 PC 侧抓包才暴露。这也是我想强调的FPGA 端看波形只是第一步协议是否符合双方约定得从链路对端视角验证。4.2 TX 方向发送链路从 AXIS 到 RGMII 的字节流转 DDR发送方向的数据通路可以这样设计MAC 层把整帧数据按字节写入 FIFO由发送状态机读出转成 4 位 DDR 数据送到 RGMII。RGMII 的 TX 端需要注意的坑是 TX_CTL 的组合逻辑。TX_EN和TX_ER只有在数据有效时才驱动空闲时需要把TX_EN拉低。如果 TX_CTL 控制不当PHY 会认为链路持续处于错误状态对端网卡可能直接断开连接。我在刚写完发送方向时遇到 PC 网卡频繁“拔出/插入”的提示排查后就是 TX_EN 信号在帧间间隙没有及时拉低导致的。简化后的发送状态机思路typedef enum {IDLE, PREAMBLE, DATA, PAD, FCS} tx_state_t; // PREAMBLE: 发送 7 字节 0x55 1 字节 0xD5 // DATA: 从 FIFO 按字节读出装成 4-bit DDR 输出 // PAD: 填充到最小 64 字节帧长不含前导码 // FCS: 输出 CRC32 校验字段FCSFrame Check Sequence的计算是实现细节中比较容易出错的地方。以太网 CRC32 是 IEEE 802.3 定义的多项式是0x04C11DB7计算范围从目的 MAC 到 payload 末尾。很多现成的 CRC32 代码模块可以直接用关键是输入字节顺序、是否反转、初值是否为0xFFFFFFFF、最终结果是否异或0xFFFFFFFF。调试时最直接的验证方式是用 Wireshark 抓 PC 发给 FPGA 的帧把帧头到 payload 的完整字节流丢进几个在线 CRC 计算器里对比 PHY 芯片自己计算的 FCS 值。4.3 RX 方向接收链路FCS 校验和最小 IP/ICMP 处理RX 方向是数据接收 帧解析 协议响应的组合。我把整个接收通路拆成三层物理适配层把 RGMII 的 DDR nibble 拼成 8 位字节流输出 AXIS 接口MAC 接收层识别前导码SFD检查目的 MAC 是否为本机 MAC 或广播地址对帧长超过1518字节的做截断处理协议处理层根据 EtherType 分流0x0806走 ARP 模块0x0800再解析 IP 头协议字段是0x01且类型为 0/8 的走 ICMP 模块。ICMP Echo 响应实现时有一个容易被忽视的点收到的请求里的 IP 头 Checksum 和 ICMP Checksum 都不需要重新验证直接用原值改几个字段再重算就行。比如 IP 头中对调源和目的 IPTTL 减 1如果原 TTL 是 64减 1 后变成 63需要重算 IP 头 ChecksumICMP 头把 Type 从0x08改成0x00Checksum 也要重算。这两个 checksum 都是 16 位求和取反的算法处理不好会导致收端直接丢弃响应包。# 伪代码IP checksum 计算 sum 0 for each 16-bit word: sum word sum (sum 0xFFFF) (sum 16) checksum ~sum 0xFFFFPC 端 ping 的显示结果是“来自 192.168.1.10 的回复”只要 ICMP 响应能被 PC 正确接收并校验就说明这条通路已经打通。5. 从 ILA 到 PC Ping 通最后一公里的完整排错5.1 排错优先顺序链路层、MAC 层、协议层逐级排查等到 ILA 里的波形看起来正常并不代表网络链路就通了。我从 ILA 抓包正常到 PC 首次 ping 通中间花了整整两天最终沉淀下来的排错顺序是固定的PHY 链路状态通过 MDIO 读 PHY 寄存器0x11或0x1的 Link Status 位确认物理层已经协商到 1Gbps 全双工。如果这里就是 0后面全都不用看。对端是否收到任何帧在 PC 上用 Wireshark 抓包看有没有源源不断的垃圾帧或 FPGA 发出来的 ARP 响应。如果 PC 侧 Wireshark 什么都看不到问题大概率在物理层或 MAC 层。FPGA 是否收到本机 MAC 的帧在 ILA 里加一个触发条件抓目的 MAC 是本机 MAC 的帧。抓不到就说明 MAC 地址比较逻辑写错了或者接收链路的字节对齐有问题。协议层响应是否正确验证 ARP 响应、ICMP 响应的源 MAC、源 IP、校验和等字段。这一步的核心逻辑是不要跨层跳着查。比如 ping 不通不要一上来就查 ICMP 模块而是先确认 ARP 是否通了。如果 ARP 不通ICMP 包根本不会被发到 FPGA 上。5.2 ARP 缓存与 PC 端行为很多“bug”其实是缓存惹的祸调试时一个让容易让人原地崩溃的现象是改了 FPGA 代码重新下载 bit 流之后ping 还是超时。查来查去FPGA 端波形完全正常代码逻辑也看不出问题最后发现是 PC 的 ARP 缓存里还留着旧的 MAC 地址。PC 缓存了 FPGA 某次运行时的 MAC 地址但 FPGA 重新配置后 MAC 地址变了比如在代码里临时改过PC 发送的 ICMP 请求会继续发往旧 MAC。由于旧 MAC 地址的机器不存在FPGA 根本收不到任何包。解决办法很简单在 PC 上用管理员权限执行arp -d ping 192.168.1.10或者手动指定静态 ARP 表项但调试期不建议容易掩盖 MAC 地址变化的真实问题。现在我的习惯是每次给 FPGA 下载新 bit 流之后立刻在 FPGA 端用 ILA 确认 PHY 有没有重新协商再在 PC 端清一次 ARP 缓存这样能把变量降到最少。5.3 Ping 通之后还要做哪些验证PC 能 ping 通 FPGA本质上只是证明 ICMP 回显通路没问题并不代表接口的稳定性。我建议至少做三组进一步测试持续 pingping 192.168.1.10 -tWindows或ping 192.168.1.10观察丢包率和延迟抖动。我实测 1000 个包在无负载情况下丢包为 0延迟稳定在 0.3~0.5ms。不同帧长用ping -l 1472发送最大非分片帧和ping -l 32小帧对比验证 FIFO 深度和 FCS 计算在大帧下的稳定性。双向压力测试如果后续准备做高速率收发最好用 Ethernet 测试仪或者利用 PC 端发包工具打流量让 FPGA 进入接收回传模式持续运行期间用 ILA 抽查是否有异常帧。6. 关键参数实测与上线前的经验清单6.1 ILA 实测示例一次完整的 ARP 请求-响应波形下面是我在 ILA 中实际抓取到的 ARP 交互过程关键字段做了脱敏阶段信号值/行为接收eth_rx_ctl拉高表示帧开始接收rx_axis_tdata[47:0]FF FF FF FF FF FF广播目标 MAC接收rx_axis_tdata[95:48]本机 MACFPGA 侧收到的源 MAC接收rx_axis_tdata[111:96]08 06EtherType ARP接收arp_opcode0x0001请求发送tx_axis_tdata[47:0]对端 MAC回给请求方发送tx_axis_tdata[95:48]本机 MAC发送arp_opcode0x0002响应从这个波形能直观看到俩个关键点一是发送帧的目的 MAC 不再是广播地址而是直接填请求方的 MAC二是从接收完成到发送开始的间隔在无优化代码里约 5~8 个时钟周期即 40~64ns对 ping 的延迟影响很小。6.2 时序收敛与 IDELAY 调试的经验数值在 Artix-7 上我最终把 RX 方向的 IDELAY 值设为IDELAY_VALUE 25对应的延迟大约 1.95ns。这个值是根据 ILA 抓到的数据正确性逐次扫描出来的。扫描方法是从 0 开始每次加 5 个 tapping 一次 FPGA统计成功率。结果我记录到IDELAY_VALUEPing 一次结果现象0超时RX 数据采样不稳定5超时帧头偶发错位10成功连续 10 包正常15成功连续 100 包正常20成功长时间稳定25成功最优区间30超时字节序偶发错位35超时大量 CRC 错误可见最优窗口相当宽但偏出窗口后行为是“间歇性故障”非常难排查。建议在完成基本 ping 通后专门留一小时做 IDELAY 扫描确认余量。6.3 上线前检查清单最后写一份我每次新板子调 RGMII 都会过一遍的清单上电后 MDIO 能读到 PHY IDPHY Link 状态为已协商速度和双工模式正确ILA 能看到连续完整的前导码和 SFD接收字节序正确无 nibble 错位发送方向 TX_CTL 在帧间隙为低PC 能 ping 通 FPGA清空 ARP 缓存后依然能通连续 1000 包 ping 无丢包大帧1472 字节 payload与小帧32 字节都能正确收发时序报告中 RX 时钟域无 setup/hold violation。按照这条路径从零开始到 PC ping 通我目前带过的人最快的记录是两天半。如果你也正在被 RGMII 折腾建议不要跳步尤其不要跳过 IDELAY 扫描和 ARP 验证这两个环节——它们是最容易“看起来没问题但实际没通”的地方。
返回列表