第四章 UDP/IP协议栈硬件实现:ARP协议处理、IP校验和计算、UDP封装与解封装、首部生成逻辑
各位同学,今天我们来啃一块硬骨头——UDP/IP协议栈的硬件实现。说实话,这部分内容在软件里跑起来很简单,但一旦要搬到FPGA上,很多细节就会冒出来。我当年第一次做这个项目时,就被ARP协议坑得不轻,后面会跟大家细说。
4.1 ARP协议处理:从“谁有IP”到“MAC在这”
ARP协议,说白了就是问一句“谁有这个IP地址?”,然后等对方回答“我的MAC地址是XX:XX:XX:XX:XX:XX”。在硬件里实现这个逻辑,核心就是一张查找表。
我习惯把ARP模块分成三个部分:
- 请求接收器:监听网络,收到ARP请求后解析目标IP
- 本地匹配器:拿目标IP跟本机IP比对
- 响应生成器:匹配成功就构造ARP应答包发回去
嗯,这里要注意一个细节。ARP请求是广播的,但应答是单播的。很多新手会搞混这个,直接把应答也广播出去,那网络里就乱套了。
核心要点:ARP模块必须维护一个缓存表,记录IP到MAC的映射。这个表可以用FPGA内部的BRAM实现,深度256就够用了。
我曾经遇到过一个坑:ARP缓存表没有做老化机制。结果某个设备换了网卡,MAC地址变了,但缓存里还是旧的。那段时间网络时通时断,排查了整整两天才发现是这个问题。
4.2 IP校验和计算:16位加法器的艺术
IP首部校验和,算法其实很简单:把首部按16位一组求和,取反码。但硬件实现时,效率是关键。
我推荐用并行加法树来做。举个例子,IP首部20字节,就是10个16位字。我们可以用三级加法器:
// 伪代码示意
stage1: sum1 = word0 + word1; sum2 = word2 + word3; ...
stage2: sum3 = sum1 + sum2; sum4 = sum3 + sum4; ...
stage3: final_sum = sum5 + sum6;
checksum = ~final_sum;
这样做的好处是延迟固定,一个时钟周期就能出结果。你想想看,如果串行累加,20字节要累加10次,延迟就大了。
小技巧:计算校验和时,别忘了处理进位回卷。16位加法如果产生进位,要加回到最低位。这个逻辑用组合逻辑就能搞定。
我记得有一次,IP校验和算出来总是差1。排查了半天,发现是字节序搞反了。网络字节序是大端,而我的加法器默认是小端。嗯,这个坑大家一定要注意。
4.3 UDP封装与解封装:轻量级的传输层
UDP比TCP简单太多了,没有握手、没有重传、没有拥塞控制。说白了就是“发出去就不管了”。但硬件实现时,反而因为简单而容易忽略一些细节。
UDP封装流程:
- 从上层拿到数据载荷
- 加上UDP首部(源端口、目的端口、长度、校验和)
- 交给IP层封装
UDP解封装就是反过来:
- 从IP层拿到UDP报文
- 解析UDP首部,提取端口号和长度
- 把数据载荷交给上层
这里有个关键点:UDP校验和是可选的。但在硬件实现里,我建议永远开启校验和计算。为什么?因为硬件算校验和几乎不花时间,但能避免很多莫名其妙的数据错误。
警告:UDP校验和计算时,要包含一个伪首部。伪首部包含源IP、目的IP、协议号、UDP长度。很多初学者会漏掉这个,导致校验和永远算不对。
4.4 首部生成逻辑:流水线里的艺术
首部生成是整个协议栈的“门面”。每个数据包出去,首部必须正确、完整、及时。我一般用状态机+流水线的方式来实现。
举个例子,一个完整的UDP/IP包首部生成流程:
| 阶段 | 操作 | 延迟 |
|---|---|---|
| Stage 1 | 组装MAC首部(目标MAC、源MAC、类型) | 1 clk |
| Stage 2 | 组装IP首部(版本、长度、ID、标志、TTL、协议、校验和) | 2 clk |
| Stage 3 | 组装UDP首部(源端口、目的端口、长度、校验和) | 2 clk |
| Stage 4 | 拼接数据载荷,输出完整包 | 1 clk |
你看,总共6个时钟周期就能生成一个完整的UDP/IP包。这个速度对于千兆以太网来说绰绰有余。
我个人习惯在Stage 2和Stage 3之间插入一个FIFO,用来缓冲IP首部计算的结果。这样做的好处是,如果UDP层还没准备好数据,IP层可以先等着,不会丢包。
4.5 整体架构图
下面这张图是我画的一个简化版UDP/IP协议栈硬件架构。你可以看到数据从MAC层进来,经过IP层、UDP层,最后到应用层。反过来也一样。
这张图里,ARP模块和IP层是并行的。为什么?因为ARP请求不需要经过UDP层,直接由MAC层处理就行。这样设计可以降低延迟。
4.6 避坑指南
最后,我把自己踩过的坑总结一下,你们遇到了可以直接拿来用:
- ARP缓存表溢出:我曾经只设计了16个表项,结果在大型网络里根本不够用。建议至少256个表项,并实现LRU淘汰策略。
- IP校验和计算时序:如果流水线没处理好,校验和计算会拖慢整个数据通路。我建议用独立的校验和计算模块,跟数据通路并行。
- UDP长度字段:这个字段包含UDP首部+数据的总长度。我见过有人只算了数据长度,结果接收端解析出错。
- 首部对齐:以太网要求数据包是4字节对齐的。如果UDP载荷不是4的倍数,需要填充0。这个细节很容易被忽略。
总结一下:UDP/IP协议栈的硬件实现,核心就是三个字——快、准、稳。快靠流水线,准靠校验和,稳靠状态机。把这三点做好了,你的网卡驱动就成功了一大半。
好了,这一章的内容就到这里。记住,纸上得来终觉浅,绝知此事要躬行。回去之后,建议你们在FPGA开发板上实际跑一下ARP协议,看看能不能抓到正确的应答包。