4. 内存操作命令详解:md、mm、mw、cp、cmp 的用法与实战

内存操作命令,是 U-Boot 调试中最常用的工具。说白了,就是让你直接读写板子上的物理内存。我刚开始做移植时,经常靠这几条命令来验证 DDR 初始化是否成功、检查外设寄存器配置是否正确。

嗯,咱们一个一个来看。

4.1 md — 内存显示

md 命令用来查看指定地址的内存内容。它的基本格式是:

md [.b, .w, .l] 地址 [长度]

其中 .b 表示字节(8 位),.w 表示半字(16 位),.l 表示字(32 位)。默认是 .l。

实战示例:

# 查看 0x80000000 开始的 16 个字节
U-Boot> md.b 80000000 10

# 查看 0x80000000 开始的 8 个半字
U-Boot> md.w 80000000 8

# 查看 0x80000000 开始的 4 个字(默认)
U-Boot> md.l 80000000 4

我个人习惯先用 md.b 看原始字节流,再用 md.l 看 32 位对齐的数据。为什么?因为很多外设寄存器是 32 位对齐的,直接看字更方便。

小技巧:如果不指定长度,U-Boot 默认显示 64 个字节(16 个字)。你可以用 md 80000000 快速扫一眼。

4.2 mm — 内存修改

mm 命令用于交互式修改内存。它每次修改一个单元后,自动递增地址,让你连续修改。

mm [.b, .w, .l] 地址

执行后,U-Boot 会显示当前地址的值,然后等待你输入新值。直接回车表示跳过,输入 q 退出。

实战示例:

U-Boot> mm.l 80000000
80000000: 00000000 ? 12345678
80000004: 00000000 ? aabbccdd
80000008: 00000000 ? q

我曾经在调试 FPGA 映射的内存时,用 mm 命令逐寄存器配置,验证每个控制位的效果。嗯,这比写驱动快多了。

注意:mm 修改的是物理内存。如果你改的是代码段或关键数据结构,系统可能立刻崩溃。我曾经手滑改过中断向量表… 嗯,板子直接死机,只能重启。

4.3 mw — 内存填充

mw 命令用指定的值填充一段内存区域。格式如下:

mw [.b, .w, .l] 地址 值 [长度]

实战示例:

# 将 0x80000000 开始的 256 字节全部填 0
U-Boot> mw.b 80000000 00 100

# 将 0x80000000 开始的 64 个字全部填 0xdeadbeef
U-Boot> mw.l 80000000 deadbeef 40

这个命令在测试 DDR 稳定性时特别有用。我会先填满 0x55 和 0xAA 的交替模式,再用 md 检查是否有位翻转。

避坑指南:我曾经用 mw.l 填一个很大的区域(比如 64MB),结果等了很久。后来发现,U-Boot 的 mw 是逐字写入的,大块填充建议用 cp 配合预置的 pattern buffer。

4.4 cp — 内存拷贝

cp 命令用于将一段内存内容拷贝到另一段地址。格式:

cp [.b, .w, .l] 源地址 目标地址 长度

实战示例:

# 将 0x80000000 开始的 256 字节拷贝到 0x90000000
U-Boot> cp.b 80000000 90000000 100

# 将 0x80000000 开始的 64 个字拷贝到 0x90000000
U-Boot> cp.l 80000000 90000000 40

你想想看,这个命令在什么场景下有用?比如从 NAND Flash 加载数据到 DDR,或者把 kernel 镜像从临时缓冲区搬到最终运行地址。

重要:源地址和目标地址不能重叠!U-Boot 的 cp 是简单逐单元拷贝,没有处理重叠区域的逻辑。如果重叠,结果不可预期。我见过有人把 kernel 拷贝到自身地址范围内,结果数据全乱了。

4.5 cmp — 内存比较

cmp 命令比较两段内存是否一致。格式:

cmp [.b, .w, .l] 地址1 地址2 长度

实战示例:

# 比较 0x80000000 和 0x90000000 的前 256 字节
U-Boot> cmp.b 80000000 90000000 100

# 如果一致,输出:
# Total of 256 byte(s) were the same

# 如果不一致,输出第一个差异位置:
# byte at 0x80000010 (0x12) != byte at 0x90000010 (0x34)
# Total of 16 byte(s) were the same

这个命令是我做 DDR 压力测试时的好帮手。我会先 mw 填数据,cp 拷贝一份,再用 cmp 验证。如果发现不一致,基本可以断定 DDR 硬件有问题。

实战经验:我曾经用 cmp 发现一个隐蔽的 DDR 地址线焊接问题。当时填了 0x00000001 的 pattern,拷贝后比较,发现每隔 4 个字就有一位错误。后来查 PCB,发现地址线 A2 虚焊了。

4.6 命令对比与选择

命令 功能 典型场景 注意事项
md 显示内存内容 查看寄存器、检查数据 注意对齐方式
mm 交互式修改 逐寄存器配置 小心改到关键区域
mw 批量填充 清空内存、写 pattern 大块填充较慢
cp 内存拷贝 数据搬运、镜像加载 源和目标不能重叠
cmp 内存比较 数据验证、DDR 测试 只报告第一个差异

4.7 综合实战:DDR 快速自检流程

我每次拿到新板子,都会用下面这套流程快速验证 DDR 是否正常工作:

  1. 写 pattern:mw.l 80000000 aabbccdd 1000 — 填 16KB 的已知数据
  2. 读回验证:md.l 80000000 10 — 检查前 64 字节是否正确
  3. 拷贝:cp.l 80000000 90000000 1000 — 拷贝到另一块区域
  4. 比较:cmp.l 80000000 90000000 1000 — 验证拷贝是否完整
  5. 反写 pattern:mw.l 80000000 55aa55aa 1000 — 用交替位模式再测一次
  6. 重复比较:cmp.l 80000000 90000000 1000 — 确保没有位粘连

为什么用两个不同的 pattern?因为有些 DDR 问题只在特定数据模式下才暴露。比如地址线短路,用 0x00000001 和 0x00000002 就能测出来。

下面这张图总结了这五个命令的关系和典型用法:

内存操作命令关系图 内存操作命令关系与典型流程 DDR 内存 md 内存显示 cmp 内存比较 mw 内存填充 mm 交互修改 cp 内存拷贝 典型调试流程 mw 填 pattern → md 验证 → cp 拷贝 → cmp 比较 换 pattern 再测一次,确保 DDR 无位错误

最后提醒:这些命令操作的是物理地址,不是虚拟地址。在启用 MMU 的系统中,你看到的是物理内存的直接映射。如果你不确定当前地址是否有效,先用 bdinfo 查看内存布局。

嗯,这五个命令虽然基础,但用好了能解决很多调试问题。我建议你在自己的板子上多练练,特别是 cmp 和 mw 的组合,能帮你快速定位硬件问题。