1. 项目概述:从硬件视角理解嵌入式系统的存储基石

在嵌入式系统开发,尤其是基于ARM架构的Linux系统开发中,存储器的选型与配置是决定系统性能、成本和开发难度的关键一步。很多刚入行的朋友,拿到像友善之臂Mini2440这类经典开发板时,常常会困惑:为什么板上同时焊接了NOR Flash和NAND Flash两种芯片?它们各自扮演什么角色?启动时那个小小的拨码开关,拨向不同方向背后又隐藏着怎样的硬件原理?这些问题看似基础,却直接关系到我们对系统启动流程、内存地址映射乃至后续驱动开发的理解深度。今天,我就结合自己多年在嵌入式底层摸爬滚打的经验,以S3C2440和Mini2440为蓝本,彻底拆解NOR Flash与NAND Flash这对“黄金搭档”,不仅告诉你它们是什么,更重点剖析为什么这么设计,以及在实战中如何利用它们的特点。

简单来说,你可以把NOR Flash想象成一本可以随机翻阅、但容量不大的精装书(程序存储器),而NAND Flash则是一个容量巨大、但需要按顺序查找的档案库(数据存储器)。在ARM Linux系统中,前者常用于存储Bootloader等关键启动代码,后者则用来存放庞大的内核镜像、根文件系统以及用户数据。理解它们的差异,是优化系统启动速度、设计可靠存储方案以及进行底层调试的必备知识。无论你是正在学习ARM体系结构的在校学生,还是需要为产品选型的嵌入式工程师,这篇文章都将为你提供从理论到实践的清晰指引。

2. 核心原理:NOR与NAND的物理与电气特性深度对比

要理解它们在系统中的应用,必须从最底层的物理结构和工作原理说起。这不仅仅是性能参数的罗列,更是理解后续所有“为什么”的基础。

2.1 内部结构与访问机制的本质差异

NOR Flash和NAND Flash虽然都叫“Flash”,但其内部晶体管阵列的架构截然不同,这直接导致了它们访问方式的根本区别。

NOR Flash的结构与“内存式”访问 :NOR的名字来源于“Not OR”,其内部存储单元采用并联连接。这种结构使得每一个存储单元都有独立的位线(Bit Line)和源线(Source Line)引出。因此, NOR Flash拥有独立的数据总线和地址总线 。对CPU而言,访问NOR Flash就像访问一块普通的SRAM或ROM一样:CPU给出一个具体的地址信号,NOR Flash内部的地址译码器就能直接定位到那个存储单元,数据几乎可以立即呈现在数据总线上。这种特性被称为“芯片内执行”(eXecute In Place, XIP),即CPU可以直接从NOR Flash中取指并执行代码,无需先将代码复制到RAM中。

NAND Flash的结构与“块设备式”访问 :NAND的名字来源于“Not AND”,其存储单元采用串联连接,多个存储单元共享一条位线。这种高密度结构牺牲了独立的地址引脚。 NAND Flash的接口通常是复用的,数据、地址和命令都通过同一组8位或16位的I/O总线传输 。要读取一个数据,CPU必须先向NAND Flash控制器发送特定的命令序列(如读命令0x00),然后分周期送出地址(列地址和行地址),最后才能等待数据就绪并读取。整个过程需要严格的时序控制,由CPU或专用的NAND Flash控制器通过软件编程来实现。

注意 :正是这个“独立地址线”与“复用接口”的根本区别,导致了ARM CPU无法直接从NAND Flash启动。因为CPU上电复位后,第一条指令是从地址0x00000000获取的,它期望这个位置有一个能立即响应、给出指令的存储器。只有具备独立地址总线的NOR Flash(或ROM)能满足这个要求。

2.2 性能参数背后的工程权衡

性能比较不能只看数字,要理解数字背后的工程代价和适用场景。

读取速度 :NOR的随机读取延迟通常在100ns以内,确实比NAND快。这是因为NOR的随机访问无需复杂的命令周期。但在连续读取大块数据时,高性能的NAND Flash借助其更高的时钟频率和内部缓存,吞吐量可能反超。对于启动时需要随机跳转执行少量初始化代码的场景,NOR的优势明显。

