2. QSPI Flash工作原理

讲XIP之前,必须先把QSPI Flash本身搞清楚。说白了,Flash就是一块可以反复擦写的存储芯片,里面密密麻麻排着存储单元。我刚开始做嵌入式那会儿,总觉得Flash和RAM差不多,后来才发现完全不是一回事——Flash读起来快,写起来慢,擦起来更慢,而且还有寿命限制。这块你不摸透,后面做XIP地址映射一定会踩坑。

QSPI Flash,全称是Quad SPI Flash。标准SPI只有4根线:CS、CLK、MOSI、MISO,一次只传1位。QSPI多出4条数据线,一次能传4位。数据吞吐量直接翻4倍,这对XIP非常关键。

2.1 串行Flash基本结构

串行Flash的内部结构,从大往小分:

  • 存储阵列 —— 真正的数据存放区,由浮栅晶体管组成
  • 页缓冲区 —— 页写时的临时缓存,大小通常是256字节或512字节
  • 状态寄存器 —— 记录Flash忙/闲、写保护等状态
  • 命令解码器 —— 解析主机发来的SPI指令
  • 控制逻辑 —— 协调擦除、编程、读取等操作

我有一回调试一个Flash读超时的问题,怎么查都查不出来。后来发现是状态寄存器的忙标志位没清干净,控制逻辑卡死在内部状态机里。从那以后,我每次读写Flash都会先检查状态寄存器,确保Flash真的准备好了再操作。

结构单元 作用 注意
存储阵列 存放用户数据 按扇区组织,可擦除
页缓冲区 页写临时缓存 写满才触发编程
状态寄存器 反映Flash状态 读之前先查忙标志
命令解码器 解析SPI命令 常见的命令:READ、PP、SE

关键理解:Flash的存储阵列不能按字节直接改写。你想写一个新值,必须先擦除整个扇区。这是个硬约束,设计XIP时一定要考虑进去。

2.2 页读 / 页写

页读是QSPI最基础的操作。原理很简单:CPU发一个读命令,带上起始地址,Flash就开始往外吐数据。读不需要擦除,也不需要等待——这也是为什么XIP能直接在上面跑代码。

页写就复杂了。写数据前,目标扇区必须处于擦除状态(全为0xFF)。然后CPU把要写的数据送到页缓冲区,发一个写命令,Flash内部会把缓冲区里的数据编程到存储阵列。这个过程要花点时间,典型的页写时间在0.5ms-3ms之间。

千万注意:页写前一定要确认当前页所在的扇区已经擦除过。我曾经在量产固件里忘记检查这个,结果每次写的数据都是和上一次的残留数据做"与"操作,出来的全是乱码。那个bug查了我整整两天。

页写的流程,我习惯这么记:

  1. 发送写使能命令(WREN)
  2. 发送页写命令(PP)和起始地址
  3. 连续发送要写入的数据(不超过页大小)
  4. 等待内部编程完成(读状态寄存器查忙标志)
  5. 验证写入的数据是否正确

你想想看,如果每次写数据都要先擦再写,那效率得多低?所以很多Flash还支持连续写——只要你写在同一个页里,可以一次性发完。这在大批量数据写入时特别有用。

2.3 扇区擦除

扇区擦除是Flash的"大动作"。擦除的时候,整个扇区(通常4KB、32KB或64KB)全部被置成0xFF。这个过程比较慢,大扇区可能要几百毫秒甚至超过1秒。

常用的擦除命令:

  • 擦除4KB扇区(用20h命令)
  • 擦除32KB块(用52h命令)
  • 擦除64KB块(用D8h命令)
  • 整片擦除(用C7h命令)

我的经验:尽量用4KB扇区擦除,虽然看起来慢,但灵活性大。我见过有人为了图快,总是用64KB块擦除——结果只是改了一个字节,却把整个64KB都擦了一遍。固件跑着跑着就崩了,因为其他代码也被擦了。

擦除的时间一般会标在数据手册里。比如说:

  • 4KB扇区擦除:典型45ms,最大400ms
  • 64KB块擦除:典型150ms,最大1000ms
  • 整片擦除:典型2s,最大20s

这里有个坑:擦除期间的Flash是不能响应其他命令的。如果你在擦除过程中切到XIP模式,CPU读到的全是垃圾数据。所以擦除操作一定要在Flash空闲时做,或者加同步保护。

QSPI Flash内部结构与操作关系 QSPI接口 命令解码器 状态寄存器 控制逻辑 页缓冲区(Page Buffer) 存储阵列(Memory Array) 扇区(4KB/32KB/64KB) 页(256B/512B) 外部QSPI控制器 通过4线并行发送命令/地址/数据

图上其实已经讲得很清楚了。QSPI接口收到命令后,命令解码器解析出要做什么。读的话,数据就直接从存储阵列出去。写的话,数据先到页缓冲区,再一次性编程进阵列。擦除则是控制逻辑直接操作存储阵列的整个扇区。

说到寿命,每一页的擦写次数是有上限的,通常10万次左右。代码区因为很少擦写,基本不用担心。但日志或者配置数据区就不一样了——我建议用磨损均衡算法,或者在设计时就留大一点空间。

嗯,关于页读、页写和扇区擦除,核心就这些。记住了:读不需要擦除,写之前必须先擦,擦除最费时间。这三个操作的差异,直接决定了你在XIP模式下该怎么安排代码和数据的位置。