sf 是 Serial Flash 的缩写,它用于与 SPI(Serial Peripheral Interface)接口的闪存芯片进行交互,这种闪存芯片在嵌入式系统中非常常见,例如用于存储引导程序(如 U-Boot 本身)、设备树、内核镜像等。

sf 命令是 U-Boot 提供的一个强大工具集,主要用于:
- 读取 (
read) 闪存内容。 - 写入 (
write) 数据到闪存。 - 擦除 (
erase) 闪存的某个区域。 - 更新 (
update) 闪存(一个结合了擦除和写入的原子操作)。 - 保护 (
protect) 设置闪存的写保护区域。
前提条件:环境配置
在使用 sf 命令之前,必须确保 U-Boot 已经正确配置并加载了 SPI Flash 驱动。
-
内核配置: 在 U-Boot 的配置文件(如
configs/<board_name>_defconfig)中,必须启用相关的 SPI 选项。CONFIG_CMD_SF=y // 启用 sf 命令 CONFIG_SPI_FLASH=y // 启用 SPI Flash 支持 CONFIG_SPI_FLASH_WINBOND=y // 根据你板子上实际的 Flash 型号选择对应的驱动 // ... 其他 SPI 相关配置,如 CONFIG_DM_SPI, CONFIG_DM_SPI_FLASH 等 -
设备树配置: 在板子的设备树文件中,必须正确描述 SPI 控制器和 Flash 芯片,这通常包括:
(图片来源网络,侵删)- SPI 控制器的节点(如
spi0,spi1)。 - Flash 芯片作为 SPI 控制器的一个子节点,并指定其兼容型号(compatible),
winbond,w25q32。 - Flash 芯片的寄存器大小(
reg)。
- SPI 控制器的节点(如
-
运行时环境: 在 U-Boot 命令行中,可能需要先初始化 SPI 总线,这通常通过
sf probe命令完成。
sf 命令详解
sf 命令的基本格式是 sf <operation> [offset] [size] [addr]。
sf probe [bus:num]
-
作用:探测并初始化 SPI Flash 设备。
-
参数:
(图片来源网络,侵删)bus:num(可选): 指定要探测的 SPI 总线号和从设备号。sf probe 0:0表示探测第 0 个 SPI 总线上的第 0 个从设备,如果不指定,U-Boot 会使用默认配置。
-
示例:
# 探测默认的 SPI Flash sf probe # 探测 SPI 总线 0 上的从设备 0 sf probe 0:0
sf read <addr> <offset> <size>
- 作用:从 SPI Flash 的指定偏移量处读取数据到内存的指定地址。
- 参数:
addr: 目标内存地址(0x81000000)。offset: 在 SPI Flash 中的起始偏移量(0x0)。size: 要读取的字节数(0x100000,表示 1MB)。
- 示例:
# 从 Flash 的 0x0 地址读取 1MB 数据到内存 0x81000000 sf read 0x81000000 0x0 0x100000
sf write <addr> <offset> <size>
- 作用:将内存中的数据写入到 SPI Flash 的指定偏移量处。
- 参数:
addr: 源数据在内存中的地址。offset: 在 SPI Flash 中的目标起始偏移量。size: 要写入的字节数。
- 示例:
# 将内存中 0x81000000 处的 1MB 数据写入到 Flash 的 0x0 地址 sf write 0x81000000 0x0 0x100000
- 注意:写入操作前,目标区域必须已经被擦除,Flash 芯片只能将
1写成0,不能将0写成1。
sf erase <offset> <size>
-
作用:擦除 SPI Flash 的指定区域,擦除操作会将指定区域的所有位都设置为
1。 -
参数:
offset: 要擦除的起始偏移量。size: 要擦除的字节数。
-
重要:擦除操作必须以 Flash 芯片支持的扇区或块为单位进行,你不能只擦除一个字节,如果 Flash 的扇区大小是 4KB (
0x1000),那么擦除大小必须是0x1000的整数倍。 -
示例:
# 擦除从 Flash 的 0x0 地址开始的 1MB 区域 # 假设 1MB 是 Flash 扇区大小的整数倍 sf erase 0x0 0x100000 # 擦除一个扇区 (假设扇区大小为 4KB) sf erase 0x10000 0x1000
sf update <addr> <offset> <size>
- 作用:这是一个非常有用的复合命令,它会先擦除目标区域,然后再写入数据,它比手动执行
erase+write更安全,因为它是一个原子操作(在 U-Boot 的执行层面),可以避免在擦除和写入之间发生意外断电导致数据损坏。 - 参数:与
write命令相同。 - 示例:
# 将 u-boot.bin 更新到 Flash 的 0x0 位置 # 假设 u-boot.bin 已经加载到内存 0x82000000 sf update 0x82000000 0x0 ${filesize}注意:这里使用了 U-Boot 的环境变量
${filesize},该变量在通过load或fatload等命令加载文件时自动设置。
sf protect lock/unlock <offset> <size>
-
作用:锁定或解锁 Flash 的特定区域,防止意外写入或擦除,这通常对应 Flash 芯片的 BP (Block Protection) 寄存器。
-
参数:
lock/unlock: 操作类型。offset: 要保护/取消保护的起始偏移量。size: 要保护/取消保护的区域大小。
-
示例:
# 锁定从 0x0 开始的 512KB 区域,防止被修改 sf protect lock 0x0 0x80000 # 解锁上述区域 sf protect unlock 0x0 0x80000
实用场景和完整示例
假设你的开发板上有一片 Winbond W25Q32 SPI Flash (4MB),U-Boot 已经加载到内存地址 0x81000000,你想将它烧录到 Flash 的 0x0 位置。
步骤 1:探测 Flash
U-Boot> sf probe SF: Detected W25Q32 with page size 256 Bytes, erase size 4 KiB, total 4 MiB
看到这个输出说明 Flash 探测成功,U-Boot 识别出了它的型号(W25Q32)、页大小、擦除块大小和总容量。
步骤 2:擦除目标区域
W25Q32 的擦除块大小通常是 4KB (0x1000),我们要烧录整个 U-Boot,假设它的大小是 512KB,那么我们需要擦除一个至少 512KB 的区域,为了对齐,我们擦除 1MB (0x100000)。
U-Boot> sf erase 0x0 0x100000 SF: 1048576 bytes @ 0xx00000000 erased: OK
步骤 3:写入数据
将内存中的 U-Boot 写入到刚刚擦除的区域。
U-Boot> sf write 0x81000000 0x0 ${filesize}
SF: 524288 bytes @ 0x00000000 written: OK
注意:这里 ${filesize} 变量需要事先被设置,如果你是手动加载的二进制文件,需要先计算好大小并设置该变量。
步骤 4:验证写入(可选但推荐)
为了确保写入成功,我们可以将刚刚写入的数据读回内存的另一个地址,然后与原始数据进行比较。
# 1. 从 Flash 读回数据到新的内存地址 U-Boot> sf read 0x82000000 0x0 0x100000 # 2. 比较原始数据和读回的数据 # 假设原始数据在 0x81000000,读回的在 0x82000000,比较大小为 512KB U-Boot> cmp.b 0x81000000 0x82000000 0x80000 # 如果输出为 "Total of 0x80000 bytes were the same",则说明写入成功。
使用 sf update 命令(更简单的方法)
上面的步骤 2 和 3 可以合并为一步,使用 sf update 命令,它会自动处理擦除和写入。
U-Boot> sf update 0x81000000 0x0 ${filesize}
SF: 524288 bytes @ 0x00000000 erased: OK
SF: 524288 bytes @ 0x00000000 written: OK
这个命令更简洁,是推荐的更新方式。
常见问题与排错
-
sf probe失败或未定义命令:- 原因:U-Boot 编译时没有开启
CONFIG_CMD_SF。 - 解决:重新编译 U-Boot,确保配置文件中启用了该选项。
- 原因:U-Boot 编译时没有开启
-
sf erase或sf write报错 "erase/write failed":- 原因:
- 写入前没有擦除区域。
- 擦除/写入的大小不是 Flash 扇区大小的整数倍。
- Flash 芯片被锁定 (
sf protect lock)。 - SPI 时钟频率过高,导致不稳定。
- Flash 型号在设备树中配置错误。
- 解决:
- 检查是否先执行了
erase。 - 确认你的 Flash 芯片扇区大小,并对齐擦除/写入大小。
- 尝试解锁 Flash (
sf protect unlock)。 - 在设备树中降低 SPI 时钟频率。
- 检查是否先执行了
- 原因:
-
sf read读出的数据不正确:- 原因:
- 内存地址
addr与 U-Boot 的memmap冲突,导致数据被覆盖。 - Flash 的偏移量
offset计算错误。
- 内存地址
- 解决:
- 使用一个安全的内存地址,通常是 U-Boot 之后或 DRAM 的保留区域。
- 仔细核对 Flash 的分区布局,确保偏移量正确。
- 原因:
希望这份详细的指南能帮助你更好地理解和使用 U-Boot 的 sf 命令!
