3、行情数据链路层解析:以太网帧结构、UDP/IP协议栈硬件实现、CRC校验

各位同学,咱们今天聊点实在的。行情数据从网线进来,到FPGA能认出来,中间这层解析工作,说白了就是“扒皮”。你得一层层把协议头剥开,才能拿到最里面的行情数据。我当年刚接触这个方向时,总觉得软件能做的事,干嘛非要用硬件?后来被一次延迟事故教育了——嗯,从那以后,我再也不敢小看硬件解析了。

3.1 以太网帧结构:第一层“皮”怎么扒?

行情数据最先碰到的是以太网帧。你想想看,网线上跑的都是比特流,FPGA得先把它对齐成字节,再拼成帧。标准以太网帧长这样:

前导码 SFD 目的MAC 源MAC 类型/长度 Payload FCS
7字节 1字节 6字节 6字节 2字节 46-1500字节 4字节

我个人习惯,在FPGA里做帧解析时,会先做一个“帧起始检测”模块。前导码是0x55的重复,SFD是0xD5。检测到SFD后,才开始正式收数据。这里有个坑——我曾经遇到过某家交换机会在SFD后面多塞一个时钟周期的抖动,导致我的状态机跳飞了。后来加了毛刺滤波才搞定。

关键点:FPGA里不要等整帧收完再解析,要流水线处理。边收边解析,延迟才能压到纳秒级。

3.2 UDP/IP协议栈硬件实现:这才是重头戏

以太网帧剥完后,里面是IP头,再里面是UDP头。软件里用socket几行代码搞定,硬件里你得自己搭状态机。我给大家画个图,看看数据流怎么走:

以太网帧解析 剥MAC头 + FCS校验 IP头解析 校验和 + 路由判断 UDP头解析 端口过滤 + 长度校验 行情数据 Payload输出 时钟域 流水线处理示意 时钟1: 收前导码 → 检测SFD 时钟2: 收MAC头 → 过滤MAC地址 时钟3: 收IP头 → 校验和计算 时钟4: 收UDP头 → 端口匹配 总延迟:约 8-12 时钟周期(取决于数据宽度)

你看这个图,每个模块只做一件事,做完就往后传。这就是硬件加速的精髓——流水线。软件里是串行处理,硬件里是并行流水,延迟差距就在这里。

3.2.1 IP头解析:校验和怎么算最快?

IP头里有校验和字段,软件里用补码求和。硬件里怎么做?我建议用树形加法器。举个例子,IP头20字节,拆成10个16位字,并行相加。代码大概这样:

// 伪代码:IP校验和并行计算
always @(posedge clk) begin
    sum1 <= word0 + word1;
    sum2 <= word2 + word3;
    sum3 <= word4 + word5;
    sum4 <= word6 + word7;
    sum5 <= word8 + word9;
    
    // 第二级
    sum12 <= sum1 + sum2;
    sum34 <= sum3 + sum4;
    
    // 第三级
    sum1234 <= sum12 + sum34;
    
    // 最终
    checksum <= ~(sum1234 + sum5);
end

这样做,三个时钟周期就能出结果。如果你用串行累加,20字节要累加10次,延迟多7个周期。在金融行情里,7个周期可能就是几百纳秒——够行情跳好几个价位了。

小技巧:IP头校验和计算时,可以跟数据接收并行。边收边算,收完头的同时校验和也出来了。这叫“零延迟校验”。

3.2.2 UDP头解析:端口过滤要快

UDP头里最重要的是源端口和目的端口。行情数据通常用固定端口,比如深交所行情是UDP 11000。FPGA里做端口过滤,说白了就是一个比较器。

但我提醒你——我曾经遇到过端口号跨字节边界的情况。UDP端口是16位,在字节流里可能是高字节在前,也可能是低字节在前。你得先确认网络字节序(大端),再比较。否则过滤出来全是错的。

字段 偏移 长度 说明
源端口 0 2字节 大端序,行情源通常固定
目的端口 2 2字节 大端序,FPGA根据此字段过滤
长度 4 2字节 UDP报文总长度,含头
校验和 6 2字节 可选,行情里通常为0

3.3 CRC校验:最后的防线

以太网帧尾有4字节FCS,用的是CRC32。FPGA里实现CRC,有查表法和LFSR法。查表法快,但占资源;LFSR法省资源,但延迟大。我个人习惯用并行CRC——一次处理32位数据,一个时钟周期出结果。

为什么CRC这么重要?你想想看,行情数据在传输过程中,哪怕一个比特翻转,价格就可能从100变成-100。这种错误必须拦截在链路层。我记得有一次,某家交易所的交换机光模块老化,偶尔会吐出CRC错误的包。要不是FPGA里做了CRC校验,那批行情数据就直接送进交易引擎了——后果不堪设想。

警告:不要用软件里的CRC查表法直接移植到FPGA。软件查表是256项,硬件里查256项的表会消耗大量LUT。建议用CRC生成多项式直接推导并行逻辑。

CRC32的多项式是:0x04C11DB7。并行CRC的推导过程有点复杂,我直接给个结论:32位数据并行CRC,逻辑大概需要30-40个LUT,延迟1个时钟周期。代码示例:

// 并行CRC32核心逻辑(32位数据宽度)
// 多项式:0x04C11DB7
// 注意:这是简化版,实际需要完整推导
assign crc_next[0] = data[31] ^ data[30] ^ crc[28] ^ crc[29];
assign crc_next[1] = data[30] ^ data[29] ^ crc[27] ^ crc[28];
// ... 省略中间位 ...
assign crc_next[31] = data[0] ^ crc[0];

嗯,这里要注意,CRC计算必须跟数据流对齐。以太网帧的FCS是从目的MAC开始算到Payload结束。FPGA里要精确控制计算使能信号,多算一个字节或少算一个字节,结果都不对。

3.4 链路层解析的整体时序

把上面这些串起来,一个完整的链路层解析流程大概是:

  1. 帧同步:检测SFD,开始收数据
  2. MAC过滤:比较目的MAC,不是自己的直接丢
  3. IP头解析:校验和验证,检查协议类型(必须是UDP)
  4. UDP头解析:端口过滤,提取Payload长度
  5. CRC校验:收完帧尾后,比对FCS
  6. 数据输出:所有校验通过后,输出行情Payload

整个流程在FPGA里,从第一个字节进入到最后Payload输出,延迟大概在10-15个时钟周期。如果时钟跑200MHz,那就是50-75纳秒。软件里同样的流程,少说也要几微秒。这就是硬件加速的价值所在。

核心总结:链路层解析是行情加速的第一道关卡。以太网帧结构要熟记,UDP/IP协议栈要流水线实现,CRC校验不能省。这三样做好了,后面的应用层解析才能站得住脚。

好了,这一章就到这里。链路层解析是基本功,但也是最容易出问题的地方。我见过太多项目,上层解析写得花里胡哨,底层链路层却漏了CRC校验,最后数据全是错的。记住:基础不牢,地动山摇