1. 为什么这两个名词总被摆在一起比较
SD NAND和SPI NAND,光看名字就差了一个SD,但它们确实是很多硬件工程师、嵌入式软件工程师在做存储选型时最容易纠结的一对。原因很简单:两者底层都是NAND Flash,容量区间重合度高,都覆盖了从128MB到4GB这个嵌入式产品最常用的范围,价钱也差不太多。真正拉开差距的,是它们外部的“壳”——一个长成了SD卡控制器的样子,一个还得靠主控端用软件方式把NAND管起来。
最近一两年,我身边做产品的朋友陆续在SD NAND和SPI NAND之间反复横跳。有人从SPI NAND切到SD NAND,理由是驱动开发太省心了,Linux内核自带SDIO驱动,设备树里配两行就能跑起来;也有人从SD NAND换回SPI NAND,理由是成本、供货、还有对底层坏块管理的可控性。说实话,这两个方向没有绝对的谁优谁劣,关键要看你的产品跑在什么系统上、你对存储可靠性的要求有多高、以及量产之后你打算怎么维护。
这篇文章我想从驱动开发、系统移植、量产烧录、后期维护这几个维度,把SD NAND和SPI NAND的差异彻底掰开揉碎。文章里涉及的Linux驱动配置、设备树写法、文件系统选择、坏块管理机制这些内容,都是我实际调板子、跑量产测试时总结出来的,不说教你抄作业,至少能让你在选型时少走几趟弯路。
文章适合正在做嵌入式产品选型的硬件工程师、Linux驱动开发刚入门的同学,以及那些已经在用SPI NAND但被底层坏块管理搞得头大的老手。读完你大概能清楚三件事:你的产品到底适不适合用SD NAND,SPI NAND的性能天花板在哪里,以及量产阶段哪些坑是可以在设计阶段就提前绕开的。
2. 接口差异决定开发模式的分水岭
2.1 物理接口:引脚数量与电气特性
先看硬件层。SPI NAND走的是SPI总线,标准四线制:CLK、MOSI、MISO、CS,算上电源和地,整个存储电路占用6到8个引脚。SD NAND走的是SDIO总线,在SD 1-bit模式下需要CLK、CMD、DAT0三根信号线,如果跑到4-bit模式,还要再加上DAT1到DAT3,总共六根数据信号线,加上电源地,占用10个以上的引脚。如果你的主控芯片引脚资源紧张,SPI NAND有明显优势;如果你的主控本身就有SDIO控制器,那SD NAND在布线复杂度和信号完整性上反而更省心。
还有一个容易被忽略的电气差异:SPI总线的信号速率上限一般是几十MHz到一百多MHz,而SDIO总线在UHS-I模式下能跑到SDR104即104MHz,DDR50模式下等效速率更高。这意味着SD NAND的理论带宽上限远高于SPI NAND。我实测过不少SD NAND芯片,在4-bit SDIO模式下顺序读速度能跑到23MB/s左右,顺序写在10到20MB/s之间,具体看芯片品牌和等级。而SPI NAND即便是用了比较新的DTR双数据率模式,满打满算也就是50MB/s级别的模块带宽,实际文件系统层跑下来,顺序读通常在20MB/s上下。
当然,引脚多不代表一定好,SDIO总线对PCB走线的等长要求、串联匹配电阻的摆放,都比SPI要讲究一些。低频低速SPI甚至飞线都能工作,SDIO跑高速时布线不规范就可能出现CRC错误。
2.2 控制器归属:谁在替你干脏活累活
这是我反复强调的一个点:SD NAND和SPI NAND最大的本质区别,在于NAND Flash的控制逻辑跑在哪里。
SD NAND在芯片内部集成了一个完整的SD控制器,相当于把一颗NAND Flash和一颗SD控制芯片封装在了一起。这个控制器负责NAND的坏块管理、磨损均衡、ECC纠错,对外暴露的是一个标准的SD/SDIO接口。主控端只负责发SD命令,完全不需要关心NAND物理层的页大小、块大小、坏块标记位置、ECC算法选择这类问题。
SPI NAND则完全相反。它在物理结构上就是一颗裸NAND Flash,仅仅是把并行接口换成了SPI串行接口。坏块管理、ECC校验、磨损均衡这些工作,一个不落地全落在了主控端的软件上。如果你的方案是Linux+MTD子系统,那内核里的SPI NAND驱动框架会帮你处理坏块表、实现基础的ECC算法,但你要自己做分区规划、自己决定坏块替换策略、自己评估磨损均衡够不够用。如果是MCU裸机或RTOS环境,这些底层的脏活累活基本全得你亲自上阵。
打个比方,SD NAND像是你在外面租了一个精装办公室,空调、水电、网络都给你配好了,你拎包入住就行;SPI NAND则像是租了个毛坯房,墙要自己砌、网线要自己拉,好处是你想怎么装修都行,坏处是什么都得自己动手。
2.3 对驱动开发的直接影响
这个差异在驱动开发阶段体现得淋漓尽致。
在Linux系统下,SD NAND直接挂到SDIO控制器上,内核的mmc子系统提供了完整的协议栈支持,从mmc core到块设备层、再到文件系统层,全部都是现成的。你要做的基本就是两件事:第一,在设备树里把SDIO控制器的状态配置好;第二,选择合适的总线宽度和速度模式。整个过程通常半天到一天就能搞定,甚至不需要写任何C代码。
SPI NAND在Linux下的驱动路径是:SPI控制器驱动加上SPI NAND驱动框架,通过MTD子系统暴露成/dev/mtdX设备节点,然后再挂上UBIFS文件系统。内核里确实也有现成的框架,但你需要额外确认三件事:你选的SPI NAND芯片类型是否被内核支持,你要用的ECC强度和算法是否匹配这颗芯片的规格,以及你打算给系统预留多少坏块替换空间。如果你用的是比较新的芯片,或者内核版本比较老,很可能还需要自己补一个chip ID的匹配条目,甚至要调整NAND控制器驱动里关于页大小、块大小的配置。
这里顺便提一句,热搜词里经常出现“Linux设备驱动开发详解”和相关PDF资料,很多入门同学把精力都花在读源码上,却忽略了一个基本事实:在SD NAND这类标准接口设备面前,最难的驱动工作内核早就替你做了。真正见功底的地方,恰恰是SPI NAND这种需要你理解NAND物理特性的场景。
3. Linux驱动开发阶段的实操对比
3.1 基于SDIO子系统的SD NAND驱动配置
如果你的产品主控是i.MX、RK、全志这类常见的应用处理器,SD NAND的接入路径基本是固定的。以我在RK平台上的实际经验为例,设备树里主要改SDIO控制器的节点。
&sdmmc { status = "okay"; bus-width = <4>; cap-sd-highspeed; sd-uhs-sdr50; no-1-8-v; keep-power-in-suspend; non-removable; mmc-pwrseq = <&sdmmc_pwrseq>; };有几个属性需要特别解释一下。
bus-width = <4>指定使用4-bit模式,这是发挥SD NAND性能的基础配置。日子常见有人只配了1-bit模式,结果读写速度直接砍到四分之一,还来问我是不是芯片不行。
non-removable这个属性非常关键。它告诉内核这是一颗焊接在板子上的存储芯片,不是可插拔的卡。没有这个属性,内核可能会把它当可移动设备处理,引发一系列电源管理、挂载时机的问题。
keep-power-in-suspend表示系统休眠时保持供电,这对于需要快速唤醒恢复存储状态的场景很重要。如果省掉这个属性,系统suspend后存储可能掉电,回来之后文件系统状态要重新初始化,耗时还容易出问题。
特别提醒一下,如果你的主控SDIO控制器同时连接了eMMC和SD NAND,一定要确认设备树里两个控制器节点的base address各归各的,别配串了。有一次我就因为复制粘贴设备树,把两个控制器的时钟都配到了同一个节点,结果是eMMC读正常、SD NAND写数据全是CRC错误,查了一整天才发现是时钟配置打架。
3.2 基于MTD子系统的SPI NAND驱动配置
SPI NAND在Linux下的配置比SD NAND复杂一些,但也没有难到不可收拾。我以一颗常见的SPI NAND芯片接入imx6ull平台为例来说明。
第一步,确认SPI控制器节点,设置好时钟频率。
&ecspi1 { status = "okay"; pinctrl-0 = <&pinctrl_ecspi1>; cs-gpios = <&gpio4 26 GPIO_ACTIVE_LOW>; spidev@0 { compatible = "spi-nand"; reg = <0>; spi-max-frequency = <50000000>; spi-tx-bus-width = <1>; spi-rx-bus-width = <1>; }; };注意这里compatible写的是"spi-nand",匹配的是内核drivers/mtd/nand/spi目录下的框架驱动,而不是spidev。很多新手会在这里踩坑:为了图方便把节点配成spidev,然后在用户态自己发命令读写NAND。这么做看起来绕开了MTD,但坏块管理、ECC全都要自己实现,生产环境很少有人这么干。
第二步,确认内核配置选项。
CONFIG_MTD=y CONFIG_MTD_SPI_NAND=y CONFIG_MTD_UBI=y CONFIG_UBIFS_FS=y这三项不开,SPI NAND基本是白搭。MTD_SPI_NAND是核心驱动框架,MTD_UBI和UBIFS_FS则是为了挂载UBIFS文件系统用的。
第三步,在系统启动后查看分区和挂载情况。
cat /proc/mtd如果你的驱动加载正常,应该能看到类似下面的输出:
dev: size erasesize name mtd0: 08000000 00020000 spi-nand0没有看到设备节点,优先查两件事:芯片的ID是否在驱动支持的列表中,以及SPI控制器的片选信号是否真的拉下去了。前者可以通过打开内核日志确认,后者得拿示波器量,或者说你在probe阶段直接加打印。
3.3 文件系统的选择逻辑
很多驱动开发的同学容易忽略的一个问题是:文件系统选不对,前面所有工作都白搭。
SD NAND因为走的是SD/MMC协议栈,对文件系统的兼容性非常宽松。ext4、vfat、f2fs都可以直接用,就像操作普通SD卡一样。如果你的产品需要在Windows上被识别为U盘或者需要在板子上直接被电脑读取日志,vfat是最省事的选择。如果追求数据完整性和日志能力,ext4稳妥。SD NAND的控制器已经做了一层地址映射,文件系统层不太需要关心底层NAND的磨损和坏块,这也是它驱动开发简单的一个重要原因。
SPI NAND则不同。如果没有专门的NAND Flash文件系统配合,直接用传统的ext4或vfat,用不了多久就会因为磨损均衡不到位、掉电导致的数据错乱让人崩溃。MTD子系统下最经典的选择是UBIFS,它专门针对NAND特性设计,有自己的wear-leveling机制,配合UBI卷管理,能在一定程度上做坏块管理和损耗均衡。
这就引出一个实战经验:如果你决定用SPI NAND,建议从第一天开始就用UBIFS,不要图省事直接挂ext4跑两周再切换到UBIFS。因为文件系统格式不对,底层NAND的块状态、ECC记录方式都不同,后期切换意味着要全盘擦除重新烧录,量产阶段改这个代价非常大。
3.4 裸机和RTOS环境的开发差异
并不是所有产品都跑Linux。很多MCU级别的产品,比如智能门锁、小家电、IoT模组,用的是裸机或者RTOS,像FreeRTOS、RT-Thread、LiteOS,这时候SD NAND和SPI NAND的驱动开发体验又是另一番景象。
在RTOS + SPI NAND场景里,如果你有一颗强大的主控,比如带硬件ECC的QSPI控制器,那还有救。但如果是普通MCU通过软件模拟SPI去接NAND,那你得自己写一套完整的NAND Flash驱动,包括reset、read ID、page read、page program、block erase、坏块扫描、ECC校验。整个工程做下来,少说也要一到两周。我见过很多团队在这个环节反复延期,主要原因是NAND的ECC算法和坏块标记策略在RTOS生态里没有统一标准,每个厂家、每个工程师的做法都可能不同。
在裸机或RTOS + SD NAND场景里,很多MCU并没有内置SDIO控制器,这时你需要通过SPI接口去模拟SD卡协议,也就是所谓的SPI模式SD卡。好消息是SD卡的SPI模式协议是公开标准,坏块管理、ECC纠错依然由SD控制器芯片完成,你只需要实现命令收发和数据读写。这种模式下,你实际上是在用一个相对简单的SPI协议,换来了硬件级别的坏块管理和纠错能力。很多芯片厂商提供的SD NAND,本身就是设计成支持这种使用方式的。
从开发工作量来看,同一个MCU平台上,接SD NAND大概只需要写两百行左右的SDIO或SPI模式驱动代码,而接SPI NAND即使是借助第三方库,通常也要上千行。这个差距就是接口标准化的红利。
4. 底层存储管理机制:坏块、ECC与磨损均衡
4.1 SD NAND的硬件级管理策略
SD NAND之所以驱动简单,根本原因在于芯片内部有专门的硬件模块在做存储管理。行业里通常把这套机制叫做内置FLL(Flash Translation Layer)或者类似的逻辑映射层。
具体来说,SD NAND芯片内部维护着一张逻辑地址到物理地址的映射表。主控端操作的是连续的LBA逻辑块地址,而芯片内部会根据NAND物理块的实时状态,动态地把逻辑块映射到健康的物理块上。写数据的时候,FLL会优先选择磨损次数少的物理块;发现坏块的时候,会自动把它标记出来,从映射表中摘除,然后用备用块顶上。
ECC纠错也是在控制器内部完成的。SD NAND通常使用BCH或LDPC纠错算法,不同品牌、不同等级的芯片纠错能力不一样。以目前主流品质合格的SD NAND产品为例,通常能保证每512字节数据纠错能力在8bit到24bit之间,具体看规格书标注。主控完全不需要关心底层NAND的raw bit error rate,因为控制器已经把纠过错的数据返回给你了。
这带来的一个重要好处是:存储管理机制在主控看来是实时生效的。你在应用层看到的就是一个稳定、可靠的块设备,几乎没有底层NAND的那些幺蛾子。量产阶段如果有部分芯片出现了较多坏块,FLL会在初始化时扫描并屏蔽它们,不影响用户数据区。当然,坏块多到超过备用块上限时,芯片会表现为容量缩水或者读写异常,但这通常已经在芯片寿命末期了。
4.2 SPI NAND的软件管理方案
SPI NAND因为没有控制器,所以在Linux下这些管理工作要分两部分来完成:一部分由内核MTD/UBI层的软件算法承担,另一部分则需要你在系统设计时手动规划。
先看ECC。SPI NAND通常页大小为2KB,每页附带64字节的OOB区域。部分SPI NAND芯片内部集成了硬件ECC引擎,芯片可以在page read时自动完成纠错并把状态位反映出来。这个特性是有用的,但需要驱动正确配置:你要在设备树或驱动代码里指定ECC强度,让驱动知道每个扇区应该纠几个bit。如果驱动把ECC关掉了或者配置错了,读取数据时错误率会直线上升,文件系统会频繁报错。
再看坏块管理。NAND Flash出厂时就可能有坏块,使用过程中也会产生新坏块。SPI NAND在Linux下的坏块管理由MTD框架实现,初识化时会扫描全盘,把标记为坏块的物理块记录下来。UBI层在此基础上做动态坏块替换和磨损均衡。这个过程是软件完成的,虽然灵活,但需要注意几个问题:一是系统要预留足够多的备用块,不然坏块增多之后没地方换;二是UBI的磨损均衡算法在写压力不均匀的场景下可能表现不够好,数据更新频繁的区域会先磨损。对于日志型频繁写入的应用,需要在应用设计时尽量避免对小区域做高频重复写。
裸机或RTOS环境下,这些管理逻辑你需要用第三方协议栈或者自己实现。常见的做法是移植一套类似littlefs的文件系统,它在设计上考虑了对裸NAND的支持,自带掉电保护、磨损均衡和坏块管理能力。但要注意littlefs跑在SPI NAND上同样需要OOB区域做元数据存储,对页大小有一定要求,如果芯片页大小只有512字节,管理空间的规划会更紧张。
4.3 掉电保护能力对比
掉电保护是存储设备最容易翻车的场景,我把它单独拿出来说,是因为量产后的返修案例里有相当大的比例跟掉电有关。
SD NAND内部的FLL在做写操作时,会先写临时区,再更新映射表,最后把数据搬到目标位置。正常工作的芯片会保证在任意时刻掉电,要么数据没写、要么数据写完整,很少出现中间状态。这是因为FLL带有一定的日志机制,上电后能从半途状态恢复。
SPI NAND则不同,它没有内部映射层,数据写到一半掉电时,可能会出现页编程不完整、块擦除中断的情况。这时如果文件系统没有足够的掉电保护机制,就可能同时出现“旧数据消失、新数据不完整”的尴尬。UBIFS在Linux下确实有日志和提交点机制,但并不能做到100%保证在任何掉电瞬间都不丢数据。要想提高可靠性,要么在硬件上留掉电检测电路,检测到掉电后强制给主控几百毫秒时间把缓存刷入NAND;要么在文件系统层做好数据冗余和验证设计。裸机环境下做掉电保护则更加困难,这往往是产品最后阶段加班最凶的部分。
5. 量产烧录与后期维护的实战经验
5.1 烧录方式的差异
量产烧录是很多工程师第一次真正感受到两者差异的地方。
SD NAND的烧录方式非常灵活。因为它对外表现就是一个块设备,所以你可以直接把SD NAND芯片通过转接座放到普通SD读卡器上,用电脑上的量产工具把镜像文件整个灌进去。市面上有专门的SD NAND烧录座,比如microSD卡座加转接板的形式,用起来和烧录一张TF卡没什么区别。这意味着你的产线工人不需要特殊技能,只要会用电脑插卡复制文件就行。从批量生产的角度讲,这一步节省了大量夹治具和培训成本。
我实际工作中遇到过一种比较难受的SD NAND烧录方式:先把空片焊上板,然后用主控的USB下载模式配合烧录工具,通过网络或USB把镜像写入SD NAND。这个过程也不是不行,但产线效率低很多,而且对烧录工具和主控连接的稳定性要求很高。如果你的产品有USB口而且量产量不大,这种方式还能接受,但大批量出货时还是建议用烧录座方案先预烧好芯片再贴片。
SPI NAND的烧录主要是两种方式:一种是在线烧录,也就是主控通过SPI接口写镜像;另一种是离线烧录,用专用烧录器在芯片贴片之前先把镜像写进去。离线烧录需要专门的烧录器,一般几千到上万块钱一套,还要针对不同芯片型号做配置文件配置。在线烧录要考虑的环节更多:要先把bootloader烧进内部或其他存储,再由bootloader去初始化SPI NAND并加载镜像。这个流程在Linux平台下通常需要做定制化的量产工具,在RTOS平台倒是相对简单,很多厂商的IAP方案已经支持SPI NAND了。
5.2 镜像管理:离线升级策略对比
产品要升级固件,就得把存储、文件系统、应用镜像统一管理起来。这个环节SD NAND和SPI NAND的思路差异很大。
SD NAND在Linux系统下通常会被分成多个分区:boot分区放内核镜像,rootfs分区放根文件系统,data分区放用户数据,类似eMMC的分区管理方式。升级时只需要往对应分区写入新镜像,然后更新bootloader里记录的分区版本号即可。因为SD NAND的块设备接口对应用透明,你可以直接在文件系统层面做升级,也可以用dd命令整块地覆写某个分区。甚至可以在系统运行的时候,把新系统镜像下载到data分区,然后在reboot时由bootloader去校验并刷写。这种方案的灵活度很高。
SPI NAND在Linux下走的是MTD,升级工具通常是命令行下的flashcp或者flash_erase配合nandwrite。由于MTD设备不能像普通块设备那样直接挂载文件系统再用cp命令写,升级流程会更机械。你可以用mtd-utils工具先擦除指定分区再写入新镜像,但这要求你对当前系统的分区布局非常清楚。部分系统会选用双备份方案,即Boot分区放两份镜像,升级时先写备份区,校验通过后再切换,启动失败还能自动回滚。
这里分享一个容易踩的坑:SPI NAND在Linux下的find_rootfs逻辑、在内核cmdline里指定的mtdparts分区编号,一旦在升级过程中因为分区布局变更而错位,容易直接导致系统无法启动。镜像里要严格固化mtdparts配置,任何修改都要经过完整的升级链路回归测试。
5.3 寿命评估与数据可靠性维护
NAND Flash的寿命由P/E擦写次数决定,不同等级的NAND颗粒擦写次数差异很大。SLC可以做到几万次,MLC通常在三千到五千次,TLC则在五百到一千次区间。这个参数直接决定了你的产品能用多久。
SD NAND因为自带FLL,磨损均衡做得比较好,所以可以在一定程度上延长产品实际使用寿命。不过也别把它神话,FLL的磨损均衡算法也是针对通用场景设计的。如果你的产品有某个区域会被高频次反复写入,比如日志分区,即使在SD NAND内部,这个区域对应的物理块磨损速度也会明显快于其他区域。产品设计时还是要尽量把高频写操作分散到多个文件或使用日志型文件系统的平衡策略。
SPI NAND的磨损均衡在Linux下依赖UBI层,UBI对整盘做磨损均衡,效果还可控。裸机环境下用的是自己移植的协议栈,磨损均衡能力就看你的代码水平了。如果完全没有做磨损均衡,同一块区域反复写,寿命可能只有设计值的十分之一甚至是百分之一。
量产之后最怕的是芯片寿命耗尽导致的数据静默损坏。无论SD NAND还是SPI NAND,都需要在产品中建立巡检机制:定期读取关键扇区或页面数据并校验,发现校验错误及时报警。这个机制做起来不难,但很多产品都没有,等用户报告数据丢失时往往已经太晚了。
6. 性能、功耗与成本:一个都不能少
6.1 理论带宽与实际读写测试
我们直接用实测数据说话。我这里用的是一颗主流的SD NAND芯片和一颗同容量级别的SPI NAND芯片,测试环境是同一个Linux 5.10系统、同样的CPU平台。
| 测试项 | SD NAND (4-bit SDIO) | SPI NAND (50MHz SPI) |
|---|---|---|
| 顺序读 | 22.5 MB/s | 18.9 MB/s |
| 顺序写 | 11.3 MB/s | 6.8 MB/s |
| 4K随机读 | 2,100 IOPS | 850 IOPS |
| 4K随机写 | 380 IOPS | 120 IOPS |
这个结果在意料之中。顺序读差距不算悬殊,因为两边的接口速率都在这个量级;顺序写和随机写,SD NAND的优势就很明显了,特别是随机写场景,FLL的内部映射和缓冲策略带来的提升非常显著。如果你的产品需要频繁写入小数据块,SD NAND在体验上会好很多。
如果你用的是支持DTR模式的更高规格SPI NAND,同时主控的SPI控制器也支持双倍数据率,顺序读确实能拉得更近,但随机写的差距依然存在。这个差距的根源不在于接口速率,而在于NAND本身的写机制和坏块管理策略差异。
6.2 系统启动时间与功耗对比
启动时间对于很多IoT产品是硬指标。SD NAND的初始化只要识别SD卡协议、读取CID/CSD寄存器、建立块设备映射,整个过程在几十毫秒级别。之后内核就能直接从分区上加载内核镜像。SPI NAND的初始化多了一个全盘坏块扫描的步骤,驱动要读取每个块的OOB区判断坏块标记,对于一颗128MB的芯片,全盘扫描通常要一到两秒。如果芯片容量更大,这个时间更长。当然,你可以通过DTB里的bad-block-table来跳过全盘扫描,前提是要提前烧录一份坏块表,让内核驱动在init时直接加载,这才有比较大的优化空间。
功耗方面,SD NAND因为有控制器芯片,实际功耗一般比SPI NAND高一些。空闲状态两者都差不多,都能做到微安级待机。读写时SD NAND的峰值电流通常在几十毫安级别,SPI NAND则在十几到二十几毫安级别。如果你的产品对功耗非常敏感,比如电池供电的穿戴设备,SPI NAND在功耗上有一点优势,但这个差距对大多数产品来说并不致命,真正功耗大头通常在屏幕和无线模块上。
6.3 单颗成本与总拥有成本
从硬件BOM成本来看,同容量的SD NAND通常比SPI NAND贵大概几毛钱到一两块钱人民币,具体看容量和品质等级。这个差价在消费类产品上可能会被重视,但在工业级产品上通常不是首要考虑因素。
真正的总拥有成本差异在开发维护环节。SD NAND驱动的开发成本低,几乎不需要为底层NAND的坏块管理、ECC算法操心,这样可以把宝贵的开发时间投入到产品业务逻辑上。SPI NAND的驱动开发成本明显更高,在Linux平台上虽然能用MTD框架,但调优EEC强度、处理坏块替换策略、排查掉电问题都是需要时间的。如果项目工期紧,这部分隐形成本可能远超芯片本身的差价。
另外要考虑到供应链的因素。SD NAND做成了标准化封装,供货来源相对集中,但产能和交付周期依然会受到整个NAND市场波动的影响。SPI NAND因为内核逻辑简单,封装门槛相对低,供应商选择面广。工业类产品会做双源备货,建议选型时就要调研同样封装、同样容量、引脚兼容的替代料有哪些,提前做好验证,避免后期因为单一货源问题卡死量产。
7. 选型决策:不同场景怎么选最省心
7.1 适合SD NAND的场景
先说说我建议优先考虑SD NAND的情况。
一是你的产品跑Linux系统,且对开发周期要求很紧。用SD NAND可能一周就能完成存储相关开发,用SPI NAND则要预估一到两周甚至更长时间,特别是团队里还得有人熟悉MTD与UBIFS。此时SD NAND带来的时间收益非常值钱。
二是你的产品有大量随机写入或频繁掉电的场景。比如数据采集设备、边缘网关、行驶记录仪这类产品,数据几乎一直在写,掉电保护要求又不低。SD NAND内部的FLL天然做了映射和缓冲,抗掉电能力明显优于裸NAND方案,省去你在应用层做各种掉电保护设计的精力。
三是你的产品涉及产线烧录效率问题。SD NAND可以在贴片前预烧好镜像,产线工人工作量小,生产节拍快,产品返修时还能直接用读卡器读出数据做分析。这点在售后维护阶段帮助特别大。
举个例子,我之前做一款数据采集盒子,主控是NXP i.MX6ULL,存储方案最开始选的是SPI NAND。开发到一半发现日志写入频繁,UBIFS在掉电时反复出问题,光是设计掉电检测电路和刷写逻辑就花了两周多。后来评估下来换成了SD NAND,存储相关的代码量直接砍掉一大半,整体研发进度反而追回来了。
7.2 适合SPI NAND的场景
SPI NAND依然有自己的主战场,而且在这些场景下优势更明显。
首先是引脚受限的小型主控方案。很多IoT模组主控引出的可用GPIO本来就少,加一颗 SPI NAND可能只用四根信号线,而SD NAND则要多占好几个引脚,有些小封装主控甚至根本没有SDIO控制器。在这种场景下SPI NAND几乎是唯一选择。
其次是成本敏感、大批量的消费类产品。如果产品出货量是百万级别的,每个物料省一块钱就是省一百万。只要研发团队有足够能力驾驭MTD/UBI这套体系,而产品本身的写入频率又不高,SPI NAND的成本优势非常可观。
第三个场景是软件生态完全由自己掌控的产品,比如基于RT-Thread、LiteOS等RTOS开发的方案,已经有一套成熟的NAND管理组件或文件系统库。这时候SPI NAND不仅成本低,而且灵活性高,你能完全把控底层逻辑,优化的空间很大。
7.3 折中方案与实际妥协
如果你面临的情况比较纠结,可以参考我见过的一些折中方案。
一个常见做法是:SPI NAND负责存放只读或极少写的固件镜像,SD NAND或者另一块存储负责存放频繁读写的用户数据和日志。这种“组合拳”在成本、可靠性、速度之间找到了一个相对平衡点。比如有的路由器产品,bootloader和内核镜像放SPI NOR或SPI NAND,根文件系统和配置数据放SD NAND。系统启动速度、存储可靠性、Flash成本都控制得不错。
还有一条路线是尽量选品质等级较高的SD NAND芯片。很多厂商提供“工业级”版本,工作温度更宽,擦写寿命更长,坏块率更低,价格上去一些但可靠性明显提升。如果你的产品定位是工业级,这个差价通常是值得的。
8. 常见问题与排查技巧实录
8.1 问题速查表
我把自己在两类存储上遇到过的典型问题整理成了一个表格,方便排查时对照。
| 问题现象 | 可能原因 | 排查思路与对策 |
|---|---|---|
| SD NAND初始化失败,mmc0无法识别 | 引脚焊接、上拉电阻缺失、时钟频率问题 | 先用示波器测量CLK/CMD/DAT0信号,确认是否有命令响应;检查上拉电阻是否接对,Sdio总线DAT和CMD线通常需要上拉到VDD |
| SD NAND写入文件后重启丢失 | 使用了vfat但没有sync,掉电导致缓存未落盘 | 增加sync操作频次,或在文件系统挂载时使用sync/async选项;对于关键文件考虑双备份 |
| SPD NAND挂载UBIFS时报“corrupted node header” | ECC配置不正确导致数据读出错误 | 确认驱动里ECC强度设置与芯片规格一致;检查SPI信号质量和时钟上限,降低spi-max-frequency试验 |
| SPI NAND初始化慢,启动时间过长 | 全盘坏块扫描导致 | 提前扫描并固化坏块表;使用bad-block-table功能让驱动跳过扫描 |
| SPI NAND在写较多数据后文件系统损坏 | 备用块不足或磨损均衡策略失效 | 增加预留块数量;用UBI的autoresize功能让卷自动扩展;检查是否有高频写区域需要特殊处理 |
| SD NAND在系统休眠后无法唤醒 | 电源管理配置问题,可能SDIO设备掉电 | 正确配置keep-power-in-suspend、non-removable属性;检查硬件电路是否保证休眠时断电但VDD保持 |
| 使用SPI NAND时裸机程序读不到芯片ID | SPI时序参数不满足芯片要求 | 检查片上拉、片选的初始化时序,对照datasheet确认模式0/模式3,降低SPI时钟重试 |
| 量产中部分主板写入镜像后校验失败 | 烧录接触不良或者芯片批次异常 | 检查烧录治具的接触电阻,加严离线烧录后的自动校验;确认不同批次芯片ID和ECC规格一致 |
8.2 排查细节里的几个独家心得
查驱动问题时,日志是最直接的突破口。Linux下SD NAND相关的日志可以通过dmesg | grep mmc来过滤,SPI NAND相关的则看dmesg | grep spi-nand或者dmesg | grep mtd。很多时候问题不是靠读源码解决的,而是靠把日志里每一行看不懂的内容都查一遍,慢慢缩小范围。
遇到SPI NAND读写数据异常,不用急着改驱动代码,先用一个最基础的测试手段:通过devmem直接对SPI控制器寄存器和NAND芯片做数据读写,验证SPI链路本身是否可靠。如果裸读写都有问题,那就是硬件问题,驱动再怎么写都救不了。如果裸读写正常,再往上排查是MTD层的ECC配置问题还是文件系统层的问题。
SD NAND排查时有一个容易被忽略的点:由于它内部有FLL,芯片上电后第一次访问会比较慢,因为它要加载映射表。系统启动时如果SDIO超时时间设置得太短,可能出现偶发性的初始化失败。可以通过在设备树的SDIO控制器节点里增加max-frequency限制或者在驱动里加大超时时间来规避。
8.3 售后返修数据分析
我做了几年产品维护,最有价值的经验之一就是建立完善的售后数据记录。对于存储相关的返修,记录这四项数据就够了:返修设备使用的是哪个批次芯片、故障现象是什么、读取芯片smart数据或OOB坏块信息的结果、返修前的使用时长。积累一段时间后,你就能看出某批次芯片的坏块率有没有异常,某个应用场景下的寿命是否符合预期。
不要等到出现大量故障后才做数据分析。量产初期就定期抽检一些在用户环境运行过的设备,把内部存储的健康状态数据导出,看看磨损分布和坏块增长趋势。当发现某些NAND平均磨损量到了寿命的三分之一时,就该考虑产品是否需要提前更新换代或优化软件的写入逻辑。这个做法对SD NAND和SPI NAND都适用,只是数据获取方式不同:SD NAND很多型号支持读取类似SMART的统计信息,SPI NAND则需要通过MTD工具扫描OOB分析。
9. 最后说说我对选型这件事的体会
手里的项目做多了以后,我对选SD NAND还是SPI NAND这件事的态度变得非常务实:不要被“底层可控”这种情怀绑架,也不要单纯因为SD NAND驱动简单就无脑选。真正该问的是三个问题:你的团队里有没有一个熟悉NAND Flash底层机制的人?你的产品是否对随机写入和掉电可靠性有硬性要求?你的产品生命周期内预期会有多少次固件升级?
这三个问题的答案基本能帮你锁定方向。团队里没有熟悉NAND底层机制的人,产品又需要频繁写入和升级,大概率你会被SPI NAND的底层问题拖到项目延期;团队里有人能驾驭MTD/UBIFS体系,产品只是跑Linux加少量配置数据存储,那SPI NAND的成本优势确实值得争取。
还有一点经验之谈:无论选了哪一种存储,都建议在硬件设计阶段留出调试接口。SD NAND的话,把SDIO总线的信号引到测试点;SPI NAND的话,最好把SPI控制器对应的引脚也引出来。这样在量产初期如果出现存储异常,你能直接在不良品上飞线抓波形、读寄存器,排查效率完全不一样。
最后再分享一个细节:做产品选型时,别忽略芯片厂商提供的文档质量。优质的SD NAND芯片规格书会明确写出FLL的策略、坏块管理机制、电源时序要求;优质的SPI NAND芯片规格书会给出详细的ECC配置建议和参考驱动代码。有些厂商就差到规格书写得模棱两可,甚至连芯片ID表都要你自己去摸索。选芯片也是在选合作伙伴,这一点在量产维护阶段会让你体会得特别深。