【Zynq 进阶】深度解析 PetaLinux 存储布局:NAND Flash 分区与 DDR 内存分配全攻略
文章目录
- [【Zynq 进阶】深度解析 PetaLinux 存储布局:NAND Flash 分区与 DDR 内存分配全攻略](#【Zynq 进阶】深度解析 PetaLinux 存储布局:NAND Flash 分区与 DDR 内存分配全攻略)
-
-
- [📝 前言](#📝 前言)
- [📦 第一部分:大局观------NAND 与 DDR 在 Zynq 中的角色](#📦 第一部分:大局观——NAND 与 DDR 在 Zynq 中的角色)
- [🗄️ 第二部分:NAND Flash 分区全攻略](#🗄️ 第二部分:NAND Flash 分区全攻略)
-
- [1. 经典的 PetaLinux NAND 分区方案](#1. 经典的 PetaLinux NAND 分区方案)
- [2. 如何在 PetaLinux 中配置 NAND 分区?](#2. 如何在 PetaLinux 中配置 NAND 分区?)
- [🧠 第三部分:DDR 内存分配全攻略](#🧠 第三部分:DDR 内存分配全攻略)
-
- [1. DDR 内存映射宏观图](#1. DDR 内存映射宏观图)
- [2. PetaLinux 中的 DDR 基础配置](#2. PetaLinux 中的 DDR 基础配置)
- [3. 核心进阶:如何为 PL 端保留内存?(防止互踩)](#3. 核心进阶:如何为 PL 端保留内存?(防止互踩))
- [🚀 第四部分:串联起来------从 NAND 到 DDR 的数据搬运之旅](#🚀 第四部分:串联起来——从 NAND 到 DDR 的数据搬运之旅)
- [💣 第五部分:避坑指南 (Troubleshooting)](#💣 第五部分:避坑指南 (Troubleshooting))
- 总结
-
📝 前言
在 Zynq 嵌入式 Linux 开发中,很多开发者(尤其是从纯 FPGA 或纯单片机转过来的工程师)在熟悉了"点亮 LED"和"Hello World"之后,往往会在这两件事上栽跟头:
- 系统起不来,U-Boot 找不到内核报错
Bad Magic Number或Wrong Image Format。 - 跑着跑着内核崩溃(Kernel Panic),或者 PL 端(FPGA)的 DMA 搬运数据时把 Linux 系统给搞死了。
这背后的罪魁祸首,往往是存储布局(Storage Layout)没有规划好 !
简单来说:Flash 分区不合理导致镜像重叠或找不到,DDR 内存分配冲突导致系统与 PL 端"互相踩踏"。
今天,我们就来深度扒一扒 PetaLinux 下的存储布局,彻底搞懂 NAND Flash 的分区哲学和 DDR 内存的分配艺术。
📦 第一部分:大局观------NAND 与 DDR 在 Zynq 中的角色
为了让大家通俗易懂地理解,我们可以打一个比方:
- NAND Flash(非易失性存储) = 书架 。系统断电后,东西都存在这里。Bootloader、内核、文件系统就像一本本书,必须分门别类地放在书架的不同格子里(这就是 Flash 分区)。
- DDR(易失性内存) = 书桌 。系统上电运行时,要把书架上的书拿下来摊在桌子上读(这就是 Boot 流程 )。桌子很大,但你要规定好哪块区域放系统运行数据,哪块区域留给 PL 端(FPGA)搞硬件加速用(这就是 DDR 分配 )。

🗄️ 第二部分:NAND Flash 分区全攻略
对于 Zynq 来说,NAND Flash 容量通常比较大(256MB~1GB甚至更大),非常适合存放完整的 Linux 系统。但在存放前,我们必须通过 MTD (Memory Technology Device) 子系统对其进行合理分区。
1. 经典的 PetaLinux NAND 分区方案
一个标准的 PetaLinux 生产环境,NAND Flash 建议按如下结构划分:
| 分区名 (MTD Partition) | 建议大小 | 存放内容说明 |
|---|---|---|
boot |
10MB ~ 16MB | BOOT.BIN (包含 FSBL, FPGA Bitstream, U-Boot) |
bootenv |
1MB | U-Boot 环境变量 (存放启动参数) |
kernel |
30MB ~ 40MB | image.ub (打包好的 Kernel, 设备树 dtb) |
rootfs |
100MB+ | 根文件系统 (建议使用 UBIFS 格式应对坏块) |
data |
剩余空间 | 用户数据区 (存放业务程序、日志、配置文件) |
⚠️ 注意:不同于 NOR Flash 可以随意读写,NAND Flash 存在坏块机制,强烈建议 rootfs 和 data 分区使用 UBI/UBIFS 文件系统,而不是 ext4 或 JFFS2。
2. 如何在 PetaLinux 中配置 NAND 分区?
步骤 A:在 PetaLinux 菜单中配置
运行 petalinux-config:
- 进入
Subsystem AUTO Hardware Settings->Flash Settings - 选择你的 Primary Flash (比如
nand_flash) - 在
Partition Table中,严格按照上面的表格填入各个分区的名字和大小(Size)。
步骤 B:通过设备树(Device Tree)硬性绑定(推荐做法)
有时候图形界面的配置不够直观,高级开发者喜欢在 project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi 中直接定义 MTD 分区。代码示例如下:
dts
&smcc { // 根据具体的 NAND 控制器节点修改
nand@0,0 {
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "boot";
reg = <0x0 0x1000000>; // 起始地址 0x0,大小 16MB
};
partition@1000000 {
label = "bootenv";
reg = <0x1000000 0x100000>; // 起始地址 16M,大小 1MB
};
partition@1100000 {
label = "kernel";
reg = <0x1100000 0x2800000>; // 大小 40MB
};
partition@3900000 {
label = "rootfs";
reg = <0x3900000 0x10000000>; // 大小 256MB
};
};
};
原理揭秘: Linux 内核启动时,会解析这个设备树文件,在 /dev/ 目录下生成 mtd0 (boot), mtd1 (bootenv) 等设备节点,你就可以使用 nandwrite 或 ubiformat 命令在 Linux 下更新固件了。

🧠 第三部分:DDR 内存分配全攻略
系统启动后,舞台就交给了 DDR。Zynq-7000 典型挂载的 DDR 大小为 512MB 或 1GB。内存分配最核心的问题是:如何防止 PS(ARM)和 PL(FPGA)抢同一块内存?
1. DDR 内存映射宏观图
假设我们有 1GB DDR (地址范围:0x00000000 - 0x3FFFFFFF):
- 底部区域 (
0x00000000起):存放 Linux 内核镜像、设备树,以及中断向量表。 - 中间区域:Linux 系统的动态内存分配区(供应用程序运行、kmalloc 等使用)。
- 顶部区域:U-Boot 在引导内核前,会将自己重定位到 DDR 的最高地址端运行。
- 保留区域 (Reserved Memory) :这是 Zynq 进阶的核心所在! 为 PL 端的 DMA(如 AXI VDMA、AXI DMA)专门预留的物理内存。
2. PetaLinux 中的 DDR 基础配置
运行 petalinux-config,进入 Memory Settings:
Primary Memory System base address:通常是0x0Primary Memory System size:你的 DDR 总大小(如0x40000000表示 1GB)
3. 核心进阶:如何为 PL 端保留内存?(防止互踩)
如果你的 FPGA 逻辑里有一个 AXI DMA,它要把摄像头采集的视频流直接写进 DDR。如果不告诉 Linux,Linux 可能会把这块 DDR 分配给某个 App 使用。结果就是:视频画面花屏,或者 Linux 突然死机重启。
绝招:使用设备树的 reserved-memory 节点
打开 system-user.dtsi,加入如下配置:
dts
/ {
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
/* 方法一:静态保留固定物理地址 (推荐用于裸机DMA或特定IP) */
pl_dma_reserved: buffer@30000000 {
no-map; /* 关键:告诉 Linux 内核不要映射这块内存 */
reg = <0x30000000 0x08000000>; /* 从 0x30000000 开始保留 128MB */
};
/* 方法二:CMA (Contiguous Memory Allocator) 连续内存分配 */
linux,cma {
compatible = "shared-dma-pool";
reusable; /* Linux 可以借用,但 DMA 需要时必须归还 */
size = <0x10000000>; /* 分配 256MB 作为 CMA 内存池 */
linux,cma-default;
};
};
};
深入浅出解释:
no-map属性极其霸道!它直接告诉 Linux 内存管理单元 (MMU):"这 128MB 物理内存不存在,你别碰!" 此时,你可以放心地在 PL 端的 Vivado Block Design 中,将 DMA 的目标地址硬编码为0x30000000。CMA则稍微温和一些。它允许 Linux 在内存宽裕时使用这块空间,当 PL 端的 V4L2 或 DRM 驱动申请大块连续物理内存时,系统会自动分配这里的内存。
🚀 第四部分:串联起来------从 NAND 到 DDR 的数据搬运之旅
理解了上面两部分,我们来看看系统启动时,数据是如何流动的(The Boot Flow):
- BootROM 阶段:Zynq 上电,片内 ROM 从 NAND Flash 的起始地址(或指定偏移)读取 FSBL(First Stage Boot Loader)并加载到 OCM(片内 256KB 的小内存)运行。
- FSBL 阶段 :FSBL 在 OCM 中运行,它做的最重要的事情之一就是初始化 DDR 控制器 。然后,它将
BOOT.BIN中的 FPGA Bitstream 烧录进 PL 端,最后把 U-Boot 从 NAND Flash 搬运到 DDR 中。 - U-Boot 阶段 :U-Boot 在 DDR 中跑起来了。它通过读取 NAND 中的
bootenv分区获取环境变量(如bootcmd)。- U-Boot 将 NAND 中
kernel分区的数据(image.ub)读取到 DDR 的特定地址(如0x10000000,由netstart或loadaddr环境变量决定)。
- U-Boot 将 NAND 中
- Linux 内核阶段 :U-Boot 把控制权交给 DDR 中的内核。内核解压自身,解析设备树(避开
reserved-memory),最后挂载位于 NAND Flash 的rootfs分区,完成启动。
💣 第五部分:避坑指南 (Troubleshooting)
在实际开发中,你可能会遇到以下几个天坑:
- 坑1:BOOT.BIN 越来越大,超过了 boot 分区大小
- 现象:启动卡死,或者烧录失败。
- 解法 :随着 PL 端逻辑变复杂,Bitstream 会变大。务必在设备树和 PetaLinux 配置中同步扩大
boot分区(例如从 10MB 扩大到 16MB)。
- 坑2:内核无法挂载根文件系统 (VFS: Unable to mount root fs)
- 现象:Kernel Panic。
- 解法 :检查 U-Boot 传给内核的
bootargs参数。如果 NAND 分区改了,root=/dev/mtdblockX的序号必须对应上rootfs所在的分区序号!
- 坑3:DMA 启动后,系统随机死机
- 解法 :百分之百是内存互踩。回去检查第三部分的
no-map配置,确保 Vivado 中 AXI 总线的地址空间和 Device Tree 中保留的物理地址完全一致。
- 解法 :百分之百是内存互踩。回去检查第三部分的
总结
掌握 PetaLinux 的存储布局,是 Zynq 开发者从"新手"走向"老鸟"的必经之路。
- NAND Flash 的分区 ,关键在于统一。U-Boot 知道的 MTD 分区表和 Linux 内核知道的必须是同一个版本,设备树(Device Tree)是连接它们的桥梁。
- DDR 内存的分配 ,核心在于隔离 。用好
reserved-memory和CMA,是保证软硬件协同工作稳定性的基石。