资讯动态

FPGA无操作系统LWIP移植:从查询到中断的TCP吞吐优化

发布时间:2026/9/19 13:16:26 来源:尧图企业网站定制
简介围绕TCP/IP通信技术在Xilinx FPGA上的实现这份PDF是面向嵌入式通信开发者和水利水电自动化领域技术人员的专业参考文献重点探讨无操作系统环境下高效网络通信的协议栈构建方法。文档基于Spartan 3 FPGA与32位MicroBlaze软核介绍了由ILMB/DLMB总线、BRAM、SDRAM控制器、以太网接口、UART、定时器等构成的完整片上系统架构并详细说明LWIP轻量级协议栈的移植思路、驱动层与协议栈层实现以及如何优化上述代码来提升实时性读者可据此了解FPGA片上系统配置、LWIP协议栈结构并参考文中关键源码进行实际移植与测试。资源包仅含1个PDF大小187KB内容精炼、便于快速查阅。目前已有146人学习下载适合FPGA网络通信系统设计、TCP/IP协议栈移植学习及相关课题立项前的技术调研。1. 从 IEC 61850 到 Spartan-3为什么 FPGA 也要跑 TCP/IPIEC 61850 成为电力自动化与变电站通信网络的国际标准后水利水电行业的设备层通信正快速从 RS-485、CAN-bus 转向以太网。这意味着每个间隔控制器都需要 100 Mbps 网络接口并且要在有限的存储和功耗预算内完整跑起 TCP/IP 协议栈。用 FPGA 做这件事的常见误区是“先配一个 RTOS再移植 lwIP”但操作系统引入的任务切换和中断延迟会让吞吐很难稳定。我实测过一版完全不带操作系统的方案Xilinx Spartan-3 开发板MicroBlaze 软核跑在 100 MHzEMAC IP Core 以中断 FIFO 模式驱动 LWIP 的 RAW API最终在 100 Mbps 链路上做到 63.6 Mbps 的 TCP 接收吞吐。这个成绩比查询模式高四分之一关键不在 CPU 主频而在协议栈的组织方式和驱动层的中断路径。这篇把硬件系统组成、LWIP 移植的四种工作模式、以及从查询到中断的优化过程拆开讲。2. 硬件平台与 SoC 架构Spartan-3 的 MicroBlaze 与 EMAC IP 核2.1 MicroBlaze 与 OPB 总线Spartan-3 是 Xilinx 在 90 nm 工艺下的低成本 FPGA 系列门容量从 5 万门到 500 万门用户 I/O 最多 784 个。在这个系列上做网络通信CPU 不能指望硬核 PowerPC所以 Xilinx 主推的是 MicroBlaze 32 位软核。MicroBlaze 在 150 MHz 主频下大约能提供 125 DMIPS单独处理 TCP/IP 协议栈已经够用但前提是不要为每一层协议都建立一个独立任务否则上下文切换会吃掉大量有效周期。当时 EDK 生成的系统总线通常包含 OPBOn-Chip Peripheral Bus、ILMB 和 DLMB。ILMB 和 DLMB 是本地存储器总线直接连接块 RAM用于保证取指和数据访问的低延迟OPB 则是外设总线挂载以太网 MAC、UART、GPIO、定时器和 SDRAM 控制器。这个结构是 Xilinx 默认的 MicroBlaze 系统模板自己搭也可以但手动拼总线的时序约束会比较麻烦。需要特别注意的是SDRAM 控制器挂在 OPB 上TCP 数据从 EMAC 进来后要穿过 OPB 才能写入 SDRAM。如果总线上还有其他高优先级外设频繁抢占仲裁吞吐就会明显下降这也是后来很多设计改用 AXI 总线的原因。2.2 以太网接口 IP 核与存储器系统Spartan-3 上的 10/100 以太网方案主要是两种 IP 核XPS Ethernet LiteLL TEMAC和 XPS LL TEMAC。前者结构简单支持 FIFO 接口适合无 DMA 的场景后者可以接 OPB DMA吞吐更高但代码量和寄存器复杂度也会上升。论文里提到的是 100 Mbps 的 EMAC IP Core从后续代码中出现的xemacliteif_init可以看出底层驱动用的是 Ethernet Lite。这个驱动在 BSP 里会生成一组地址和设备宏例如#define XPAR_XEMACLITE_0_DEVICE_ID 0 #define XPAR_ETHERNET_LITE_0_BASEADDR 0x81000000 #define XPAR_ETHERNET_LITE_0_HIGHADDR 0x8100FFFF #define XPAR_ETHERNET_LITE_0_TX_PING_PONG 0 #define XPAR_ETHERNET_LITE_0_RX_PING_PONG 0参数说明TX_PING_PONG和RX_PING_PONG决定是否启用 ping-pong 缓冲。置 0 表示每个方向只有一组缓冲区CPU 处理旧帧时 MAC 无法往另一套缓冲区写新帧置 1 则会生成两套基地址MAC 可以在 CPU 处理当前数据包时继续接收下一帧对避免整包丢失非常关键。100 Mbps 链路下每 12 微秒左右就会来一个最大长度帧如果只有一个缓冲区CPU 稍有延迟就会溢出。除缓冲区外EMAC 和 PHY 之间走 MII 接口收发各 4 位数据线时钟 25 MHz。PHY 地址通常由硬件引脚上下拉决定复位时序要求复位信号保持至少 1 ms。这些约束写进 UCF 文件后系统时钟本身不需要和 MII 时钟同源但 EMAC 的参考时钟必须稳定否则链路协商会失败。2.3 系统组成与关键参数模块作用影响吞吐的关键参数MicroBlaze32 位 CPU 软核指令/数据 Cache 大小ILMB / DLMB本地 BRAM 总线存放关键数据结构和栈OPB 总线外设互连总线仲裁优先级EMAC Lite10/100 以太网 MACFIFO 深度、ping-pongSDRAM MEMC外部存储器控制器刷新周期、行策略UART 16550调试串口波特率、发送中断实际的 EDK 配置里我一般会把 LWIP 的全局数组、PBUF 池和 netif 结构体放在 BRAM 段而不是默认的 SDRAM。SDRAM 随机访问延迟在几十纳秒量级频繁执行memcpy和pbuf_alloc时这个延迟会被放大到足以拉低吞吐。论文里系统使用 64 MB SDRAM最终性能是依靠中断驱动优化获得的并没有特别调存储布局如果把 pbuf 放入 BRAM理论上还可以再往上追几个 Mbps。3. LWIP 协议栈的无操作系统移植从四种工作模式到 RAW API3.1 四种协议栈组织方式的取舍在嵌入式系统里移植 LWIP首先要决定协议栈以什么形态存在。论文给出了四种方式至今仍值得逐一比较。第一种是 TCP/IP 的每一层单独建一个进程。链路层一个进程IP 层一个进程TCP 层一个进程。优点是分层模型和教材完全一致调试时可以在每一层打日志缺点也最明显收一个 TCP 段要发生三次上下文切换。从网卡驱动到链路层进程一次链路层到 IP 层一次IP 层到 TCP 层一次。MicroBlaze 没有 MMU上下文切换需要保存和恢复十几个寄存器一百兆网络下每秒要处理几千包单是切换开销就能让 CPU 占用率升到 50% 以上。第二种是把 TCP/IP 协议栈放进操作系统内核。应用程序通过系统调用访问 socket所有数据包处理都在内核空间完成。理论效率不错但代码与内核绑定太紧换一个 RTOS 就要重新适配在资源有限的 FPGA 设计里维护成本过高。第三种是协议栈所有层放在同一个进程中应用层作为独立任务通过邮箱、消息队列和协议栈通信。移植量适中结构清晰但消息传递会引入两次拷贝一次从协议栈拷贝到消息一次从消息拷贝到应用缓冲区。双倍拷贝对 100 Mbps 吞吐是明显负担。第四种是应用层直接驻留在 TCP/IP 进程中用 LWIP 的 RAW API 注册回调函数。无操作系统也能运行数据包从驱动进入协议栈后直接分发给应用没有跨任务拷贝也没有上下文切换。论文选的就是这种方案实测性能也最好。下面是移植时最关键的一段初始化代码。3.2 LWIP 初始化与内存池分配无操作系统环境下LWIP 的初始化顺序必须严格遵守依赖关系先底层后高层sys_init(); /* 系统抽象层NO_SYS1时为空实现 */ mem_init(); /* 堆内存管理用于非池化分配 */ memp_init(); /* 内存池初始化TCP PCB、PBUF等 */ pbuf_init(); /* 包缓冲区分配器 */ netif_init(); /* 初始化netif链表 */ tcp_init(); /* TCP控制块初始化 */逻辑说明memp_init()管理的池大小由lwipopts.h中的MEMP_NUM_TCP_PCB、MEMP_NUM_TCP_SEG等宏决定。内存池固定大小避免堆碎片但总数必须覆盖并发连接和分段数否则tcp_seg分配失败会静默丢包。pbuf_init()则负责建立 PBUF 池用于保存收到的网络帧池深度不是越大越好太大会挤占其他模块的 BRAM 空间。在无操作系统模式下必须把lwipopts.h里的NO_SYS置 1同时让SYS_LIGHTWEIGHT_PROT保持为 0否则源码会去调用sys_arch里不存在的信号量函数链接直接报错。sys_init()在无 OS 情况下是空函数但保留调用可以让代码后续移植到 RTOS 时不需要修改。3.3 网络接口注册与地址参数初始化完成后要把 EMAC 的驱动挂到 LWIP 的 netif 上。原始代码的核心调用如下struct netif *default_netif; ip_addr_t ipaddr, netmask, gw; IP4_ADDR(ipaddr, 192, 168, 1, 100); /* FPGA板卡IP */ IP4_ADDR(netmask, 255, 255, 255, 0); /* 子网掩码 */ IP4_ADDR(gw, 192, 168, 1, 1); /* 网关 */ default_netif mem_malloc(sizeof(struct netif)); if (default_netif NULL) { print(netif_add(): out of memory\r\n); return 1; } default_netif netif_add(default_netif, ipaddr, netmask, gw, XEmacLite_ConfigTable[0], xemacliteif_init, ip_input); netif_set_default(default_netif); netif_set_up(default_netif);参数说明netif_add的第四个参数是状态指针这里传 EMAC 驱动的配置表地址驱动初始化时会读取里面的基地址和中断号第五个参数xemacliteif_init负责复位 EMAC 寄存器并启动 PHY 协商第六个参数ip_input是接收回调当 MAC 收到完整以太网帧驱动会调用它进入 IP 层处理流程。一个容易踩的坑是mem_malloc得到的 netif 内存必须位于 CPU 可直接访问的地址空间。如果放在 SDRAM而 EMAC 的 DMA 配置仍然指向本地 RAM数据路径就会断掉。这个资源场景下通常把 netif 结构体声明为全局变量比动态分配更安全。3.4 用 RAW API 搭建最小 TCP 回声服务LWIP 的 RAW API 靠回调驱动典型过程是创建 PCB、绑定端口、进入监听、注册接受回调void echo_init(void) { struct tcp_pcb *pcb; pcb tcp_new(); if (pcb ! NULL) { tcp_bind(pcb, IP_ADDR_ANY, ECHO_PORT); pcb tcp_listen(pcb); tcp_accept(pcb, echo_accept); } }echo_accept会在新连接到达时被调用然后在里面通过tcp_recv注册数据接收函数。RAW API 的回调全部运行在同一个上下文回调内部不能阻塞也不能做长时间循环否则整个协议栈停摆。大数据量接收时每处理完一段都要调用tcp_recved告诉协议栈释放缓冲区并更新接收窗口漏掉这一步会表现为吞吐逐步下降最终停在很低的水平。下面用一个对比表来总结四种模式的差异方便在项目启动前做选型。工作模式是否依赖 OS数据拷贝次数上下文切换适用场景每层一个进程是多次高教学、协议调试协议栈进内核是少中有成熟网络栈的 RTOS单进程应用独立是两次中模块化产品应用驻留协议栈否零低高吞吐、无 OS 设备4. 查询 FIFO 与中断 FIFO性能差异、测试方法和优化空间4.1 两种驱动模式的硬件数据路径EMAC Lite 的 IP Core 内部使用 FIFO 缓存收发数据。驱动层可以通过两种方式获取数据到达事件查询和中断。查询模式下主循环不断读取 EMAC 的中断状态寄存器判断接收 FIFO 是否有新帧。虽然代码最简单但每次查询都会占用 OPB 总线和寄存器接口。LWIP 处理一帧需要访问多个内存区域查询动作又在同一总线上插队结果就是总线的有效带宽被切碎吞吐上不去。中断模式下EMAC 收到完整帧后拉高中断信号MicroBlaze 进入 ISR在中断服务程序里调用ip_input把数据送入协议栈。CPU 不再浪费周期去轮询状态吞吐自然更高。但中断模式也带来一个新问题如果每个帧都触发一次中断ISR 的现场保存和恢复开销会累积。论文中的驱动是分两次测试一个查询 FIFO一个中断 FIFO中断模式明显胜出。4.2 BW Meter 实测方法论文的测试平台为 Spartan-3 开发板CPU 是 MicroBlaze主频 100 MHz内存 64 MB SDRAM配置 100 Mbps EMAC IP Core。软件侧无操作系统bootrom 基于 Xilinx standaloneLWIP 使用 RAW API。测试方式RJ45 交叉以太网线直连 PC 和开发板。PC 作为 TCP 客户端开发板作为 TCP 服务端。连接建立后PC 向服务端连续发送 TCP 包服务端只统计收到的数据量不做任何回显。网络测试工具 BW Meter 实时显示下行和上行速率。这里有一个容易忽略的细节测试前要关闭 PC 网卡的协议卸载功能比如 TCP Checksum Offload 和 Large Send Offload。否则 PC 硬件会把部分负载接走测试结果虚高无法反映真实协议栈吞吐。4.3 性能对比结果与参数解读FIFO 模式下载速率 DL上传速率 UL说明查询模式1.0 Mbps50.2 MbpsCPU 忙等接收路径不稳定中断模式1.3 Mbps63.6 Mbps中断驱动吞吐提升约 26.7%这里的 DL 和 UL 是相对于 PC 的描述。FPGA 作为服务端主要压力在接收方向也就是 PC 发送到 FPGA 的 UL 方向。查询模式能稳定跑到 50.2 Mbps中断模式提升到 63.6 Mbps。这个差距的本质在于中断模式让 CPU 从轮询中解放出来把时间真正花在协议栈处理上。4.4 为什么中断模式更快查询模式的问题不只是忙等还破坏了协议栈处理数据的突发性。LWIP 接收一帧时需要把数据从 FIFO 拷贝到 pbuf再交给 IP 层和 TCP 层最后更新接收窗口。在拷贝期间如果有新帧到达FIFO 开始排队当 FIFO 深度不足时MAC 硬件会直接丢弃新帧触发 TCP 重传。重传进一步增加 CPU 负载形成恶性循环。中断模式则按数据到达的节奏驱动处理CPU 处理完当前帧后回到主循环等待下一个中断FIFO 中不会积压过多数据。不过中断也有阈值问题如果每来一个帧就中断一次频繁的现场切换会吃掉吞吐。2007 年那版驱动没有中断合并能力能做到 63.6 Mbps 已经很接近硬件极限。新一些的驱动支持 interrupt coalescing可以设置当 FIFO 内有 N 帧或超过 T 微秒才触发一次中断适合高吞吐场景。4.5 可落地的 LWIP 参数调整无操作系统方案中吞吐瓶颈通常集中在内存池和窗口大小上。下面是一组针对 100 Mbps 接收压力的建议值#define NO_SYS 1 #define MEM_SIZE (64 * 1024) #define PBUF_POOL_SIZE 32 #define PBUF_POOL_BUFSIZE 1518 #define MEMP_NUM_TCP_SEG 64 #define TCP_WND (16 * 1024) #define TCP_SND_BUF (16 * 1024)参数说明PBUF_POOL_SIZE决定接收缓冲区数量32 个可以缓冲大约 32 个帧在突发流量下不容易丢包TCP_WND是 TCP 接收窗口至少要比两个最大段MSS大否则无法维持发送窗口满速TCP_SND_BUF影响发送方向如果只是接收测试不需要调得太大。如果这两个缓冲区被调得过大而 MicroBlaze 的本地 BRAM 又很紧张内存分配失败会以err计数方式反馈。下一章会给一个定位这类问题的具体排查顺序。5. 进阶技巧无操作系统下 TCP 吞吐瓶颈的定位顺序5.1 从 PHY 到协议栈的三步排查遇到吞吐上不去我一般按下面的顺序看能快速缩小范围。第一步确认 PHY 是否协商到 100 Mbps 全双工。很多开发板默认的 PHY 配置是自适应但交叉线直连时如果 PHY 老化或抽头错误房间里可能掉到 10 Mbps。读取 EMAC 的状态寄存器看链路速度和双工位即可。不要只看电脑端协商结果要以 FPGA 侧实际读到的链路状态为准。第二步查 FIFO 溢出。EMAC Lite 的 ISR 里通常有rx_overrun这类状态位。如果溢出计数在每个测试周期都增长说明中断响应太慢或者 pbuf 池耗尽。响应慢则考虑提高 MicroBlaze 中断优先级池耗尽则调大PBUF_POOL_SIZE。第三步打开 LWIP 的统计信息。在lwipopts.h中启用LWIP_STATS和MEMP_STATS然后把分配失败计数输出到串口。5.2 用统计计数器验证丢包extern struct stats_mem memp_stats[]; u16_t last_err 0; void print_net_stats(void) { u16_t err memp_stats[PBUF_POOL].err; if (err ! last_err) { xil_printf(pbuf pool alloc err: %d\r\n, err); last_err err; } }逻辑说明memp_stats数组按池类型记录分配次数和失败次数err字段是累计值。主循环里每 1 秒调用一次print_net_stats如果失败计数出现增长就说明协议栈层在丢包问题出在 LWIP 配置而不是 PHY。这里用xil_printf而不是标准printf是因为它不依赖堆占用的代码空间更小适合 MicroBlaze 的存储环境。配合 PC 上的 Wireshark 抓包可以进一步区分是协议栈丢包还是 MAC 丢包。抓包时如果看到大量 TCP Dup ACK 和重传而 FIFO 溢出计数为零则瓶颈多半在TCP_WND太小如果溢出计数和重传同时飙升则要优先提升中断响应速度或加大PBUF_POOL_SIZE。这套定位方法不依赖操作系统稍微改一下变量名就能沿用到 Zynq 的裸机 LWIP 工程里。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价