**问题:**启动启动后从 64M nand flash 读取数据,flash 的分区地址, uboot , 内核, 文件系统,搬运到 ddr ,ddr 的分区地址, 内核 A, 内核 B, 文件系统 A/B, 堆栈空间,影虎空间,这些地址怎么分布的,给出详细的地址分布情况,以上面生成的例子,再给出 uboot 从 flash 拷贝出来的数据分别在 flash 里面每个分区的首位打印 uboot/kernela/kernelb/rootfsa/rootfsb/data 以及配置分区里面的数据,并且在他们移植到 ddr 后也分别打印出 uboot/kernela/kernelb/rootfsa/rootfsb/data 的头信息,给出上面读命令的解释,将上次生成的文档中的影虎空间全部替换为 DDR 缓存 ,上面的 flash 地址范围和 ddr 地址范围哪里给的,代码中哪里可以看到,给出一个 ddr 堆栈地址和 rootfs 地址重叠导致系统启动挂死问题的完整代码分析过程,使用代码分别打印出系统启动每个阶段的镜像头进行对比分析。
一、基础硬件规格定义
1. 存储:64MB NAND Flash
总容量:64MB = 0x00000000 ~ 0x04000000 NAND 特性:按 Block 擦除(标准 128KB/Block = 0x20000),所有分区起始、大小必须块对齐。
2. 内存:DDR 256MB(ARM32 通用基址 0x40000000)
DDR 完整范围:0x40000000 ~ 0x50000000 适用:WiFi 网关 / 华为灵犀 WiFi 类嵌入式方案、A/B 双系统 OTA。
- 堆栈:Uboot 运行栈 + 预留内核初始栈;
- A/B 双分区:kernelA/rootfsA 运行分区,kernelB/rootfsB OTA 备份分区;
- 搬运逻辑:NAND 读出镜像→先放镜像临时缓存区缓存→再拷贝到内核运行 DDR 地址。
二、64MB NAND Flash 完整分区表(偏移、大小、用途)
总空间:0x00000000 ~ 0x04000000 对齐单位:128KB (0x20000) NAND Block
| 分区名称 | 起始偏移 | 分区大小 | 结束地址 | 功能说明 |
|---|---|---|---|---|
| spl | 0x00000000 | 0x00040000 | 0x00040000 | 一级引导,初始化 NAND、DDR,加载 uboot |
| uboot | 0x00040000 | 0x000C0000 | 0x00100000 | Uboot 主程序,A/B 槽切换、nand 读写、解压、boot 启动 |
| uboot_env | 0x00100000 | 0x00040000 | 0x00140000 | 环境变量:boot_slot、bootcount、OTA 标记 |
| misc | 0x00140000 | 0x00080000 | 0x001C0000 | 校准参数、WiFi 射频参数、启动失败计数 |
| kernel_a | 0x001C0000 | 0x00400000 | 0x005C0000 | A 分区:内核镜像 + dtb |
| rootfs_a | 0x005C0000 | 0x00800000 | 0x00DC0000 | A 分区:只读文件系统 squashfs |
| kernel_b | 0x00DC0000 | 0x00400000 | 0x011C0000 | B 分区:备份内核(OTA 升级写入) |
| rootfs_b | 0x011C0000 | 0x00800000 | 0x019C0000 | B 分区:备份文件系统 |
| userdata | 0x019C0000 | 0x02640000 | 0x04000000 | 用户数据、WiFi 配置、升级缓存、永久配置 |
flash地址定义分析:
分区校验: spl (256K)+uboot (768K)+env (256K)+misc (512K)+kernelA (4M)+rootfsA (8M)+kernelB (4M)+rootfsB (8M) = 25.25MB,剩余全部给 userdata,满足配置存储需求。
Flash 分区关键规则
- kernelA /kernelB 大小完全一致,rootfsA /rootfsB 大小完全一致,OTA 只写备用槽,不破坏当前运行系统;
- spl 放在最前端,SOC 硬编码从 NAND 起始地址加载;
- env/misc 全局共享,记录当前启动 A 还是 B;
- NAND 必须块对齐,所有分区起止都是 0x20000 倍数。
三、DDR 256MB 完整地址分区分布(0x40000000 ~ 0x50000000)
地址从低到高依次:镜像临时缓存区 → 内核运行区 → rootfs 解压区 → 系统堆 Heap → Uboot 重定位区 → 堆栈 + DMA 预留区
1. 镜像临时缓存区
起始:0x40000000
结束:0x40600000
大小:6MB
用途:
- nand read 将 kernel/rootfs 镜像从 NAND 读出暂存此处;
- gzip/squashfs 解压临时缓冲区;
- OTA 升级包临时缓存; 内核启动后该区域回收,不再占用。
2. 内核固定运行地址(kernelA /kernelB 共用同一 DDR 地址)
内核加载基址:0x40600000
区间:0x40600000 ~ 0x41600000 16MB
逻辑: 无论启动 A 还是 B 槽内核,都是从 NAND 对应分区读到镜像临时缓存区缓存,再拷贝到 0x40600000 运行;DTB 设备树紧跟内核末尾。 Uboot 搬运示例:
nand read 0x40000000 0x1C0000 0x400000 // 读取kernel_a到镜像临时缓存区
cp.b 0x40000000 0x40600000 0x400000 // 搬运到内核运行地址
bootm 0x40600000
3. 文件系统解压临时区(rootfs A/B 复用)
0x41600000 ~ 0x42600000 16MB
squashfs 解压缓冲,A、B 文件系统不会同时加载,分时复用;内核挂载完文件系统后释放部分内存。
4. 系统堆 Heap(业务主内存)
0x42600000 ~ 0x4F800000 210MB
全部业务内存:Linux 内核 slab、应用进程、灵犀 WiFi 驱动、网络协议栈、数据包缓存、射频缓存均在此分配。
5. Uboot 重定位运行区(Uboot 自身代码运行空间)
0x4F800000 ~ 0x4FF00000 7MB
SPL 初始化 DDR 后,将 uboot 从 NAND 搬运到高地址运行;放高位避免覆盖低地址镜像缓存、内核加载区。
6. 堆栈空间 + WiFi DMA 硬件预留
0x4FF00000 ~ 0x50000000 1MB
- Uboot 运行栈:栈向下生长,放在 DDR 最顶端;
- 预留连续物理 DMA 内存:WiFi 射频、以太网硬件收发专用,内核不可随意分配。
DDR 总地址总图(由低到高)
0x40000000 ~ 0x40600000 6MB 【镜像临时缓存区】镜像读取、解压临时缓存
0x40600000 ~ 0x41600000 16MB 【内核运行区】kernelA/kernelB统一运行地址+DTB
0x41600000 ~ 0x42600000 16MB 【rootfs解压区】A/B文件系统解压缓冲
0x42600000 ~ 0x4F800000 210MB 【堆Heap】系统、WiFi驱动、应用动态内存
0x4F800000 ~ 0x4FF00000 7MB 【Uboot重定位代码/数据段】
0x4FF00000 ~ 0x50000000 1MB 【堆栈Stack + WiFi DMA硬件预留】
ddr地址定义函数分析
四、完整上电启动搬运流程(以启动 A 分区为例)
- 芯片内置 ROM 加载 NAND 最前端 spl 分区到片内 SRAM;
- SPL 初始化 DDR、NAND 控制器,将 uboot 从 NAND
0x040000搬运到 DDR 高位0x4F800000,跳转运行 Uboot; - Uboot 读取 env/misc 分区,判断当前启动槽为 A;
- 执行 nand read,把 NAND kernel_a (0x1C0000) 镜像读取到 DDR【镜像临时缓存区 0x40000000】;
- 将镜像临时缓存区缓存内的内核拷贝到标准运行地址
0x40600000; - 再读取 rootfs_a 到镜像临时缓存区,解压到 0x41600000;
- bootm 0x40600000 启动 Linux 内核;
- 内核接管全部 DDR,释放镜像临时缓存区、Uboot 重定位区域,仅保留 Heap 和顶部 DMA 预留;
- 若 A 分区启动失败,bootcount 计数 + 1,重启后自动切换读取 kernel_b、rootfs_b 重复加载流程。