1. 从一根网线说起:为什么FPGA工程师绕不开以太网
很多人第一次接触FPGA以太网通信实验时,脑子里想的都是"我只要把数据从A点搬到B点就行了",结果打开参考设计一看,MAC、PHY、MII、RMII、MDIO、前导码、帧间隙……一堆名词砸过来,直接懵了。我当初也是这个状态,拿着一块开发板和一份原理图,盯着那颗PHY芯片的引脚定义看了整整一个下午,才慢慢把"以太网通信到底是怎么一回事"这条线捋清楚。
这篇日记主要想解决三个问题:第一,以太网通信在物理层和链路层到底遵循什么规则,为什么要有这些规则;第二,MAC和PHY这两个模块各自负责什么,它们之间是怎么"对话"的;第三,在FPGA里做以太网实验时,哪些细节是文档里一笔带过但实际会卡住你的。适合正在做FPGA网络通信入门实验、或者已经跑通了回环但说不清楚底层原理的读者。
先把一个核心认知摆在前面:以太网通信不是"发一串数据出去"这么简单,它是一套有严格时序、有帧结构、有握手规则的协议体系。你在FPGA里写的逻辑,本质上是在模拟MAC控制器的行为,而MAC和PHY之间的接口,就是你这套逻辑和外部世界之间的唯一通道。搞不清楚这个通道的规则,后面写再多代码都是碰运气。
2. 以太网帧的"交通规则":从比特流到有意义的数据
2.1 前导码与SFD:接收端如何找到"起点"
以太网在物理介质上传输的是一连串的比特流,接收端面对的是源源不断的电平变化。问题来了:接收端怎么知道哪一段比特是一个帧的开始?答案就是前导码(Preamble)和帧起始定界符(SFD)。
前导码是7个字节的0x55,也就是二进制01010101重复7次。这个 pattern 的作用是让接收端的时钟恢复电路"锁定"发送端的时钟频率。你可以把它理解成唱歌之前先打拍子,让伴奏和歌手对齐节奏。SFD是1个字节的0xD5,二进制是11010101,最后两位01标志着"正片开始",后面的就是真正的帧数据了。
这里有个实际调试中容易忽略的点:前导码和SFD通常是由PHY芯片或者MAC硬件自动添加和剥离的,你在FPGA逻辑里一般不需要手动去拼这8个字节。但如果你用的是纯逻辑实现的软MAC,那就得自己处理。我在第一次做实验时,因为参考设计里PHY配置成了自动添加前导码,而我的逻辑又手动加了一遍,结果抓包工具看到的帧全是错的,排查了大半天才发现是重复添加。
2.2 帧结构拆解:每个字段都在解决一个具体问题
一个标准以太网帧(不含VLAN标签)的结构如下:
| 字段 | 长度 | 作用 |
|---|---|---|
| 前导码 | 7字节 | 时钟同步 |
| SFD | 1字节 | 帧起始标志 |
| 目的MAC地址 | 6字节 | 接收方硬件地址 |
| 源MAC地址 | 6字节 | 发送方硬件地址 |
| 类型/长度 | 2字节 | 标识上层协议(如0x0800是IP) |
| 数据载荷 | 46~1500字节 | 实际传输的数据 |
| FCS | 4字节 | 帧校验序列 |
目的MAC地址在前、源MAC地址在后,这个顺序和很多人的直觉相反。为什么要这样设计?因为接收端需要第一时间判断这个帧是不是发给自己的,把目的地址放在最前面可以让硬件在收到前6个字节后就立刻做过滤决策,不需要等整个帧收完。这是一个典型的"为硬件实现优化"的设计思路。
数据载荷最小46字节这个限制也很有意思。如果实际数据不足46字节,需要填充到46字节。为什么要设下限?因为以太网早期是共享总线的,帧太短可能导致发送端还没发完,接收端就已经认为帧结束了,碰撞检测机制会失效。虽然现在基本都是交换式全双工,但这个最小帧长的规则一直保留了下来。
FCS用的是CRC-32校验,多项式是0x04C11DB7。在FPGA里实现CRC-32有两种常见做法:一种是查表法,用Block RAM存256个表项,每个时钟处理1字节;另一种是线性反馈移位寄存器(LFSR)法,每个时钟处理1比特,逻辑资源少但吞吐率低。如果要做千兆以太网,建议用查表法或者并行CRC计算,否则时钟频率跟不上。
2.3 帧间隙IFG:给接收端留一口喘息的时间
以太网规定,两个帧之间必须至少有12个字节时间的间隔,叫做帧间隙(Inter-Frame Gap, IFG)。在千兆速率下,12个字节时间就是96ns。这个规则的存在是因为接收端的PHY需要时间做时钟恢复、帧结束检测和状态复位。如果没有IFG,连续不断的帧会让接收端来不及处理。
在FPGA发送逻辑里,IFG通常由MAC控制器自动插入。但如果你是自己写的发送状态机,一定要记得在帧与帧之间留够这个间隔。我见过有同学写的发送模块背靠背连续发帧,结果接收端丢帧严重,就是因为忽略了IFG。
3. MAC与PHY的分工:谁负责什么,边界在哪里
3.1 用"寄信"来理解MAC和PHY的角色
把以太网通信类比成寄信:MAC负责写信封、贴邮票、决定这封信寄给谁,PHY负责把信装进信封、送到邮局、通过物理线路发出去。MAC处理的是数字逻辑层面的帧组装和解析,PHY处理的是模拟信号层面的电平转换和线路编码。
具体来说,MAC层(Media Access Control)的核心职责包括:帧的封装与解封装、地址过滤、CRC校验的生成与验证、流量控制(PAUSE帧)、以及和上层协议对接。PHY层(Physical Layer)的核心职责包括:并行数据与串行比特流的转换、线路编码(如4B/5B、8B/10B)、时钟恢复、信号驱动与接收、以及自协商(Auto-Negotiation)。
在FPGA实验中,MAC功能通常由FPGA内部逻辑实现(或者用厂商提供的MAC IP核),PHY则是开发板上那颗独立的芯片。两者之间通过MII系列接口连接。
3.2 MII、RMII、GMII、RGMII:接口选型的核心考量
MAC和PHY之间的接口有好几种标准,选哪种取决于你的速率需求和FPGA引脚资源:
| 接口类型 | 速率 | 数据位宽 | 时钟频率 | 引脚数(约) |
|---|---|---|---|---|
| MII | 10/100Mbps | 4位 | 25MHz/2.5MHz | 16 |
| RMII | 10/100Mbps | 2位 | 50MHz | 8 |
| GMII | 1000Mbps | 8位 | 125MHz | 24 |
| RGMII | 1000Mbps | 4位 | 125MHz(双沿) | 12 |
MII是最基础的标准,发送和接收各用4位数据线,外加时钟、使能、错误指示等控制信号。RMII是MII的简化版,数据线从4位减到2位,但时钟频率提高到50MHz,引脚数少了一半,适合引脚紧张的场合。GMII是千兆版本的MII,8位数据线,125MHz时钟。RGMII则在GMII基础上把数据线减到4位,用双沿采样(DDR)来维持吞吐率,引脚数大幅减少。
选型的核心逻辑是:速率要求决定基本接口类型,引脚资源决定是否需要用简化版本。如果你的FPGA引脚富裕,MII或GMII最省心,时序余量大;如果引脚紧张,RMII或RGMII是必然选择,但时序约束会更严格。
我在实际项目里用过RGMII,踩过一个典型的坑:RGMII的发送端需要输出时钟(TX_CLK)和数据(TXD[3:0])之间有一个约2ns的相位偏移,这样接收端才能在时钟沿正确采样数据。很多PHY芯片内部可以配置这个延迟,但如果配置不对,就会出现数据采样错误。解决办法是仔细阅读PHY芯片的数据手册,确认延迟是加在PHY侧还是FPGA侧,然后相应地在FPGA的时序约束里做处理。
3.3 MDIO管理接口:MAC如何"遥控"PHY
除了数据通道,MAC和PHY之间还有一条管理通道,叫做MDIO(Management Data Input/Output)。这是一条两线接口:MDC是时钟,MDIO是双向数据线。MAC通过MDIO读写PHY内部的寄存器,实现速率协商、双工模式设置、链路状态读取等功能。
MDIO的帧格式是固定的:前导码(32个1)+ 起始码(01)+ 操作码(读10/写01)+ PHY地址(5位)+ 寄存器地址(5位)+ 数据(16位)。在FPGA里实现MDIO控制器不难,本质上就是一个移位状态机。但要注意MDC的时钟频率不能超过2.5MHz,而且MDIO是双向线,需要正确处理三态缓冲。
很多初学者会忽略MDIO的作用,觉得"我只要把数据通道跑通就行了"。但实际上,如果PHY没有通过MDIO正确配置,数据通道根本不可能正常工作。比如PHY上电默认可能是10Mbps半双工模式,你不通过MDIO把它设成1000Mbps全双工,那通信速率和稳定性都会有问题。
4. 上电之后发生了什么:自协商与链路建立的完整过程
4.1 自协商的本质:双方在"谈判"
当网线插入开发板的RJ45接口后,PHY芯片并不会立刻开始传数据,而是先进入**自协商(Auto-Negotiation)**阶段。这个过程是双方PHY通过发送一种叫做"快速链路脉冲(FLP)"的特殊信号来交换各自支持的能力集,最终协商出一个双方都支持的最高速率和双工模式。
FLP的编码方式比较特殊,它把16位的能力信息编码成33个脉冲位置。这16位里包含了速率(10/100/1000Mbps)、双工模式(半双工/全双工)、以及是否支持PAUSE帧等信息。协商的规则是:双方取交集,然后选最高优先级。优先级从高到低依次是1000M全双工、1000M半双工、100M全双工、100M半双工、10M全双工、10M半双工。
这里有个实际调试中的经验:如果你的FPGA逻辑只支持100M,但PHY自协商到了1000M,通信就会失败。解决办法有两种:一是通过MDIO强制PHY工作在100M模式,关闭自协商;二是在FPGA逻辑里实现千兆MAC。前者简单粗暴,后者工作量大但更灵活。我在做第一个以太网实验时,就是通过MDIO把PHY强制设成100M全双工,先把数据通道跑通,再慢慢研究千兆的实现。
4.2 链路状态检测:怎么知道网线插好了
PHY芯片有一个链路状态寄存器(通常在寄存器地址0x01),其中的Link Status位会实时反映链路是否建立。FPGA可以通过MDIO轮询这个位,或者使用PHY输出的Link Status引脚(如果有的话)来检测链路状态。
在FPGA逻辑里,一个常见的做法是:在链路未建立时,复位MAC发送逻辑;链路建立后,再释放复位。这样可以避免在链路不稳定时发送无效数据。另外,链路状态变化时(比如网线被拔掉),PHY通常会产生一个中断信号,FPGA可以利用这个中断来及时响应。
4.3 从复位到第一个帧发出:完整时序链路
把上面的过程串起来,一个完整的以太网上电启动流程大致是这样的:
- 系统上电,FPGA和PHY各自完成内部复位。
- FPGA通过MDIO配置PHY的工作模式(速率、双工、是否开启自协商等)。
- PHY开始自协商,与对端交换能力信息。
- 自协商完成,链路建立,PHY的Link Status置位。
- FPGA检测到链路建立,释放MAC逻辑的复位。
- MAC开始可以发送和接收帧。
- 发送第一个帧时,MAC按照帧格式组装数据,通过MII/RGMII接口送给PHY。
- PHY对数据进行线路编码,通过网线发送出去。
这个流程看起来简单,但每一步都有细节。比如第2步,MDIO配置的时序必须符合标准,MDC频率不能超标;第5步,释放复位的时机要对,太早可能在链路还没稳定时就发数据,太晚则影响通信效率。
5. 实验中的那些"坑":从抓包异常到时钟约束
5.1 抓包工具看到的帧全是错的:排查思路
我第一次用抓包工具观察FPGA发出的帧时,发现所有帧的FCS都是错的,抓包工具直接标红。排查过程如下:
首先确认FPGA发送的帧结构是否正确。用逻辑分析仪抓MII接口上的数据,逐字节对照帧格式,发现目的MAC、源MAC、类型字段都对,数据载荷也对。那问题只能出在FCS上。
然后检查CRC-32的实现。我用的是查表法,表项是根据标准多项式生成的。仔细核对后发现,CRC计算的初始值设错了。以太网CRC-32的初始值是0xFFFFFFFF,而我设成了0x00000000。改过来之后,FCS就对了。
这个坑的教训是:CRC这类校验算法,初始值、多项式、输入输出是否反转,这三个参数必须和标准完全一致,差一个比特结果就全错。
5.2 接收端丢帧:可能是IFG不够
另一个常见问题是接收端丢帧。我用FPGA连续发送1000个帧,接收端只收到了900多个。排查后发现,发送逻辑在帧与帧之间没有插入足够的IFG。
在千兆模式下,IFG是12个字节时间,也就是96ns。我的发送状态机在发完一个帧后,只等了几个时钟周期就开始发下一个帧,远不够96ns。修改状态机,在帧结束后插入一个计数器,确保等待时间满足IFG要求后,丢帧问题就解决了。
这里有个经验:IFG的计算要基于实际的数据速率。100M模式下,12个字节时间是960ns;10M模式下是9.6us。如果你的FPGA逻辑要支持多种速率,IFG的等待时间需要根据当前速率动态调整。
5.3 时序约束:RGMII的建立保持时间怎么算
RGMII接口的时序约束是另一个容易出问题的地方。因为RGMII用双沿采样,时钟频率125MHz,周期8ns,每个沿都要正确采样数据,时序余量很小。
以发送方向为例,FPGA输出TX_CLK和TXD[3:0]。PHY在TX_CLK的上升沿采样TXD[0]和TXD[2],下降沿采样TXD[1]和TXD[3]。为了保证PHY能正确采样,TXD相对于TX_CLK需要有一个延迟。这个延迟通常由PHY内部提供,也可以通过FPGA的IOB延迟单元实现。
在时序约束里,需要设置set_output_delay,约束TXD相对于TX_CLK的偏移。具体数值要查PHY数据手册,通常在1.5ns到2.5ns之间。如果约束设得不对,综合出来的时序可能不满足,导致通信不稳定。
我的建议是:先用PHY内部延迟,把时序约束设宽松一些,确保功能跑通;然后再逐步收紧约束,观察通信质量。如果PHY内部延迟可调,可以通过MDIO尝试不同的延迟值,找到最稳定的配置。
6. 从实验到产品:还需要补哪些课
6.1 从MII到RGMII:接口切换的注意事项
如果你已经用MII跑通了实验,想升级到RGMII做千兆通信,需要注意几点:第一,RGMII的数据位宽从4位变成4位但时钟翻倍,发送和接收逻辑的状态机需要重新设计;第二,RGMII的时序约束比MII严格得多,必须做静态时序分析;第三,PHY的配置寄存器需要重新设置,确保工作在RGMII模式。
6.2 硬件设计上的配合:PHY外围电路不能马虎
FPGA逻辑写得再好,如果PHY外围电路有问题,通信照样不稳定。几个关键点:PHY的电源滤波要做好,模拟电源和数字电源要分开;晶振的精度要够,以太网对时钟精度要求是±50ppm以内;RJ45接口的变压器中心抽头要正确连接,否则信号完整性会出问题。
6.3 调试手段:没有抓包工具怎么办
不是每个人都有专业的以太网抓包工具。在没有抓包工具的情况下,可以用以下方法调试:第一,用FPGA内部的ILA(集成逻辑分析仪)抓MII接口上的数据,直接看帧内容;第二,在接收逻辑里加计数器,统计收到的帧数、错误帧数、CRC错误数等;第三,用LED指示链路状态和通信状态,快速判断问题出在链路层还是数据层。
我个人最常用的方法是ILA加计数器。ILA可以看到每一帧的详细内容,计数器可以快速定位是偶发问题还是必现问题。两者结合,大部分问题都能定位到。
以太网通信实验是FPGA学习中的一个经典项目,它涉及的知识面很广:数字逻辑设计、时序约束、协议理解、硬件调试。把这一套跑通,对FPGA工程师的成长非常有帮助。我在这个过程中最大的体会是:不要急着写代码,先把协议规则和硬件接口搞清楚,后面会省很多时间。很多看似复杂的通信问题,根源往往是对某个基础规则的理解不到位。