ARTICLE DETAIL

资讯详情

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

FPGA光通信UDP传输中的TEMAC核配置与调试指南

FPGA光通信UDP传输中的TEMAC核配置与调试指南 1. 为什么在光通信链路里必须用TEMAC核MAC层的脏活累活到底干了什么1.1 从SFP光口到UDP数据包中间隔了多少层拿到一块带SFP光口的板子想要跑起来千兆UDP传输很多初学者第一反应是我是不是应该自己写一个MAC控制器毕竟UDP协议栈也不复杂CRC校验网上也有现成代码为什么要花钱花时间去例化一个Tri Mode Ethernet MAC IP核我先把这个链路拆开讲。一个UDP包要从FPGA逻辑发到对端电脑实际经过的路径是用户逻辑 → MAC层 → SGMII/PCS子层 → PHY芯片 → SFP光模块 → 光纤 → 对端。其中MAC层的职责范围比很多人想象的要大得多生成前导码和帧起始定界符SFD这是物理层能识别帧从哪开始的关键。计算并附加FCS帧校验序列也就是CRC32这玩意儿自己写不难难的是在千兆速率下做到每个时钟周期都正确且时序收敛。保证帧间隙IFG符合协议要求千兆以太网标准规定帧间隙至少96 bit time也就是96ns。如果IFG不对对端交换机会直接把帧丢掉。处理冲突检测、退避算法半双工时需要现在基本用不到但核里自带了。处理超大帧、VLAN标签、流控PAUSE帧这些功能如果全自己写工作量是厂商IP核的三倍以上。所以在千兆光通信这个场景里TEMAC核不是选不选的问题而是必须用成熟方案的问题。你自己写的MAC可能在小包、低速率下能跑通一旦到了千兆线速、数据不间断灌进来的时候各种边界条件会把你逼疯——比如CRC校验在多字节对齐时怎么处理、FIFO快满的时候刚好来一个超长帧怎么办。1.2 Tri Mode到底指什么光通信场景下实际用哪种模式Tri Mode Ethernet MAC支持10Mbps、100Mbps、1000Mbps三种速率自适应但做光通信的都知道光模块一上来就是千兆根本不会协商到百兆去。这块的重点不是速率自动协商而是你要搞清楚TEMAC核内部的PCS/PMA层在SGMII配置下负责了8B/10B编码、时钟恢复、自动协商这些脏活这些才是真正难搞的部分。千兆光模块工作在1000BASE-X标准下物理编码子层用的是8B/10B编码并且有专门的自动协商机制CJPAT、DCP等配置序列。如果不用TEMAC自带的SGMII功能你就要自己在逻辑里实现8B/10B编解码、comma对齐、运行不一致校验工程量直接翻倍。TEMAC核选择SGMII接口时内部会自动例化一个SGMII IP核。你在配置界面里勾选一个选项生成的工程里就多了一整套PCS/PMA逻辑这就是IP核的价值——它把以太网MAC SGMII物理层适配打包成了一个黑盒你只需要关心用户侧接口和物理侧引脚。1.3 一个反直觉的结论MAC核不是性能瓶颈用户逻辑才是很多人在设计初期会担心TEMAC核是不是很占资源、是不是延时很高实测下来在7系列和UltraScale系列上单纯TEMAC核的资源消耗大概是几百个LUT加几个Block RAM相比整个工程的资源占用可以忽略不计。真正的性能瓶颈在用户侧——你给MAC核喂数据的逻辑能不能做到每个时钟周期都有数据输出。举个例子我的发送逻辑一开始是用状态机逐字节搬运UDP数据结果千兆速率下用户时钟125MHz64位数据总线状态机每跳转一次要花好几个周期实际吞吐量只有300Mbps左右。后来改成流水线结构数据从一个FIFO直接流到MAC核的AXI4-Stream接口带宽才跑满。这个问题的根子不在IP核而在用户逻辑的设计思路。2. 图形化配置里每个关键选项对应的硬件行为2.1 接口模式SGMII与1000BASE-X到底怎么选打开TEMAC IP核的配置界面第一个让你纠结的就是Physical Interface选SGMII还是1000BASE-X。这个选项直接决定物理侧引脚和时钟方案选错了后面全盘皆输。配置选项适用场景物理侧差异编码方式SGMII板上有PHY芯片PHY再接RJ45或光模块通过SGMII接口连接外部PHY引脚是差分对8B/10B1000BASE-X板上没有PHYSGMII信号直接进光模块无需外部PHYGMII信号直接映射到光模块接口8B/10B我的板子是无PHY方案SFP光模块通过一个小的电平转换电路直接连到FPGA的高速串行收发器GTX/GTH这时候TEMAC核内部要把MAC层的数据转换成1000BASE-X的PCS/PMA信号再交给GTX去发送。如果选成SGMII核会认为外部还有一个PHY芯片协商逻辑就会出问题表现为光模块link不上或者能link上但数据全是乱码。判断方法很简单看你的板卡上FPGA的高速引脚是直接连SFP光模块还是先经过一个PHY芯片比如88E1111、RTL8211之类的。前者选1000BASE-X后者选SGMII。如果选错了现象是MDIO能读到PHY的寄存器但链路永远处于down状态或者光模块的RX_LOS信号一直拉高。2.2 Shared Logic选项的深坑一个决定你少写很多代码的选项配置界面里有一组单选按钮叫Shared Logic选项是Include Shared Logic in core和Include Shared Logic in example design。这个选项的官方解释很晦涩我给你翻译成人话Include Shared Logic in core把SGMII需要的时钟管理MMCM/PLL、复位逻辑、状态监测这些公共模块直接编译进IP核内部。你拿到手的IP核是一个完整的、自包含的模块顶层只需要引出物理引脚和用户接口。Include Shared Logic in example designIP核只包含MAC核心逻辑把SGMII的时钟和复位逻辑放在外面的example design里。你可以修改这些公共逻辑比如把MMCM换成自己的时钟方案但代价是你必须自己把SGMII核、GTX、时钟管理这些模块连起来。实际项目中我强烈建议选Include Shared Logic in core。理由有三条第一Xilinx把GTX和SGMII的时钟连接关系都给你接好了你自己连的话容易漏掉一些初始化时序第二SGMII的复位时序比较讲究核内部处理好了外部只要给一个全局复位即可第三调试的时候引脚少很多不容易接错。如果非要选Include Shared Logic in example design你要有心理准备example design里有大量IO约束和原语比如IBUFDS_GTE2、BUFG_GT这些迁移到自己的工程时光删这些代码就要花半天时间。我最初为了灵活性选了后者结果光是适配自己的板卡时钟就折腾了一整天最后还是老老实实改回核内共享逻辑。2.3 数据位宽、时钟频率和复位极性的实际选择配置界面里还有几个关键选项直接影响你后面的代码写法和约束文件数据位宽AXI4-Stream接口的Data Width可选8、16、32、64位。千兆以太网在125MHz时钟下8位宽刚好够用8bit × 125MHz 1000Mbps但实际使用中几乎没人选8位因为用户逻辑处理UDP帧头、IP校验和时8位总线意味着每个字节都要单独操作状态机极其繁琐。我用64位原因是125MHz下64位总线带宽是8Gbps远高于千兆这样即使发送逻辑偶尔停一拍比如计算校验和时也不会成为性能瓶颈。而且64位对齐到以太网帧的8字节倍数每帧数据可以整块写入FIFO状态机逻辑简单很多。用户侧时钟TEMAC核会根据你的接口位宽自动生成对应的用户时钟。64位位宽时用户时钟为125MHz32位时用户时钟为31.25MHz的倍数关系注意是125MHz × 32 / 64 62.5MHz。这里有无数人踩过坑在配置界面里看到User Interface Clock显示125MHz以为所有模式都一样实际上选了32位后用户时钟会变成62.5MHz而SGMII和RGMII接口的物理时钟仍是125MHz很多人按照125MHz去写约束结果时序收敛不了跑起来偶发丢包。复位配置复位极性选Active Low这是AXI4-Stream的标准约定跟很多国产IP核的习惯不一样。另外Synchronous或Asynchronous复位都可以建议选Synchronous避免在时钟未稳定时复位释放产生亚稳态。核会产生一个tx_reset和rx_reset信号这两个信号在复位释放后会保持一段时间必须等它们拉低后才能开始收发数据否则第一帧大概率是坏帧。3. UDP帧的用户侧组装从组帧到接口时序3.1 软件视角和硬件视角看到的以太网帧差了整整8个字节用wireshark抓包的时候你看到的每一帧都是以目的MAC地址开头的。但在FPGA侧MAC核在实际发送到线路上的数据是前导码7字节0x55 SFD1字节0xD5 目的MAC6字节 源MAC6字节 类型/长度2字节 载荷46~1500字节 FCS4字节。其中前导码和SFD是MAC核自动生成的FCS也是MAC核自动计算并附加的用户不需要也不应该提供这些字节。用户侧给的帧只需要从目的MAC开始一直到IP/UDP数据结束。MAC核会在你给的数据前面自动加前导码在后面自动加CRC32。我第一次用TEMAC核时不知道这个规则在用户逻辑里手动加了前导码和FCS结果对端wireshark显示每一帧都带了一堆奇怪的字节抓包界面里前导码全都显示成unknown字段。UDP数据报文的完整组包结构如下目的MAC地址6字节——FF:FF:FF:FF:FF:FF是广播单播写对端网卡的MAC源MAC地址6字节——写自己板卡的MAC注意不能全零以太网类型2字节——0x0800表示IPv4IP头20字节标准无选项——版本头长0x45、服务类型0、总长度、标识、标志位、TTL、协议号17表示UDP、IP头校验和、源IP、目的IPUDP头8字节——源端口、目的端口、UDP长度、UDP校验和应用数据N字节很多人问到UDP校验和不计算行不行。IPv4的UDP校验和是可选的填0即可但有个前提如果对端设备较严格可能会丢包。实测下来电脑端的wireshark能正常识别UDP校验和为0的报文大部分网络的中间设备也不会拦截但在光通信这种对可靠性要求高的场景我建议还是把UDP校验和算上。3.2 用Verilog手写一个最简单的UDP发送状态机搞清楚帧结构之后发送逻辑的框架就清晰了。核心是用一个状态机把上面这些字节按顺序输出到AXI4-Stream接口。以下是一个64位数据位宽的简化发送逻辑核心部分// 发送状态机IDLE → SEND_ETH_HEADER → SEND_IP_UDP_HEADER → SEND_PAYLOAD → DONE always (posedge user_clk or negedge reset_n) begin if (!reset_n) begin state IDLE; end else begin case (state) IDLE: if (tx_start) state SEND_ETH_HEADER; // 每个周期输出64bit8字节数据计数发送了多少个周期 SEND_ETH_HEADER: begin if (byte_count ETH_HEADER_BYTES/8 - 1) state SEND_IP_UDP_HEADER; end // ... endcase end end // last信号在最后一拍拉高keep信号指示有效字节数 assign s_axis_tlast (byte_count TOTAL_BYTES/8 - 1) valid_condition; assign s_axis_tkeep last_byte_valid ? 8h0F : 8hFF; // 最后一拍可能只有部分字节有效这段代码的关键点是tkeep和tlast。tlast告诉MAC核这一帧结束了MAC核收到tlast后会在下一拍把前导码和FCS补完发送出去。tkeep告诉MAC核最后一拍有几个字节是有效的——比如UDP数据总长度不是8的倍数最后一拍只有4个字节有效就把tkeep设成4b1111MAC核会从数据里正确截断。处理UDP长度字段和IP总长度字段时需要特别注意这两个字段的单位不一样UDP长度包含UDP头本身8字节IP总长度包含IP头20字节加后面的UDP段。很多人算错这里导致对端收到包后wireshark显示Malformed Packet。3.3 接口时序上的三个必须AXI4-Stream接口时序如果不满足就算数据内容全对MAC核也不会按预期工作。以下是实测总结的要点必须保证tvalid在tready拉高时保持稳定。TEMAC核的发送接口在大部分情况下tready是常高的但在FIFO快满时tready会拉低一小段时间。如果你的逻辑在tready拉低时改变了tdata或tvalid就会造成数据错位。规范做法是tvalid拉高后必须等到tready也拉高数据才算被成功接收在此之前不能改变tdata。必须为帧间隙留出空间。MAC核内部会处理IFG但用户侧如果在连续两帧之间一拍都不停MAC核的FIFO可能来不及处理表现为偶发丢帧。我在发送逻辑里每发完一帧就回到IDLE状态至少等8个时钟周期再发送下一帧。必须注意tuser信号。接收方向的tuser信号在以太网MAC核里表示接收错误指示当MAC核检测到FCS错误、长度错误、接收超时等情况时会在对应帧的最后一拍把tuser拉高。这是在调试阶段最实用的信号——如果tuser高频拉高说明收到的数据帧有问题MAC核已经帮你做了第一道质检。4. 系统联调与实测排错从ILA波形到wireshark报文4.1 先用三种回环把链路切成四段逐段定位完整的光通信链路是FPGA用户逻辑 → TEMAC核 → GTX → SFP光模块 → 光纤 → 对端。任何一环出问题现象都很相似——收不到数据或者疯狂报错。我的排查方法是把链路切成四段从两端向中间逼近内部回环FPGA内部在用户逻辑侧把发送的AXI4-Stream数据直接接到接收的AXI4-Stream接口上。这一步验证用户逻辑组帧是否正确。PCS/PMA回环TEMAC核内部通过TEMAC核的配置寄存器把发送的PCS数据直接环回接收PCS。这一步验证GTX和SGMII逻辑是否正常不经过外部PHY。PHY回环光模块自环插一根光纤跳线把SFP的发端和收端短接。这一步验证光模块和光纤链路。远端回环对端设备的网卡把收到的帧原样发回来。这一步验证对端协议栈是否正常工作。以最常用的ILA调试为例抓取AXI4-Stream接口上的信号时建议设置触发条件为tlast 1这样每抓到一帧的结束时刻方便观察完整帧的波形。如果能看到一帧数据正常地从发送接口流出且接收接口的tvalid在对应时刻拉高说明用户逻辑和MAC核协同工作正常。4.2 排查案例接收方向tuser频繁拉高最终锁定IP头校验和有一次联调发送端逻辑看起来一切正常ILA上能看到数据从TEMAC发送接口送出去了但接收端同一块FPGA的另一路的tuser频繁拉高说明接收到的帧CRC校验失败。排查链路如下先做内部回环发现tuser没有拉高说明用户逻辑组帧正确排除了发送侧问题。换成PCS/PMA回环tuser正常说明TEMAC核和GTX工作正常。插上光纤让两端对打tuser开始拉高——问题锁定在光模块或远端设备。用wireshark在远端电脑上抓包发现电脑能收到UDP包但wireshark报Invalid IP header checksum。回去仔细检查IP头校验和算法发现校验和计算时把UDP长度字段当成了IP总长度导致校验和计算错误。这是一个非常经典的低级错误。IP头校验和的计算方法网上到处都是先把校验和字段置零以16位为单位做反码求和再把结果取反。但计算时一定要从IP头第一个字节算到最后一个字节只算IP头本身20字节不包括后面的UDP头和数据。如果算错帧能发出去但无法通过对端的校验。而MAC核算的CRC32是覆盖整个帧的IP校验和错了CRC也错所以tuser会拉高——这是一个连锁反应。4.3 link状态与PHY寄存器调试如果光模块link不上最先看的是SFP模块的RX_LOS信号和GTX的rx_reset状态。在TEMAC核生成的设计里可以通过MDIO接口读取外部PHY如果有的话的状态寄存器。我的板子无外部PHY所以是通过GTX的eyescan功能和光模块的数字诊断接口I2C来确认光路是否正常。这里分享一个实测技巧用wireshark筛选UDP前后两包的时间间隔用frame.time_delta_displayed这个过滤字段可以查看相邻两个UDP包的到达时间差。如果发现时间间隔不稳定忽大忽小说明发送端的发送节奏不均匀可能是用户逻辑里FIFO读空导致的需要检查发送FIFO的深度和水线设置。在iperf3使用UDP打流时如果Wireshark里看到重传、乱序多半不是网络问题而是发送端FPGA的包间隔控制不合理。5. 收尾建议先跑Example Design仿真再改自己的逻辑最后分享一个我自己走了弯路才明白的流程希望对你有帮助。第一次做TEMAC核接SGMII的项目时我犯了一个大忌——拿到IP核配置完就直接复制example design里的端口连接代码然后急着写自己的UDP发送逻辑。结果仿真波形一出来全是一些匪夷所思的信号。后来老老实实花了一个下午把example design里的仿真工程完整跑了一遍看着那些预置的测试用例怎么收发数据才算真正理解了各个信号的时序关系。具体建议先在Vivado里把生成的example design综合、仿真跑通观察几个关键信号——s_axis_tready什么时候拉低、m_axis_tvalid什么时候拉高、tx_ifg_delay的设置对帧间隙的影响。仿真通过之后再用计数器构造一个简单的测试帧目标MAC和源MAC填固定值IP和UDP头全部手工算好通过ILA抓到线上实际数据和wireshark抓到的对端数据做逐字节对比。这一套流程走下来UDP光通信的收发链路基本上就能稳定跑起来。TEMAC核本身不难难的是把它周边的时钟、复位、FIFO、GTX这些模块理顺。按照仿真先行 → 回环分段验证 → 实机对打的顺序来能省掉至少一半的调试时间。
返回列表