4. 网络协议栈基础:以太网帧结构、ARP协议、IP/UDP协议、MAC层与PHY层
做高频交易的FPGA加速,说白了就是跟网络报文打交道。你想想看,行情数据从交易所出来,经过网线、光模块,最终落到你的FPGA逻辑里。这中间每一层协议,都是我们必须要打通的关卡。
我个人习惯把网络协议栈比作一个「俄罗斯套娃」。最外面是物理层,往里是MAC层,再往里是IP和UDP,最里面才是你关心的业务数据。今天我们就一层层把它剥开。
4.1 以太网帧结构——最基础的「信封」
以太网帧,就是网络世界里的标准信封。不管里面装的是什么协议,外面这层信封的格式是固定的。
一个标准的以太网帧长这样:
| 前导码(7B) | 帧起始定界符(1B) | 目的MAC(6B) | 源MAC(6B) | 类型/长度(2B) | 载荷(46-1500B) | FCS(4B) |
嗯,这里要注意几个关键点:
- 前导码和SFD:这8个字节是物理层用的,FPGA的MAC核会自动处理,我们一般不用操心。
- MAC地址:6字节,全球唯一。目的MAC是下一跳设备的地址,源MAC是你自己的地址。
- 类型/长度字段:如果值大于0x0600(1536),就表示上层协议类型。比如0x0800是IP协议,0x0806是ARP协议。
- FCS:4字节的CRC校验。我在项目中遇到过好几次,因为PCB布线问题导致CRC错误率飙升,排查了整整两天才发现是信号完整性问题。
高频交易中的关键点:以太网帧的最小长度是64字节(从目的MAC到FCS)。如果载荷不足46字节,需要填充。这个填充逻辑在FPGA里要特别注意,处理不好会导致丢包。
4.2 ARP协议——问路用的「广播」
ARP协议,全称地址解析协议。说白了就是:我知道对方的IP地址,但我不知道它的MAC地址,怎么办?
ARP的工作流程很简单:
- 主机A广播一个ARP请求:「谁的IP是192.168.1.2?请告诉我你的MAC地址。」
- 主机B收到后,发现是自己的IP,就回复一个ARP应答:「我是192.168.1.2,我的MAC是AA:BB:CC:DD:EE:FF。」
- 主机A收到应答后,把IP和MAC的映射关系缓存起来。
在FPGA里实现ARP协议,我建议你注意两点:
- ARP请求的解析:收到广播请求后,要快速判断目标IP是否是自己。这个比较可以用一个简单的32位比较器搞定。
- ARP应答的生成:需要交换源和目的MAC地址,填充自己的MAC地址。这个逻辑很简单,但要注意时序——高频交易里,ARP应答的延迟会影响链路建立速度。
避坑指南:我曾经在ARP缓存表的设计上栽过跟头。如果缓存表满了,新的ARP学习请求会被丢弃,导致链路中断。建议至少保留4-8个表项,并且实现LRU淘汰策略。
4.3 IP协议——网络层的「路由信封」
IP协议是网络层的核心。以太网帧的Type字段为0x0800时,载荷里装的就是IP报文。
IP报文头长这样(固定部分20字节):
| 版本(4bit) | 首部长度(4bit) | 服务类型(8bit) | 总长度(16bit) |
| 标识(16bit) | 标志(3bit) | 片偏移(13bit) |
| 生存时间(8bit) | 协议(8bit) | 首部校验和(16bit) |
| 源IP地址(32bit) |
| 目的IP地址(32bit) |
在高频交易场景下,我们最关心的是:
- 总长度字段:用来判断报文是否完整。我习惯在FPGA里先解析这个字段,确认整个报文长度后再做后续处理。
- 协议字段:值为17时表示UDP,值为6时表示TCP。高频交易里绝大多数是UDP,因为延迟低。
- 首部校验和:这个校验和只覆盖IP头,不覆盖载荷。FPGA里可以用一个简单的16位累加器实现。
注意:IP分片在高频交易里几乎不会出现,因为行情数据包通常很小(几百字节)。但如果你遇到分片报文,处理起来会非常麻烦。建议直接丢弃分片报文,或者确保网络MTU配置足够大。
4.4 UDP协议——传输层的「快件」
UDP协议,全称用户数据报协议。它比TCP简单得多,没有握手、没有重传、没有拥塞控制。说白了就是「发了就不管了」。
UDP报文头只有8个字节:
| 源端口(16bit) | 目的端口(16bit) |
| 长度(16bit) | 校验和(16bit) |
在高频交易里,UDP是绝对的主角。原因很简单:
- 延迟低:不需要三次握手,不需要确认重传。
- 实现简单:FPGA里解析UDP头只需要几十个逻辑单元。
- 支持组播:交易所通常用UDP组播分发行情数据,一台机器发,多台机器收。
我个人习惯在FPGA里做UDP校验和的计算。虽然UDP校验和是可选的(可以设为0),但为了数据完整性,我建议还是算一下。毕竟高频交易里,一个错误的行情数据可能导致错误的交易决策。
4.5 MAC层与PHY层——物理世界的「桥梁」
MAC层和PHY层,是FPGA与物理网络的接口。
MAC层负责:
- 帧的封装与解封装
- CRC校验的生成与检查
- 流量控制(Pause帧)
- 半双工模式下的CSMA/CD
PHY层负责:
- 信号的编码与解码(如8B/10B编码)
- 时钟恢复与同步
- 线路驱动与接收
在FPGA里,我们通常使用厂商提供的MAC IP核(比如Xilinx的Tri-Mode Ethernet MAC)。这些IP核已经实现了MAC层的核心功能,我们只需要配置好参数,然后通过AXI-Stream接口收发数据即可。
高频交易中的实践:我建议你直接使用10G/25G Ethernet MAC IP核,配合SFP+或QSFP28光模块。PHY层通常集成在光模块里,FPGA只需要通过Serdes接口与之连接。这样延迟最低,也最稳定。
4.6 知识体系总览
下面这张图展示了网络协议栈的层次关系,以及各层之间的数据封装过程:
4.7 高频交易中的协议栈优化思路
了解了协议栈的基础结构后,我们来看看在高频交易场景下,FPGA是怎么做优化的。
核心思路就一句话:能硬件解析的,绝不留给软件。
具体来说:
- MAC层过滤:在FPGA里直接比较目的MAC地址,不是自己的帧直接丢弃。这样可以减少后续处理的数据量。
- IP层过滤:比较源IP和目的IP,只处理感兴趣的行情源。
- UDP端口过滤:只解析特定端口的UDP报文,其他端口直接忽略。
- 应用层解析:在FPGA里直接解析行情数据,提取出Order Book的增量更新。
这样做的好处是:整个协议栈的处理延迟可以控制在几百纳秒以内。而如果用软件(比如DPDK)来做,延迟通常在几微秒到几十微秒。
个人经验:我在一个项目中,把协议栈全部用Verilog实现,从网口到应用层解析,总共用了不到2000行代码。最终的延迟只有约150纳秒。嗯,这个数字在软件层面是几乎不可能达到的。
好了,协议栈的基础就讲到这里。下一层我们就要进入真正的「战场」——如何在FPGA里实现这些协议的硬件解析。到时候我会拿出我实际项目中的代码片段,咱们一起看看那些坑是怎么填平的。