FLASH产品提速方法
从接口、阵列、命令、主机到系统的五层提速路径 —— 一份以「先定位瓶颈层,再选路径,最后核算代价」为主线的 Flash 性能设计参考 | 编制日期 2026-10-14 | 版本号 Rev 1.0
「Flash 不够快」是存储类产品里最常听到的抱怨。落到工程上,它其实是五个不同层的问题叠在一起:接口跑得够不够快、阵列能不能并行、命令开销有多大、主机有没有把接口喂饱、系统有没有把并行度用起来。任何一层没到位,其它层再快也看不出效果——时钟加到器件的上限,却每次只读一小块数据,总线占用率可能还不到一成。
这份文档只讲一件事:把「提速」拆成五层可独立优化、可分别测量的路径,每一层给出机制、做法、验证方法与代价,并给出「先定位、再选路、后核算」的推进顺序。对象覆盖 SPI NOR、并行 NAND、SD NAND、eMMC 等常见 Flash 产品形态;文中所有速率、时间、容量均为量级性参考,实际数值一律以器件 datasheet 与对应规范为准。
结论: Flash 的速度不是单一指标,而是五层天花板的叠加——接口速率、阵列并行度、命令固定开销、主机突发行为、系统并行度。 提速的本质动作只有四个:缩短某一段、隐藏某一段、摊薄固定开销、换更宽的路。 正确的推进顺序是先测量定位瓶颈层,再选该层的路径,最后核算代价;跳层优化是这一领域最高频的浪费。 所有速率与时间均为量级性参考,实际数值一律以器件 datasheet 与所处系统条件为准。
读者路径
- 想知道「为什么慢」:第 1 章(三种操作的速度性格 + 读请求延迟链分解)。
- 想知道「从哪下手」:第 2 章(五层路径 + 三步定位)+ 第 11 章(三层测量方法)。
- 接口还能怎么挖:第 3 章(时钟、数据宽度、虚拟周期、连续读)。
- 器件侧并行:第 4 章(多平面、页读缓存、多片交叉、写缓冲)。
- 命令与控制器:第 5 章(队列、合并、流水、读重试)。
- 主机与软件:第 6、7 章(突发、对齐、DMA、中断聚合、调度)。
- 系统级并联与边界:第 8 章(多通道)+ 第 9 章(信号完整性)+ 第 10 章(代价核算)。
- 系统已经出问题:第 12 章(速查表)+ 附录 B(核查清单)。
- 只想要结论:第 12.1 节(按场景的组合建议)+ 封面的结论。
1. 问题本质:Flash 的速度由什么决定
这一章不涉及任何具体配置,只把「快」与「慢」还原成机制:三种操作各自的速度性格、一个读请求在时间上被切成哪几段、吞吐与延迟为什么必须分开谈。后面所有章节的判断,都是这一章四条原理在具体层上的投影。
1.1 读、写、擦除:三种操作的速度性格
Flash 的三种基本操作速度性格完全不同,提速方法也因此完全不同。读内部其实分两段:先把整页数据从阵列读进页缓存,再把缓存数据送上接口;前一段由阵列物理决定,后一段由接口速率决定。写(编程)按页进行,页内是一个慢过程,控制器的任务是把它藏起来。擦除按块进行,是写入的前置条件——先擦后写,所以写慢常常不是写本身慢,而是擦除与写放大在后面拖累。
| 操作 | 最小粒度 | 量级特征(典型) | 能否并行 | 主要隐藏手段 |
|---|---|---|---|---|
| 读 | 页(page) | 阵列读以数十微秒量级计;数据输出速率取决于接口模式 | 不同平面、不同片之间可并行;页读入与页输出可流水 | 页读缓存、多平面并行、突发读 |
| 写(编程) | 页(page) | 页编程以数百微秒至毫秒量级计 | 不同平面、不同片之间可并行;同一平面内必须排队 | 写缓冲、多平面、掉电安全与速度的平衡 |
| 擦除 | 块(block) | 块擦除以毫秒量级计,且是写入的前置条件 | 块间可并行,但受磨损均衡与后台调度约束 | 后台擦除、预留空间、坏块管理 |
「Flash 写慢是因为接口慢」是最常见的误判。写慢的根源大多在阵列(页编程时间、擦除前置)与控制器(写放大、磨损均衡),接口只决定数据搬进搬出的那一小段。把接口时钟提上去,写入吞吐可能一动不动——因为时间根本没花在总线上。谈写入提速之前,先分清时间花在哪一段。
1.2 访问延迟链:一个读请求经历了什么
把一个读请求从发起到收尾拆成五段,就能看清每一段的决定因素与可优化动作。前两段与后两段属于接口与软件,中间一段属于阵列——这个「两段接口、一段阵列、两段固定与软件开销」的结构,是第 3~7 章所有内容的骨架。
图中比例为量级示意:阵列读通常是大头,接口两段随模式变化最大,主机软件路径方差最大。每一段对应的优化手段在后续章节逐一展开。
| 延迟链分段 | 主要决定因素 | 量级特征 | 可用的优化动作 |
|---|---|---|---|
| 命令与地址传输 | 接口时钟、数据宽度、命令格式 | 固定几个时钟周期,与速率强相关 | 提高时钟、加宽数据线、精简命令格式 |
| 片选与内部译码 | 器件内部结构 | 固定开销,与时钟速率关系不大 | 基本不可优化,靠并行掩盖 |
| 阵列读入页缓存 | 单元物理特性与阵列规模 | 数十微秒量级,与接口速率无关 | 页读缓存、多平面并行、多片交叉 |
| 缓存数据输出 | 接口宽度、突发长度、时钟 | 带宽决定,与数据量成正比 | 连续读、加大突发、双沿采样 |
| 主机接收与处理 | 驱动、缓存、中断、调度 | 软件路径,方差最大的一段 | DMA、中断聚合、地址对齐、预取 |
缩短某一段(提高时钟、精简命令、DMA 取代轮询)、隐藏某一段(页读缓存、多平面并行、多片交叉)、摊薄固定开销(连续读、加大突发、命令合并)、换更宽的路(加宽接口、多通道条带化)。
后面每一章,都是这四件事在某一层上的具体形态。动手之前先问:我做的是缩短、隐藏、摊薄还是加宽?对应哪一段?答不上来,就还没有开始优化。
1.3 吞吐与延迟:两个被混为一谈的指标
延迟是单次操作从发起到完成的时间;吞吐是单位时间搬动的数据量。顺序大块读由吞吐决定,小随机读由延迟加命令固定开销决定。把两个指标混在一起谈,是很多「明明提速了却感觉不到」的根源。
| 负载形状 | 主导指标 | 常见瓶颈层 | 第一响应动作 |
|---|---|---|---|
| 顺序大块读(数百 KB 以上) | 吞吐(MB/s) | 接口层、系统层 | 先看总线利用率,再谈加宽 |
| 小随机读(数百字节级) | 延迟 + 命令开销 | 命令层、阵列层 | 先数每条命令平均搬了多少数据 |
| 混合读写 | 两者叠加,写还受擦除制约 | 阵列层、命令层 | 读写路径分开测量,分别定位 |
| 深队列并发 | 队列深度与并行度 | 系统层、阵列层 | 先确认并行度真的被用起来了 |
一次搬多少数据、一共发多少条命令、队列有多深。这三个数字不知道,任何「提速」都是盲改——第 11 章会给出一套把这三个数字测出来的方法。
1.4 把「慢」拆成可优化的分量
把延迟链的各分量按「可缩短 / 可隐藏 / 可摊薄 / 需加宽」归类,就得到一张行动清单。任何一层动手之前,先确认下游真的被喂饱了——接口带宽翻倍而主机每次只提交一小块请求,等于白翻。
| 分量 | 类别 | 所属层 | 手段出处 |
|---|---|---|---|
| 接口传输时间 | 可缩短、可加宽 | 接口层 | 第 3 章 |
| 阵列读取时间 | 可隐藏 | 阵列层 | 第 4 章 |
| 命令固定开销 | 可摊薄、可合并 | 命令与控制层 | 第 5 章 |
| 主机软件路径 | 可缩短 | 主机层 | 第 6、7 章 |
| 并行度不足 | 可加宽 | 系统层 | 第 8 章 |
第 2 章把五层路径展开成一张全景图与一棵定位决策树;第 3 章起逐层给出机制、做法与验证。
2. 提速路径全景:五层路径与定位方法
这一章建立全文的地图:五层路径各自解决什么问题、瓶颈怎么定位、各路径的收益与代价、组合时按什么顺序叠。后面十章都是这张地图上某一个格子的放大。
2.1 五层路径总览
五层路径是串联关系:数据要从阵列流到应用,五层每一层都可能成为最窄的那段管子。任何一层没到位,其它层再快也看不出效果。
| 层 | 核心问题 | 典型手段 | 见效条件 |
|---|---|---|---|
| 接口层 | 总线速率与数据宽度够不够 | 时钟、双沿采样、多 I/O、连续读 | 主机与器件都支持对应模式,SI 有余量 |
| 阵列层 | 阵列并行度用没用好 | 多平面、页读缓存、多片交叉 | 器件支持且控制器会交织调度 |
| 命令与控制层 | 每条命令搬多少数据 | 队列、合并、流水、读重试 | 负载有足够队列深度 |
| 主机层 | 接口有没有被喂饱 | 突发长度、地址对齐、DMA、中断聚合 | 驱动与硬件配合,负载粒度足够 |
| 系统层 | 并行度有没有兑现 | 多通道条带化、负载均衡 | 有多器件、多通道物理在位 |
2.2 瓶颈定位:三步法
提速的第一步永远不是动手,而是定位。三步走的顺序不能颠倒:先分负载形状,再用三层测量取证,最后对照路径总览表锁定层。跳过第一步直接按经验改,是盲目优化;跳过第二步凭感觉判断,是猜。
| 测量发现 | 指向的层 | 优先动作 |
|---|---|---|
| 总线时钟远低于模式上限 | 接口层 | 核对模式协商与配置顺序(第 3 章) |
| 时钟跑满但每次突发很短 | 命令层 / 主机层 | 加大突发、合并命令、连续读(第 5、6 章) |
| 命令间隔里器件长时间忙 | 阵列层 | 多平面、页读缓存、多片交叉(第 4 章) |
| 总线空闲但吞吐上不去 | 系统层 / 主机层 | 并行度、DMA、调度(第 7、8 章) |
| 速率一提误码就抬头 | 信号完整性 | 先修 SI 再谈提速(第 9 章) |
2.3 各层路径的收益与代价
每一条路径都有代价,没有免费的速度。把收益与代价摆在一起,是为了避免「只看收益不看代价」的选择——后者在量产阶段才会结账。
| 路径 | 主要收益 | 主要代价 | 前置条件 |
|---|---|---|---|
| 提高接口时钟 | 带宽线性提升 | SI 裕量收缩、误码风险、功耗上升 | 眼图有余量、走线与端接达标 |
| 加宽数据线(1 到 2/4/8) | 带宽倍增 | 引脚增多,成本与占位上升 | 双方支持,封装有对应引脚 |
| 双沿 DDR 采样 | 同一时钟下带宽翻倍 | 建立保持窗口减半,SI 要求更严 | 拓扑、等长、端接重新设计 |
| 多平面 / 多片并行 | 隐藏阵列时间 | 地址映射变复杂,一致性要重新验证 | 控制器支持交织调度 |
| 命令队列加深 | 摊薄命令固定开销 | 功耗与复杂度上升,乱序完成要处理 | 负载有足够队列深度 |
| 多通道条带化 | 并行度倍增 | 控制器与 PCB 更复杂,成本上升 | 多器件物理在位 |
2.4 组合策略与叠加顺序
收益不是线性叠加:接口带宽翻倍而阵列才是瓶颈,吞吐可能只动几个百分点。两条铁律:先测量后优化(不知道瓶颈层,一切优化都是猜)、先修下游再修上游(主机没把接口喂饱时,先改主机侧,往往零成本)。
最常见的两种浪费:把接口时钟加到器件的上限,却发现每次只读一小块、阵列才是瓶颈;把命令队列加到很深,主机却始终只有一个请求在飞。顺序反了,越努力离目标越远。
| 顺序 | 先做什么 | 为什么 |
|---|---|---|
| 1 | 定位(第 11 章三层测量) | 不知道瓶颈层,一切优化都是猜 |
| 2 | 主机侧零成本项(对齐、突发、DMA) | 不改硬件就能拿满的部分,先拿走 |
| 3 | 接口模式(时钟、宽度、连续读) | 带宽天花板直接抬高 |
| 4 | 阵列并行(多平面、页读缓存) | 隐藏阵列时间,喂饱更宽的接口 |
| 5 | 系统并行(多通道、负载均衡) | 最后才动用结构与成本 |
第 3 章起逐层展开:先接口(第 3 章),再阵列(第 4 章),然后命令与控制(第 5 章)、主机(第 6 章)与软件栈(第 7 章),最后是系统级并行(第 8 章)与高速的边界——信号完整性(第 9 章)和代价(第 10 章)。
3. 接口层提速:时钟、数据宽度与连续读
接口是数据离开器件的唯一通路。带宽的直接公式只有一句话:带宽 = 时钟频率 × 每时钟位数 × 数据线数。这一章把这三个旋钮逐一拧开,再补上把它们用好的两个前提——虚拟周期与连续读。所有速率均为量级性参考,实际数值以器件 datasheet 与所处系统条件为准。
3.1 单沿到双沿:SDR、DDR 与 QDR
单沿采样(SDR)只在时钟的一个边沿(通常是上升沿)采样一位;双沿采样(DDR)在上下两个边沿都采样,同一时钟频率下数据率翻倍。代价是采样窗口被砍半——原来一个完整时钟周期的建立/保持时间,现在压缩到半个周期,对信号完整性的要求显著更严。
| 采样方式 | 每时钟位数 | 采样窗口 | 典型适用 | 提速边界 |
|---|---|---|---|---|
| SDR(单沿) | 1 | 一个完整时钟周期 | SPI NOR、低速并行接口 | 受时钟频率上限约束 |
| DDR(双沿) | 2 | 半个时钟周期 | 高速并行 NAND、eMMC、UFS | 受信号完整性约束 |
| QDR(四沿) | 读/写各 2(分时) | 四边沿分时 | 同步 SRAM、部分高速器件 | 以器件规范为准,Flash 少见 |
要不要上 DDR,不是「能不能跑双倍速」的问题,而是「被砍半的采样窗口还有没有余量」的问题。眼图余量充足(第 9 章)才谈得上 DDR;否则先修走线与端接。
3.2 数据宽度扩展:1 / 2 / 4 / 8 线
把 1 条数据线扩到 2、4、8 条,是带宽最直接的乘法。SPI 系从 1 线起步,逐步演进到双线(Dual)、四线(Quad)、八线(Octal);并行 NAND 与 NAND 类接口本就是多位宽。模式命名采用 xSPI 的三段记法:命令-地址-数据各标注线数与沿数,例如 1S-1S-4S(命令地址单线单沿、数据四线单沿)、8D-8D-8D(全八线双沿)。
| 线数 | xSPI 典型记法 | 相对带宽 | 引脚代价 | 兼容注意 |
|---|---|---|---|---|
| 1 线 | 1S-1S-1S | 1× | 最少(CS/CLK/DI/DO) | 上电默认,所有器件都能识别 |
| 2 线 | 1S-1S-2S | 约 2× | 多 1 条 IO | 需器件与主机都支持 Dual |
| 4 线 | 1S-1S-4S | 约 4× | 多 2 条 IO | 最常用加速档,注意封装引脚 |
| 8 线 | 8D-8D-8D | 约 8×(含双沿) | 多 4 条 IO + 双沿 | 封装面积与 SI 要求显著上升 |
「引脚不够就别想提速」是误区,但反过来「引脚够就一定能八线」也是误区。八线模式需要封装有对应引脚、PCB 有对应走线、SI 能撑住双沿三者同时成立;只满足其一,八线只是 datasheet 上的一行字。
3.3 虚拟周期与突发长度的权衡
虚拟周期(dummy cycle)是命令发出后、数据开始前的若干个「空拍」,给器件内部电路建立时间——尤其是高速模式下,地址译码与阵列唤醒需要时间。虚拟周期越多,内部电路越从容,但每条命令的固定延迟也越长。连续读场景只在首条命令付出一次虚拟周期,后续数据源源不断,于是「多虚拟周期」的代价被摊薄;而碎片化小读每次都要付,虚拟周期就成了不可忽视的固定开销。
| 场景 | 虚拟周期的角色 | 对吞吐的影响 | 配置取向 |
|---|---|---|---|
| 连续大块读 | 只在首条命令付出 | 几乎不占比例 | 可适度增加以保高速采样 |
| 碎片化小读 | 每条命令都付出 | 占固定开销大头 | 在满足 SI 前提下尽量减少 |
| 高速模式(DDR/八线) | 内部建立时间更紧 | 必须够,否则误码 | 以规范与 datasheet 为准 |
虚拟周期不是「越少越好」——它是高速模式下的保命拍。调它的正确做法是:在保证眼图余量与误码率的前提下取最小值,而不是无脑压到 0。连续读把它的代价摊薄后,可以相对宽松地取保证值。
3.4 连续读与 XIP:摊薄命令开销
连续读(continuous read)让一条命令「打开」后持续输出数据,后续不再重复发命令码、地址与虚拟周期;XIP(eXecute-In-Place,就地执行)是它最典型的应用——代码从 Flash 直接取指,顺序流水读出。突发长度(一次命令搬多少数据)与主机缓存行对齐越好,固定开销占比越低。
连续读不是万能加速。它假设访问是顺序的;一旦业务是随机跳读,每次跳读都要先退出连续模式、重发命令,反而更慢。判断该不该用连续读,先看负载的地址局部性。
3.5 接口速率上限与信号完整性边界
接口时钟能提多高,由两个因素共同决定:器件电气能力(datasheet 标称的最大时钟)与信号完整性余量(眼图还有多少空)。当余量被吃掉,再提频不会变快,只会变错——误码抬头,控制器被迫读重试,体验上就是「越提速越慢」。因此接口提速的天花板在第 9 章,而读重试的兜底在第 5.4 节。
| 决定因素 | 来源 | 越过边界的表现 | 应对 |
|---|---|---|---|
| 器件电气能力 | datasheet 最大时钟 | 超出即无法识别命令 | 不超过标称最大值 |
| 信号完整性余量 | 走线、端接、回流(第 9 章) | 误码率抬头、偶发变慢 | 先修 SI,再提频 |
| 双方模式协商 | SFDP / 参数页 | 配置不匹配「听不懂」 | 先查能力再配置(3.2 节) |
第 4 章转去器件内部:接口再快,也替不了阵列的并行度——页读有数十微秒量级的固有延迟。
4. 阵列层提速:多平面、缓存读与多片交叉
接口是门,阵列才是院子。一个页读有数十微秒量级的固有延迟,接口速率再高也改不了这一段。这一章讲怎么把这段「硬延迟」用并行和流水藏起来:多平面、页读缓存、多片交叉、写缓冲,以及——最重要的是——怎么核对你的器件到底支持哪些。
4.1 多平面并行读写
一个 die 内部常分为多个平面(plane),每个平面有独立的页缓存。读、写、擦都可以在不同平面并行发起:当平面 0 还在阵列读时,平面 1 已经可以开始下一次操作。地址编码里通常含「平面位」,控制器负责把并发请求映射到不同平面,这就是并行度 = 平面数的来源。
| 维度 | 说明 | 并行度来源 | 控制器责任 |
|---|---|---|---|
| 平面(plane) | die 内独立页缓存的分区 | 平面数 | 交织映射并发请求 |
| 页缓存 | 每平面一个,承接阵列读结果 | 读/输出可流水 | 调度读阵列与输出重叠 |
| 地址位 | 编码含平面选择位 | 寻址即选平面 | 按并发度分配请求 |
4.2 页读缓存:隐藏阵列读取时间
页读缓存(cache read)让「阵列读入页缓存」与「上一页数据往外输出」流水重叠:当上一页还在接口上往外吐时,下一页已经在阵列里悄悄读好了。于是用户感受到的延迟接近「输出时间」而非「阵列读 + 输出」。但前提是控制器提前知道下一页地址——顺序读天然满足,真随机读则无效。
把页读缓存当成「随机读也变快」是典型误用。它只在下一地址可预知时成立;纯随机负载下,流水重叠段消失,提速归零。此时应回到命令队列与预取(第 5、6 章)。
4.3 多片(die)交叉与交错
一颗封装里可能叠了多个 die,每个 die 有独立的片选与状态机。die 间交叉(interleave)让「die A 忙时操作 die B」成为现实:把并发请求轮流派到不同 die,整体并行度 = 平面数 × 片数。编址上分交错编址(地址相邻轮转落到不同 die/通道,利于顺序大块吞吐与负载均衡)与连续编址(地址相邻落在同一 die,利于单 die 内访问局部性)两种风格,对顺序与随机负载的友好度不同。
| 层级 | 并行单位 | 并行度公式 | 映射责任方 |
|---|---|---|---|
| 平面 | die 内分区 | 平面数 | 控制器交织 |
| die(片) | 封装内多粒 | 片数 | 控制器 / 主机协商 |
| 叠加 | 平面 × 片 | 平面数 × 片数 | 控制器统一调度 |
4.4 写缓冲与编程时间隐藏
页编程(写)慢,典型数百微秒到毫秒量级。写缓冲(写缓存寄存器)先承接主机送来的数据,控制器再在背后慢慢编程,主机不必干等;多平面写入还能并行编程。但缓冲里的数据掉电即失——这与掉电保护(PLP)直接冲突:速度要缓存,安全要落盘,二者必须显式权衡。写缓冲不是「写了就安全」,而是「写了先占着位」。
启用写缓冲拿到的写入提速,前提是接受「缓冲数据掉电即丢」。若产品有掉电一致性要求(金融、车载、工控),必须配 PLP 或显式 flush,否则提速换来的是数据损坏。速度与安全的平衡要写进设计约束,不能默认开启。
| 写法 | 主机感知的写延迟 | 掉电安全性 | 适用 |
|---|---|---|---|
| 无写缓冲 | 等页编程完成(数百 us~ms 量级) | 数据落阵列后安全 | 对延迟不敏感、强一致场景 |
| 有写缓冲 | 写完缓冲即返回(快) | 缓冲内数据掉电即失 | 追求写入吞吐、配 PLP / 显式 flush |
4.5 阵列能力的核对与取证
上面所有阵列级提速,前提都是「器件真的支持」。能力从三处取证:datasheet 电气特性与特性表、ONFI 参数页 / SFDP、控制器文档。动手前先做一张核对清单,避免把「以为支持」当「确实支持」。
| 核查项 | 取证来源 | 通过判据 | 不确定时的动作 |
|---|---|---|---|
| 平面数 | datasheet 特性表 / ONFI 参数页 | 明确标注平面数与交织支持 | 按单平面设计,不假定并行 |
| 页读缓存 | 命令集 / 参数页 | 支持 READ_CACHE 类命令 | 退回非缓存读路径 |
| die 数 | 封装与料号 / 参数页 | 标注多 die 与 interleave | 按单 die 设计 |
| 并发上限 | 控制器文档 + 器件时序 | 可同时进行的操作数 | 以较小值为准 |
| 掉电行为 | datasheet 数据保持说明 | 明确缓冲掉电即失 / 有保护 | 默认按「即失」处理,配 PLP |
阵列层提速的本质是「隐藏硬延迟」:多平面与多片交叉靠并行,页读缓存与写缓冲靠流水。但一切以器件真实能力为准——核对清单不填,后面所有优化都是空中楼阁。第 5 章回到命令本身:怎么让每条命令搬更多数据。