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几行代码搞定,硬件里你得自己搭状态机。我给大家画个图,看看数据流怎么走:
你看这个图,每个模块只做一件事,做完就往后传。这就是硬件加速的精髓——流水线。软件里是串行处理,硬件里是并行流水,延迟差距就在这里。
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 链路层解析的整体时序
把上面这些串起来,一个完整的链路层解析流程大概是:
- 帧同步:检测SFD,开始收数据
- MAC过滤:比较目的MAC,不是自己的直接丢
- IP头解析:校验和验证,检查协议类型(必须是UDP)
- UDP头解析:端口过滤,提取Payload长度
- CRC校验:收完帧尾后,比对FCS
- 数据输出:所有校验通过后,输出行情Payload
整个流程在FPGA里,从第一个字节进入到最后Payload输出,延迟大概在10-15个时钟周期。如果时钟跑200MHz,那就是50-75纳秒。软件里同样的流程,少说也要几微秒。这就是硬件加速的价值所在。
核心总结:链路层解析是行情加速的第一道关卡。以太网帧结构要熟记,UDP/IP协议栈要流水线实现,CRC校验不能省。这三样做好了,后面的应用层解析才能站得住脚。
好了,这一章就到这里。链路层解析是基本功,但也是最容易出问题的地方。我见过太多项目,上层解析写得花里胡哨,底层链路层却漏了CRC校验,最后数据全是错的。记住:基础不牢,地动山摇。