ESP-IDF eFuse 摘要实战:用 idf.py efuse-summary 解读 ESP32-S2 的 eFuse 配置

原创2026-09-15 10:00:16186 阅读
文章标签:物联网嵌入式

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_* 熔丝配置时需要一并考虑。

四、使用建议与注意事项

  1. 先摘要、后修改:eFuse 写入大多不可逆(只能 0→1)。任何 efuse-burn / efuse-burn-key 之前先运行 idf.py efuse-summary 记录基线,写入后再复核一次。
  2. 端口是必填项:_parse_efuse_args 的源码逻辑表明,除 --virt 场景外,未提供 --port 会直接报错终止;--chip 会自动取项目的 target,无需手工指定。
  3. 批量脚本化:--format json 输出便于程序解析,配合 --do-not-confirm 可在产线流程中无交互执行;如需查看将要烧写的敏感数据可加 --debug(efuse-burn-key 下还有 --show-sensitive-info)。
  4. 关注 R/W 状态列:摘要中的 Readable/Writeable 列直接反映 RD_DIS/WR_DIS 等控制位的实际效果,是验证“保护是否生效”的最快方式。
  5. 版本适用性:本文示例输出来自 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 引脚映射和供电配置等所有关键信息,是量产前校验与产线调试的核心工具。

登录后查看全文
esp-idf