NOR Flash 块保护与 OTP 安全寄存器
写保护机制、OTP 一次性区域与唯一标识的配置、验证与工程实践 —— 一份以「数据写不坏、升级不锁死」为主线的电路设计参考 | 编制日期 2026-10-14 | 版本号 Rev 1.0
NOR Flash 的电路设计里,有一个环节几乎每个项目都会遇到、又几乎每个项目都凭经验配:写保护。块保护位配几级、写保护引脚接不接 GPIO、OTP 区域写什么、什么时候锁定——这些问题 datasheet 都给了机制说明,但很少给「按什么顺序、怎么验证、错了会怎样」的完整答案。凭经验配的后果,往往在量产或售后才显现:该保护的没保护(数据被误擦),不该锁的锁死(固件无法升级)。
这份文档把写保护当成一个分层体系讲透:块保护位管「哪块能写」、写保护引脚管「硬件总闸」、OTP 安全寄存器管「一次性不可逆数据」。从块结构与寻址基础讲起,逐层讲机制、配置流程、固件管理、失效案例、验证方法与标准依据,最后落成检查单与速查表。文中不写寄存器地址、位域位置与命令码数值——这些随型号而变,一律以器件 datasheet 与 JEDEC 正式规范为准。
结论: NOR Flash 的写保护是分层体系——块保护位管区域、写保护引脚管总闸、OTP 管一次性数据;配置铁律只有一条:先验证、再配置、后锁定。 两类典型事故都源于顺序错误:该保护的没保护(被误擦)、不该锁的锁死(无法升级)。把「上电读状态、规划分组、逐级配置、读回验证、最后锁定」五步走完,两类事故都可消除; 而 OTP 写错不可逆,所以一次性区域的写入验证必须比块保护严格一个量级。所有位号、分组、容量与方向口径,以器件 datasheet 与 JEDEC 正式规范为准。
读者路径
- 想先建立全景:第 1 章(问题本质与威胁模型)+ 第 2 章(块结构与寻址基础)。
- 要配块保护位:第 3 章(机制)+ 第 7 章(设计流程)+ 附录 B(检查单)。
- 要接写保护引脚:第 4 章(硬件保护的电气行为与边界)。
- 要写 OTP 与唯一标识:第 5、6 章 + 附录 A(参数速查)。
- 固件升级与保护冲突:第 8 章(固件侧管理)+ 第 9 章(失效案例与定位)。
- 要做验证与测试:第 10 章;换料兼容性:第 11 章。
- 只想要结论:第 12 章(要点汇总与强度分级)+ 附录 C(术语)。
前言
这份文档回答一个在 NOR Flash 电路设计里反复被问、又反复被做错的问题:写保护到底怎么配、按什么顺序配、配完怎么验证、配错了会怎样。问题被做错,不是因为机制难——块保护位、写保护引脚、OTP 三样东西的机制都不复杂——而是因为 datasheet 只讲「器件支持什么」,不讲「你的产品该怎么排」。本文补的正是后者:把机制翻译成配置顺序、验证方法与检查单。
范围上,本文覆盖 SPI 接口 NOR Flash 的三类保护机制:块保护位(状态寄存器内的非易失配置)、写保护引脚(WP#,硬件总闸)、OTP 安全寄存器(一次性区域与唯一标识)。不覆盖的内容同样要划清:加密引擎、安全启动、认证签名等系统级安全设计超出本文范围;NOR 之上的文件系统磨损均衡属于固件层话题,本文只在接口处提一句。
写作口径上,全文遵守三条约束:不写寄存器地址、位域位置与命令码数值(随型号而变,以 datasheet 为准,标准命令名可以出现);不写商业数据(不涉及任何厂商比较与价格);引图只用文字(按章与图号引用,不做超链接)。文中所有容量、组数、比例均为量级示意,落到设计里的一笔一画,一律以器件 datasheet 与 JEDEC 正式规范为准。
1. 问题本质:NOR 为什么需要写保护
这一章把「写保护」还原成机制:它要防的是什么、保护对象分几层、块保护与 OTP 怎么分工、以及据此建立的威胁模型与设计假设。后面所有章节的判断,都是这一章四条原理在具体配置流程上的投影。
图 1 把三层防护并排摆开。三层的设计角色完全相反,这是全篇最重要的一个判断:块保护位管运行期,必须留后门(未锁定时可改,否则没法升级);OTP 管一辈子,不许有后门(写错即定格,所以验证要严);写保护引脚是硬件总闸,它的价值不在「常用」而在「兜底」——软件全线失灵时,还有一根线能把整片按住。把任何一层的角色派错(比如指望 OTP 做运行期禁区、或指望块保护位锁一辈子),都会以第 9 章案例的形式回来。
1.1 写保护要防住的四类风险
NOR Flash 的擦写靠浮栅电荷实现:编程把电荷注入浮栅、擦除把电荷抽走,每经过一次擦写循环,浮栅的物理状态就变一点。这个「磨损不可逆」的物理背景,决定了所有写保护讨论的出发点——保护的目的是让「不该发生的擦写」根本不发生,而不是靠事后恢复。数据被误擦之后,没有任何机制能把它找回来,备份与冗余只能减少损失,不能消除损失。
按发生频率从高到低,写保护要防住的风险可以归成四类:
| 风险 | 典型场景 | 后果形态 | 主要防护层 |
|---|---|---|---|
| 误擦除与误编程 | 固件 bug 写出错地址、调试代码未清理、OTA 写错分区 | 数据被莫名改写,功能时好时坏 | 块保护位(区域禁区) |
| 异常掉电中断写 | 页编程或扇区擦除执行到一半断电 | 半旧半新数据,上电校验失败 | 软件重试与校验(保护位只防掉电后继续被写) |
| 未授权写入与篡改 | 产线之外被非法改写引导区 / 参数区 | 设备被刷入非预期固件 | 写保护引脚 + OTP 锁定 |
| 调试与售后误操作 | 上位机直连器件,选错扇区、忘关写使能 | 现场数据被清空,无法恢复 | 块保护位分组 + 配置留痕 |
四类风险里,前两类主要靠软件设计与供电设计兜底(重试、校验、掉电检测),后两类才主要由保护机制接管。把保护机制当成万能兜底——「反正配了保护,掉电写坏了也能恢复」——是配置设计里最常见的预期错位。保护机制减少的是「误写进入允许区」的概率,不是「写操作本身出错」的概率,两者作用在不同的面上。
1.2 保护对象的三个层级
保护对象分三个层级,对应三种完全不同的机制载体。区分层级是配置设计的第一步:层级选错,后面全错——拿区域机制去管一次性数据,或拿一次性机制去管运行期禁区,都是常见错配。
| 层级 | 保护对象 | 机制载体 | 可逆性 | 典型配置时机 |
|---|---|---|---|---|
| 整片级 | 整颗器件的所有写操作 | 写保护引脚(WP#)拉低 | 电平有效,随时恢复 | 烧录、运输、仓储等非工作期 |
| 区域级 | 若干连续块组成的块组 | 块保护位(位于状态寄存器内) | 未锁定时可改 | 量产烧录后、运行期按需调整 |
| 一次性数据级 | 序列号、密钥、配置字 | OTP 安全寄存器 | 不可逆 | 产线一次写入后即锁定 |
三个层级有一个共同前提:保护状态本身必须非易失——掉电重启后,块保护位还在、OTP 内容还在、引脚电平由硬件重新建立。这正是 NOR Flash 相对于易失存储的优势:保护配置不需要每次上电重写,也就不存在「上电初始化还没跑完、保护先被抹掉」的窗口。这个窗口在固件侧仍然以另一种形式存在(初始化代码本身就在被保护或待保护的区域内),第 8 章会专门处理。
三个层级各对应一种日常物件:块保护位是门锁(管哪个房间能进,钥匙在软件手里)、写保护引脚是总闸(一拉全楼断电,钥匙在硬件手里)、OTP 是封条(贴上去就撕不下来,所以贴之前要看三遍)。三样东西在真实建筑里从来不互相替代,在 NOR 电路里也一样——分层的目的就是任何单点失效都不致命。
1.3 块保护与 OTP 的分工
块保护位与 OTP 长在同一颗器件、同一类存储介质上,但设计角色完全相反:块保护管运行期,要留后门;OTP 管一辈子,不许有后门。这句话是第 5、7 章全部配置流程的总纲。
| 维度 | 块保护位 | OTP 安全寄存器 |
|---|---|---|
| 管什么 | 哪一块能写(区域维度) | 一次性不可逆数据(内容维度) |
| 可逆性 | 未锁定时可反复调整 | 写下去就改不回 |
| 调整时机 | 运行期可动态调整 | 仅产线配置窗口 |
| 主要事故 | 该保护的没保护(被误擦) | 不该写的写了(不可逆报废) |
| 验证严格度 | 配完读回一次即可 | 写入前逐字节预演,写后只读校验 |
| 一句话 | 管运行期的误写 | 管一辈子的数据 |
块保护位配错了,大不了重新配一次——只要没锁定,它就是个普通的状态寄存器位。OTP 写错了,没有「擦除重写」这个动作:一次性区域的每一位只能从空白态变成已写态,反向操作在器件里不存在。所以 OTP 的写入流程必须比块保护严格一个量级:先在小批量样片上验证流程、再在量产工位上逐字节核对内容、最后才执行写入(第 7 章配置流程展开)。
1.4 威胁模型与设计假设
图 2 把四类风险与三层防护的对应关系画成了矩阵。读这张图要带着两个「不」:写保护不防读——读操作永远放行,「防读」从来不是块保护的职责(第 6 章讲只读区域时再收口这个边界);写保护不防断——掉电写一半靠软件重试与校验兜,保护位只负责掉电之后不继续被误写。
矩阵里「中」字最多的两列值得多说一句。写保护引脚看起来是「强」最少的机制,那是因为它的定位是总闸而非日常手段:拉低一次全片禁写,误伤面太大,不适合运行期频繁动作;但正因为它在硬件层、绕过全部软件栈,它在「软件已经失灵」的场景里价值最大。OTP 列的两个「中」则提醒另一件事:一次性区域的内容是可读的,锁定只挡住「改」,挡不住「看」——把明文密钥直接写进 OTP 而不做任何封装保护,是第 6 章要讨论的可信边界问题。
| 威胁 | 设计假设 | 对应保护动作 | 本文章节 |
|---|---|---|---|
| 固件 bug 越界写 | 固件一定有 bug,任何地址都可能被写 | 禁区由器件层拒绝,不靠软件自律 | 第 3 章 |
| 掉电中断 | 写操作可能停在任何一步 | 保护状态非易失,写流程可重入 | 第 8 章 |
| 工具直连误操作 | 产线与售后都会直连器件 | 分组禁写,配置与解锁留痕 | 第 7、8 章 |
| 换料 / 换版本 | 保护配置随型号而变 | 兼容替换前重核保护参数 | 第 11 章 |
威胁模型里最危险的假设是「别人会按设计使用」。产线工程师用上位机直连器件、售后现场刷新固件、测试工位跑循环写——这些场景没有一个是按你的固件逻辑走的。保护设计必须假设:任何能物理接触到器件的人,都可能发出任何命令,包括你没实现过的命令组合。这也是为什么「软件自律」永远不能单独构成保护:它只防守规矩的人。
1.5 本文主线与读法
本文的主线只有一句话:把「数据写不坏、升级不锁死」翻译成可执行的三层配置与五步流程。三层配置对应第 3~6 章(块保护位、写保护引脚、OTP、唯一标识);五步流程对应第 7 章(上电读状态、规划分组、逐级配置、读回验证、最后锁定);第 8 章讲固件侧怎么配合,第 9 章用三个失效案例反推设计规则,第 10 章给验证方法,第 11 章处理标准与换料,第 12 章收口。
「数据写不坏、升级不锁死」这两个目标在配置层面天然冲突:保护配得越死,数据越安全,升级越困难。全部机制讲解,都是在这两个目标之间找那条可验证的线——线的位置由产品形态决定(是否现场升级、升级频率、数据价值),本文给出的是找线的方法,不是一条放之四海皆准的线。第 12 章会把不同产品形态对应的线位分级给出。
读法上,三类读者三条路径。硬件工程师重点走第 2~5 章加第 11 章:块结构决定分组,分组决定保护位,引脚决定硬件闸门,标准决定换料风险。固件工程师重点走第 3、7、8 章:写使能锁存、保护检查、临时解锁与异常中断处理。测试与产线工程师重点走第 7、9、10 章加附录 B:配置工位怎么设计、失效怎么定位、验证怎么覆盖。三类路径在第 12 章汇合。
2. NOR Flash 块结构与寻址基础
讲保护之前,先看保护作用在什么上面。这一章建立三样东西:块的组织(保护粒度的物理来源)、寻址与访问方式(保护判断的地址依据)、状态轮询机制(保护状态与写完成的观测手段)。不建立这三样,后面讲的块保护位、OTP 区域都会变成没有坐标的抽象概念。本章所有容量与组数均为量级示意,以型号 datasheet 为准。
2.1 扇区与块的组织
NOR Flash 的地址空间是一条自顶向下的直线,但这条直线不是均质的。按擦除单位的大小,可以分成两到三段:顶部是小扇区区(引导块与参数块,扇区容量在 4KB 量级)、底部是大扇区区(主块区,扇区容量在 64KB 量级)。前者存在的理由很实际:引导程序体积小但更新频繁,用大扇区放它,改一行代码就要擦掉几十KB;后者反过来,主体数据体积大、批量更新,用大扇区减少擦除次数、提高编程效率。
在「统一架构」的型号上,全部扇区同一大小;在「混合架构」的型号上,大小扇区分段排布。两种架构对保护设计的影响是直接的:混合架构天然给出了「引导 / 参数 / 主体」的分区骨架,保护分组顺着它排即可;统一架构下所有扇区等大,分组只能按数量切,切完还要人工标注每组的用途。
| 架构 | 组织方式 | 典型容量档(量级) | 对保护设计的影响 |
|---|---|---|---|
| 统一架构 | 全部扇区同一大小 | 64KB 量级均分 | 分组简单,保护组等大,用途需人工标注 |
| 混合架构 | 顶部小扇区 + 底部大扇区 | 小 4KB 量级 + 大 64KB 量级 | 引导与参数放小扇区,主体放大扇区,分组顺骨架排 |
| 块组(保护组) | 若干连续扇区划为一组 | 组大小随容量而变 | 保护位按组划界,边界与扇区对齐 |
4KB 与 64KB 是建立概念的量级参考,不是任何具体型号的承诺。同一容量标称(比如同属一个容量档)的不同型号,扇区划分、组数量、组大小都可能不同——分组的唯一依据是 datasheet 的块结构表。把「都是那个容量的片子,分组应该一样」当经验用,是第 11 章兼容替换风险的第一个坑。
2.2 统一寻址与访问方式
SPI NOR 对外是一条总线:片选、时钟、四条数据线(单线模式下一进一出)。地址空间线性连续,读操作可以按字节随机访问——这是 NOR 相对于 NAND 的根本优势,也是「代码原地执行」的前提。但写侧的约束完全不同,一句话概括:读是自由的,写是带刻度的。
- 读:任意地址、任意长度、随时发起。保护机制不拦截读——「防读」从来不是块保护的职责。
- 编程(写):以页为单位(页容量在 256B 量级),且只能把位从 1 改成 0。所以写之前必须确认目标区域允许写,且目标位置已经是擦除态(全 1)。
- 擦除:以扇区(或块、整片)为单位,把整块恢复成全 1。这是破坏性最强的操作,也是保护机制的首要拦截对象。
固件每次发起写之前,先回答三个问题:这个地址落在哪个保护组、该组当前是允许写还是禁止写、写使能锁存是不是开的。这个判断本身是纯软件逻辑,真正的拒绝发生在器件内部——地址命中禁区时,编程或擦除命令会被静默拒绝(状态寄存器的标志位会说明原因,以 datasheet 为准)。软件自律与器件拒绝是两道独立的墙,缺任何一道都不算保护,第 8 章会把这个判断落成驱动代码的结构。
2.3 擦除粒度与编程粒度
把「带刻度的写」展开成一张表。三种操作的粒度差异,是后面所有保护规则的物理来源:保护为什么按组不按字节、为什么不可能保护半个扇区、为什么写使能是「一次一授权」,答案都在这张表里。
| 操作 | 最小单位 | 关键约束 | 与保护粒度的关系 |
|---|---|---|---|
| 读 | 任意字节 | 无(除只读区域的内容限制外) | 保护不限制读;只读区靠内容锁定实现 |
| 编程(写) | 页(256B 量级) | 只能把 1 写成 0;写前须为擦除态 | 写之前目标组必须处于允许写状态 |
| 擦除 | 扇区(或块 / 整片) | 把整块恢复成全 1;破坏性最强 | 擦除的最小单位 = 保护的最小单位 |
| 状态查询 | 状态寄存器读 | 随时可读,不影响操作 | 配置后必须读回验证(见 2.4) |
NOR Flash 的标准命令集里,写使能(Write Enable)是一条独立命令,且每次掉电或一次写操作完成后自动失效。这个「一次一授权」的设计不是历史包袱,而是被页 / 扇区粒度逼出来的:如果写使能是持续状态,一次误触发的编程命令就会毁掉整页;一次一授权把「误命令」和「误数据进入」之间的窗口压到最短。理解这一点,才能理解第 3 章「写使能锁存与保护状态」为什么要放在块保护位之前讲。
2.4 状态轮询机制
块保护配得对不对、写操作有没有完成,都不靠猜——全部通过状态寄存器观测。状态寄存器是 NOR Flash 的仪表盘,按用途分三类信息:操作进度(忙位,标记编程 / 擦除正在进行)、写使能状态(标记当前是否允许一次写操作)、保护配置(块保护位,标记哪些组被保护)。块保护配置掉电后保持(非易失);忙位与写使能状态反映当前操作状态,操作结束或上电后回到基态(写使能为易失锁存)。
| 观测对象 | 观测手段 | 判据 | 典型用途 |
|---|---|---|---|
| 操作进行中 | 读状态寄存器的忙位 | 忙位清零 = 操作完成 | 编程 / 擦除后轮询,勿空等固定延时 |
| 写使能状态 | 读状态寄存器的写使能位 | 置起 = 允许一次写操作 | 确认写使能命令已生效,再发数据 |
| 块保护状态 | 读状态寄存器的块保护位 | 与配置表逐位比对 | 上电校验、配置后读回验证 |
| 引脚状态 | 万用表 / 逻辑分析仪测 WP# | 电平符合设计值 | 硬件保护排查(第 4 章) |
其一,编程 / 擦除后不轮询忙位,按固定延时往下走——延时给少了读到旧数据,给长了拖慢产线,表现为「偶发的数据不一致」。其二,配置保护位后不读回验证——配置命令可能被静默拒绝(比如 WP# 引脚正拉着低电平),保护其实没生效,而表面上天衣无缝。这两个失误的共同点是:都把「应该观测」当成了「可以不观测」。第 10 章的验证清单会把轮询与读回都列为必测项。
2.5 保护粒度与块结构的对应
图 3 左栏是块组织、右栏是保护粒度,两栏叠起来读,得到保护设计的第一条硬约束:保护边界永远与扇区边界对齐。块保护位按「块组」划界,一个组覆盖若干连续扇区,不可能出现「半个扇区被保护」的状态。这不是设计选择,是物理约束:擦除的最小单位是扇区,保护如果比扇区还细,被保护的半个扇区既不能擦也不能写,器件状态机无法处理。
第二条约束关于方向:多数器件的块保护位支持两种划界方向——从顶部往下数组、或从底部往上数,部分型号通过一个附加配置位切换。方向配错的效果是「保护了数据区、放开了引导区」:看起来保护生效了,实际最关键的引导程序裸露在外,而且这种错误在「读回验证」时看不出来——位确实配成你想要的样子了,只是数的方向反了。方向与分组的对应关系必须以型号 datasheet 为准,这是第 11 章兼容替换风险的第一个检查点。
| 保护组 | 典型覆盖区域 | 建议存放内容 | 建议锁定时机 |
|---|---|---|---|
| 顶部小组 | 引导块区(小扇区) | 引导程序、首启动镜像 | 量产烧录并验证通过后锁定 |
| 中部组 | 参数块区(小扇区) | 校准参数、序列号索引 | 产线一次写入后锁定 |
| 下部大组 | 主块区(大扇区) | 应用程序、可升级固件 | 升级窗口期保持可写,窗口外锁定 |
| 剩余组 | 主块区其余扇区 | 数据日志、用户数据 | 按产品策略锁定或留空 |
先定「哪块永远不许动」(引导与参数),再定「哪块需要周期性更新」(应用与数据),最后把剩余空间归组。顺序反了——先保护大区再留升级口——就会得到「升级窗口越留越小」的尴尬格局:每次改版多一块固件,就要重新规划分组,最终以第 9 章案例二的形式爆发。分组表应该在项目早期就画出来,跟着产品整个生命周期走,附录 B 的检查单第一项就是它。
本章小结:NOR Flash 的块组织给出保护粒度的物理上限(扇区),寻址方式给出保护判断的地址依据(地址到组的映射),状态轮询给出保护状态的观测手段(读回与忙位)。带着这三样东西,第 3 章进入块保护机制本身:写使能锁存、分级结构、方向性与验证方法。
3. 块保护机制
这一章讲透块保护位的四个侧面:写使能锁存怎么与它配合、分级结构怎么划分禁区、方向性怎么决定数的方向、以及配完之后怎么读回验证。核心结论:块保护是运行期的主力,但它的全部价值建立在「读回验证」这个动作上——没验证的配置等于没配置。
图 4 上半是写使能的状态机,下半是它与块保护位的正交关系。两个要点。其一,写使能是易失的:一次一授权,用完(或掉电)即失效,所以每次写操作前都要重新发 Write Enable——这条命令的成本是几百纳秒量级,换来的是误触发的窗口最小化。其二,块保护位是非易失的:它住在状态寄存器里,掉电重启后还在,这才是「保护配置不需要每次上电重写」的原因。把这两者的掉电行为混为一谈(比如指望写使能跨掉电保持、或指望块保护位随写操作自动失效),是固件侧最常见的一类误解。
3.1 写使能锁存与保护状态
写使能锁存(Write Enable Latch)是器件内部的一个易失位:为「开」时,紧随其后的一条编程或擦除命令会被执行;为「关」时,写命令被直接忽略。标准命令集用两条命令控制它——Write Enable 打开、Write Disable 关闭——另外编程 / 擦除完成、掉电、复位也都会让它回到关闭态。
这个设计常被新手当成累赘:「反正每次都要开,不如默认开着」。但默认开着意味着任何一条误触发的总线操作都可能改写数据——地址线毛刺、DMA 跑飞、调试器误点,在写使能常开的器件上都是真实事故。一次一授权把所有「非预期命令」挡在门外,只放行「明确打算写」的那一次。块保护位守第二道门:地址对不对;写使能守第一道门:意图真不真。
| 标准命令(名称) | 作用 | 与保护体系的关系 | 备注 |
|---|---|---|---|
| Write Enable | 打开写使能锁存 | 一切写操作的前置条件 | 一次一授权,用完自动失效 |
| Write Disable | 关闭写使能锁存 | 主动收回写权限 | 保护动作的软件兜底手段 |
| Read Status Register | 读状态寄存器 | 观测忙位 / 写使能位 / 块保护位 | 配置前后各读一次,见 3.4 |
| Write Status Register | 写状态寄存器 | 配置块保护位的入口命令 | 可能被 WP# 或锁定位拒绝 |
| Page Program | 页编程 | 写数据本体 | 目标组必须处于允许写状态 |
| Sector / Block Erase | 扇区 / 块擦除 | 破坏性最强的写操作 | 保护机制的首要拦截对象 |
| Chip Erase | 整片擦除 | 清空整片 | 与块保护位的约束关系以型号 datasheet 为准 |
| 读数据 / 读标识类命令 | 读数据、读标识、读安全寄存器 | 不受写保护约束 | 保护不防读(见 3.5) |
| 字段(名称) | 归属 | 读 / 写属性 | 在保护体系中的角色 |
|---|---|---|---|
| 忙位 | 操作进度 | 只读 | 轮询判断编程 / 擦除是否完成 |
| 写使能位 | 写使能锁存 | 只读(由命令改变) | 确认一次一授权的当前状态 |
| 块保护位 | 保护配置 | 非易失、可写(受锁定位约束) | 若干位组合成档位,划定禁区 |
| 方向 / 附加配置位 | 保护配置 | 非易失、可写(以型号为准) | 决定从顶部数还是从底部数 |
| 保护锁定位 | 保护配置 | 一次性(以型号为准) | 置起后保护配置不可再改 |
有些固件图省事:初始化时发一次 Write Enable,之后所有写操作复用。这等于把「一次一授权」退化成「永久授权」,误触发的窗口从一条命令宽变成整个运行期。正确做法是每次写操作前重新发、写完确认失效——成本是每条命令几百纳秒量级,相对于一次页编程(毫秒量级)可以忽略。写流程的标准结构:Write Enable → 发数据命令 → 轮询忙位 → (必要时)确认写使能已自动失效。
3.2 块保护位的分级结构
块保护位是状态寄存器内的若干位,它们的组合值决定一个「档位」,每个档位对应一片受保护区域。档位数量由位数决定:两位就是四档、三位八档(量级示意,具体以型号 datasheet 为准)。受保护区域随档位单调增大——从顶部一个小组,一路扩展到几乎整片。
图 5 左栏把状态寄存器里的保护字段摊开:忙位与写使能位属于「操作维度」,块保护位、方向配置位与锁定位属于「保护配置维度」,两组字段互不干涉——这也是图 4 正交关系的来源。右栏是档位阶梯:注意它画的是量级示意,真实器件的每档覆盖多少组、组内多少扇区,必须查 datasheet 的分组表。
分级结构有两个工程含义。其一,档位是「组合值」不是「位数」:块保护位作为一个整体编码档位,不是每一位独立开关一个区域——所以「我想多保护一组」不是加一,而是把组合值抬到对应档位,沿途的组会被一起保护。配置前必须把「目标档位会连带保护哪些组」列清楚。其二,部分型号的档位只升不降:降档要先解除全部保护(回零)再重配,而回零这个动作本身可能被锁定位拒绝——一旦锁定,档位就永久固定了。
| 档位(量级) | 块保护位组合(示意) | 受保护区域(从顶部数) | 典型用途 |
|---|---|---|---|
| 档位 0 | 组合值全零 | 无 | 产线烧录期,全片可写 |
| 档位 1 | 最低非零组合 | 顶部一个小组 | 保护引导程序 |
| 档位 2 | 次高组合 | 顶部引导 + 参数区 | 保护引导与参数 |
| 档位 3 | 更高组合 | 含主块上半 | 保护系统区 |
| 档位 4 | 最高组合 | 几乎整片 | 终端出厂态 |
从档位 0 直接配到最高档,比逐档抬升省事,但会把你并不想保护的组一起锁进去——量产烧录时这么做,后续要放开的每一组都得重新走一遍配置流程。建议按「当前状态 → 目标档位」的最小步进配置,每步之间读回验证(3.4),把误伤控制在一步之内。具体档位数与每档覆盖范围,以型号 datasheet 为准。
3.3 保护锁定的方向性
本节讨论的是保护边界的方向参数(TB 位),不是保护锁定操作。方向性是块保护里最容易被忽视、后果最直接的一个维度。数组有两个方向:从地址顶部往下数(保护区域从高端往下吞)或从底部往上数(从低端往上吞),部分型号用一个附加配置位切换,部分型号把方向与取反模式编进同一组配置位。档位数值完全相同的一行配置,在两个方向下保护的是完全不同的区域。
| 方向配置(量级) | 数组起点 | 档位递增时保护区域的变化 | 误配症状 |
|---|---|---|---|
| 从顶部数(常见默认) | 地址高端 | 从上往下扩展 | 数据区被锁、引导区裸露 |
| 从底部数 | 地址低端 | 从下往上扩展 | 引导区被锁、数据区裸露,升级必失败 |
| 取反模式(部分型号) | 取决于配置位 | 保护「未被档位列出」的组 | 与实际意图相反,最易误判 |
方向错误的症状有两个特点,都让它格外危险。其一,读回验证看不出来:位确实配成了你想要的值,状态寄存器读回来一模一样——错的不是值,是值的解释方式。其二,它在「该保护的地方」看起来完全正常:引导区被锁定时读写全拒、升级必失败(案例二);数据区被锁定时引导读写正常、只是应用升级失败——两种症状都会先被怀疑成固件或硬件问题,转一圈才回到方向配置。唯一的对策是把「方向 + 档位 + 区域」三者的对应关系写进配置表,逐项核对:
- 读回方向配置位当前值,与配置表记录的方向一致
- 按 datasheet 分组表,把「当前档位 + 当前方向」翻译成实际受保护组清单
- 用引导区首地址做试写验证(小样片上做),预期结果为禁止写
- 用应用区(升级窗口)做试写验证,预期结果为允许写
- 把上述两条的「预期结果」写进测试记录,不靠现场判断
值是对的、读回是对的、保护是「生效」的——只有被保护的区域是错的。这类错误在功能测试里表现为「某些地址能写、某些不能」,而「哪些」取决于方向位,不取决于你的意图。把方向核对从「经验」变成「清单动作」,是这个维度上唯一的预防手段。第 11 章会看到:换一颗不核对方向的料,能让整个量产保护方案反向失效。
3.4 保护状态的读取与验证
读回(Read Back)是配置闭环的最后一环,也是块保护机制从「器件功能」变成「工程可信」的那一步。标准命令集里的 Read Status Register 是唯一入口,但它回答的不只是「配上了吗」——它同时回答「器件现在认为的保护状态是什么」,这中间隔着静默拒绝、方向解释、锁定位约束三层不确定性。
图 6 把读回验证落成了流程:三次读回(上电、写入后、锁定前)+ 影子副本预演 + 失败分支处置。三个设计要点。影子副本:目标配置先在内存里组装成完整的状态寄存器镜像,与当前读回值做差异对比,差异项逐条过「这是我要改的吗」——它能挡住九成以上的手误,包括把档位配高一级、把方向配反。失败分支要先查硬件再查固件:WP# 电平与锁定位状态是写入被拒的两大硬件原因,查完这两样再怀疑命令时序。锁定前最后一次读回是不可逆操作前的最后一道闸,任何不一致都不锁定(第 7 章展开)。
| 读回时机 | 手段 | 比对对象 | 失败处理 |
|---|---|---|---|
| 上电初始化 | Read Status Register | 量产配置表(归档值) | 告警;未锁定时按表重配,已锁定则上报 |
| 每次配置写入后 | Read Status Register | 本次目标值 | 查 WP# 电平与锁定位,排除后重试一次 |
| 锁定前最后一次 | Read Status Register + 只读内容校验 | 目标值(及 OTP 内容,如设计采用) | 任何不一致都放弃锁定,保持可配置 |
| 运行期巡检 | 周期 Read Status Register | 期望值 | 记录并上报,作为防篡改观测点 |
| 检查项 | 方法 | 通过判据 |
|---|---|---|
| 忙位已清零 | 轮询读状态寄存器 | 上一个操作确已完成,再谈配置 |
| 写使能位状态 | 读回 | 配置前应为关闭态(避免误触) |
| 块保护位组合 | 读回逐位比对 | 与目标值完全一致,无静默拒绝 |
| 方向配置位 | 读回 | 与配置表记录的方向一致 |
| 锁定位状态 | 读回 | 未锁(需继续配置时)/ 已锁(出厂时) |
| WP# 电平 | 硬件测量(万用表 / 逻辑分析仪) | 与设计一致:配置期间应为允许写电平 |
把目标配置在内存里组装成完整镜像:当前读回值 → 应用差异 → 得到预期新值。预期新值先与「我真的想要的状态」人肉比对一遍,再拿去写器件、再读回比对。三步下来,手误(档位配高、方向配反、忘改附加位)在第一步就会暴露——因为预期新值和你脑子里想的那张图对不上。配置类操作的成本不在执行,在验证;影子副本把验证提前到执行之前。
3.5 保护机制的边界与例外
把块保护的边界集中列清,是为了防止「配了保护就万事大吉」的预期错位。六条边界,每一条都对应一类真实事故:
| 边界 / 例外 | 说明 | 设计含义 |
|---|---|---|
| 保护不防读 | 读操作永远放行,与保护位无关 | 敏感数据要加密,不能只靠保护位 |
| 保护不防「写一半」 | 掉电中断在允许写的组内照样发生 | 靠软件重试与校验兜底(第 8 章) |
| 整片擦除的约束随型号而变 | 部分型号的整片擦除与块保护位的约束关系特殊(以 datasheet 为准) | 固件禁用整片擦除,或加额外校验 |
| 编程挂起 / 恢复(部分型号) | 挂起期间擦除暂停、读仍可进行 | 实时性场景可用,但流程变复杂,以 datasheet 为准 |
| 只读区域的内容锁定 | 与块保护位独立的机制(第 6 章) | 唯一标识区的内容保护不走块保护位 |
| 锁定位的不可逆性 | 部分型号置起后不可清除 | 锁定前必须完成全部验证(第 7 章) |
整片擦除是「一键清空」的命令,也是产线与售后最爱的命令。部分型号上,它与块保护位的约束关系并非常识中的「全保护即全禁」——具体约束必须以型号 datasheet 为准。稳健做法是固件侧直接禁用整片擦除命令:需要清空时按扇区逐个来,每个扇区各自过保护检查。禁用一条命令的成本几乎为零,换来的是保护模型保持简单可推。
本章小结:块保护位的四个侧面——写使能锁存(第一道门,易失、一次一授权)、分级结构(第二道门,非易失、按档位划区)、方向性(档位的解释方式,最易误配)、读回验证(让配置可信的唯一动作)。下一章讲硬件总闸:写保护引脚。
4. 写保护引脚与硬件保护
这一章讲硬件总闸:写保护引脚(WP#)的电气行为、上电默认状态、与主控 GPIO 的配合、强度边界与失效原因。核心结论:WP# 是唯一不经过软件栈的保护层,它的价值在「软件全线失灵时还能按住整片」;而它最常见的失效原因不是设计错,是「以为接了其实没接」。
4.1 写保护引脚的电气行为
写保护引脚是器件上的一个硬件输入,电平语义以型号 datasheet 为准(多数型号低电平为保护态,但不排除例外,接反的效果比不接更危险)。保护态下,器件拒绝两件事:写状态寄存器(块保护位改不了)与一切数据写操作(编程、擦除被拒)。读操作不受影响——这再次印证第 3 章的边界:写保护体系从头到尾不管「读」。
不同型号在细节上有差异,常见变体包括:WP# 为低时 Write Enable 命令是否也被拒绝、WP# 是否参与锁定位的置起条件、以及是否只有「保护字段」被禁写而其他状态寄存器字段仍可写。这些细节全部以型号 datasheet 为准,本节只讲共通的机制骨架。
图 7 的三种接法覆盖了绝大多数产品形态。直接接地最简单,适合「烧录后不再改」的产品——保护是默认态,不需要固件参与,也不存在固件忘了配的窗口。接主控 GPIO 把总闸交给软件日程表:平时拉低保护,升级窗口期拉高允许写;代价是上电默认电平由复位电路决定,固件初始化前有一段「硬件说了算」的窗口。上拉电阻 + 开漏输出是前两者的合体:上拉保证默认保护(断电、复位、固件跑飞都回到保护态),开漏输出保留临时拉低的能力——安全敏感场景的首选。
| WP# 电平(低保护型) | 状态寄存器写 | 编程 / 擦除 | 读 | 典型用途 |
|---|---|---|---|---|
| 低(保护态) | 禁止(保护字段) | 禁止 | 允许 | 非工作期:运输、仓储、现场运行 |
| 高(允许态) | 允许(仍受锁定位约束) | 允许(仍受块保护位约束) | 允许 | 烧录、升级窗口期 |
| 悬空 | 不可预期 | 不可预期 | 允许 | 大忌:行为随噪声漂移 |
「低电平保护」是多数型号的行为,不是标准承诺。接反的效果是双向失效:该禁写的时候放行(保护形同虚设)、该放行的时候禁写(升级必失败),比不接更危险,因为两种症状都会被先怀疑成别的环节。核对动作只有一步:查型号 datasheet 的引脚描述,把「保护态电平」写进设计文件,再画线。
4.2 上电默认状态与不可变性
WP# 的保护价值很大程度上来自「默认」二字:不依赖任何软件正确性,上电那一刻就是安全的。但这个「默认」不是器件的单向承诺,而是器件内部上下拉与外部电路共同决定的结果——多数型号内部有弱上下拉(以 datasheet 为准),外部设计可以加强它,也可以无意中破坏它。
真正需要设计介入的是主控复位期间的那段窗口:主控 GPIO 在复位期间默认输入高阻,此时 WP# 的电平完全由外部电路决定。如果既没有外部上下拉、主控又没及时驱动,WP# 在这段时间是悬空的——而悬空意味着行为不可预期。这段窗口通常有几十毫秒量级(以主控复位时序为准),足够一次误写发生。
| 项目 | 机制 | 设计含义 |
|---|---|---|
| 上电默认电平 | 器件内部上下拉与外部电路共同决定(以 datasheet 为准) | 不能假设「上电即安全」——要用实测确认 |
| 主控复位期间 | GPIO 默认输入高阻,外部上下拉决定 WP# 电平 | 必须外部上下拉兜底,不能只靠固件 |
| 固件初始化前窗口 | 主控未起、保护未配置、总线可能被其他主控驱动 | 这段时间的安全完全由硬件默认值承担 |
| 不可变性 | 引脚电平是硬件事实,软件改不了它的语义 | 「软件改不了」正是它强度的来源 |
上电后用逻辑分析仪抓 WP# 从复位释放到固件驱动 GPIO 的完整波形,能同时回答两个问题:默认电平是不是保护态(若不是,外部上下拉设计错)、窗口有多长(决定这段风险暴露的时长,也是第 10 章上电保持验证的输入)。这次测量在项目初期做一次就够,成本十分钟,收益是排除一整类「偶发误写」。
4.3 与主芯片 GPIO 的配合
接主控 GPIO 时,有三个设计细节决定这个方案成不成立。其一是驱动方式:推荐开漏输出加上拉电阻——开漏保证「主控不驱动时上拉生效」(回到保护态),推挽输出在固件跑飞时可能输出任意电平,把总闸交给一个不可信的源头。上拉阻值在几十千欧量级(以双方 datasheet 的漏电流与沿率要求为准),阻值太大抗噪差,太小则开漏灌电流超出 GPIO 能力。
其二是电平兼容:主控 IO 电压与器件工作电压不一致时(比如 1.8V 器件接 3.3V 主控),要确认 WP# 的输入高低门限在对方的输出电平范围内——这类跨电压配合问题在电源轨设计中常见,落到 WP# 上的症状是「电平看着对、器件不认」。
其三是默认极性:GPIO 复位默认值(部分主控可配)要保证「上电即保护」——如果主控支持「复位期间 GPIO 保持上次状态」,还要防止上次状态恰好是允许写的电平被带上来。
推挽输出不加外部上下拉,固件跑飞时 GPIO 可能输出高阻(推瑰失效模式)或不定电平,WP# 随之悬空或误置允许态——总闸被交给了一个不可信的源头。改成开漏加上拉,成本是一个电阻,把「默认安全」从固件的责任还给硬件。这是第 12 章保护强度分级里「硬件级」与「软件级」的分界线所在。
4.4 硬件保护的强度与边界
先讲强度。WP# 是保护体系里唯一不经过软件栈的一层:它不需要固件参与判断、不依赖总线时序正确、不受固件 bug 影响。绕过它需要物理动作——割线、飞线、拆器件。在「板卡在位上、软件可能出错」这个最常见的威胁场景里,它是最后一道也是最可靠的一道闸。
再讲边界,两条,都必须记住。其一,WP# 不防读——与块保护位完全一致,读永远放行。其二,WP# 不防「离板」:器件从板上拆下来、用编程器直连时,WP# 被编程器按它自己的默认值驱动(通常是允许写),防护链在离板那一刻就断了。硬件保护防的是「板上的误操作与误触发」,不防「离板的攻击」——后者的兜底只有 OTP 锁定与内容加密(第 5、6 章)。
「接了 WP# 就安全了」是一个常见的过度自信。把威胁模型摆清楚:WP# 防固件误写、防现场误触、防总线异常——这些恰是量产与现场最高频的风险,所以它值得接;但它不防拆机直连、不防内容读取、不防供应链环节的替换——这些需要 OTP 锁定、加密与防伪标识(第 6 章)另做一层。分层防护的意义就在于:每一层只回答它能回答的问题,不互相冒充。
4.5 硬件保护失效的常见原因
硬件保护的失效几乎不发生在「设计正确但器件坏了」这条路上,而是集中在「设计有洞、洞里平时看不见」这条路上。五类根因,按出现频率排:
| 失效现象 | 根因 | 排查方法 | 预防设计 |
|---|---|---|---|
| 保护形同虚设 | WP# 悬空:GPIO 未初始化且无外部上下拉 | 量电平 + 读状态寄存器试写 | 外部上下拉兜底(4.3) |
| 上电瞬间被写 | 主控复位慢于器件,总线被其他主控驱动 | 上电时序实测 + 逻辑分析仪 | 上电顺序设计 + 默认保护态 |
| 升级时写不进 | WP# 常低,升级流程忘了拉高 | 读状态寄存器 + 量电平 | 升级流程内嵌电平检查(第 8 章) |
| 时好时坏 | 上下拉太弱,被总线冲突拉偏 | 示波器看电平建立过程 | 阻值与驱动能力核算 |
| 台架正常、现场失效 | 温湿度 / 老化后电平漂移或接触不良 | 高低温复测 + 振动复测 | 降额设计与工艺核查 |
WP# 相关的奇怪现象,九成能在前两步定位:量电平回答「硬件以为它在什么态」,读寄存器回答「器件以为它在什么态」,两者一对,根因就出来了——电平对但器件不认,查电平语义与门限;电平不对,查上下拉与固件驱动;两者都对还写不进,才轮到查命令时序与锁定位。跳过前两步直接翻固件代码,是最常见的绕路。
本章小结:写保护引脚是硬件总闸——上电默认安全、不经过软件栈、强度来自物理;边界是不防读、不防离板。下一章进入 OTP 安全寄存器:一次性不可逆数据怎么定位、怎么写入、怎么与块保护组合。