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 是否正常工作:
- 写 pattern:
mw.l 80000000 aabbccdd 1000— 填 16KB 的已知数据 - 读回验证:
md.l 80000000 10— 检查前 64 字节是否正确 - 拷贝:
cp.l 80000000 90000000 1000— 拷贝到另一块区域 - 比较:
cmp.l 80000000 90000000 1000— 验证拷贝是否完整 - 反写 pattern:
mw.l 80000000 55aa55aa 1000— 用交替位模式再测一次 - 重复比较:
cmp.l 80000000 90000000 1000— 确保没有位粘连
为什么用两个不同的 pattern?因为有些 DDR 问题只在特定数据模式下才暴露。比如地址线短路,用 0x00000001 和 0x00000002 就能测出来。
下面这张图总结了这五个命令的关系和典型用法:
最后提醒:这些命令操作的是物理地址,不是虚拟地址。在启用 MMU 的系统中,你看到的是物理内存的直接映射。如果你不确定当前地址是否有效,先用 bdinfo 查看内存布局。
嗯,这五个命令虽然基础,但用好了能解决很多调试问题。我建议你在自己的板子上多练练,特别是 cmp 和 mw 的组合,能帮你快速定位硬件问题。