ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

NAND Flash与SD NAND扇区分配表架构差异及优化实战

2026/9/17 3:50:43 拓冰建站 浏览量
NAND Flash与SD NAND扇区分配表架构差异及优化实战 如果这几年你一直在做嵌入式存储相关的东西肯定绕不开NAND Flash和SD NAND贴片式SD卡这两个名字。尤其是产品从原型走向量产的时候存储方案的选型往往直接决定了后续稳定性、成本和生产效率。我早年做车载数据记录仪踩过裸NAND搭配FATFS导致掉电后文件系统损坏的坑也经历过换成SD NAND后功耗和随机写性能双双不达标的窘境最后硬着头皮把两条技术路线都吃透了才算是真正掌握主动权。这篇文章我不会讲太多浮于表面的概念对比而是瞄准一个很多人没有细究的深层角度——存储扇区分配表架构差异把NAND Flash和SD NAND在地址映射、GC行为、掉电一致性以及文件系统协同方面的本质不同掰开讲清楚再给出我实测有效的优化策略。适合正在做存储方案选型、调试FATFS挂载问题或者想自研NAND控制器的工程师参考。1. 存储扇区分配表从NAND约束到两种方案的分水岭很多人把扇区分配表理解成只是文件系统FAT表那一层的事情其实真正的重点在更底层的逻辑地址到物理地址映射关系。NAND Flash因为存储单元本身的物理特性不允许像NOR那样按字节随机改写才有了这套映射机制。搞懂这个你才能真正理解为什么裸NAND和SD NAND用起来差距那么大。1.1 裸NAND Flash的物理约束和数据访问模式要理解扇区分配表为什么存在先得明白NAND Flash的物理结构。一片裸NAND按照页Page、块Block、平面Plane逐级组织1KB到16KB不等的页是数据读写的最小单位而擦除操作的最小单位是块一个块通常包含64到256个页。这个特性就意味着你想改写某个页里的几个字节不能像操作SRAM一样直接写进去因为NAND的编程操作只能把存储单元从1变成0想恢复成1就必须对整个块做擦除。再加上坏块这个客观存在的现象NAND Flash出厂时就有一定比例的坏块使用过程中还会持续产生新坏块。如果主控直接把逻辑地址映射到物理地址上遇到坏块就直接写不进去整个存储系统就得瘫痪。于是业界设计出了FTLFlash Translation Layer这一层软件核心就是一张逻辑地址到物理地址的映射表也就是我标题里说的存储扇区分配表。它负责把逻辑扇区号翻译成具体的物理页和物理块地址同时管理坏块、磨损均衡和垃圾回收。1.2 SD NAND的隐藏控制器把FTL封装进芯片内部SD NAND从封装形式上看跟普通的贴片式存储芯片差不多但内部比裸NAND复杂得多。它把NAND Flash裸片和一颗专门处理存储管理的控制器晶圆封装在一起对外暴露的是标准SD接口协议引脚直接贴上就能用。这颗内部控制器从硬件层就把FTL、ECC校验、磨损均衡、坏块管理这些工作全部承担了。也就是说你在主控芯片上看到的SD NAND就是一个普通的逻辑块设备跟SD卡、eMMC这些设备没有任何本质区别。你发送CMD24、CMD25这些SD协议命令就能以512字节为一个扇区进行读写完全不需要关心底层它到底把数据写到哪个物理页、内部哪几个块正在做垃圾回收。这个“黑盒”特性对开发效率是极大的提升但也带来一个隐患你永远无法直接控制内部的映射策略和GC时机。1.3 “扇区分配表”在不同语境下的双重含义辨析谈架构差异之前必须把概念厘清楚。扇区分配表在嵌入式存储语境下至少有两层含义。第一层是FTL层的逻辑块地址LBA到物理块地址PBA映射表这是存储介质内部的地址翻译桥梁第二层是文件系统层的FAT表、文件描述符表等用来记录文件数据在逻辑块设备上的分布情况。FATFS这类文件系统跑在逻辑块设备上面它看到的存储空间已经是连续的LBA地址了。换句话说文件系统层的扇区分配表跟物理NAND映射没有直接关系文件系统正确与否取决于逻辑块设备是否正确工作。所以当FATFS挂载失败或者FAT表损坏时根因往往有两个方向一个是文件系统自身因为掉电等原因记录了不完整的数据另一个是底层FTL映射表出了问题导致逻辑地址上读出来的内容根本就不是之前写入的数据。排查时要分清楚大部分新人容易在这个地方绕圈子。2. 核心架构差异拆解映射表到底怎么实现这一节是全文的技术主线。我分别从裸NAND体系和SD NAND体系出发把映射表的存储位置、更新机制、性能影响讲透最后用表格做一个直观对比。2.1 裸NAND体系的FTL映射表主控工程师绕不开的坎使用裸NAND时映射表的设计直接决定了产品性能、可靠性和代码复杂度。先看映射粒度这个最基础的问题。以常见128MB SLC NAND、页大小2KB、块大小128KB为例。如果采用页级映射逻辑页可以映射到任意物理页灵活性最高但映射表要记录所有页的对应关系65536个页每个页需要4字节条目映射表就要占用256KB。如果采用块级映射逻辑块只映射到物理块块内页偏移固定映射表只需要1024个条目总共4KB但随机更新某个页时可能要连带擦写整个物理块写放大明显。实际工程中很多低成本方案选择折中的混合映射数据区走块级映射另开几个日志块使用页级映射用来吸收频繁的小数据写入。我早期在STM32F4上做的裸NAND方案就是这种思路成本和性能比较均衡。这里建议各位在做裸NAND项目之前一定要先用Excel或者脚本把映射表大小、RAM占用、最坏情况写放大这几个指标算一遍不要在代码写完一半了才发现RAM装不下映射表。映射表的持久化是一个更大的坑。因为映射表本身也需要存储在NAND里掉电后重新上电需要能够恢复映射关系。工程上常用“固定系统块存储映射表”的方式每次更新映射表后直接改写系统块中的对应区域但这样做有风险——如果更新到一半掉电映射表可能损坏。更健壮的做法是双备份映射表交替写入每次更新加上校验上电时优先选择版本号最新的有效表。这套逻辑在NAND控制芯片的固件里早就是标配但在自研裸NAND项目中经常被忽略结果就是掉电后造型数据丢失或者文件系统彻底损坏。2.2 SD NAND体系的黑盒FTL内部固件帮你接管了脏活SD NAND内部有专门的控制器芯片一般是一个低频MCU配合硬件加速单元固件由存储原厂或模块商提供。开发者通过SD协议访问时所有命令都会进入控制器由固件完成LBA到PBA的翻译、坏块替换、磨损均衡和垃圾回收。这些逻辑对主控完全透明你读出来的永远是逻辑扇区上的数据物理层的杂活不会暴露出来。但是透明不意味着没有代价。SD NAND的随机写性能受内部映射粒度和GC策略影响很大不同的控制器固件表现差异明显。我在项目中测试过两个不同品牌的SD NAND同样4KB随机写一个稳定能做到几千次每秒另一个频繁GC掉到只有几百次每秒。因为外部无法调节内部GC阈值只能从应用层做写模式优化。这个特性在选型时必须通过实测写入性能曲线来判断不要只看资料上的理论IOPS。另一个需要注意的点是SD NAND虽然替主控承担了ECC和坏块管理但并不意味着数据绝对安全。内部掉电保护能力因型号而异有的控制器固件做得好设计上考虑了脏页缓存和映射表原子更新掉电后不至于损坏已有数据有的方案偷工减料掉电时正在写的扇区以及映射表更新点都可能在最糟糕的时机被切断导致数据丢失甚至逻辑分区异常。所以可靠性要求高的场合还是要从文件系统和应用层做多重兜底。2.3 两种方案架构差异对照表我把关键差异项整理成一张表格方便选型和评审时直接用对比项裸NAND FlashSD NAND贴片式SD卡映射表维护者主控软件开发者自研FTL内部控制器固件映射表存储位置主控RAM加载 NAND系统块持久化控制器内部SRAM/专用NAND区域映射粒度页级/块级/混合开发时可设计厂家固件定死通常专有算法坏块管理需要自研包含坏块扫描与替换控制器自动处理ECC校验主控完成需要足够的计算能力内部硬件ECC / 可调强度磨损均衡动态/静态均衡需自己实现固件内置垃圾回收需要显式或后台任务触发控制器自行安排外部数据接口并行NAND接口CLE/ALE/WE/RE控制标准SDIO接口支持1-bit/4-bit对主控CPU资源占用较高需要处理时序和纠错较低SD协议栈相对简单产品调试难度高需要逻辑分析仪/编程器辅助低SD卡相关工具就能测常见应用场景高性价比大容量存储、成本敏感产品中小容量、开发周期紧、可靠性优先方案这张表不是绝对的因为SD NAND厂商之间的固件水平差异很大但方向上不会有错。理解这个差异才能针对性做优化。2.4 从实测视角看差异对业务场景的影响架构差异最终要落到业务指标上。我测试过一套对比环境同一颗MCU一边接裸NAND一边接SD NAND跑同一个评测程序结果是顺序读性能上裸NAND通过并行DMA可以做到更高带宽但这是以占用大量主控引脚和CPU为代价的。SD NAND走SDIO 4-bit传输正常时钟下顺序读也能达到几十MB/s对绝大多数工业应用绰绰有余。随机写场景才是分水岭。裸NAND如果映射粒度设计得好读改写逻辑处理得当可以做到比较稳定的随机写延迟但如果映射表设计粗糙一个4KB逻辑写可能触发整个块擦写性能直接暴跌。SD NAND的随机写则完全取决于内部固件算法小IO频繁写时垃圾回收会放大写放大倍数导致块快速磨损。所以无论是哪种方案应用层都应该尽量设计成顺序写或者批量写避免零散小IO。3. 文件系统协同扇区分配表之上的又一战场如果只停留在FTL层很多实际难题还是无法解释。文件系统层与底层映射策略的交互是故障高发区域。我在调试FATFS时发现不少所谓“NAND不行”的问题其实是文件系统与底层配合出了问题。3.1 FATFS的工作机制与底层映射的关系FATFS本身是独立于存储介质类型的它通过一个diskio层接口访问底层块设备。对FATFS来说它只需要读扇区、写扇区、获取容量信息这三个基本能力。裸NAND方案需要自己实现diskio层的读扇区/写扇区函数内部完成LBA到物理地址的翻译SD NAND方案直接调用SDIO驱动的读写扇区函数即可。但这种透明性是双刃剑。FATFS在更新目录区、FAT表时会产生相当多的随机小IO尤其是FAT表通常位于分区开始的几个扇区每次新建文件或者修改文件大小都需要更新FAT表。在裸NAND上这些随机写会经过FTL转成潜在的读改写和块擦写在SD NAND上则完全交给内部控制器消化。如果你的应用存在高频日志写入、频繁创建小文件底层映射策略就会成为性能瓶颈。3.2 文件系统簇大小与底层块大小的对齐优化很多工程师对FATFS的“簇大小”不敏感直接使用默认值。但簇大小和底层块大小的对齐对性能和寿命影响非常大。FATFS的簇是逻辑块设备上的分配单位簇太小时文件系统内部碎片化严重一个4KB日志文件可能占用十几个簇每个簇更新都要改FAT表随机写压力成倍增加。以128KB物理块的NAND为例如果把FATFS的簇大小设置为16扇区8KB则每个文件簇可以对应64个物理页块的子区域读写模式相对规整。如果设置为512B单扇区则一个4KB日志就要跨8个簇底层映射压力陡增。我的建议是簇大小尽量不小于底层物理块的1/8同时避免簇跨越物理块边界产生跨块IO。虽然SD NAND内部屏蔽了物理块信息但底层仍然存在类似的GC开销这个原则同样适用。3.3 掉电一致性和写缓存策略的协同设计掉电问题是嵌入式存储永恒的话题。纸面上SD NAND似乎比裸NAND可靠但实际谁都不敢只靠底层必须在文件系统层做设计。FATFS本身没有日志功能掉电时正在写FAT表或者目录项就可能损坏文件系统结构。我在实际项目里总结了两个相对可靠的策略。一是FATFS配置为每个写入周期后调用f_sync或者直接f_close强制将脏数据落盘虽然性能有一定损耗但换来的是掉电安全性。二是做双分区冗余比如A/B分区交替写入关键配置每次写入前写一个“完成魔法字”上电启动时如果魔法字不对就回退到另一个分区。这个方法对两种方案都适用兼容SD NAND的内部掉电保护不完全的情况。4. 优化策略实战手册从裸NAND到Verilog控制器的全流程经验理论知识聊透了接下来是实操环节。这一部分会从工程代码、硬件设计、固件选型到FPGA Verilog实现给出我验证过的优化策略。4.1 裸NAND方案映射表设计、磨损均衡和ECC选型的三步走裸NAND方案的优化核心是映射表、磨损均衡、ECC三方协调。映射表建议优先选择混合映射用一个块级映射若干日志页的方式均衡随机写性能和RAM占用如果产品对随机写性能要求很高并且主控RAM足够可以直接把页级映射表全部驻留内存。我实际算过16MB容量的SLC NAND页级映射表只需32KBCortex-M4系列跑起来毫无压力但要留意这部分RAM不能断电丢失需要重新从NAND加载。磨损均衡方面动态磨损均衡是最基本的即分配物理块时优先选择擦写次数少的块保证新写的块磨损均匀。但静态磨损均衡往往被忽略——长期不更新的静态数据块占据着低擦写次数的“好块”导致其他块快速老化。好的方案会周期性地把静态数据搬运到高擦写次数的块释放低磨损块给频繁写入的数据使用。ECC能力要够用SLC建议至少4bit/512B这个校验强度用BCH算法就能实现不要为了省CPU硬用1bit校验。4.2 SD NAND方案从SDIO驱动到FATFS的全栈优化SD NAND的优化主要集中在外围。首先是接口层面务必使用4-bit SDIO和DMA传输单纯用SPI协议模拟SD卡会慢上一个数量级达不到芯片实际性能。其次在应用层文件读写尽量用多块读写命令比如连续写多个扇区时不要分割成多次单扇区CMD24直接用CMD25一次完成内部控制器能大幅减少GC次数。FATFS层可以做几件小事一是簇大小根据应用数据特征来设不盲目跟默认值二是开启FAST_SEEK等选项减少文件系统的查找开销三是在写日志数据时维护一个固定大小的环形缓冲区攒到一定量再批量落盘。测试下来这种批量写入方式可以让SD NAND的随机写场景性能提升两三倍对延长内部块寿命也有帮助。我建议在产品原型阶段就做一次“写寿命压力测试”连续写入一个月观察性能衰减不要等量产后再发现。4.3 FPGA侧Verilog实现NAND Flash读写的关键细节很多FPGA项目因为主控没有现成NAND控制器接口需要自己用Verilog实现读写逻辑。这个工程的重点在于状态机设计、时序匹配和对NAND命令集的精通。简单来说NAND读写的基础流程分四步发命令、发地址、传数据、等待忙状态。给出一个典型的页编程状态机框架// 简化版NAND Flash页编程状态机框架 module nand_page_program ( input clk, input rst_n, input start, input [31:0] row_addr, // 块地址页地址 input [15:0] col_addr, // 页内偏移 input [4095:0] page_data, // 4KB数据总线 output reg done, output reg nand_cle, output reg nand_ale, output reg nand_we_n, output reg nand_wr_data, input nand_rb_n ); localparam IDLE 4d0; localparam CMD_80 4d1; localparam ADDR_CYC 4d2; localparam DATA_IN 4d3; localparam CMD_10 4d4; localparam WAIT_RB 4d5; localparam SET_DONE 4d6; reg [3:0] state; reg [7:0] addr_cnt; reg [31:0] data_cnt; always (posedge clk or negedge rst_n) begin if (!rst_n) begin state IDLE; done 1b0; end else begin case (state) IDLE: if (start) begin done 1b0; nand_cle 1b1; nand_wr_data 1b0; // 20h/80h等命令由外部mux控制 state CMD_80; end CMD_80: begin nand_cle 1b0; nand_ale 1b1; // 进入地址周期 addr_cnt 8d0; state ADDR_CYC; end ADDR_CYC: begin if (addr_cnt 8d4) begin nand_ale 1b0; data_cnt 32d0; state DATA_IN; end else begin // 按列地址(2周期)行地址(3周期)顺序送出行地址 addr_cnt addr_cnt 8d1; end end DATA_IN: begin // 按字节/字写入page_data遍历完一个page后 if (data_cnt 32d4095) begin nand_cle 1b1; state CMD_10; end else begin data_cnt data_cnt 32d1; end end CMD_10: begin nand_cle 1b0; state WAIT_RB; end WAIT_RB: if (nand_rb_n) begin // 忙信号释放编程完成 done 1b1; state SET_DONE; end SET_DONE: if (start 1b0) state IDLE; default: state IDLE; endcase end end endmodule这里有个很容易踩的坑NAND的忙检测信号R/B是开漏结构FPGA内部必须接上拉或者配置成弱上拉输入否则上电后永远检测不到空闲状态。另外不同厂商NAND的地址周期数和命令码可能不同代码里最好用一个配置寄存器集中管理不要硬编码。比如某国产芯片用0x71读状态另一家厂商用0x70写代码的时候不统一会导致量产时换料就得烧新固件非常被动。如果你用现成的Verilog NAND控制器IP也要检查它是否处理了“编程失败”状态。NAND页编程后需要立刻读状态寄存器确认是否成功有些廉价IP直接跳过这一步导致坏块写入时主控浑然不觉数据静默丢失。我建议在状态机里增加STATUS_CHECK步骤读取状态寄存器只有0xC0才认为写入成功。4.4 量产烧录与编程器BeeProg2配合的实用技巧量产阶段你大概率会用编程器把固件、配置、文件系统镜像烧到Flash里。裸NAND和SD NAND在编程器操作上完全是两个世界。对裸NAND烧录前必须进行坏块扫描如果编程器一直按线性地址写下去遇到出厂坏块直接写入失败。我在使用BeeProg2这类通用编程器时通常会开启“自动跳过坏块”模式它会扫描OOB区的坏块标记并跳过坏块继续后续地址写入。但这样烧出来的镜像在主控自研FTL时会出现坏块重映射不一致的问题。所以主控端的FTL设计要预留“量产态处理逻辑”如果发现某个逻辑页对应的物理块是坏块就从生产起始的备份区寻找替换块并在映射表中记录这种替换关系。SD NAND在编程器上的烧录相对简单BeeProg2可以把SD NAND当成一块小容量的SD卡或者eMMC来读直接把格式化好的镜像文件和分区表一并写入不需要关心物理地址。这里有个细节写入完整镜像后最好回读校验一遍并且验证启动分区和用户分区的边界是否正确。有些产品需要在出厂时写入唯一序列号或MAC地址要注意不要在烧录的时候就写死让整机测试环节通过SD NAND的绕写命令单独更新省去返工流程。5. 常见问题与排查技巧实录做了多年存储方案顺手把高频问题整理成一份速查表每一条都是我实际遇到过并且排查过根因的。现象可能原因排查方法处理建议FATFS挂载失败返回FR_NO_FILESYSTEM分区DBR区域损坏底层FTL映射错乱用十六进制工具读0扇区检查55AA结束标志批量生产前做至少100次掉电循环验证重新格式化并优化掉电保护写入一段时间后读出来的数据不对偶发错误ECC纠错能力不足翻新片使用次品NAND通过裸读NAND OOB区查看ECC状态位提高ECC等级选择正规货源大厂颗粒SD NAND写速度突然暴跌内部GC被触发碎片化严重持续监控写延迟观察是否有周期性卡顿应用层改为批量写定期TRIM或垃圾清理掉电后SD NAND部分扇区数据丢失内部缓存未落盘映射表未原子更新做掉电瞬时切换测试看丢数据是否集中在最后N个扇区应用层写“完成标志”来区分完整数据使用双存储区量产时发现大量主板上的SD NAND无法识别贴片焊接虚焊SDIO信号线阻抗不匹配检查SD通信前几个命令是否有正确响应用示波器测CLK和CMD信号质量优化PCB走线匹配焊接工艺增加AOI检查FPGA读写NAND时经常卡在R/B检测R/B引脚没有加上拉电阻用示波器量R/B电平确认是常低还是高阻内部启用上拉或外部放10k上拉电阻编程器烧录裸NAND后主控读不到数据坏块未跳过或者映射表没有初始化用编程器回读burn信息检查坏块标记烧录时开启坏块管理关闭线性地址写排查问题的通用思路是先分两边数据通路问题和控制通路问题。数据通路问题往往表现为文件内容读出来不对、校验失败控制通路问题则表现为设备无法识别、命令无响应。先确定是SD NAND环境下出问题还是裸NAND环境下出问题再配合逻辑分析仪抓SDIO信号或者示波器量NAND接口时序基本都能定位到具体环节。最后分享一个我踩过不止一次的坑。有次用SD NAND做数据存储样机阶段怎么测都稳定一到低温-20度环境测试批量出现文件系统损坏。排查了很久最终发现是控制器在低温下GC行为发生了变化加上FATFS的脏页没有即时同步两者叠加把FAT表写坏了。后来我所有的存储方案都强制加上一个“脏页周期同步”策略在文件系统层做兜底。所以你说NAND Flash和SD NAND哪个更好我的答案是把底层机制摸透、在应用层做足防护哪个都好用。裸NAND方案可以做到极致性价比前提是你团队有足够的FTL研发能力SD NAND方案省心可靠但别忘了从文件系统层面补上掉电保护的措施。这两个方向我都会继续用关键还是看产品约束到底是什么。