ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

国产FPGA实战:基于PGL50H的千兆网口RGMII与UDP通信开发记录

国产FPGA实战:基于PGL50H的千兆网口RGMII与UDP通信开发记录 紫光同创PGL50H这颗芯片在国产FPGA圈子里其实已经积累了一批忠实用户。不能说它性能有多炸裂但在“成本可控、供货稳定、国产化替代”这个区间它确实是很多人做通信、数据采集类项目时的第一选择。我手上这块盘古50KN网口开发板就是围绕PGL50H做的一块带千兆网口的板子拿来跑高速通信和数据处理刚刚好。这篇不是产品软文也不是开发板说明书而是我基于这块板子从零开始做网口通信的完整实战记录硬件资源怎么用、PDS工具链怎么搭、RGMII/UDP协议栈怎么实现、带宽怎么测以及在调试过程中踩过的那些坑。想入门国产FPGA或者正纠结怎么把网口功能跑起来的可以参考这份记录。先说明一下这套方案用到的关键点包括PGL50H的片上资源分配、盘古50KN网口板的板级搭建、基于RGMII接口的千兆PHY通信、UDP协议栈的简化实现以及跨时钟域FIFO的数据缓存策略。下面按我实际操作的顺序来写有些内容看起来基础但恰恰是这些基础决定了后面能不能稳定跑起来。1. 为什么是PGL50H盘古50KN网口板的硬件底子1.1 PGL50H芯片的定位与资源摸底PGL50H属于紫光同创的Logos系列定位是中等规模、低功耗、高性价比的FPGA。官方数据手册里它的资源规模大致在5万级逻辑单元这个档位内部集成了不少DSP单元、块RAM、锁相环PLL以及高速收发器等资源。我没法把每个数字都精确背出来毕竟不同批次手册上的参数会有微调但有一点可以确定在2.5G/千兆网口、图像处理、电机控制、工业总线协议这类中低端应用场景里它的资源量级是完全够用的。从实际开发的体验来说PGL50H最让我满意的是IO资源相对充裕。做网口板的时候除了要接RGMII信号往往还需要接DDR3地址线、串口、LED、按键、扩展IO等IO不够用就得拿pin脚复用方案硬凑非常难受。PGL50H在这块板子上预留的IO数量比较宽裕我在做数据采集扩展时还额外接了一路并行ADC和一路SPI接口的传感器资源占用上没有出现捉襟见肘的情况。还有个细节值得提——PGL50H内部集成了DDR存储控制器相关的硬核辅助资源。虽然不少教程会让你用软核方式实现DDR控制但在这块盘古50KN网口板上官方已经提供了DDR3的参考设计跑起来比自己调软核省心得多。做高速网口数据处理如果没有DDR3做数据缓存遇到突发流量基本必死所以板载DDR3这个设计是非常关键的加分项。1.2 板级资源网口板到底“板”在哪里盘古50KN网口板顾名思义就是围绕PGL50H做的一块专门验证网络通信和高速数据处理的开发板。板上的核心外设可以分成几类千兆以太网PHY芯片常见型号是瑞昱的RTL8211系列或裕太微的YT8531系列具体到板子上需要查看原理图确认DDR3内存颗粒或内存条接口用于大容量数据缓存UART串口用来做调试日志输出和简单命令交互时钟系统一般包含一颗25MHz的无源晶振或可编程时钟芯片供PHY和FPGA内部PLL使用按键、LED、拨码开关这些交互元件方便做状态指示和功能配置扩展排针或FPC连接器引出大量用户IO、差分对、电源和地。从电路设计角度来看这块板子的网口部分FPGA和PHY之间走的是RGMII接口MAC逻辑在FPGA内部实现PHY只负责物理层信号的编码、解码和收发。PHY的配置通过MDIOManagement Data Input/Output接口读写内部寄存器来完成这一点后面会详细说。1.3 选型逻辑什么场景选它什么场景放弃结合我对开发板的整体使用感受这套方案适合下面几种场景项目要求国产化率必须使用国产FPGA需要跑千兆以太网数据收发但不需要跑到40G/100G那种量级需要一定量的数据缓冲能力配合DDR3做数据暂存团队缺少FPGA开发经验想选一块资料相对完善、学习曲线没那么陡峭的国产板子入门。如果项目需求是超大逻辑资源、多路高速SerDes比如PCIe Gen3 x8、复杂SoC集成那PGL50H这块板子确实不合适应该去看更高端的Titan系列或者其他平台的方案。选型说白了就是对需求的诚实评估资源够了就上资源不够别硬塞否则后面综合实现时序不过的时候痛苦的还是自己。2. 环境搭建与工程模板PDS工具链的完整落地2.1 PDS安装与License激活紫光同创FPGA的官方开发环境是PDSProgrammable Design Suite名字听起来有点像某国际大厂工具链的即视感但使用逻辑和Vivado/Quartus大同小异。安装过程没有什么太特殊的坑Windows下直接解压安装包按提示把组件勾选完就行。有一点特别提醒PDS对中文路径的支持并不友好工程路径尽量不要出现中文和特殊字符否则在跑综合实现时可能出现一些莫名其妙报错。我一开始没注意放在“D:\工程\网口”下面结果编译报错找半天发现是路径编码问题改回纯英文路径后一切正常。License是PDS绕不开的一个环节。紫光同创的License一般通过官方网站申请绑定主机MAC地址或者网卡信息。申请下来后在PDS里通过License Manager指定license文件路径即可。如果找不到License路径可以先在环境变量里设置“LM_LICENSE_FILE”指向license文件再启动PDS。注意License文件不要放在有中文或空格的目录里我吃过这个亏换成“D:\license.dat”后激活过程秒过。2.2 从建工程到点亮LED一次完整的代码流这里用一个最简单的LED流水灯来走一遍流程因为不管多复杂的系统工程的建立和下载流程是共通的。启动PDS后新建工程芯片型号选择PGL50H对应的具体封装。如果下拉列表里找不到型号多半是器件库没有安装完整回到安装包补装器件库组件即可。工程建好之后会默认生成一个顶层文件通常是Verilog格式。接下来写代码一个最小工程可以这样module led_test( input wire clk, input wire rst_n, output reg [3:0] led ); reg [24:0] cnt; always (posedge clk or negedge rst_n) begin if (!rst_n) cnt 25d0; else if (cnt 25d24_999_999) cnt 25d0; else cnt cnt 1b1; end always (posedge clk or negedge rst_n) begin if (!rst_n) led 4b0001; else if (cnt 25d24_999_999) led {led[2:0], led[3]}; // 流水灯移位 end endmodule上面的代码基于一个假设开发板上有50MHz时钟输入。如果实际晶振频率是25MHz计数器的最大值要相应调整否则LED闪动频率就不是预期的1秒。这是一种很常见的“参考代码参数需按硬件自行修正”的情况建议拿到板卡先看原理图确认时钟频率再接代码。然后是管脚约束。PDS的约束文件后缀是.adc或.fdc具体以工具版本为准。管脚约束的写法与Xilinx的XDC有一些相似但关键词不同建议直接用PDS自带的模板改。比如IO_LOC clk T14; IO_PORT clk IO_TYPELVCMOS33; IO_LOC rst_n K16; IO_PORT rst_n IO_TYPELVCMOS33; IO_LOC led[0] P4; IO_PORT led[0] IO_TYPELVCMOS33; IO_LOC led[1] P5; IO_PORT led[1] IO_TYPELVCMOS33; IO_LOC led[2] P6; IO_PORT led[2] IO_TYPELVCMOS33; IO_LOC led[3] P7; IO_PORT led[3] IO_TYPELVCMOS33;如果你拿到的是官方例程工程里面的约束文件已经写好且经过验证直接在此基础上改即可。因为你会经常增删管脚建议在写约束之前先核对原理图上的网络标号和bank电压避免管脚分配错误或电平标准不匹配导致下载后不工作甚至损伤芯片。约束写完后依次执行综合Synthesis和实现Place Route。这个过程跑完大约需要几分钟PGL50H属于中等规模芯片综合速度还挺快的。最后生成bit流文件连接下载器在PDS里点击“Program Device”把bit文件烧录到FPGA中。如果板卡连接正常LED应该按照预期闪烁第一个工程就算跑通了。2.3 关于bit文件与环境的补充经验热词里经常有人问“ISE生成的bit文件怎么下载到开发板”这里顺带提一嘴不同厂商的FPGA工具链生成的配置文件格式不能通用。Xilinx用.bit紫光同创用PDS生成的文件格式也不一样需要各自的工具链配合相应的下载器才能烧录。国产FPGA的下载器接口往往与JTAG类似PDS里直接识别即可。另外有朋友想在Ubuntu环境下跑PDS这个是可以的。PDS官方提供了Linux版本安装包安装后同样需要配置License。在Linux下使用PDS需要留意USB下载器的驱动权限设置经常需要添加udev规则才能识别下载器否则系统检测不到设备。这个问题在Windows下基本不存在所以如果是新手建议前期先用Windows环境等跑通整个流程后再迁移到Linux。3. 千兆网口实现RGMII、PHY与UDP协议栈3.1 RGMII接口时序真的没那么玄乎RGMIIReduced Gigabit Media Independent Interface是千兆以太网最常用的FPGA与PHY之间的接口方式。相比GMII动辄十几根线RGMII把数据线减半通过DDR双沿采样达到同样的带宽。RGMII总共有四组关键信号TXD[3:0]发送数据线在时钟上升沿发送低4位下降沿发送高4位TX_CLK发送时钟频率125MHz由MACFPGA内部产生给PHYTX_CTL发送控制信号上升沿表示使能下降沿表示错误标志RXD[3:0]、RX_CLK、RX_CTL接收方向对应的信号其中RX_CLK是由PHY恢复出来的时钟频率同样是125MHz。用大白话讲RGMII就是“在同一个时钟周期内把8位数据劈成两半分别用时钟的上沿和下沿传出去”。这样4根数据线配合双沿采样等效一天内能传8位数据正好匹配千兆125MHz x 2 x 4bit 1000Mbps速率。在PGL50H做网口设计时FPGA内部需要例化以太网MAC逻辑然后通过IO约束把MAC侧RGMII信号接到PHY芯片上。这里有个极易踩坑的点TX_CLK到PHY的延迟和RX_CLK内部的相位关系。如果PCB走线长度不当或者FPGA内部没有做IO时序约束可能出现PHY采不到正确数据的情况。常用的解决办法是在PDS里对RGMII接口补上输入延时和输出延时的约束具体数值根据PHY芯片手册的Tskew参数来计算。如果PDS自带的模板里已经包含RGMII约束直接套用模板再做微调即可。3.2 PHY芯片初始化MDIO读写寄存器FPGA内部实现MAC逻辑时整个链路分为两层MAC负责以太网帧的处理、CRC校验、字节流组装而物理层的编码、线路信号收发则交给PHY芯片完成。要让PHY正常工作必须通过MDIO接口对它的内部寄存器进行初始化设置工作模式、速率、自协商开启状态等。MDIO接口只有两根线MDC时钟线和MDIO数据线最多可以访问32个PHY地址。FPGA侧需要写一个简单的MDIO控制器模拟MDIO时序来读写PHY寄存器。寄存器0是控制寄存器bit0.13配置速度为1000Mbpsbit0.12配置全双工bit0.9配置开启/关闭自协商。寄存器1是状态寄存器用来确认协商结果。还有一个比较常用的寄存器是PHY的ID寄存器寄存器2和3读出来的值可以用来反查PHY型号确认板子上实际用的什么芯片。关于PHY初始化我个人的经验是不要一上来就手动强制配置千兆全双工先默认开启自协商等PHY把链路速率协商出来后再去读状态寄存器确认。这样做的原因是很多网卡或交换机可能在协商过程中对主从模式有偏好强制配置容易导致模式不匹配物理上Link不上之后排查起来更麻烦。3.3 UDP协议栈自研还是用IP核以太网协议栈可以做得非常庞大完整的TCP/IP协议栈实现起来工作量巨大。但在FPGA网口开发中90%的需求用UDP就够了。UDP协议无连接、无重传实现开销极小非常适合实时数据采集和传输。自研UDP协议栈需要处理的层次包括MAC层组装以太网帧头目的MAC、源MAC、Type字段追加FCS帧校验序列IP层组装IP头版本、头长、总长度、标识、TTL、协议号、源IP、目的IP计算IP头部校验和UDP层组装UDP头源端口、目的端口、长度、校验和可选计算UDP校验和。如果是在PDS里面例化现成的以太网IP核MAC层往往已经帮你处理好了你只需要把用户数据封装成IP和UDP的头部按MAC核的接口时序送进去即可。如果PHY芯片和MAC核配合良好PDS自带的IP核例化向导里甚至可以直接配置MAC地址、IP地址省去不少手工拼头的工作。在自研和IP核的选择上我的建议是如果追求开发速度、项目周期短用现成IP核如果希望深入理解以太网协议、方便后续定制特殊帧格式自研如果数据量非常大需要多通道并行发送自研能更方便地做流水线优化。我自己在这块板子上选择的是自研UDP发送模块加IP核MAC的组合既保证了对帧格式的灵活控制又不至于从零造MAC层轮子。工程里用户数据经过FIFO缓存后由状态机逐段拼装成UDP帧送入MAC核发送。3.4 跨时钟域处理为什么网口设计离不开异步FIFO网口设计里用户逻辑运行在自定义频率下比如150MHz而MAC侧工作时钟通常是125MHz两个时钟域之间交换数据时绝对不能用寄存器直接传递必须经过异步FIFO或异步握手。异步FIFO是跨时钟域数据缓存的标准方案。数据源侧以用户时钟写入FIFOMAC发送侧以125MHz时钟从FIFO读出数据两侧读写指针通过格雷码跨时钟域同步避免亚稳态问题。在设计FIFO深度时需要考虑两点一是FIFO为空时MAC是否会产生欠载通过加入最小帧长度的填充机制解决二是FIFO为满时数据源是否要暂停写入通过反压信号通知前级逻辑暂停发送。关于FIFO深度我提供一个工程估算思路假设数据源突发写入1KB数据发送速率为千兆约100MB/s那么1KB数据在100MB/s速率下发完约需10微秒。如果写入速率是150MHz x 64bit约1200MB/s那10微秒写入的数据量约12KB。这个场景下FIFO深度如果只有4KB数据源必须在10微秒内停止写入否则必丢数据。实际设计中需要根据数据源的突发长度和发送带宽来反推FIFO最小深度再留出至少2倍余量避免时序抖动导致误差。4. 高速数据通路从数据源到网口发送的工程实践4.1 系统架构设计数据从哪里来到哪里去这块板子做“高速数据处理”典型的数据通路是这样的数据源ADC芯片、SPI传感器、或FPGA内部高速生成器把数据写入异步FIFO的写端口FIFO的读出端连接UDP发送模块UDP发送模块把数据封装成以太网帧通过RGMII接口经PHY发送到PCPC端用自写的上位机软件或Wireshark抓包验证数据正确性。在数据源的选择上前期调试时我个人强烈建议先用FPGA内部生成固定模式的数据比如递增值、伪随机序列如LFSR或者正弦波查表。用已知模式的数据做调试一旦收到错误字节能立刻对照出来是哪个字节错了、错位量有多少。如果一上来就接真实ADC数据数据本身就是随机的收到异常数据很难判断问题出在链路、缓存、还是时序上。以LFSR伪随机序列生成器为例一个32位LFSR可以生成一个周期足够长的伪随机序列reg [31:0] lfsr; always (posedge clk or negedge rst_n) begin if (!rst_n) lfsr 32hDEAD_BEEF; else begin lfsr {lfsr[30:0], lfsr[31] ^ lfsr[21] ^ lfsr[1] ^ lfsr[0]}; end end接收端PC软件如果也实现了同样的LFSR算法就能逐字节比对数据是否完全一致这是验证链路可靠性的最直接方法。4.2 UDP打包状态机从数据到帧的组装流程UDP发送模块的核心是一个状态机负责把FIFO里的数据拆成若干个不超过1472字节的UDP数据段逐段封装发送。标准以太网MTU是1500字节减去IP头20字节、UDP头8字节UDP数据段最大就是1472字节。如果你一次性发超过这个长度的数据就必须自己分片否则上层协议或者PC网卡会直接丢包。所以写状态机的时候必须每读到1472字节就强制成帧发送剩余不足1472字节的数据最后单独一帧送出。发送状态机的典型状态如下IDLE等待FIFO非空进入PACK状态PACK拼接MAC头、IP头、UDP头设定本次帧的数据长度SEND_HEADER发送头部数据按字节宽度顺序送出SEND_DATA发送用户数据每发送一个字节读指针加一计数减一计数归零后进入SEND_FCSSEND_FCS由MAC核或自定义模块计算并附加CRC校验WAIT等待发送完成信号返回IDLE准备下一帧。这里有一点要特别留意UDP校验和如果懒得计算可以把UDP头校验和字段设为0。在IPv4网络中UDP校验和为0是合法值接收端可以不校验。这样可以省掉一个较大的组合逻辑计算对吞吐量提升有帮助。IP头校验和不能省略但IP头只有20字节计算量可以接受。4.3 带宽计算与实测千兆到底能跑多少很多人以为千兆网口就能跑到1000Mbps的UDP吞吐量实际不可能。1000Mbps指的是物理层速率经过MAC层前导码、帧间隙、以太网头、IP头、UDP头的封装开销之后有效数据吞吐大约在940Mbps左右这还是在帧长度达到MTU上限的理想情况下。如果用100字节的小帧有效吞吐率会掉到600Mbps以下所以学会算封装开销是很有必要的。一个典型的带宽计算例子如下以太网帧总开销 7字节前导码 1字节定界符 12字节帧间隙 14字节MAC头 4字节CRC 20字节IP头 8字节UDP头 66字节假设每帧载荷1472字节则总字节数 1472 66 1538字节有效带宽 1000Mbps × (1472 / 1538) ≈ 957Mbps。实际测试中我通过PC端网卡抓包统计能够稳定跑到900Mbps左右。为什么比理论略低一是PC的网卡主控和驱动有开销二是PC端接收程序如果处理不过来也会造成丢包降速。所以如果你看到实测只有800-900Mbps不用太焦虑这是正常水平。在FPGA侧做性能优化时可以关注下面三个点发送数据的位宽尽量使用32位或64位数据通路而不是逐字节发送能显著降低时钟频率需求FIFO读出的连续性FIFO读请求发出后如果数据延时不固定会拉长帧间隙导致吞吐量下降最好使用FWFTFirst Word Fall Through模式的FIFOCRC计算方式连续字节流的CRC建议用并行CRC实现8位并行比逐位计算快得多。4.4 接收链路不能忽视做完发送数据接收链路同样是调试重点。FPGA接收PC发来的UDP包时MAC核会输出接收到的整帧数据包含以太网帧头、IP头、UDP头和载荷。最简单的做法是在FPGA内部写一个帧解析模块把MAC头、IP头、UDP头逐字节剥离只把载荷部分存入FIFO交给用户逻辑处理。帧解析的流程可以用一个“状态过滤”的思路检测到帧起始符后先按字节计数跳过14字节MAC头再判断IP头长度字段通常20字节跳过IP头然后读取UDP头长度字段最后一并提取载荷。解析时需要警惕短帧和错误帧MAC核通常会提供接收数据有效信号和错误标志在有效信号为低时清空状态机等待下一帧即可。5. 排坑实录网口调试的常见问题与解决思路5.1 链路Layer不上的排查顺序使用网口板最怕遇到的现象就是插上网线后PHY的Link指示灯不亮或者亮了但Ping不通整个系统看起来死气沉沉。这里的排查顺序很重要不要一上来就翻代码而是先按照物理层—数据链路层—网络层逐层定位。第一步确认PHY芯片供电和复位。用万用表测量PHY的电源引脚电压是否正常复位引脚的电平状态是否处于释放状态。RTL8211等PHY芯片的复位要求低电平有效如果复位引脚被电阻拉低或者 FPGA 侧一直输出低电平PHY永远处于复位状态指示灯自然不亮。第二步确认PHY的时钟。大部分千兆PHY需要一个外部25MHz时钟或者由FPGA提供一个参考时钟。如果PHY内部锁相环没有参考时钟即使电源正常也无法工作。用示波器测量PHY晶体引脚或时钟输入引脚确认125MHz或25MHz信号确实存在。第三步确认MDIO配置是否成功。可以写一个简单的MDIO读操作读取PHY ID寄存器寄存器2和3。如果读出来的值是0xFFFF或0x0000说明MDIO链路有问题或者PHY地址不对需要查看原理图上PHY地址引脚如PHYAD[4:0]的上下拉配置修改FPGA内部访问的PHY地址。第四步确认RGMII数据是否真正在收发。用PDS自带的片上逻辑分析仪抓取RGMII的RX_DV信号和RXD数据在PC端用ping命令往FPGA发数据。如果抓到的RXD全是0或者RX_DV不拉高问题在PHY如果RX_DV拉高且RXD数据符合预期问题在MAC侧或上层协议栈。5.2 RGMII时序的典型错误如果你抓到的RGMII信号看起来明明有数据活动但MAC核处理出来的帧校验总是错误大概率是RGMII的时钟相位没有调整正确。RGMII的RX_CLK由PHY恢复输出数据线相对于时钟有建立保持时间要求FPGA内部必须对RX_CLK或者数据线加合适的延迟否则采样的数据点落在数据转换的边缘采到的就是乱码。PDS里可以通过原语或IO约束来调整输入延时。具体做法是在约束文件里对RGMII的接收总线设置输入延时Input Delay数值通常为2ns左右再根据实际抓波形的眼图中心微调。有的PHY支持通过寄存器配置内部的时钟延迟功能比如RTL8211的部分版本有RX delay的配置位可以在MDIO初始化时写入相应寄存器减少PCB布线的时序压力。发送方向比较简单只要保证FPGA输出的TX_CLK与TXD、TX_CTL之间的相对关系满足PHY侧的要求即可。这里同样可以通过输出延时约束来做微调如果遇到PHY收不到问题优先调整发送方向的输出延时。5.3 大流量场景下的丢包原因通信速率一高丢包问题就开始频繁冒头。结合实战经验我把丢包原因分成三类第一类是FPGA侧FIFO溢出。如果数据源写入速率大于MAC发送速率FIFO迟早被塞满。解决办法是增加FIFO深度或者提前做数据分发与多通道并行发送把单一发送通道的负载降下来。最有效的方法是让数据源侧感知发送状态在FIFO快满时暂停数据产生做成“反压式”数据流。第二类是PC端接收处理能力不足。PC网卡接收到大量UDP包时如果上位机程序没有启用足够大的接收缓冲区操作系统会直接丢弃来不及处理的包。用Wireshark抓不到某些帧不代表FPGA没发很可能数据已经到网卡驱动却被操作系统丢弃了。解决办法是增大接收缓冲、使用多线程接收或者降低PC端显示日志的频率。第三类是UDP分片或乱序。如果发送端把一个大数据包强制拆成多个IP分片有些网卡驱动对分片包的重组能力很差表现为大量丢包。解决办法是无论如何都在应用层控制每个UDP包大小不超过1472字节杜绝IP分片发生。5.4 调试工具与心得做FPGA网口调试有两个工具是每天晚上都离不开的PDS自带的片上逻辑分析仪和PC端的Wireshark。片上逻辑分析仪主要用来抓FPGA内部信号比如FIFO读写指针、发送状态机状态、MDIO读写波形Wireshark则用来验证PC端实际看到的网络数据是否与期望一致。我在调试中习惯先让FPGA向PC发送固定2024字节测试帧再用Wireshark分析帧内容检查MAC地址、IP地址、UDP端口和数据内容。确认没问题之后才把数据源换成真实采样数据。如果能做到“发送端与接收端比对数据完全一致”说明整条链路已经打通剩下的就是性能优化和业务逻辑处理了。6. 一些后话这套板子做下来的整体感觉是PGL50H加盘古50KN的组合非常适合作为国产FPGA网口通信的入门平台和工程验证平台。芯片资源不算顶级但把千兆网口、数据处理、DDR缓存、UART调试这些常用功能全都覆盖到了社区资料和官方例程也比较丰富遇到问题不至于完全没地方查。对我个人而言最大的收获反而是对以太网协议栈和跨时钟域设计有了更深入的理解——这些知识不只是紫光同创能用换到任何FPGA平台都是通用的。如果你也准备在这块板子上做网口通信最后分享一个小建议先把简单链路跑通再叠加业务复杂度。不要一上来就想着把UDP、DDR、ADC、传感器全部串起来链路越复杂调试时越难定位问题。先让PC和FPGA能互相收发纯测试数据再逐步加功能模块每一步都验证后面反而省时间。国产FPGA的工具链虽然和国外主流平台有些差异但使用逻辑是相通的耐心啃了一遍你会发现并没有想象中那么难。
返回列表