1. 项目概述
这个FPGA以太网接口设计项目最硬核的地方在于完全用Verilog手写实现了UDP/TCP协议栈,从MAC层到传输层全部用硬件逻辑实现。不同于常见的软核方案,这套设计把协议处理全都压在硬件时序里跑,实测在Xilinx Artix-7上TCP吞吐能跑到850Mbps。架构上采用分层设计,MAC层与协议栈物理分离,支持RMII(百兆)和GMII/RGMII(千兆)两种物理接口,已经成功在Nexys4 DDR、黑金Artix7 200T和Xilinx KC705三款开发板上验证。
注意:纯硬件实现TCP协议栈需要特别注意状态机的健壮性,特别是遇到异常报文时要有完善的错误恢复机制,否则容易死锁。
2. 核心架构设计
2.1 分层模块化设计
整个系统分为三个主要层次:
- PHY接口层:处理RMII/GMII物理信号,包含时钟域转换
- MAC控制器:实现IEEE 802.3帧结构处理,带CRC校验模块
- 协议栈层:独立实现ARP、IP、ICMP、UDP、TCP协议
verilog复制// 顶层模块接口示例
module eth_top (
input rgmii_rxc, // RGMII接收时钟
input [3:0] rgmii_rxd, // RGMII接收数据
output rgmii_txc, // RGMII发送时钟
output [3:0] rgmii_txd, // RGMII发送数据
// AXI Stream接口
output [63:0] axis_tdata,
output axis_tvalid,
input axis_tready
);
2.2 关键状态机设计
TCP连接管理用经典的四层状态机实现:
- CLOSED
- SYN_SENT
- ESTABLISHED
- FIN_WAIT
每个状态转换都严格遵循RFC 793规范,超时重传采用指数退避算法:
verilog复制// TCP重传定时器逻辑
always @(posedge clk) begin
if (retransmit_en) begin
if (timer == 0) begin
retransmit_packet();
timer <= (timeout << retry_count); // 指数退避
retry_count <= retry_count + 1;
end else begin
timer <= timer - 1;
end
end
end
3. MAC层实现细节
3.1 RMII接口处理
百兆网版本采用RMII接口,需要特别注意时钟同步问题:
- 接收路径:
- 50MHz时钟域采样RX_ER和RXD[1:0]
- 实现4b/2b转换逻辑
- CRC校验模块采用流水线设计
verilog复制// CRC32计算优化实现
always @(posedge clk) begin
if(rx_en) begin
crc[0] <= crc[24] ^ crc[30] ^ rx_data[0];
crc[1] <= crc[25] ^ crc[31] ^ rx_data[1];
// ...省略中间30位
crc[31] <= crc[23] ^ rx_data[7];
end
end
3.2 RGMII时序约束
千兆网版本需要处理RGMII的DDR时序,在XDC文件中添加如下约束:
code复制set_input_delay -clock [get_clocks rgmii_rxc] \
-max 1.5 [get_ports rgmii_rxd[*]]
set_input_delay -clock [get_clocks rgmii_rxc] \
-min -0.5 [get_ports rgmii_rxd[*]]
4. TCP协议栈实现
4.1 滑动窗口管理
采用环形缓冲区实现发送窗口,关键参数:
- 窗口大小:16KB(可配置)
- MSS:1460字节
- 序列号空间:32位循环计数
verilog复制// 发送窗口数据结构
reg [7:0] tx_buffer [0:16383];
reg [13:0] win_start; // 窗口起始指针
reg [13:0] win_end; // 窗口结束指针
reg [31:0] next_seq; // 下一个待发送序列号
4.2 三次握手实现
SYN包处理需要正确实现TCP选项字段,特别是窗口缩放因子:
verilog复制// SYN包选项字段构造
localparam TCP_OPTIONS = {
8'h02, 8'h04, 8'h05, 8'hb4, // MSS 1460
8'h01, 8'h03, 8'h03, 8'h08, // 窗口缩放值8
8'h04, 8'h02 // SACK允许
};
5. 性能优化技巧
5.1 零拷贝接收路径
通过精心设计的流水线,实现从PHY到应用层的零拷贝传输:
- MAC层直接解析帧头
- IP层校验与分片重组并行处理
- TCP负载直接写入AXI Stream FIFO
5.2 校验和卸载
利用FPGA并行计算优势,在4个周期内完成IPv4/TCP校验和:
verilog复制// 并行校验和计算
always @(posedge clk) begin
sum0 <= sum0 + data[15:0];
sum1 <= sum1 + data[31:16];
sum2 <= sum2 + data[47:32];
sum3 <= sum3 + data[63:48];
end
6. 实测数据与调试
6.1 资源占用对比
| 模块 | LUT用量 | BRAM用量 | 最高频率 |
|---|---|---|---|
| MAC层 | 423 | 2 | 250MHz |
| TCP协议栈 | 1287 | 9 | 166MHz |
| UDP协议栈 | 215 | 1 | 200MHz |
| AXI适配器 | 85 | 1 | 150MHz |
6.2 常见问题排查
-
连接建立失败:
- 检查SYN/ACK选项字段对齐
- 确认初始序列号随机性
- 验证窗口缩放因子协商
-
数据传输中断:
- 检查重传计数器溢出
- 确认接收窗口更新及时
- 监控缓冲区溢出标志
-
性能瓶颈:
- 使用ILA抓取关键路径时序
- 检查流水线停顿周期
- 优化仲裁逻辑优先级
7. 应用实例
7.1 与Python通信示例
python复制import socket
def fpga_communicate():
with socket.create_connection(('192.168.1.100', 6000)) as s:
# 发送测试数据
s.sendall(b'FPGA Network Stack Test')
# 接收响应
data = s.recv(1024)
print(f"Received: {data.decode()}")
7.2 AXI Stream接口使用
verilog复制// 示例:将接收数据连接到DMA
eth_top eth_inst (
.rgmii_rxc(phy_rxc),
.rgmii_rxd(phy_rxd),
.axis_tdata(dma_tdata),
.axis_tvalid(dma_tvalid),
.axis_tready(dma_tready)
);
dma_controller dma (
.s_axis_tdata(dma_tdata),
.s_axis_tvalid(dma_tvalid),
.s_axis_tready(dma_tready)
);
8. 开发注意事项
-
时序收敛:MAC层需要严格满足建立/保持时间要求,建议:
- 对跨时钟域信号使用双触发器同步
- 关键路径添加流水线寄存器
- 使用Xilinx的BUFGCE优化时钟网络
-
内存管理:TCP重传缓冲区深度建议:
- 百兆网络:16-32个报文
- 千兆网络:64-128个报文
-
调试技巧:
- 内置环回测试模式
- 实现带时间戳的日志缓存
- 开发自动化测试脚本
这套设计最难能可贵的是完整实现了TCP的可靠性机制,包括序列号管理、超时重传、流量控制等核心功能。在KC705开发板上与Linux主机进行iperf测试时,连续传输1GB数据零丢包,RTT稳定在200μs以内。对于想深入理解网络协议硬件实现的开发者,这个项目提供了绝佳的学习样板。