写入与擦除速度 :这是NAND的绝对优势领域。NOR Flash的擦除操作必须以扇区(Sector,通常64KB或128KB)为单位,耗时长达数秒。写入(编程)前也必须先擦除对应扇区。而NAND Flash以页(Page,通常2KB或4KB)为单位编程,以块(Block,通常64个页或128个页)为单位擦除,擦除一个块仅需几毫秒。这种差异源于两者不同的浮栅晶体管结构和擦除电压施加方式。NAND的高密度串联结构使得它可以同时对一整块晶体管进行擦除,效率极高。

容量与成本 :NAND的单元结构更简单,相同工艺下可以实现比NOR高得多的存储密度。这使得NAND在每兆字节的成本上远低于NOR。因此,市面上NOR Flash容量大多在1Mb到256Mb之间,而NAND Flash轻松从256Mb发展到数百Gb。在Mini2440上,2MB的NOR和64MB的NAND配置,正是这一成本与容量关系的典型体现。

可靠性与寿命 :NOR Flash的每个存储单元相对独立,位翻转错误率极低,数据保存年限长(通常超过20年)。NAND Flash由于高密度和复杂的工艺,存在固有的位翻转可能,必须配合 错误检测与纠正(ECC) 算法使用。同时,NAND Flash的每个块有擦写次数限制(SLC约10万次,MLC/TLC更低),需要 坏块管理(BBM) 磨损均衡 算法来延长使用寿命。这些特性决定了NOR适合存放极少修改的关键代码,而NAND适合存放频繁更新的数据。

3. 实战解析:Mini2440启动模式与地址空间映射

理解了原理,我们来看它在具体硬件平台上的实现。以S3C2440和Mini2440为例,这是学习嵌入式Linux的经典组合。

3.1 S3C2440的启动模式配置奥秘

S3C2440芯片内部有一个至关重要的硬件设计,它通过芯片的 OM[1:0](Operating Mode)引脚 在上电复位时的电平状态来决定系统的启动方式。这不是软件配置,而是硬件级的“硬连线”决策。

  • OM[1:0] = 01 或 10 :CPU被配置为 非NAND Flash启动模式 。此时,CPU将外部总线Bank0(对应片选信号nGCS0)的地址范围(0x0000_0000 ~ 0x0800_0000)映射给挂接在nGCS0上的外部存储器。在Mini2440上,这个存储器就是那块2MB的NOR Flash(SST39VF1601)。因此,CPU复位后从0x0000_0000取指,实际上是从NOR Flash的第一个地址读取指令。这就是 NOR启动模式

  • OM[1:0] = 00 :CPU被配置为 NAND Flash启动模式 。此时,硬件行为发生根本变化:S3C2440内部一块 4KB大小的静态RAM(Steppingstone或BootSRAM) 被硬件自动映射到地址0x0000_0000开始的位置。同时,NAND Flash控制器被激活。复位后,NAND Flash控制器自动将NAND Flash 前4KB的内容 加载到这片内部的4KB SRAM中,然后CPU从SRAM的0地址开始执行。这就是 NAND启动模式

Mini2440开发板上的“NOR/NAND选择开关”,本质上就是通过物理跳线,改变了连接到CPU OM[1:0]引脚的上拉/下拉电阻网络,从而在硬件上设置了不同的启动模式。开关拨向“Nor”一侧,OM[1:0]被设置为非00状态;拨向“Nand”一侧,OM[0]和OM[1]被下拉到地,形成00状态。

3.2 两种模式下的内存地图(Memory Map)剖析

启动模式不仅决定了第一条指令的来源,更深刻影响了整个系统的内存地址空间布局,这对Bootloader和内核移植至关重要。

在NOR Flash启动模式下

  • 0x0000_0000 ~ 0x0020_0000 (2MB) :这片地址空间直接映射到外部NOR Flash芯片。CPU可以像访问内存一样直接读写(XIP)。通常,Bootloader(如U-Boot)就烧写在这里。Bootloader运行起来后,如果需要,可以将自己的一部分代码(尤其是需要修改变量、栈操作的部分)重定位(Relocate)到SDRAM中运行,以提升速度。
  • 0x3000_0000 ~ 0x3400_0000 (64MB) :这片地址空间映射到外接的SDRAM(内存)。这是系统的主内存,Linux内核启动后,内核映像和所有应用程序都运行在这片空间。
  • 其他地址段 :映射到其他片选(nGCS1~nGCS7)连接的设备,如网卡、USB控制器等。

