以太网基础回顾:OSI模型与TCP/IP协议栈,MAC帧格式,以太网PHY芯片接口

各位同学,咱们今天聊点基础的,但也是最重要的东西。做UDP解析引擎,说白了就是在跟以太网打交道。你要是连以太网怎么传数据都不清楚,那后面的设计肯定要踩坑。我当年刚入行时,就吃过这个亏——上来就写RTL,结果MAC层和PHY层的时序怎么都对不上,折腾了两周才发现是接口理解错了。

好,咱们从头捋一遍。先看这张图,把整个知识体系串起来。

以太网基础知识体系 OSI七层模型 vs TCP/IP四层模型 应用层 → 传输层 → 网络层 → 数据链路层 → 物理层 MAC帧格式(核心) 前导码(7B) | SFD(1B) | 目的MAC(6B) | 源MAC(6B) | 类型/长度(2B) | 数据(46-1500B) | FCS(4B) ⚠ 最小帧64字节,最大帧1518字节(不含前导码) 以太网PHY芯片接口 GMII(千兆媒体独立接口) | RGMII(简化千兆媒体独立接口) 🔑 关键信号:TXD/RXD、TX_CLK/RX_CLK、TX_EN/RX_DV、TX_ER/RX_ER FPGA实现UDP解析引擎 = 吃透这三层

OSI模型与TCP/IP协议栈

先说说OSI七层模型。教科书上把它分成了物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。但说实话,咱们做FPGA的,真正打交道的就下面四层。上面三层,那是软件工程师的活。

TCP/IP协议栈更实在,直接给你浓缩成四层:

  • 应用层:HTTP、FTP、TFTP这些。咱们的UDP解析引擎,最终服务的就是这层。
  • 传输层:TCP和UDP。咱们要做的UDP,就在这里。UDP头只有8个字节,比TCP简单多了。
  • 网络层:IP协议。负责路由和寻址。IP头20字节,里面有个总长度字段,咱们解析UDP时要用到。
  • 数据链路层:MAC层。这是FPGA直接打交道的层。MAC帧怎么封装、怎么解封,全在这层。

我个人习惯:做硬件设计时,脑子里要时刻想着「数据流」。数据从PHY进来,经过MAC解帧,再到IP层解包,最后到UDP层提取数据。每一层都在做「剥洋葱」的工作。

你想想看,为什么我们要回顾这个?因为UDP解析引擎,本质上就是在FPGA里实现一个「硬件协议栈」。它要完成的工作,就是从物理层拿到原始比特流,然后一层层解析上去,直到把UDP数据提取出来。

MAC帧格式

好,重点来了。MAC帧格式,这是咱们写RTL时最常打交道的东西。我直接给你画个表格,一目了然。

字段 长度(字节) 说明
前导码(Preamble) 7 0x55重复7次,用于时钟同步
帧起始定界符(SFD) 1 0xD5,标志帧开始
目的MAC地址 6 接收方MAC,全F为广播
源MAC地址 6 发送方MAC
类型/长度(Type/Len) 2 大于0x0600表示类型(如0x0800=IP),否则表示长度
数据(Payload) 46~1500 IP数据包,最小46字节(不足则填充)
帧校验序列(FCS) 4 CRC32校验,覆盖目的MAC到数据字段

我曾经踩过一个坑:做千兆以太网时,前导码和SFD在GMII接口上是8个字节一起传的。但有些PHY芯片会把前导码吃掉,只给SFD。如果你没看数据手册,直接按标准帧格式去解析,第一个字节就对不上,整个帧就废了。

这里有个关键点:MAC层的最小帧长是64字节(从目的MAC到FCS)。为什么?因为要防止「冲突检测」出问题。如果帧太短,发送方还没检测到冲突就发完了,那CSMA/CD就失效了。虽然现在全双工模式已经不用CSMA/CD了,但这个约束一直保留着。

咱们做UDP解析时,IP头20字节 + UDP头8字节,所以UDP数据至少要有18字节才能凑够46字节的MAC数据段。如果数据不够,MAC层会自动填充0。这个细节,你在RTL设计时一定要处理。

以太网PHY芯片接口

PHY芯片,说白了就是把MAC层的数字信号转换成模拟信号,扔到网线上去。咱们FPGA和PHY芯片之间,通过GMII或RGMII接口通信。

GMII(千兆媒体独立接口)

GMII是并行接口,数据位宽8位,时钟频率125MHz。关键信号如下:

  • TXD[7:0]:发送数据总线
  • TX_CLK:发送时钟,125MHz
  • TX_EN:发送使能,高电平表示TXD有效
  • TX_ER:发送错误,用于插入错误码
  • RXD[7:0]:接收数据总线
  • RX_CLK:接收时钟,由PHY提供
  • RX_DV:接收数据有效
  • RX_ER:接收错误

GMII的时序很简单:每个时钟周期,TXD/RXD上就是1字节数据。TX_EN拉高时,数据有效。但要注意,GMII用了25根信号线,PCB布线时很占地方。所以现在主流都用RGMII。

RGMII(简化千兆媒体独立接口)

RGMII把数据位宽减半,变成4位,但用DDR(双沿采样)来保持吞吐量。时钟还是125MHz,但上升沿和下降沿各传4位,合起来还是1字节。

RGMII的关键信号更少:

  • TXD[3:0]:发送数据,上升沿传低4位,下降沿传高4位
  • TX_CTL:发送控制,上升沿是TX_EN,下降沿是TX_EN XOR TX_ER
  • RXD[3:0]:接收数据,同样DDR
  • RX_CTL:接收控制,上升沿是RX_DV,下降沿是RX_DV XOR RX_ER

我建议:新手做RGMII时,一定要在FPGA内部做IODELAY校准。因为PCB走线长度不同,时钟和数据的相位关系会偏移。我见过有人直接拿RGMII信号去采,结果10次有3次采错。加个IODELAY,调到眼图中心,稳得很。

为什么RGMII要把TX_EN和TX_ER编码到一根线上?说白了就是为了省引脚。TX_CTL在上升沿传TX_EN,下降沿传TX_EN XOR TX_ER。接收端解码时,先采上升沿得到TX_EN,再用TX_EN去异或下降沿的值,就得到TX_ER了。这个小技巧,在RTL里实现起来也就几行代码。

嗯,这里要注意:RGMII的时钟是源同步的。发送时钟由发送端提供,接收时钟由PHY提供。所以你在FPGA里做RGMII接口时,接收时钟域和发送时钟域是异步的。跨时钟域处理,那是基本功了。

好了,以太网基础就回顾到这里。这些内容看起来简单,但每一个细节都可能在你的UDP解析引擎设计中冒出来。记住一句话:硬件设计,细节是魔鬼。


公众号:蓝海资料掘金营,微信deep3321