
去年做了一款带多网口通信的工业设备主控是FPGA外部要扩展5个百兆网口。当时对比了几种方案最后选了RTL8306交换机芯片加FPGA自研MAC核这种方式。整个项目从画原理图到联调通过前前后后踩了不少坑尤其是“5个网口1个MAC”这个组合寄存器配置、时钟时序、VLAN隔离、MAC地址表这些问题稍不注意就会让链路静默到怀疑人生。这篇文章把这次联调的完整思路、配置要点和实际排查过程整理出来希望能帮到正在做类似架构的朋友。RTL8306是瑞昱一颗很经典的5口百兆交换芯片但它内部实际上是6个端口5个带内置PHY的物理网口外加1个MII/RMII接口可以接外部MAC这里就是FPGA。这种“51”的结构特别适合做工业网关、网络测试仪器、多网口嵌入式主板、视频采集前端这类产品用一颗低成本的交换芯片代替5颗独立PHY还能顺手把交换逻辑、VLAN、流控这些功能交给芯片硬件完成FPGA只需要维护一个MAC口的数据通路。本文面向正在做FPGA网络通信、以RTL8306为交换核心的硬件工程师和嵌入式开发也会涉及一部分需要跟硬件工程师配合确认的链路细节建议先看懂架构再上手调代码。1. 项目整体设计与架构选型1.1 “51”拓扑到底是怎么一回事RTL8306这颗芯片的端口分布用最简单的话说就是芯片自带5个百兆PHY这5个PHY分别对应5个RJ45网口对外的信号已经是差分线了直接加网络变压器就能接连接器。第6个端口不长PHY而是留了一个MII/RMII数字接口这个接口的职责是连接一颗外部MAC。外部MAC可以是CPU内置的MAC控制器也可以是FPGA里用逻辑搭出来的MAC核。我们项目里的具体结构是FPGA作为主控负责业务数据的处理和转发RTL8306的5个LAN口连接外部设备比如PLC、摄像头、上位机FPGA侧通过RMII接口接到RTL8306的端口6这样整个设备就有“5个对外网口1个内部MAC”的完整交换能力。外部设备之间可以直接二层互通外部设备也可以经过交换机芯片把数据送到FPGA做协议处理或者FPGA主动往任意一个LAN口发数据。1.2 为什么不用5颗独立PHY做硬件选型的时候最容易想到的方案是FPGA外接5颗百兆PHY芯片比如IP101G、KSZ8081之类。这个方案在逻辑上很简单每个PHY对应一个MACFPGA内部做5个MAC再加一个5口交换逻辑听着也不难。但实际上这么做PCB面积会非常难看5颗PHY加变压器加连接器一块板子光是网络部分就要占掉将近三分之一的空间而且交换功能得在FPGA里自己写VLAN、MAC地址表、端口镜像这些功能全是工作量时序收敛也会被拖累。换RTL8306之后交换引擎是芯片自带的FPGA只处理一个MAC口的数据逻辑量直接少了一个量级。成本上一颗8306加一个FPGA通常比FPGA加5颗PHY更便宜BOM也简洁很多。而且RTL8306内部交换是线速转发的不会像FPGA自己写转发逻辑那样容易出现吞吐瓶颈。1.3 这个方案适合哪些场景我理解这种架构在下面几类产品里出现得最多多网口工业网关需要把现场多台设备的网络数据汇聚到主控或者主控下发配置到多台设备。网络测试仪器比如便携式网络分析仪一个口接被测设备另外几个口接不同网段做测试环境搭建。视频采集与传输设备摄像头数据经交换机汇聚后交给FPGA做处理或转发。带网管功能的小型交换机通过FPGA实现SNMP、Web管理等网管协议RTL8306提供转发面。这类产品本质上都是用交换芯片扩展端口数量同时保留一个主控口做管理面和业务面处理。理解了这一点后面做寄存器配置的时候就不会迷茫因为很多寄存器的作用就是围绕“这个主控口怎么跟其他5个口通信”展开的。2. 核心器件原理与关键信号拆解2.1 RTL8306内部结构速览RTL8306管脚比较多但实际用起来可以按功能分区记忆端口0到端口45个10/100M自适应PHY支持Auto-Negotiation外部走差分对接网络变压器。端口5MII/RMII接口用来连接外部MAC。注意这里的“端口5”在不同版本文档里可能会被叫作CPU端口、第6端口或者管理口本质上就是那个不带PHY的MAC侧接口。MDIO/MDC管理接口用来访问芯片内部寄存器的通道上电初始化、VLAN配置、端口使能、状态查询都靠它。EEPROM接口部分型号支持外挂EEPROM加载配置但我们在实际项目里没有用EEPROM全部配置都由FPGA通过MDIO在上电后写入。要注意的细节是RTL8306的RMII口接的是外部MAC这意味着PHY侧的时钟、数据方向跟FPGA侧是完全对应的。FPGA做MAC就要按照MAC侧的时序来收发数据。2.2 RMII接口信号与时序要点RMIIReduced Media Independent Interface是MII的简化版把MII的4位数据位宽砍成2位时钟频率从25MHz提到50MHz所以收发各用2根数据线就能达到100Mbps。RMII接口一共这几根线REF_CLK50MHz参考时钟收发共用由MAC侧或者外部晶振提供。RTL8306的RMII REF_CLK可以配置为输入或输出我们当时为了时钟统一直接用FPGA输出50MHz给RTL8306。TXD[1:0]发送数据两bit并行。TX_EN发送使能为高时TXD有效。RXD[1:0]接收数据。CRS_DV载波侦听和数据有效指示在接收数据期间拉高。时序上有个容易出问题的地方RMII没有独立的RXDV信号CRS_DV既当载波侦听又当数据有效标志数据来的时候CRS_DV拉高数据帧发完后可能还会因为载波侦听延迟继续高一段时间。FPGA接收逻辑如果机械地把“CRS_DV高”等同于“正在收数据”就容易在帧尾多收几个字节。正确做法是在CRS_DV拉高之后同步RXD[1:0]解析出前导码和帧起始定界符SFD再按字节收数据最后用CRC校验和长度字段判断帧边界。2.3 MDIO/MDC管理通道MDIOManagement Data Input/Output是一种两线的串行管理总线MDC是时钟MDIO是双向数据线。FPGA通过MDIO可以读写RTL8306的内部寄存器支持的速率一般不超过2.5MHz左右我在实际项目里习惯跑1MHz稳定可靠。MDIO读写的帧格式是标准化的包含前导码、操作码、PHY地址、寄存器地址、数据等字段。RTL8306的MDIO地址映射跟普通PHY不一样它不是简单的“每个PHY一个寄存器组”而是全芯片的管理寄存器空间都通过MDIO访问通过寄存器地址来区分不同的功能模块。RTL8306系列的寄存器很多分页机制也比较灵活手册里通常会按页page组织寄存器配置前一定要先切到正确的页。2.4 FPGA侧MAC怎么选FPGA需要实现以太网MAC接收来自RTL8306的数据、发送数据到RTL8306。这里有两个选择一是用FPGA厂商提供的MAC IP核比如Xilinx的三速以太网MAC配置成RMII模式。好处是IP核经过验证FIFO接口现成带宽管理省心缺点是IP核的配置项多接口时序要花时间熟悉。二是自己用Verilog写一个精简百兆MAC核。百兆MAC相对简单发送侧就是拼以太网帧头、计算CRC32、按RMII时序逐bit发送接收侧就是检测前导码、按字节接收、剥离FCS校验。我们最后用的是自研MAC主要原因是想完全控制FIFO和DMA逻辑不想被IP核的固定接口束缚。如果你时间紧张或者对以太网帧格式不够熟直接用IP核会更稳。3. 寄存器配置与FPGA侧关键实现3.1 上电初始化流程RTL8306上电后默认就能转发但端口5是否连到交换核心、VLAN成员表怎么配、流控开不开都需要通过MDIO写入配置。我习惯把初始化流程分成几步每一步单独验证复位芯片通过GPIO拉低复位脚保持至少10ms然后释放。等待芯片稳定释放复位后延时50ms左右再访问MDIO。验证MDIO通道读芯片ID寄存器确认读到的是RTL8306系列对应的ID值这一步能排除接线和时序问题。配置端口5为RMII模式RTL8306的端口5可以配置为MII或者RMII必须跟硬件接线一致。当时我们就是在这里吃亏默认配置跟实际接法不匹配后面读到链路状态才发现问题。配置VLAN成员关系把端口5加入VLAN1的成员表同时确保5个LAN口也在VLAN1里。如果需要端口隔离可以按业务需求配置不同VLAN。配置流控与协商RTL8306默认可能开启流控FPGA侧如果不处理pause帧建议关闭流控或者让FPGA侧实现pause帧响应。打开端口5的收发使能。上面这些步骤的具体寄存器地址不同批次的RTL8306可能略有差异务必以官方Datasheet为准。我下面给出的是一种经过验证的配置思路而不是逐地址照抄的脚本。3.2 端口5 RMII模式配置的坑端口5工作在MII还是RMII通常是芯片的一个配置引脚或者某个寄存器决定的。我们在第一版固件里默认寄存器值显示端口5处于MII模式而实际PCB上走的是RMII导致FPGA发出的信号对不上接收端完全没反应。排查到最后发现是模式配置没写对心里别提多憋屈。如果你是照着参考设计画的板子先确认参考设计里端口5的接法然后对照寄存器配置保持一致。如果板子是自己设计的、参考设计又不完整务必在原理图阶段就确定MII还是RMII并留出调试手段比如跳线电阻以便切换。3.3 VLAN和端口隔离配置交换机芯片在默认状态下端口的VLAN成员关系不一定是所有端口互通。RTL8306这类芯片出厂默认配置通常是“所有端口在同一个VLAN”但如果你从其他项目拷了一段初始化代码或者外挂EEPROM里存了历史配置VLAN成员表就可能是乱的。我们的实际配置目标是“6个端口全互通”也就是端口0到端口5都在同一个VLAN里。配置VLAN时用VLAN ID为1或者自定义一个管理VLAN把6个端口全部加入该VLAN的成员列表同时设置出口规则为不改变VLAN标签。对于纯二层透传业务不需要设置Tagged/Untagged的复杂规则全部设成Untagged就够用。另外需要注意默认的Port VLAN IDPVID要和端口所在VLAN一致否则收到的帧会被打上错误的VLAN ID可能被交换引擎过滤掉。这个点在多个项目里反复出现排查不通的第一个思路就是检查PVID和VLAN成员表。3.4 FPGA侧数据通路设计FPGA侧的数据通路我按“发送方向”和“接收方向”两条链路来设计发送方向FPGA发给交换机业务数据经FIFO缓存由状态机拼装成以太网帧加上前导码、帧起始符、目的MAC、源MAC、类型/长度字段有效载荷从FIFO读出最后计算CRC32并附加在帧尾。拼装完成后按RMII时序把数据通过TXD[1:0]发出TX_EN在有效数据期间拉高。接收方向交换机发到FPGA持续监测CRS_DV信号一旦拉高就开始采集RXD[1:0]跳过前导码和SFD按字节重组数据帧。收完后再做一次CRC校验和帧尾自带的FCS比对一致则把数据推送进FIFO供上层协议处理不一致则直接丢弃。这里有个工程细节CRC32的算法在以太网里是标准的但要注意初始值和输出异或FPGA实现时可以用查表法也可以逐位计算。百兆速率下用逐位CRC32完全够用资源占用也不大。3.5 MAC地址表与转发逻辑RTL8306内部会自动学习MAC地址建立MAC地址表。FPGA作为外部MAC发出的帧源MAC地址必须是合法且唯一的不能是全0也不能频繁变化。如果FPGA发出的帧源MAC是全0交换芯片可能不学习后续从LAN口回来的单播帧就不知道该往哪里转发结果就是“明明能收到却回不去”。这是一个特别隐蔽的坑。我们用FPGA发测试帧的时候为了省事把源MAC写成了全0结果PC怎么都ping不通FPGA侧后来用Wireshark抓包发现FPGA发出去的帧源地址就是0交换机直接不去学习这个地址。把源MAC改成固定合法值之后链路瞬间通了。4. 联调过程中的问题与排查实录4.1 端口5不通先从这三个方向下手如果你遇到LAN口能互通、但FPGA侧就是不工作的情况我建议按顺序检查第一查RMII模式配置。确认端口5的寄存器值跟硬件接法相符MII/RMII模式错了后面的时序和数据全是乱的。第二查REF_CLK。RMII的50MHz时钟必须稳定存在可以用示波器测RTL8306的REF_CLK引脚。如果时钟没有输出或被拉低芯片内部的RMII收发逻辑根本无法工作。这个时钟可以由FPGA输出、外部晶振或者RTL8306内部PLL提供但必须保证相位和电平规范。第三查数据方向。RMII接口的TXD是MAC到PHY方向RXD是PHY到MAC方向TX_EN由MAC输出、CRS_DV由PHY输出。接线方向接反了也会导致完全不通。用万用表量一下引脚间是否有短路、虚焊特别是在PCB密度较高的板子上。4.2 时钟与复位带来的疑难杂症有一次板子焊出来FPGA的MDIO可以正常读ID但RMII数据链路就是不通。查了好久发现是RTL8306的复位信号跟FPGA的复位时序没有对齐芯片在上电后还没有完全初始化完成时FPGA就急着发配置了MDIO写操作虽然“看起来成功”了但实际没生效。解决办法很简单但很有效释放复位后延时更久一点我们改成了200ms然后再跑MDIO初始化流程。如果你发现类似“怎么配置都不生效”的情况先怀疑复位时序在复位释放后加足够延时用示波器确认电源纹波稳定后再操作。时钟方面还有一个经验REF_CLK不要从FPGA的普通IO低速输出直接接过去除非你确认IO能稳定输出50MHz且抖动可接受。最好是使用FPGA的专用时钟输出引脚或者用一个独立的50MHz有源晶振同时给RTL8306和FPGA供时钟保证两侧参考同源降低数据采样时序问题。4.3 帧发出去收不到先别急着怀疑交换芯片联调中最常见的一种现象是FPGA往发送方向塞了一帧数据示波器能看到TXD和TX_EN都在变化但PC端完全收不到包或者抓包只看到CRC错误帧。我遇到过的原因有两类。第一类是CRC计算错误。百兆以太网的CRC32覆盖从目的MAC到载荷的全部字节算法细节漏一点就全错。建议先用一个已知正确的以太网帧做测试向量把CRC算出来跟抓包结果对比。第二类是MAC地址表老化问题。前面提到的源MAC全0或者源MAC频繁变化交换芯片学习不到转发自然无从谈起。排查方法是在PC上用Wireshark挂到某个LAN口上抓包看FPGA发出的帧是不是到了交换机口上。如果到了但带CRC错误标记就是FPGA发出帧格式有问题如果根本没到那就要检查端口5的配置和转发规则。4.4 VLAN配置错误导致的“假故障”还有一种情况特别迷惑人FPGA发出的广播帧能到PC单播帧却不行。这通常是VLAN配置问题交换机芯片对广播帧和单播帧的转发路径不一致。排查时不要想当然认为广播能通单播就能通我会直接读回VLAN寄存器确认端口5的PVID和VLAN成员表是否符合预期。如果你配置了端口隔离或者不同VLAN检查是否会因为VLAN ID不匹配导致单播帧被丢弃。把VLAN配置暂时简化成所有端口同一个VLAN这是一个非常快速的问题定位手段。4.5 流控对调试的影响RTL8306默认可能开启流控在工作状态下如果接收端处理不过来交换芯片会发送pause帧让对端暂停发送。FPGA自研MAC如果不实现pause帧解析收到这种帧就当成普通数据扔进FIFO严重时还会造成帧错位、缓冲堆积。联调初期我建议直接把流控关闭等基础收发都通了再按实际业务需要打开并处理pause帧。这样能排除一个很大的变量特别是用自研MAC核的时候。4.6 实测一套可复现的联调用例我在联调时主要用三组测试用例验证整个链路是否正常第一组是二层连通性测试用PC连接任意一个LAN口配置同网段的IP地址PC ping FPGA侧业务模块的IP。如果VLAN、MAC表、FPGA数据通路都正常ping基本能通。这一组主要验证FPGA接收和发送的对称性。第二组是吞吐与背靠背测试用测试仪或者打流工具向FPGA灌入连续帧观察是否有丢包。百兆口理论速率约100Mbps实际能达到线速不丢包说明FIFO深度和状态机设计基本合理。如果丢包优先查看FIFO满信号的产生时机以及是否因为流控关闭导致溢出。第三组是多口互联测试PC接在LAN1另一个设备或者测试仪接在LAN2FPGA内部把LAN1收到的数据再发回LAN2验证交换芯片的转发和FPGA的桥接功能是否正常。这组用例能顺带验证MAC地址表学习和老化机制。这三组用例覆盖了链路层、转发层、业务处理层跑完基本可以放心的进入协议开发阶段。5. 实战经验总结与下一步扩展这次用RTL8306加FPGA做多网口联调整体上是一次“思路清晰但细节极多”的过程。站在项目复盘的角度有几点经验想单独拎出来说。一是硬件阶段就要把RMII/MII模式确定好并且留下切换手段。不要指望软件去自适应硬件接线的错误软件只能背锅。二是初始化代码尽量独立成一个模块保证可反复执行。RTL8306的配置寄存器有掉电不保存的特性每次上电都得重新初始化所以这个模块会被调用很多次逻辑一定要写干净。三是调试时建议先用最简单的拓扑把所有VLAN隔离、端口限速、流控都关掉让所有端口裸奔互通再逐层叠加高级功能。很多人一上来就按产品需求把VLAN配得很复杂结果出了问题根本不知道从哪查起。四是源MAC地址一定要合法且固定。这在自研MAC里特别容易被忽略我至少见过三个项目因为这个原因在联调阶段卡了一两天。下一步如果想继续扩展这套架构可以往这几个方向走把RMII换成百兆双口或者上到千兆交换芯片比如RTL8367系列对FPGA侧接口就要换成RGMII甚至SGMII也可以在FPGA里实现支持VLAN Tag的收发逻辑这样FPGA本身能参与带标签帧的处理还可以把MDIO配置逻辑升级成类似Linux下phy驱动那样的分层结构方便复用。最后再分享一个调试小技巧如果链路怎么也通不了找一个最简单的以太网帧比如ARP请求用Wireshark抓下来比对你的FPGA发送波形或者接收FIFO里的数据逐字节核对。很多看起来玄学的问题最后都是字节序、CRC、或者时序多一拍少一拍的问题逐字节比对是最笨但最有效的方法。