在NAND Flash启动模式下

  • 0x0000_0000 ~ 0x0000_1000 (4KB) :这片地址空间映射到芯片内部的4KB BootSRAM。上电后,NAND Flash的前4KB内容被自动拷贝至此。一个极其精简的、不超过4KB的启动代码(通常是U-Boot的第一阶段 start.S )在这里运行。这段代码的核心任务就是初始化最关键的系统时钟、存储控制器(特别是SDRAM控制器),然后将NAND Flash中完整的、更大的Bootloader第二阶段代码(可能几十KB到几百KB)搬运到SDRAM中,并跳转到SDRAM中执行。
  • 0x3000_0000 ~ 0x3400_0000 (64MB) :同样是SDRAM。在Bootloader的第一阶段代码初始化SDRAM控制器后,这片内存才变得可用。
  • NOR Flash的地址 :在NAND启动模式下,NOR Flash仍然物理连接在nGCS0上,但CPU的地址0x00000000不再指向它。Bootloader在初始化后,可以通过配置内存控制器,将NOR Flash映射到另一个未被使用的地址段(例如0x1000_0000),以便对其进行读写操作(如更新NOR中的环境变量)。

实操心得 :调试时,如果系统从NAND启动失败,首先应检查这前4KB的启动代码是否正确。因为任何超出4KB的代码或数据引用都会导致错误。一个常见的技巧是,用仿真器直接连接板子,在NOR启动模式下先烧写一个完整的、支持NAND操作的Bootloader到NOR Flash中,然后利用这个Bootloader来烧写NAND Flash,这比单纯依赖NAND启动的4KB代码要可靠和方便得多。

4. 系统设计中的选型与应用策略

了解了差异和原理,我们如何在真实的嵌入式Linux系统设计中运用它们呢?

4.1 角色定位与存储规划

一个典型的嵌入式Linux系统存储介质规划如下:

  1. Bootloader存储

    • 首选NOR Flash :由于其XIP特性,NOR是存储Bootloader最自然的选择。开发阶段尤其方便,可以通过JTAG直接烧写和调试。对于功能复杂的U-Boot,NOR是理想家园。
    • 使用NAND Flash :为了极致降低成本,产品量产时通常只贴NAND Flash。此时Bootloader必须被设计成两阶段:第一阶段(<4KB)固化在SOC内部ROM或通过NAND自动加载,负责初始化SDRAM;第二阶段从NAND加载到SDRAM运行。这对Bootloader的健壮性和坏块处理提出了要求。
  2. 内核与根文件系统存储

    • 内核 :压缩后的内核镜像(zImage或uImage)通常存放在NAND Flash的固定分区。Bootloader将其读入SDRAM,解压并启动。
    • 根文件系统 :这是NAND Flash的“主战场”。根据需求不同,可以选择:
      • 只读文件系统 :如Cramfs、Squashfs,适合系统程序不变的应用。
      • 可写文件系统(带损耗均衡) :如UBIFS(针对裸NAND设计)、F2FS,或基于FTL(Flash Translation Layer)的EXT4/YAFFS2。 强烈推荐UBIFS ,它直接管理NAND物理特性,无需中间FTL层,坏块管理、磨损均衡和ECC都集成在内核驱动中,是现代Linux嵌入式系统的首选。
  3. 环境变量与配置参数存储

    • NOR Flash :如果存在,U-Boot的环境变量区常放在NOR Flash末尾的一个扇区,因为NOR读写可靠,且修改不频繁。
    • NAND Flash :如果只有NAND,则需要在NAND上划分一个小的、受坏块管理的区域来存放环境变量。U-Boot支持此功能。
    • EEPROM或SPI Flash :对于需要频繁修改且要求高可靠性的参数,有时会额外增加一小块EEPROM或SPI NOR Flash。

4.2 驱动开发要点与避坑指南

在Linux内核中,NOR和NAND的驱动架构完全不同。

NOR Flash驱动(MTD架构)

  • 驱动核心是 映射驱动(Map Driver) ,负责将NOR的物理地址映射到内核的虚拟地址空间,实现XIP。
  • 通过 CONFIG_MTD_CFI 等选项配置。驱动相对简单,因为接口是内存总线。
  • 注意 :对NOR进行写/擦除操作时,必须确保目标地址不在当前CPU执行的代码路径上,否则会导致系统崩溃。通常需要在SDRAM中运行一小段“擦写子程序”来完成。

