【Zynq 进阶一】深度解析 PetaLinux 存储布局:NAND Flash 分区与 DDR 内存分配全攻略

【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"之后,往往会在这两件事上栽跟头:

  1. 系统起不来,U-Boot 找不到内核报错 Bad Magic NumberWrong Image Format
  2. 跑着跑着内核崩溃(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 存在坏块机制,强烈建议 rootfsdata 分区使用 UBI/UBIFS 文件系统,而不是 ext4 或 JFFS2。

2. 如何在 PetaLinux 中配置 NAND 分区?

步骤 A:在 PetaLinux 菜单中配置

运行 petalinux-config

  1. 进入 Subsystem AUTO Hardware Settings -> Flash Settings
  2. 选择你的 Primary Flash (比如 nand_flash)
  3. 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) 等设备节点,你就可以使用 nandwriteubiformat 命令在 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:通常是 0x0
  • Primary 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):

  1. BootROM 阶段:Zynq 上电,片内 ROM 从 NAND Flash 的起始地址(或指定偏移)读取 FSBL(First Stage Boot Loader)并加载到 OCM(片内 256KB 的小内存)运行。
  2. FSBL 阶段 :FSBL 在 OCM 中运行,它做的最重要的事情之一就是初始化 DDR 控制器 。然后,它将 BOOT.BIN 中的 FPGA Bitstream 烧录进 PL 端,最后把 U-Boot 从 NAND Flash 搬运到 DDR 中。
  3. U-Boot 阶段 :U-Boot 在 DDR 中跑起来了。它通过读取 NAND 中的 bootenv 分区获取环境变量(如 bootcmd)。
    • U-Boot 将 NAND 中 kernel 分区的数据(image.ub)读取到 DDR 的特定地址(如 0x10000000,由 netstartloadaddr 环境变量决定)。
  4. 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-memoryCMA,是保证软硬件协同工作稳定性的基石。
相关推荐
我爱学习好爱好爱2 小时前
Ansible 入门:ad-hoc 临时命令与常用模块
linux·服务器·ansible
lwx9148522 小时前
Linux-sftp命令详解
linux·运维·服务器
wang09072 小时前
Linux性能优化之平均负载
linux·数据库·性能优化
Kong_19942 小时前
芯片开发学习笔记·二十——时序报告分析
fpga开发·芯片开发
BieberChen2 小时前
ubuntu定时执行脚本---crontab详细使用指南
linux·运维·ubuntu
小昭在路上……3 小时前
编译与链接的本质:段(Section)的生成与定位
java·linux·开发语言
风曦Kisaki3 小时前
#Linux进阶Day04 用户 sudo 提权、IP 地址配置、SELinux 安全管理
linux·tcp/ip·安全
风酥糖4 小时前
在Termux中部署一个简单的服务导航页
linux·服务器·安卓