
1. 为什么这颗6口百兆交换芯片会出现在我的项目里以及驱动方案怎么选先说背景。去年做一个带网关功能的工业设备主控是ARM Cortex-A7双核跑嵌入式Linux需要对外提供5个百兆以太网口同时保留一个独立WAN口上联。最开始硬件同事给的建议是直接用两颗PHY加VLAN交换芯片或者干脆上RTL8367这种千兆交换芯片但评估下来成本超预算而且千兆对我们这个场景属于性能过剩。后来翻瑞昱的选型手册发现RTL8306MB这芯片很合适——6个10/100M端口内置5个PHY剩下一个端口通过RMII/MII接口接到主控的MAC刚好做成5口LAN1口WAN/CPU上行。如果没用过这类集成交换芯片容易把它理解成“一个带管理功能的Hub”实际上RTL8306MB是一颗完整的二层交换芯片支持VLAN、QoS、端口镜像、IGMP Snooping、802.1p优先级等。对运行Linux的主控来说最大的问题是主控只有一个以太网MAC怎么管理后面挂的5个物理端口这就引出驱动方案选型。当时我在两套方案之间纠结方案A是用Linux内核现成的DSADistributed Switch Architecture框架。内核里其实有RTL8306的DSA驱动路径在drivers/net/dsa/rtl8306.c配合tag_rtl4_a标签协议使用。这个方案的好处是每个LAN口会注册成独立的netdeveth0.1~eth0.5用户态看到的是干净的多网口拓扑iptables、bridge、VLAN子接口都很好接坏处是DSA框架本身有学习成本而且该驱动的维护活跃度一般要是遇到芯片版本差异可能要自己改tag处理或者寄存器初始化序列。方案B是干脆不用DSA把RTL8306MB当作一个“智能交换背板”主控MAC通过RMII接到CPU端口初始化阶段用MDIO把芯片配置成默认转发模式所有LAN口在同一个VLAN里对外只暴露一个eth0。坏处是Linux看不到每个LAN口的独立状态端口隔离、流量监控不好做但好处是实现极简调试路径短对吞吐要求不高的工业设备完全够用。我的最终选择是先走方案B验证硬件再针对交付需求决定是否迁移到DSA。实际项目因为功能需求只要求“所有LAN口互通CPU能上网”方案B直接交付了DSA方案留作后续迭代。这个决定在后面调试RMII时省了很多麻烦因为不需要同时排查DSA tag解析的问题可以把全部精力放在物理链路和时钟上。2. RMII接口的基本盘信号线、时钟拓扑和模式约定先搞清楚再动手很多人一上来就翻寄存器手册忽略了对RMII接口本身的物理时序理解结果调了几天找不到方向。RMII全称是Reduced Media Independent Interface相比传统MII接口数据线从4根砍到2根时钟从25MHz提到50MHz用双倍速率换取引脚数量减半。对RTL8306MB这种6口芯片来说少一组数据线意味着封装和PCB布线压力都小很多。2.1 RMII的信号线组成RMII接口一共就这几根信号线REF_CLK50MHz参考时钟所有信号都在这个时钟的上升沿采样TXD[1:0]发送数据线2位并行TX_EN发送使能高电平表示TXD上的数据有效RXD[1:0]接收数据线2位并行CRS_DV载波侦听/数据有效高电平表示接收通道有有效数据MDIO/MDC管理接口用于读写PHY寄存器不参与数据通路就这么8根线不含MDIO/MDC但调起来能让人怀疑人生。核心原因是RMII把MII的4位数据压缩到2位之后所有时序余量都被压缩了任何一个信号的质量问题都会在高速流量下放大。2.2 时钟拓扑的三种接线方式别搞混RMII的REF_CLK有几种典型接法这是最容易踩坑的地方。第一种是PHY提供时钟交换芯片的REF_CLK引脚输出50MHz主控MAC侧的REF_CLK由这个信号驱动。这种模式下PHY是时钟主设备MAC是时钟从设备MAC内部不需要额外的50MHz源。第二种是MAC提供时钟主控芯片的RMII接口直接输出50MHz REF_CLK给交换芯片。反过来这时候MAC是主设备交换芯片从外部获取参考时钟。第三种是外部晶振/振荡器提供时钟一个50MHz有源晶振同时给MAC和PHY两侧提供REF_CLK两边都是从设备。RTL8306MB的REF_CLK方向由硬件strapping引脚或者寄存器控制。我当时第一版原理图是让交换芯片输出REF_CLK给主控MAC因为主控的datasheet说支持“PHY提供时钟”模式。结果调试时发现主控MAC内部PLL对这个外部时钟的相位要求非常苛刻最终改成外部有源晶振同时供两边的方案才稳定。这里给个非常硬核的建议如果主控和交换芯片的距离超过5cm或者两者不在同一块PCB上优先选第三种“外部晶振共同时钟”方案。独立时钟源能避免很多跨板连接的容性和感性干扰后期的EMC问题也会少一些。2.3 电平标准和走线设计RMII标准电平是3.3V但很多主控芯片的RMII引脚可能是1.8V或2.5V电平这时候需要电平转换。RTL8306MB的I/O电平通常是3.3V如果主控侧是1.8V不能直接相连。我当时用了一颗74AVC4T245做电平转换但调试中发现转换芯片的传播延迟会给高速信号带来额外相移后来干脆换了一颗带电平转换的PHY芯片当然这就是后话了。PCB走线方面RMII的8根信号线最好等长尤其是TXD[1:0]、TX_EN和RXD[1:0]、CRS_DV这四组长度差控制在5mil以内。REF_CLK要远离数据线至少3倍线宽间距否则串扰会让时钟边沿抖动。电源和地也要干净RMII信号参考平面尽量完整不要跨分割。3. 第一个大坑MAC侧RMII模式没配对Link灯亮但ping不通这个坑我印象太深了花了整整两天。现象是这样的板子焊好系统起来Linux下看到eth0是UP状态PHY的Link状态也是up但ping网关永远不通偶尔能通一两包也是几百毫秒延迟。3.1 现象复盘Link up不等于数据通路正常很多新手有个误解觉得MAC和PHY的Link都up了二层链路肯定通了。实际上Link up只是物理层协商成功不代表数据链路能正确收发。RMII接口的数据通路必须满足以下条件MAC侧和PHY侧的RMII模式配置一致是否用RMII是否复用引脚REF_CLK频率和相位满足双方采样要求TX/RX数据线上的信号完整MAC和PHY的流控模式、双工模式匹配我当时的排查从ethtool开始。ethtool eth0看到Speed: 100Mb/sDuplex: Full一切正常。但ethtool -S eth0显示rx_crc_errors和rx_missed_errors在持续增长说明MAC侧根本没收到有效帧全是错误帧。3.2 寄存器级排查读RTL8306MB的内部寄存器用MDIO总线读RTL8306MB的PHY寄存器发现它的基础状态寄存器寄存器0x01读到0x796D其中bit5是Link Status为1bit13是Auto-Negotiation Complete也是1。从PHY的角度看自协商正常完成。但继续读PHY的特定寄存器如0x0A、0x0B时发现RMII控制位的值和数据手册要求的不一致。RTL8306MB的RMII模式通常通过配置寄存器0x1A或0x1B的特定bit来选择。不同封装、不同版本这个寄存器的地址和默认值差异很大。我建议在调试前先用I2C/MDIO工具把整个寄存器空间读一遍存档再对照数据手册逐位分析。我当时发现的问题是交换芯片的端口5CPU上行口被配置成了MII模式而不是RMII模式。虽然硬件上只连了RMII接口的线但寄存器的默认值是MIIMAC和PHY的接口模式不一致数据当然无法正确传输。3.3 根因上电默认状态和Strapping引脚配置问题出在RTL8306MB的上电默认配置上。芯片的端口模式、PHY地址、时钟方向都会通过一些外部引脚strapping pin在上电瞬间被锁存。我当时画原理图时没有仔细看数据手册的strapping引脚说明把几个关键引脚悬空了结果芯片默认进入MII模式。修改方法是用MDIO工具把端口5的模式寄存器改成RMII再软复位芯片。改完后立刻看到ethtool -S eth0的rx_crc_errors不再增长ping也通了。这里给个实战建议画原理图前一定把RTL8306MB数据手册里的strapping引脚表格完整过一遍确认所有模式引脚的上拉/下拉电阻符合预期。不要以为芯片默认值就是最常用的配置瑞昱芯片的默认值往往和你想的完全不一样。4. 第二个大坑REF_CLK相位与毛刺引发的随机丢包示波器一量全懂了解决了接口模式问题后基础通信恢复了但高速流量测试又暴露了新问题小包1500字节的UDP流吞吐只有80Mbps而且丢包率约1%。最诡异的是ping大包偶尔会丢1~2个包ping小包完全正常。这种“随机性”是排查中最难受的因为你很难稳定复现一个可分析的场景。4.1 定位思路从“偶尔”到“必然”的复现方法我用的方法是用ping -s 1472 -f打满速率同时用tcpdump -i eth0在CPU侧抓包对比发送端和接收端的序列号找到连续丢包时的规律。结果发现丢包总是发生在MAC收到连续帧的接收边界也就是一帧结束、下一帧开始的时刻。这个规律非常关键基本锁定了问题在RX路径的时序上而不是协议或缓冲问题。4.2 示波器实测REF_CLK和数据信号的边缘用示波器同时测REF_CLK和RXD[1:0]、CRS_DV发现了一个典型问题RXD和CRS_DV在REF_CLK上升沿附近发生跳变数据建立时间setup time只有约3ns而RTL8306MB数据手册要求的最小建立时间是7ns左右。也就是说交换芯片输出的RX数据到达MAC时已经太接近时钟采样沿了偶尔超出的部分就会造成采样错误表现为丢包或CRC错误。为什么会出现这种情况两个原因叠加一是REF_CLK由交换芯片输出给MAC走线较长容性负载导致时钟边沿变缓二是主控MAC的RX路径有一个额外的输入延迟导致它实际采样点比理论上升沿晚。这两个效应叠加吃掉了本来就不多的时序余量。4.3 解决方案时钟相位微调和电源滤波第一步是尝试用主控的RMII时钟相位配置寄存器把MAC的采样时钟往后延迟。具体寄存器是主控芯片的MAC时钟延迟控制位不同厂商命名不同我当时用的主控有一个tx/rx clock skew配置可以在0~1ns范围内微调RX采样点。试了几组值后发现把RX采样点延迟约1ns后建立时间从3ns提升到5ns丢包率下降了一个数量级。但还没完全消失说明硬件层面的干净度还是不够。第二步是硬件整改给REF_CLK加了一个22Ω串联电阻并靠近引脚加了一个10pF对地电容。这个组合能有效改善时钟的过冲和振铃让边沿更陡峭。同时把REF_CLK走线从数据线之间的间隙挪到单独一层避免串扰。整改后再测丢包率从1%降到0.01%以下1500字节UDP吞吐跑到99.7Mbps余量就非常健康了。4.4 一个容易忽略的点REF_CLK的方向不要随便换如果你用的是PHY提供REF_CLK给MAC的模式MAC内部的接收路径会根据这个时钟的边沿直接采样PHY侧的时钟输出能力和驱动强度会影响信号质量。如果实测发现信号质量很差可以尝试把时钟方向改成外部晶振同时供两侧或者把REF_CLK的方向切换为MAC输出给PHY。第二种模式在很多主控上可以用寄存器配置但这个改动会影响所有RMII时序改之前一定先计算好PCB走线延迟别盲目切换。5. 第三个大坑CRS_DV和RXD之间的“协同”关系比你以为的更严格RMII接口里CRS_DVCarrier Sense / Data Valid是最容易被轻视的信号。很多工程师把它理解为“接收数据有效指示”觉得只要它是高电平数据就是有效的。实际上CRS_DV和RXD[1:0]之间存在严格的时序关系违反了就会导致MAC把无效数据当成有效帧处理或者完全丢弃有效帧。5.1 现象和猜测过程在解决REF_CLK问题之后我做了72小时长时间稳定性测试。测试项是LAN口之间相互打流同时CPU侧用iperf跑双向流量。结果发现一个规律长时间运行后CPU侧接收方向的计数器中rx_missed_errors在缓慢增长但rx_crc_errors为0rx_frame_errors也为0。这意味着MAC硬件已经判定这些帧是完好的但由于某个原因未能把它们送入FIFO。这个现象很奇葩因为rx_missed_errors通常是FIFO溢出导致的但我们的流量远没到溢出量级。后来通过逻辑分析仪抓RMII波形才发现真凶是CRS_DV和RXD的对应关系不对。5.2 逻辑分析仪还原的波形规律用逻辑分析仪采样率500MHz以上同时抓REF_CLK、CRS_DV和RXD[1:0]看到一个稳定的规律RTL8306MB在发送一个帧时CRS_DV会在前导码preamble开始前拉高然后在帧结束FCS之后延迟几个时钟周期才拉低。这个“提前拉高延迟拉低”是标准的RMII行为。问题出在我主控MAC侧的RX路径配置上。主控默认要求CRS_DV在RXD第一个有效数据位之前至少一个时钟周期拉高但实际只提前了半个周期而RTL8306MB在连续帧传输时帧间隙只有约2~3个时钟周期一旦上一帧的CRS_DV延迟拉低和下一帧的提前拉高重叠MAC就会判定为“busy”丢掉一帧。5.3 解决方式调整MAC侧RX FIFO的帧起始判定逻辑这个问题的修复不在驱动层而在MAC初始化和设备树配置里。我的主控提供参数配置rx-ctl寄存器中的RX_FIFO_ERR和CRS_DV_MODE位。把CRS_DV判定模式从“上升沿触发”改为“电平数据同步触发”后这个问题就消失了。如果你用的主控没有这个配置项硬件上有个补丁做法在CRS_DV信号线上串联22Ω电阻并加一个小电容几pF做RC延迟让CRS_DV的上升沿相对RXD稳定。但这需要示波器反复验证不能盲调。另外我强烈建议在驱动初始化时对MAC的接收FIFO做一个完整性自检很多SoC的MAC都带FIFO loopback测试模式。这个测试可以在系统启动早期发现问题避免后面把时间浪费在堆栈协议上。6. MDIO读写不稳定的排查不是寄存器地址错是访问时序问题RTL8306MB的MDIO访问看起来简单就是两线的管理接口但实际调试时我遇到过一次非常隐蔽的问题寄存器偶尔能读到偶尔读到全0xFFFF偶尔读回的数据错位。这种不稳定比完全读不到还麻烦因为你不知道哪个值才是真的。6.1 第一个发现MDC时钟频率太高MDC的最大频率在IEEE 802.3规范里是2.5MHz但很多主控默认把MDC配置成很高频率。我当时的主控默认MDC是25MHz直接超了10倍。这种情况下RTL8306MB的MDIO接口根本来不及响应读到的高位全是1或者数据错乱。修改方式是在设备树中配置MDC时钟分频把MDC降到2.5MHz以下。注意不同SoC的MDIO控制器分频参数不同不要只改一个数字要确认最终的MDC频率。有个快速的验证方法用示波器测MDC引脚的周期周期不小于400ns就基本到位。6.2 第二个发现MDIO的上拉电阻MDIO是漏极开路的双向信号规范要求有1.5kΩ~10kΩ的上拉电阻。但原理图阶段硬件同事根据经验放了一颗4.7kΩ上拉调试时发现信号幅度只有2.1V接近逻辑高电平阈值边缘。这是因为板上有多个MDIO设备共用总线等效上拉电阻并联后阻值变小拉高能力下降。解决方法把上拉电阻改成2.2kΩ并且只在总线上靠近主控的位置放一颗不要每个PHY都放。同时检查MDIO总线上挂的设备数量超过4个建议加一颗总线缓冲器。6.3 第三个发现复位时序复位完成后必须延时再访问这个问题最坑。RTL8306MB的复位脚拉高后内部逻辑需要几个毫秒完成初始化如果系统在这个期间立刻发起MDIO访问芯片可能完全不响应。我当时的系统启动脚本里复位后立即执行mdio read操作导致偶发读失败。修复方案在驱动初始化代码里复位引脚拉高后至少等待20ms再开始MDIO访问。具体等待时间可以参考数据手册的“Power-on/Reset Sequence”章节RTL8306要求的时间通常在10ms以上留一倍余量更保险。给个实用的初始化序列参考将复位脚拉低保持至少5ms拉高复位脚延时20ms等待芯片内部完成初始化读取PHY ID寄存器寄存器0x02和0x03验证MDIO通路如果读回ID不是预期值RTL8306MB的OUI/型号值不要继续往下走先从物理层找原因这套序列我在多个项目里用都很稳定。另外MDIO通路验证时不要只用ethtool建议直接用busybox的devmem或mdio-tools读写寄存器排除驱动层干扰。7. 驱动初始化序列从复位到首个报文转发的标准动作把上面这些坑都填平之后剩下的事情就是整理一套可以复用的初始化序列让后续项目不再重复踩雷。7.1 我的RTL8306MB初始化清单以下序列适用于把RTL8306MB配置为“5口LAN 1口CPU上游”的经典场景硬件复位拉低复位脚5ms拉高延时20msMDIO验证读取PHY ID寄存器确认MDIO通路关闭所有端口将所有端口的TX/RX使能清零避免初始化过程中产生异常流量配置CPU端口工作模式将端口5或对应CPU端口的模式寄存器配置为RMII并按需选择是否使能内部PHY配置端口速率和双工所有LAN口设为10/100M全双工自协商CPU口固定100M全双工不参与自协商配置默认VLAN将所有端口加入同一个默认VLANVID 1设置PVID开启端口转发使能所有端口的TX/RX清除统计计数器避免历史脏数据干扰后续排查第5步是个容易被忽视的细节。CPU口的RMII模式通常不支持自协商必须由软件强制指定为100M全双工否则MAC和交换芯片之间会因为协商失败而链路异常。这个值和MAC侧配置必须一致我遇到过配置为100M全双工但MAC侧还是自协商模式的情况结果链路时通时断。7.2 验证步骤从底层到上层逐层确认初始化后我习惯按下面的顺序验证MDIO层读PHY状态寄存器确认所有端口Link状态都是upMAC层ethtool确认MAC速率和双工二层转发两个LAN口直接ping通CPU转发用ip link add br0 type bridge把eth0和某个LAN口桥接再打流验证最终压力用iperf -u -b 95M -t 60打60秒UDP流观察丢包率低于0.01%7.3 这些坑其实可以避免硬件设计阶段就该做的3件事复盘整个项目我最大的体会是RMII的调试陷阱一半以上可以在原理图和PCB设计阶段就避免。第一认真对待strapping引脚。瑞昱芯片的默认配置高度依赖外部上下拉电阻画图前花30分钟核对数据手册能省下后面几天调试时间。第二预留测试点。REF_CLK、CRS_DV、TXD[0]、RXD[1]这几个关键信号务必在PCB上预留测试点或排针。逻辑分析仪和示波器不能只靠夹pin在MCU小封装下有些引脚你很难稳定夹住。第三规划时钟方案。如果系统对成本不敏感优先用独立有源晶振同时给MAC和交换芯片提供REF_CLK不要用“PHY出时钟给MAC”这种看似省事的方案。独立时钟的时序余量最大后续调试和量产都省心。8. 最后补一个实际心得带着问题去读数据手册比从头读到尾有效得多RTL8306MB的数据手册有几百页真不是人能从第一页读到最后一页的。但经过这几轮调试我发现一个更有效的阅读方式先通过MDIO把芯片的寄存器空间完整导出然后针对你遇到的每个现象去手册里搜对应的寄存器描述。这种“现象驱动”的阅读方式效率和记忆深刻度都远超通读。举个例子丢包问题如果直接去翻“RMII Interface”章节可能不会立刻联想到CRS_DV时序。但如果你先发现rx_missed_errors增长再搜这个计数器的含义就能顺藤摸瓜找到接收路径的时序要求。这种排查路径更短也更容易沉淀出一套自己团队的调试方法。另外RTL8306MB还有一些隐藏寄存器是数据手册里写得不详细的尤其是0x1A~0x1F这段很多与RMII模式、led控制、掉电模式相关。调试这类寄存器时建议用MDIO工具逐个bit翻转观察硬件行为变化把行为记录下来再总结规律。这个方法虽然“土”但非常有效。以上这些坑可能不是所有项目都会遇到但RMII接口的时序敏感性和瑞昱芯片寄存器默认状态的隐蔽性是这类集成交换芯片驱动开发的共性难题。只要硬件设计阶段多做一点调试阶段就能少熬几个夜。