1. 项目概述:纯Verilog实现的FPGA以太网协议栈
在FPGA开发领域,网络协议栈的实现一直是个硬核挑战。最近完成了一个完全用Verilog手写的以太网接口设计,支持UDP/TCP双协议栈,从MAC层到传输层全部通过硬件逻辑实现。这个设计最显著的特点是协议栈各层严格分离,MAC层与TCP/IP层独立工作,通过标准接口互连。
目前已经实现两个版本:百兆网采用RMII接口,千兆网采用GMII转RGMII接口。两种物理层接口都在实际硬件上通过测试,包括Nexys4 DDR(Artix-7)、黑金Artix-7 200T和Xilinx KC705(Kintex-7)等开发板。TCP模块实现了完整的校验和计算与数据包重发机制,能够与PC端的网络应用程序稳定通信。
特别提示:整个设计不依赖任何软核处理器或第三方IP,全部功能通过纯逻辑实现,资源消耗约2000个LUT(Xilinx FPGA),适合作为学习参考或简单通信场景使用。
2. 架构设计与模块划分
2.1 整体架构解析
系统采用分层设计,主要分为三个层级:
- 物理接口层:处理与PHY芯片的电气接口,包括时钟域转换、数据对齐等
- MAC层:实现IEEE 802.3协议规定的介质访问控制功能
- 协议栈层:包含IP、UDP、TCP等网络协议处理
这种分层设计使得每个模块可以独立开发和测试,也便于后期维护升级。例如,当需要更换物理接口时(如从RMII改为RGMII),只需替换对应的接口模块,上层协议栈完全不受影响。
2.2 关键模块功能说明
- CRC校验模块:采用流水线设计,单周期完成32位CRC计算
- MAC控制状态机:三级流水结构,精确控制帧间隔(IFG)
- TCP状态机:完整实现11种TCP状态转换,支持滑动窗口协议
- 重传缓冲区:环形队列结构,最多缓存16个待重传数据包
- AXI接口适配层:提供AXI Stream和AXI Lite两种总线接口选项
3. MAC层实现细节
3.1 RMII接口实现
百兆网版本采用RMII(Reduced Media Independent Interface)接口,主要特点包括:
- 50MHz参考时钟
- 2位并行数据总线
- 同步信号传输
接收路径上的关键设计是CRC校验模块,采用移位寄存器实现高效计算:
verilog复制always @(posedge clk) begin
if(rx_en) begin
crc_next = {crc[22:0], 1'b0} ^ (rx_data[7] ? 32'h04C11DB7 : 0);
//...中间6位处理省略
crc <= crc_next ^ ({27'b0, rx_data[0]} << 24);
end
end
这种设计能在单时钟周期内完成8位数据的CRC计算,实测在Nexys4 DDR开发板上可以稳定处理100Mbps的全双工数据流。
3.2 GMII/RGMII接口实现
千兆网版本采用GMII转RGMII方案,主要技术特点:
- 125MHz时钟频率
- 8位并行数据总线(GMII)
- DDR双沿采样(RGMII)
- 内部自动校准输入延迟
发送端的MAC控制状态机采用三级流水设计:
- 第一级:帧头生成(前导码+SFD)
- 第二级:有效载荷传输
- 第三级:帧尾处理(FCS生成+IFG控制)
这种设计确保帧间隔(IFG)严格符合IEEE 802.3标准规定的96位时间要求。
4. TCP/IP协议栈实现
4.1 IP协议处理
IP模块主要功能:
- IPv4头部校验和计算
- TTL字段处理
- 分片和重组(未实现)
- 协议字段分发(TCP/UDP/ICMP)
校验和计算采用16位累加再取反的方式,通过流水线设计实现单周期延迟:
verilog复制always @(posedge clk) begin
if(calc_en) begin
sum <= sum + ip_header[15:0];
if(cnt == 5'd9) begin
checksum <= ~(sum + (sum >> 16));
end
end
end
4.2 TCP协议实现
TCP模块是整个设计中最复杂的部分,主要功能包括:
- 完整状态机(11种状态)
- 序列号管理
- 滑动窗口控制
- 超时重传机制
- 流量控制
重传机制采用环形缓冲区实现,每个TCP连接维护独立的发送队列:
verilog复制reg [7:0] retry_queue[0:15];
reg [3:0] wr_ptr, rd_ptr;
always @(posedge retry_clk) begin
if(need_retry && !full) begin
retry_queue[wr_ptr] <= pkt_id;
wr_ptr <= wr_ptr + 1;
end
if(!timer_active && wr_ptr != rd_ptr) begin
retransmit_pkt(retry_queue[rd_ptr]);
rd_ptr <= rd_ptr + 1;
timer <= 16'hFFFF; //初始重传超时
end
end
这种设计可以缓存最多16个待重传数据包,实测在网络中断恢复后能够自动重新建立连接。
4.3 UDP协议实现
相比TCP,UDP实现较为简单,主要功能:
- 校验和计算(可选)
- 端口号过滤
- 数据包长度检查
UDP接收状态机仅需三个状态:
- 等待头部
- 处理数据
- 完成校验
5. AXI接口封装
5.1 AXI Stream接口
为方便集成到SoC系统,设计提供了AXI Stream封装:
verilog复制axis_fifo #(.DWIDTH(64)) rx_fifo (
.s_axis_tdata(phy_rx_data),
.s_axis_tvalid(phy_rx_valid),
.m_axis_tready(cpu_ready),
.almost_full(eth_busy)
);
FIFO深度经过多次优化测试,最终确定:
- 百兆网:512深度
- 千兆网:1024深度
这种配置在丢包率和资源占用之间取得了良好平衡。
5.2 AXI Lite寄存器接口
控制寄存器通过AXI Lite总线暴露,主要包括:
- MAC地址配置
- IP地址配置
- 端口号设置
- 状态寄存器
- 中断控制
寄存器映射表:
| 地址偏移 | 寄存器名称 | 功能描述 |
|---|---|---|
| 0x00 | CTRL_REG | 控制寄存器 |
| 0x04 | STATUS_REG | 状态寄存器 |
| 0x08 | MAC_ADDR0 | MAC地址低32位 |
| 0x0C | MAC_ADDR1 | MAC地址高16位 |
| 0x10 | IP_ADDR | IP地址 |
6. 调试与测试
6.1 测试环境搭建
硬件测试平台:
- 百兆网:Digilent Nexys4 DDR(Artix-7 XC7A100T)
- 千兆网:
- 黑金Artix-7 200T开发板
- Xilinx KC705评估板(Kintex-7 XC7K325T)
软件测试工具:
- Wireshark网络抓包分析
- 网络调试助手
- Python socket程序
- C语言socket测试程序
6.2 典型测试场景
场景1:TCP连接建立
python复制import socket
s = socket.create_connection(('192.168.1.10', 6000))
s.send(b'FPGA Network Stack Test')
data = s.recv(1024)
s.close()
场景2:UDP数据传输
python复制import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(b'Hello FPGA', ('192.168.1.10', 6001))
6.3 常见问题排查
-
TCP连接失败
- 检查窗口缩放选项配置
- 确认序列号初始化正确
- 验证校验和计算
-
数据包丢失
- 检查FIFO深度设置
- 确认时钟域交叉处理正确
- 测试物理层信号完整性
-
性能不达标
- 检查流水线停顿情况
- 分析状态机转换效率
- 确认没有不必要的缓冲区满信号
7. 性能与资源占用
7.1 资源消耗统计
在Xilinx Artix-7 XC7A200T上的实现结果:
| 模块 | LUT数量 | 寄存器数量 | BRAM数量 |
|---|---|---|---|
| MAC层 | 320 | 420 | 0 |
| IP协议栈 | 280 | 310 | 0 |
| UDP协议 | 150 | 120 | 0 |
| TCP协议 | 1250 | 980 | 2 |
| AXI接口 | 200 | 180 | 1 |
| 总计 | 2200 | 2010 | 3 |
7.2 性能测试结果
-
百兆网版本:
- TCP吞吐量:94Mbps
- UDP吞吐量:98Mbps
- 延迟:<10μs
-
千兆网版本:
- TCP吞吐量:850Mbps
- UDP吞吐量:980Mbps
- 延迟:<5μs
8. 设计经验与技巧
8.1 时钟域处理
网络协议栈涉及多个时钟域:
- PHY接口时钟(25/125MHz)
- 系统时钟(通常100/150MHz)
- AXI总线时钟(可能不同步)
推荐采用以下策略:
- 使用独立的时钟域转换模块
- 对跨时钟域信号进行双寄存器同步
- 异步FIFO处理数据流跨时钟域
8.2 状态机设计
网络协议状态机设计要点:
- 明确划分状态,避免状态爆炸
- 每个状态对应明确的功能
- 状态转换条件要完备
- 添加超时异常处理
例如TCP状态机部分代码:
verilog复制always @(posedge clk) begin
case(tcp_state)
TCP_SYN_SENT: begin
if(rcv_ack && rcv_syn) begin
tcp_state <= TCP_ESTABLISHED;
end
else if(timeout) begin
tcp_state <= TCP_CLOSED;
end
end
// 其他状态处理...
endcase
end
8.3 调试技巧
-
内置环回测试模式
- MAC层环回
- 协议栈环回
- 应用层环回
-
日志缓存
- 带时间戳的关键事件记录
- 可配置的触发条件
- 通过寄存器接口读取
-
自动化测试
- 预先生成测试向量
- 自动检查响应
- 统计测试覆盖率
9. 应用场景与扩展
9.1 典型应用场景
-
嵌入式网络设备
- 工业控制
- 数据采集
- 网络监控
-
协议加速
- 网络功能卸载
- 加密通信
- 协议转换
-
教学研究
- 网络协议学习
- FPGA开发实践
- 硬件安全研究
9.2 扩展方向
-
性能优化
- 增加流水线级数
- 优化缓冲区管理
- 实现TCP卸载引擎
-
功能增强
- 支持IPv6
- 添加TLS加密
- 实现HTTP协议
-
多端口支持
- 交换机功能
- 负载均衡
- 网络分流
在KC705开发板上的实测表明,当前设计还有约60%的逻辑资源剩余,完全可以容纳这些扩展功能。特别是TCP卸载引擎,可以显著提升处理器的网络性能。