ESP-IDF eFuse 摘要实战:用 idf.py efuse-summary 解读 ESP32-S2 的 eFuse 配置
ESP-IDF eFuse 摘要实战:用 idf.py efuse-summary 解读 ESP32-S2 的 eFuse 配置
本文以 ESP-IDF 中的 idf.py efuse-summary 命令为主线,结合一份真实的 ESP32-S2 eFuse 摘要输出,逐段解读该命令的用法、底层实现(serial_ext.py 中 efuse_summary 回调)以及输出中每个分类(Config、Flash、Identity、Security 等)的 eFuse 位含义。读完后你可以独立完成一次 eFuse 状态检查,并据此判断芯片的启动模式、MAC 地址、安全启动/Flash 加密状态与 JTAG 开关等关键信息。
一、命令入口:idf.py efuse-summary
eFuse 是 SoC 上一次性可编程的熔丝存储,用于固化启动参数、MAC 地址、安全启动密钥、JTAG 开关等不可回退的配置。ESP-IDF 将 espefuse Python 模块包装为 idf.py 的子命令,其中 efuse-summary 用于获取全部(或指定)eFuse 的人类可读摘要:
idf.py efuse-summary
执行后的典型输出(摘自 espefuse_summary_ESP32-S2.rst):
Executing action: efuse-summary
"ninja efuse-summary"...
EFUSE_NAME (Block) Description = [Meaningful Value] [Readable/Writeable] (Hex Value)
----------------------------------------------------------------------------------------
Config fuses:
WR_DIS (BLOCK0) Disable programming of individual eFuses = 0 R/W (0x00000000)
RD_DIS (BLOCK0) Disable reading from BlOCK4-10 = 0 R/W (0b0000000)
DIS_ICACHE (BLOCK0) Set this bit to disable Icache = False R/W (0b0)
...
Mac fuses:
MAC (BLOCK1) MAC address
= 58:cf:79:b3:b9:54 (OK) R/W
...
Flash voltage (VDD_SPI) determined by GPIO45 on reset (GPIO45=High: VDD_SPI pin is powered from internal 1.8V LDO
GPIO45=Low or NC: VDD_SPI pin is powered directly from VDD3P3_RTC_IO via resistor Rspi. Typically this voltage is 3.3 V).
底层实现:从 idf.py 到 espefuse
从源码看,命令注册与参数组装都发生在 serial_ext.py 中:
efuse_summary回调(第 567–582 行):先调用ensure_build_directory确保构建目录存在,再拼装[PYTHON, '-m', 'espefuse'] + _parse_efuse_args(...) + ['summary']命令,最后由RunTool('espefuse', ...)执行;- 支持
--format参数:传入的格式名会经format.replace("-", "_")转换后以--format=...追加; - 支持一个可选的位置参数
efuse-name,用于只查询单个 eFuse。
命令定义处(serial_ext.py)明确了选项与参数:
'efuse-summary': {
'callback': efuse_summary,
'help': 'Get the summary of the eFuses.',
'options': EFUSE_OPTS
+ [
{
'names': ['--format'],
'help': ('Summary format.'),
'type': click.Choice(['json', 'summary', 'value-only']),
},
],
'arguments': [
{
'names': ['efuse-name'],
'nargs': 1,
'required': False,
},
],
},
也就是说 --format 支持三种取值:json、summary(默认的人类可读摘要)、value-only。而 EFUSE_OPTS 中的公共选项(见 serial_ext.py 的 _parse_efuse_args)包括:
| 选项 | 作用 | 源码行为 |
|---|---|---|
-p, --port |
指定串口号 | 未指定且非 --virt 时直接抛出 FatalError: Error: Port is required for espefuse |
--chip |
目标芯片 | 自动取项目描述中的 target,无需手工填写 |
--virt |
虚拟/主机环境 | 追加 --virt,此时不要求串口 |
--before <cmd> |
烧写/操作前先执行的串口命令 | 透传给 espefuse |
--debug |
调试模式 | 透传给 espefuse |
--do-not-confirm |
跳过交互确认 | 透传给 espefuse |
因此一次完整调用形如:
idf.py -p /dev/ttyUSB0 efuse-summary
idf.py -p /dev/ttyUSB0 efuse-summary --format json
idf.py -p /dev/ttyUSB0 efuse-summary SECURE_BOOT_EN # 只查询单个 eFuse
同族命令:与 efuse-summary 配合使用
在 serial_ext.py 中还能看到同一组 eFuse 动作,摘要结果通常与它们配合使用:
efuse-burn(第 1006 行):按名称烧写指定 eFuse;efuse-burn-key(第 1017 行):烧写 256 位密钥(BLOCK1、flash_encryption、BLOCK2、secure_boot_v1/v2、BLOCK3),默认自动对密钥做读写保护,可用--no-protect-key、--force-write-always、--show-sensitive-info调整;efuse-dump(第 1055 行):导出全部 eFuse 原始 hex,可用--file-name按块保存为 blk0.bin…blkN.bin;efuse-read-protect/efuse-write-protect(第 1070、1100 行):对指定 eFuse 施加读/写保护。
工作流上,建议先用 efuse-summary 确认当前状态,再用上述命令做修改,修改后再次 efuse-summary 复核——因为 eFuse 位一旦置 1 通常不可清零。
二、输出格式解读:EFUSE_NAME (Block) Description = Value [R/W] (Hex)
摘要的每一行遵循统一格式:
EFUSE_NAME (Block) Description = [Meaningful Value] [Readable/Writeable] (Hex Value)
EFUSE_NAME (Block):eFuse 逻辑名及其所在块(BLOCK0/BLOCK1/BLOCK4…)。块的概念对应硬件上的分组,RD_DIS中 “Disable reading from BLOCK4-10” 即指通过控制位禁止读取 4–10 号块;Description:功能描述,多行时换行缩进续排;Meaningful Value:语义化取值(如False、UART0、4 data lines、MAC 字符串),比原始 hex 更直观;R/W标志:表示当前可读/可写状态。若某位已被RD_DIS/WR_DIS锁死,这里会体现;(Hex Value):原始十六进制值,如(0b0)、(0x00000000)、(0b000)。
对多字节字段(用户数据块、密钥块、MAC、可选唯一 ID 等),输出会换行用空格分隔的字节序列表示,并单独标注 R/W。
三、ESP32-S2 输出逐段详解
以下按摘要中的分类逐段解读,全部条目取自 espefuse_summary_ESP32-S2.rst 的真实输出。
3.1 Config fuses(配置熔丝)
位于 BLOCK0 的通用启动/外设配置位:
| eFuse | 块 | 含义 | 示例值 |
|---|---|---|---|
WR_DIS |
BLOCK0 | 禁止编程指定 eFuse(位图,0 = 全部可写) | 0 R/W (0x00000000) |
RD_DIS |
BLOCK0 | 禁止读取 BLOCK4-10 | 0 R/W |
DIS_ICACHE / DIS_DCACHE |
BLOCK0 | 分别禁用 I-Cache / D-Cache | False |
DIS_TWAI |
BLOCK0 | 禁用 TWAI(原 CAN)控制器 | False |
DIS_BOOT_REMAP |
BLOCK0 | 禁用 ROM 地址空间 RAM 重映射能力 | False |
DIS_LEGACY_SPI_BOOT |
BLOCK0 | 禁用 Legacy SPI 启动模式 | False |
UART_PRINT_CHANNEL |
BLOCK0 | 选择打印启动信息的默认 UART | UART0 (0b0) |
UART_PRINT_CONTROL |
BLOCK0 | 默认 UART 启动信息输出模式 | Enable (0b00) |
PIN_POWER_SELECTION |
BLOCK0 | GPIO33-GPIO37 在 SPI Flash 初始化时的默认供电来源 | VDD3P3_CPU (0b0) |
此外还有两个大块字段:
BLOCK_USR_DATA (BLOCK3):用户数据块,示例中为 32 字节全 0,R/W;BLOCK_SYS_DATA2 (BLOCK10):系统数据第二部分(保留),示例中同样为 32 字节全 0。
3.2 Flash fuses(Flash 启动熔丝)
| eFuse | 块 | 含义 | 示例值 |
|---|---|---|---|
FLASH_TPUW |
BLOCK0 | SoC 上电后 Flash 启动延时,单位 ms/2;值 15 即 7.5 ms | 0 R/W (0x0) |
FLASH_TYPE |
BLOCK0 | SPI Flash 类型(数据线条数) | 4 data lines (0b0) |
FORCE_SEND_RESUME |
BLOCK0 | 强制 ROM 代码在 SPI 启动期间发送 Flash resume 命令 | False |
FLASH_VERSION |
BLOCK1 | Flash 版本号 | 2 R/W (0x2) |
这一组直接影响 ROM bootloader 的取指行为;例如 FLASH_TYPE 决定启动时按 1 线还是 4 线模式访问 Flash。
3.3 Identity fuses(身份熔丝)
| eFuse | 块 | 含义 | 示例值 |
|---|---|---|---|
BLOCK0_VERSION |
BLOCK0 | BLOCK0 的 eFuse 布局版本 | 0 (0b00) |
WAFER_VERSION_MAJOR / WAFER_VERSION_MINOR_HI / WAFER_VERSION_MINOR_LO |
BLOCK0/BLOCK1 | 晶圆版本主/次号 | 1 (0b01) / False / 0 (0b000) |
WAFER_VERSION_MINOR |
BLOCK0(只读计算值) | WAFER_VERSION_MINOR_HI << 3 + WAFER_VERSION_MINOR_LO |
0 (0x0) |
BLK_VERSION_MAJOR / BLK_VERSION_MINOR |
BLOCK1/BLOCK2 | 块版本;BLK_VERSION_MINOR 示例值为 ADC calib V2 (0b010) |
|
PSRAM_VERSION |
BLOCK1 | PSRAM 版本 | 1 (0x1) |
PKG_VERSION |
BLOCK1 | 封装版本 | 0 (0x0) |
OPTIONAL_UNIQUE_ID |
BLOCK2 | 可选的 128 位唯一 ID | ea 0e c6 f1 ... 00 02 |
DISABLE_WAFER_VERSION_MAJOR / DISABLE_BLK_VERSION_MAJOR |
BLOCK0 | 关闭对应版本检查 | False |
这类熔丝在出厂时由产线写入,是判断芯片批次与产线校准方案(如 ADC 校准版本)的重要依据。注意 WAFER_VERSION_MINOR 是只读的合成值——由 HI/LO 位按位域拼合得出。
3.4 Jtag fuses(JTAG 控制)
| eFuse | 块 | 含义 |
|---|---|---|
SOFT_DIS_JTAG |
BLOCK0 | 软件方式禁用 JTAG;被软件禁用后,JTAG 仍可被 HMAC 外设临时激活 |
HARD_DIS_JTAG |
BLOCK0 | 硬件方式永久禁用 JTAG |
量产中常用的做法是置位 SOFT_DIS_JTAG(保留临时激活能力),而 HARD_DIS_JTAG 一旦置位即不可恢复,需谨慎评估。
3.5 Mac fuses(MAC 地址)
MAC (BLOCK1):芯片 MAC 地址,示例输出为58:cf:79:b3:b9:54 (OK),R/W。OK表示校验通过。CUSTOM_MAC (BLOCK3):自定义 MAC,示例中为00:00:00:00:00:00 (OK),即未使用。
3.6 Security fuses(安全熔丝)
这是摘要中条目最多的一档,示例芯片全部处于未启用/未吊销状态:
| eFuse | 块 | 含义 |
|---|---|---|
DIS_DOWNLOAD_ICACHE / DIS_DOWNLOAD_DCACHE |
BLOCK0 | 下载模式下禁用 I-Cache / D-Cache |
DIS_FORCE_DOWNLOAD |
BLOCK0 | 禁用强制进入下载模式的引脚功能 |
DIS_DOWNLOAD_MANUAL_ENCRYPT |
BLOCK0 | 禁用下载启动模式下的 Flash 加密 |
SPI_BOOT_CRYPT_CNT |
BLOCK0 | 置 1 个或 3 个位时启用 Flash 加密;示例 Disable (0b000) |
SECURE_BOOT_KEY_REVOKE0/1/2 |
BLOCK0 | 分别吊销第 1/2/3 把安全启动密钥 |
KEY_PURPOSE_0 … KEY_PURPOSE_5 |
BLOCK0 | KEY0–KEY5 的用途(示例均为 USER) |
SECURE_BOOT_EN |
BLOCK0 | 使能安全启动 |
SECURE_BOOT_AGGRESSIVE_REVOKE |
BLOCK0 | 使能安全启动密钥“激进吊销”模式 |
DIS_DOWNLOAD_MODE |
BLOCK0 | 禁用全部下载启动模式 |
ENABLE_SECURITY_DOWNLOAD |
BLOCK0 | 使能安全 UART 下载模式(仅可读写 Flash) |
SECURE_VERSION |
BLOCK0 | 安全版本号,供 ESP-IDF 防回滚(anti-rollback)特性使用,示例 0 (0x0000) |
BLOCK_KEY0 … BLOCK_KEY5 |
BLOCK4–BLOCK9 | 256 位密钥/用户数据块,各 32 字节;示例中均为全 0 且 Purpose: USER |
结合输出可见:示例芯片 SECURE_BOOT_EN = False、SPI_BOOT_CRYPT_CNT = Disable、SECURE_VERSION = 0,即该芯片未启用安全启动、Flash 加密与防回滚;六个 KEY 块都是 USER 用途的空块。
3.7 Spi Pad fuses(SPI 引脚配置)
位于 BLOCK1 的 SPI_PAD_CONFIG_* 共 11 个字段,分别对应 CLK、Q(D1)、D(D0)、CS、HD(D3)、WP(D2)、DQS、D4–D7,用于固化 SPI 引脚到物理 IO 的映射。示例中全部为 0 (0b000000),即保持默认引脚分配。
3.8 Usb fuses(USB 功能控制)
| eFuse | 块 | 含义 |
|---|---|---|
DIS_USB |
BLOCK0 | 禁用 USB OTG 功能 |
USB_EXCHG_PINS |
BLOCK0 | 交换 USB D+ 与 D- 引脚 |
USB_EXT_PHY_ENABLE |
BLOCK0 | 使能外部 USB PHY |
USB_FORCE_NOPERSIST |
BLOCK0 | 置位后强制 USB BVALID 为 1 |
DIS_USB_DOWNLOAD_MODE |
BLOCK0 | 禁用 UART 下载启动模式中的 USB OTG |
3.9 Vdd fuses 与 Wdt fuses(供电与看门狗)
- Vdd 三件套位于 BLOCK0:
VDD_SPI_XPD(当VDD_SPI_FORCE=1时决定 VDD_SPI 调节器是否上电)、VDD_SPI_TIEH(决定 VDD_SPI 电压,示例值VDD_SPI connects to 1.8 V LDO)、VDD_SPI_FORCE(用上述两位配置 VDD_SPI LDO)。 WDT_DELAY_SEL (BLOCK0):RTC 看门狗超时门限(慢时钟周期),示例值40000 (0b00)。
3.10 GPIO45 决定 Flash 电压的附加说明
摘要末尾附有一段硬件提示:复位时 GPIO45 的电平决定 VDD_SPI 的来源——
- GPIO45 = High:VDD_SPI 由片内 1.8 V LDO 供电;
- GPIO45 = Low 或悬空(NC):VDD_SPI 经电阻 Rspi 直接由 VDD3P3_RTC_IO 供电,通常约为 3.3 V。
这在评估外部 Flash 供电方案与 VDD_SPI_* 熔丝配置时需要一并考虑。
四、使用建议与注意事项
- 先摘要、后修改:eFuse 写入大多不可逆(只能 0→1)。任何
efuse-burn/efuse-burn-key之前先运行idf.py efuse-summary记录基线,写入后再复核一次。 - 端口是必填项:
_parse_efuse_args的源码逻辑表明,除--virt场景外,未提供--port会直接报错终止;--chip会自动取项目的 target,无需手工指定。 - 批量脚本化:
--format json输出便于程序解析,配合--do-not-confirm可在产线流程中无交互执行;如需查看将要烧写的敏感数据可加--debug(efuse-burn-key下还有--show-sensitive-info)。 - 关注 R/W 状态列:摘要中的
Readable/Writeable列直接反映RD_DIS/WR_DIS等控制位的实际效果,是验证“保护是否生效”的最快方式。 - 版本适用性:本文示例输出来自 ESP32-S2 目标,不同芯片(ESP32、ESP32-C3、ESP32-S3 等)的 eFuse 字段集与块布局不同,
efuse-summary的输出会随--chip(即项目 target)变化,请以实际输出为准。
五、小结
idf.py efuse-summary 是 ESP-IDF 中检查芯片 eFuse 状态的统一入口:它在 serial_ext.py 中封装为对 python -m espefuse summary 的调用,自动带上端口与项目 target,并支持 json/summary/value-only 三种格式与单 eFuse 查询。以 ESP32-S2 为例,输出按 Config、Flash、Identity、Jtag、Mac、Security、Spi Pad、Usb、Vdd、Wdt 十个维度呈现,完整覆盖了启动行为、MAC/唯一 ID、安全启动与 Flash 加密、JTAG 开关、SPI 引脚映射和供电配置等所有关键信息,是量产前校验与产线调试的核心工具。