1. U-Boot网络协议栈全景概览:从启动到网络就绪的完整链路
做嵌入式开发的朋友,十有八九都跟U-Boot打过交道。但说实话,很多人对U-Boot网络协议栈的理解,停留在「能ping通就行」的阶段。我早年做一款工业网关时,就因为这个认知吃过亏——板子启动后网络时通时断,查了三天才发现是协议栈初始化顺序出了问题。
这一章,咱们就把U-Boot网络协议栈从启动到就绪的完整链路拆开来看。你想想看,一个嵌入式设备从上电到能通过TFTP下载内核,中间到底经历了什么?
1.1 整体架构:三层模型
U-Boot的网络协议栈,说白了就是三层结构。跟Linux内核的网络栈比,它精简得多,但核心逻辑是一样的。
这张图我建议你存下来。每次调网络问题,先定位是哪个层出的问题,能省不少时间。
1.2 启动链路:从复位到网络就绪
U-Boot启动后,网络协议栈不是一下子就准备好的。它有一条清晰的初始化链路。我把它拆成6个关键步骤:
| 步骤 | 阶段 | 关键函数/操作 | 说明 |
|---|---|---|---|
| 1 | 板级初始化 | board_init() |
配置GPIO、时钟、复位MAC控制器 |
| 2 | 驱动初始化 | eth_initialize() |
扫描并注册所有以太网设备 |
| 3 | PHY探测 | phy_init() |
检测PHY芯片,协商链路速率 |
| 4 | MAC上电 | eth_open() |
启动MAC控制器,准备收发 |
| 5 | 协议栈就绪 | net_init() |
初始化ARP缓存、UDP端口等 |
| 6 | 网络可用 | 等待用户命令 | 可执行ping、tftp等操作 |
关键点:步骤2和步骤3之间,经常出问题。我遇到过一块板子,PHY芯片的复位时序不对,导致eth_initialize()执行时PHY还没准备好。后来在board_init()里加了50ms延时才解决。
1.3 核心数据结构:一切从这里开始
U-Boot网络协议栈的核心,是几个全局数据结构。你调优的时候,80%的时间都在跟它们打交道。
1.3.1 全局网络状态:net_state
这个变量控制着协议栈的当前状态。说白了,就是一个状态机:
enum net_loop_state {
NET_LOOP_STOPPED = 0, // 网络停止
NET_LOOP_START, // 开始网络操作
NET_LOOP_RETRY, // 重试中
NET_LOOP_TIMEOUT, // 超时
NET_LOOP_SUCCESS, // 成功
NET_LOOP_FAIL // 失败
};
我个人习惯在调试时,把这个状态打印出来。很多网络问题,一看状态就知道卡在哪一步。
1.3.2 网络缓冲区:net_rx_packets[]
U-Boot使用一个环形缓冲区来接收数据包。默认大小是 PKTBUFSRX,通常是4个包。每个包最大 PKTSIZE(1518字节)。
注意:这个环形缓冲区是U-Boot网络性能的瓶颈之一。我曾经在一个高吞吐场景下,因为缓冲区太小导致丢包。调大 PKTBUFSRX 后,问题立刻解决。
1.3.3 ARP缓存:arp_table[]
U-Boot的ARP缓存很小,默认只有几个条目。每次ARP请求都会阻塞当前网络操作。这也是为什么你ping一个不存在的IP时,U-Boot会卡住好几秒。
1.4 数据流:一个TFTP请求的完整旅程
光讲理论没意思。咱们看一个实际例子——U-Boot执行 tftp 0x20000000 kernel.bin 时,数据是怎么流动的:
- 应用层发起:
do_tftp()构造TFTP读请求包,调用net_send_packet() - 传输层封装:UDP头被填充,源端口随机,目标端口69
- 网络层封装:IP头被填充,源IP=板子IP,目标IP=服务器IP
- 链路层封装:ARP查询目标MAC,填充以太网头
- 驱动发送:MAC控制器将数据包通过PHY发送到网线
- 接收响应:服务器回包,经过同样的路径反向解析
- 数据交付:TFTP客户端将数据写入内存地址0x20000000
小技巧:在 net/net.c 中打开 DEBUG 宏,可以打印每个收发数据包的详细信息。我调试网络问题时,第一步就是开这个宏。
1.5 常见启动网络失败场景
做项目这么多年,我总结了几种最常见的U-Boot网络启动失败场景:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| eth_initialize() 找不到设备 | 设备树配置错误,或驱动未编译 | 检查 CONFIG_DM_ETH 和驱动宏 |
| PHY链路协商失败 | PHY地址不对,或复位时序问题 | 用示波器看PHY中断引脚,或强制固定速率 |
| 能ping通但tftp超时 | MTU不匹配,或防火墙拦截 | 抓包分析,检查UDP端口是否被阻 |
| 网络时通时断 | 电源噪声,或网线接触不良 | 检查PHY供电,换网线测试 |
嗯,这里要特别提一下PHY地址的问题。我记得有一次,硬件工程师把PHY地址改成了0x1e,但驱动里写死的是0x00。结果板子死活检测不到PHY。后来我在 phy_find_by_mask() 里加了扫描逻辑才找到。
1.6 本章小结
U-Boot网络协议栈,说白了就是一套精简但完整的TCP/IP实现。从板级初始化到网络就绪,中间有6个关键步骤。核心数据结构就那么几个——net_state、环形缓冲区、ARP缓存。理解这些,你就掌握了调优的基础。