NAND Flash驱动(MTD架构)

  • 驱动核心是 NAND控制器驱动 NAND芯片驱动 。需要正确实现 nand_chip 结构体中的命令、地址、数据读写函数,以及硬件ECC计算函数。
  • ECC是关键 :必须根据NAND芯片的数据手册,选择正确的ECC算法强度(如SLC常用1-bit ECC,MLC需要更强ECC)。Linux内核提供多种ECC算法实现(如Hamming码、BCH码)。驱动需要正确报告ECC校验结果,MTD层会处理纠错和坏块标记。
  • 坏块管理(BBM) :内核MTD层提供 nand_bbt (坏块表)管理。驱动需要支持扫描并建立坏块表。对于新NAND,第一次烧写系统时,最好用 flash_erase 工具全片擦除一遍,让驱动识别出出厂坏块。

一个常见的启动问题排查流程

  1. 现象:从NAND启动,串口无任何输出。
  2. 步骤1:切换到NOR启动模式,看NOR中已有的Bootloader(如果有)能否运行。如果能,说明CPU、SDRAM等核心硬件基本正常。
  3. 步骤2:在NOR启动模式下,通过Bootloader的NAND读写命令,检查NAND前4KB内容是否正确烧写了第一阶段的启动代码。可以使用 md (memory display)命令查看。
  4. 步骤3:检查Bootloader编译时链接地址(Load Address)是否正确。第一阶段代码的链接地址必须是S3C2440内部SRAM的地址(0x00000000或0x40000000,取决于具体设计),而第二阶段代码的链接地址必须是SDRAM的地址(如0x30000000)。
  5. 步骤4:用示波器或逻辑分析仪测量NAND Flash的CE#、WE#、RE#等控制信号在上电初期是否有波形,判断NAND控制器是否在工作。

5. 进阶话题:从经典配置看现代嵌入式存储演进

虽然Mini2440的NOR+NAND组合是经典教学模型,但现代嵌入式系统存储方案已经发生了显著变化。

SPI NOR Flash的兴起 :并行NOR Flash因引脚多、封装大、功耗高,已逐渐被 串行SPI NOR Flash 取代。SPI NOR只需4根线(CLK, CS, MOSI, MISO),容量可达128Mb,同样支持XIP(通过映射到内存空间或Quad-SPI快速读取)。它在很多MCU和低端应用处理器中,成为替代并行NOR存储Bootloader和固件的首选。

eMMC取代原始NAND :对于需要大容量存储(数GB到数百GB)的应用,原始NAND搭配复杂驱动和文件系统的方案,正迅速被 eMMC 芯片取代。eMMC相当于“NAND Flash + 闪存控制器 + 标准MMC接口”的封装。它内部集成了FTL、坏块管理、ECC和磨损均衡,对外提供标准的块设备接口(就像SD卡一样),大大简化了主机软件设计。现代智能手机、平板和很多高端嵌入式设备都使用eMMC。

SD/TF卡作为扩展与备份 :SD卡接口几乎是开发板的标配,它成本极低,便于更换和升级系统。在产品开发阶段,可以将内核和文件系统放在SD卡上启动,极大加快调试迭代速度。量产时,则可以将最终镜像烧写到eMMC或NAND中。

QSPI NOR与XiP的优化 :为了进一步提升从串行Flash启动的性能,Quad-SPI(QSPI)接口允许同时使用4条数据线传输数据,并支持 内存映射模式(Memory-Mapped Mode) 。CPU可以直接通过内存总线指令访问QSPI NOR,实现高效的XIP,使得从串行Flash执行代码不再成为性能瓶颈。

因此,当我们学习NOR/NAND这些经典知识时,其核心价值在于理解 存储器层次结构 启动流程 直接执行与块设备访问的差异 以及 Flash管理(ECC/BBM) 这些永恒的概念。这些概念在SPI NOR、eMMC、UFS等现代存储设备中依然适用,只是具体的接口和实现层次发生了变化。掌握了这些底层原理,你就能快速适应任何新的存储技术,做出合理的系统架构设计。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