feat(esp32h4): add ESP32H4 esptool support

This commit is contained in:
Chen Jichang
2025-02-12 11:48:25 +08:00
parent 54fdc75780
commit edb99bd689
13 changed files with 1465 additions and 9 deletions

View File

@@ -119,6 +119,7 @@ host_tests_espefuse:
- esp32s3
- esp32s3beta2
- esp32h21
- esp32h4
script:
- coverage run --parallel-mode -m pytest ${CI_PROJECT_DIR}/test/test_espefuse.py --chip ${TARGET}
# some .coverage files in sub-directories are not collected on some runners, move them first

View File

@@ -19,6 +19,7 @@ import espefuse.efuse.esp32c61 as esp32c61_efuse
import espefuse.efuse.esp32h2 as esp32h2_efuse
import espefuse.efuse.esp32h21 as esp32h21_efuse
import espefuse.efuse.esp32h2beta1 as esp32h2beta1_efuse
import espefuse.efuse.esp32h4 as esp32h4_efuse
import espefuse.efuse.esp32p4 as esp32p4_efuse
import espefuse.efuse.esp32s2 as esp32s2_efuse
import espefuse.efuse.esp32s3 as esp32s3_efuse
@@ -63,6 +64,7 @@ SUPPORTED_CHIPS = {
),
"esp32h2": DefChip("ESP32-H2", esp32h2_efuse, esptool.targets.ESP32H2ROM),
"esp32h21": DefChip("ESP32-H21", esp32h21_efuse, esptool.targets.ESP32H21ROM),
"esp32h4": DefChip("ESP32-H4", esp32h4_efuse, esptool.targets.ESP32H4ROM),
"esp32p4": DefChip("ESP32-P4", esp32p4_efuse, esptool.targets.ESP32P4ROM),
"esp32h2beta1": DefChip(
"ESP32-H2(beta1)", esp32h2beta1_efuse, esptool.targets.ESP32H2BETA1ROM

View File

@@ -0,0 +1,3 @@
from . import operations
from .emulate_efuse_controller import EmulateEfuseController
from .fields import EspEfuses

View File

@@ -0,0 +1,92 @@
# This file describes eFuses controller for ESP32-H4 chip
#
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
#
# SPDX-License-Identifier: GPL-2.0-or-later
import reedsolo
from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters
from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError
class EmulateEfuseController(EmulateEfuseControllerBase):
"""The class for virtual efuse operation. Using for HOST_TEST."""
CHIP_NAME = "ESP32-H4"
mem = None
debug = False
def __init__(self, efuse_file=None, debug=False):
self.Blocks = EfuseDefineBlocks
self.Fields = EfuseDefineFields(None)
self.REGS = EfuseDefineRegisters
super(EmulateEfuseController, self).__init__(efuse_file, debug)
self.write_reg(self.REGS.EFUSE_CMD_REG, 0)
""" esptool method start >>"""
def get_major_chip_version(self):
return 0
def get_minor_chip_version(self):
return 0
def get_crystal_freq(self):
return 32 # MHz
def get_security_info(self):
return {
"flags": 0,
"flash_crypt_cnt": 0,
"key_purposes": 0,
"chip_id": 0,
"api_version": 0,
}
""" << esptool method end """
def handle_writing_event(self, addr, value):
if addr == self.REGS.EFUSE_CMD_REG:
if value & self.REGS.EFUSE_PGM_CMD:
self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF)
self.clean_blocks_wr_regs()
self.check_rd_protection_area()
self.write_reg(addr, 0)
self.write_reg(self.REGS.EFUSE_CMD_REG, 0)
elif value == self.REGS.EFUSE_READ_CMD:
self.write_reg(addr, 0)
self.write_reg(self.REGS.EFUSE_CMD_REG, 0)
self.save_to_file()
def get_bitlen_of_block(self, blk, wr=False):
if blk.id == 0:
if wr:
return 32 * 8
else:
return 32 * blk.len
else:
if wr:
rs_coding = 32 * 3
return 32 * 8 + rs_coding
else:
return 32 * blk.len
def handle_coding_scheme(self, blk, data):
if blk.id != 0:
# CODING_SCHEME RS applied only for all blocks except BLK0.
coded_bytes = 12
data.pos = coded_bytes * 8
plain_data = data.readlist("32*uint:8")[::-1]
# takes 32 bytes
# apply RS encoding
rs = reedsolo.RSCodec(coded_bytes)
# 32 byte of data + 12 bytes RS
calc_encoded_data = list(rs.encode([x for x in plain_data]))
data.pos = 0
if calc_encoded_data != data.readlist("44*uint:8")[::-1]:
raise FatalError("Error in coding scheme data")
data = data[coded_bytes * 8 :]
if blk.len < 8:
data = data[(8 - blk.len) * 32 :]
return data

View File

@@ -0,0 +1,452 @@
# This file describes eFuses for ESP32-H4 chip
#
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
#
# SPDX-License-Identifier: GPL-2.0-or-later
import binascii
import struct
import time
from bitstring import BitArray
import esptool
import reedsolo
from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters
from .. import base_fields
from .. import util
class EfuseBlock(base_fields.EfuseBlockBase):
def len_of_burn_unit(self):
# The writing register window is 8 registers for any blocks.
# len in bytes
return 8 * 4
def __init__(self, parent, param, skip_read=False):
parent.read_coding_scheme()
super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read)
def apply_coding_scheme(self):
data = self.get_raw(from_read=False)[::-1]
if len(data) < self.len_of_burn_unit():
add_empty_bytes = self.len_of_burn_unit() - len(data)
data = data + (b"\x00" * add_empty_bytes)
if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS:
# takes 32 bytes
# apply RS encoding
rs = reedsolo.RSCodec(12)
# 32 byte of data + 12 bytes RS
encoded_data = rs.encode([x for x in data])
words = struct.unpack("<" + "I" * 11, encoded_data)
# returns 11 words (8 words of data + 3 words of RS coding)
else:
# takes 32 bytes
words = struct.unpack("<" + ("I" * (len(data) // 4)), data)
# returns 8 words
return words
class EspEfuses(base_fields.EspEfusesBase):
"""
Wrapper object to manage the efuse fields in a connected ESP bootloader
"""
debug = False
do_not_confirm = False
def __init__(
self,
esp,
skip_connect=False,
debug=False,
do_not_confirm=False,
extend_efuse_table=None,
):
self.Blocks = EfuseDefineBlocks()
self.Fields = EfuseDefineFields(extend_efuse_table)
self.REGS = EfuseDefineRegisters
self.BURN_BLOCK_DATA_NAMES = self.Blocks.get_burn_block_data_names()
self.BLOCKS_FOR_KEYS = self.Blocks.get_blocks_for_keys()
self._esp = esp
self.debug = debug
self.do_not_confirm = do_not_confirm
if esp.CHIP_NAME != "ESP32-H4":
raise esptool.FatalError(
"Expected the 'esp' param for ESP32-H4 chip but got for '%s'."
% (esp.CHIP_NAME)
)
if not skip_connect:
flags = self._esp.get_security_info()["flags"]
GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2
if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE:
raise esptool.FatalError(
"Secure Download Mode is enabled. The tool can not read eFuses."
)
self.blocks = [
EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect)
for block in self.Blocks.BLOCKS
]
if not skip_connect:
self.get_coding_scheme_warnings()
self.efuses = [EfuseField.convert(self, efuse) for efuse in self.Fields.EFUSES]
self.efuses += [
EfuseField.convert(self, efuse) for efuse in self.Fields.KEYBLOCKS
]
if skip_connect:
self.efuses += [
EfuseField.convert(self, efuse)
for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES
]
else:
if self["BLK_VERSION_MINOR"].get() == 1:
self.efuses += [
EfuseField.convert(self, efuse)
for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES
]
self.efuses += [
EfuseField.convert(self, efuse) for efuse in self.Fields.CALC
]
def __getitem__(self, efuse_name):
"""Return the efuse field with the given name"""
for e in self.efuses:
if efuse_name == e.name or any(x == efuse_name for x in e.alt_names):
return e
new_fields = False
for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES:
if efuse.name == efuse_name or any(
x == efuse_name for x in efuse.alt_names
):
self.efuses += [
EfuseField.convert(self, efuse)
for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES
]
new_fields = True
if new_fields:
for e in self.efuses:
if efuse_name == e.name or any(x == efuse_name for x in e.alt_names):
return e
raise KeyError
def read_coding_scheme(self):
self.coding_scheme = self.REGS.CODING_SCHEME_RS
def print_status_regs(self):
print("")
self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True)
print(
"{:27} 0x{:08x}".format(
"EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG)
)
)
print(
"{:27} 0x{:08x}".format(
"EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG)
)
)
def efuse_controller_setup(self):
self.set_efuse_timing()
self.clear_pgm_registers()
self.wait_efuse_idle()
def write_efuses(self, block):
self.efuse_program(block)
return self.get_coding_scheme_warnings(silent=True)
def clear_pgm_registers(self):
self.wait_efuse_idle()
for r in range(
self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4
):
self.write_reg(r, 0)
def wait_efuse_idle(self):
deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT
while time.time() < deadline:
cmds = self.REGS.EFUSE_PGM_CMD | self.REGS.EFUSE_READ_CMD
if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0:
if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0:
# Due to a hardware error, we have to read READ_CMD again
# to make sure the efuse clock is normal.
# For PGM_CMD it is not necessary.
return
raise esptool.FatalError(
"Timed out waiting for Efuse controller command to complete"
)
def efuse_program(self, block):
self.wait_efuse_idle()
self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE)
self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2))
self.wait_efuse_idle()
self.clear_pgm_registers()
self.efuse_read()
def efuse_read(self):
self.wait_efuse_idle()
self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE)
# need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some
# efuse registers after each command is completed
# if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip.
try:
self.write_reg(
self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000
)
self.wait_efuse_idle()
except esptool.FatalError:
secure_download_mode_before = self._esp.secure_download_mode
try:
self._esp = self.reconnect_chip(self._esp)
except esptool.FatalError:
print("Can not re-connect to the chip")
if not self["DIS_DOWNLOAD_MODE"].get() and self[
"DIS_DOWNLOAD_MODE"
].get(from_read=False):
print(
"This is the correct behavior as we are actually burning "
"DIS_DOWNLOAD_MODE which disables the connection to the chip"
)
print("DIS_DOWNLOAD_MODE is enabled")
print("Successful")
exit(0) # finish without errors
raise
print("Established a connection with the chip")
if self._esp.secure_download_mode and not secure_download_mode_before:
print("Secure download mode is enabled")
if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[
"ENABLE_SECURITY_DOWNLOAD"
].get(from_read=False):
print(
"espefuse tool can not continue to work in Secure download mode"
)
print("ENABLE_SECURITY_DOWNLOAD is enabled")
print("Successful")
exit(0) # finish without errors
raise
def set_efuse_timing(self):
"""Set timing registers for burning efuses"""
# Configure clock
apb_freq = self.get_crystal_freq()
if apb_freq != 32:
raise esptool.FatalError(
"The eFuse supports only xtal=32M (xtal was %d)" % apb_freq
)
# TODO: [ESP32H4] IDF-12268
def get_coding_scheme_warnings(self, silent=False):
"""Check if the coding scheme has detected any errors."""
old_addr_reg = 0
reg_value = 0
ret_fail = False
for block in self.blocks:
if block.id == 0:
words = [
self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4)
for offs in range(5)
]
block.err_bitarray.pos = 0
for word in reversed(words):
block.err_bitarray.overwrite(BitArray("uint:32=%d" % word))
block.num_errors = block.err_bitarray.count(True)
block.fail = block.num_errors != 0
else:
addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[
block.id
]
if err_num_mask is None or err_num_offs is None or fail_bit is None:
continue
if addr_reg != old_addr_reg:
old_addr_reg = addr_reg
reg_value = self.read_reg(addr_reg)
block.fail = reg_value & (1 << fail_bit) != 0
block.num_errors = (reg_value >> err_num_offs) & err_num_mask
ret_fail |= block.fail
if not silent and (block.fail or block.num_errors):
print(
"Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]"
% (block.id, block.num_errors, block.fail)
)
if (self.debug or ret_fail) and not silent:
self.print_status_regs()
return ret_fail
def summary(self):
# TODO add support set_flash_voltage - "Flash voltage (VDD_SPI)"
return ""
class EfuseField(base_fields.EfuseFieldBase):
@staticmethod
def convert(parent, efuse):
return {
"mac": EfuseMacField,
"keypurpose": EfuseKeyPurposeField,
"t_sensor": EfuseTempSensor,
"adc_tp": EfuseAdcPointCalibration,
"wafer": EfuseWafer,
}.get(efuse.class_type, EfuseField)(parent, efuse)
class EfuseWafer(EfuseField):
def get(self, from_read=True):
# TODO: [ESP32H4] IDF-12268
return 0
def save(self, new_value):
raise esptool.FatalError("Burning %s is not supported" % self.name)
class EfuseTempSensor(EfuseField):
def get(self, from_read=True):
value = self.get_bitstring(from_read)
sig = -1 if value[0] else 1
return sig * value[1:].uint * 0.1
class EfuseAdcPointCalibration(EfuseField):
def get(self, from_read=True):
STEP_SIZE = 4
value = self.get_bitstring(from_read)
sig = -1 if value[0] else 1
return sig * value[1:].uint * STEP_SIZE
class EfuseMacField(EfuseField):
def check_format(self, new_value_str):
if new_value_str is None:
raise esptool.FatalError(
"Required MAC Address in AA:CD:EF:01:02:03 format!"
)
num_bytes = 8 if self.name == "MAC_EUI64" else 6
if new_value_str.count(":") != num_bytes - 1:
raise esptool.FatalError(
f"MAC Address needs to be a {num_bytes}-byte hexadecimal format "
"separated by colons (:)!"
)
hexad = new_value_str.replace(":", "").split(" ", 1)[0]
hexad = hexad.split(" ", 1)[0] if self.is_field_calculated() else hexad
if len(hexad) != num_bytes * 2:
raise esptool.FatalError(
f"MAC Address needs to be a {num_bytes}-byte hexadecimal number "
f"({num_bytes * 2} hexadecimal characters)!"
)
# order of bytearray = b'\xaa\xcd\xef\x01\x02\x03',
bindata = binascii.unhexlify(hexad)
if not self.is_field_calculated():
# unicast address check according to
# https://tools.ietf.org/html/rfc7042#section-2.1
if esptool.util.byte(bindata, 0) & 0x01:
raise esptool.FatalError("Custom MAC must be a unicast MAC!")
return bindata
def check(self):
errs, fail = self.parent.get_block_errors(self.block)
if errs != 0 or fail:
output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail)
else:
output = "OK"
return "(" + output + ")"
def get(self, from_read=True):
if self.name == "CUSTOM_MAC":
mac = self.get_raw(from_read)[::-1]
elif self.name == "MAC":
mac = self.get_raw(from_read)
elif self.name == "MAC_EUI64":
mac = self.parent["MAC"].get_bitstring(from_read).copy()
mac_ext = self.parent["MAC_EXT"].get_bitstring(from_read)
mac.insert(mac_ext, 24)
mac = mac.bytes
else:
mac = self.get_raw(from_read)
return "%s %s" % (util.hexify(mac, ":"), self.check())
def save(self, new_value):
def print_field(e, new_value):
print(
" - '{}' ({}) {} -> {}".format(
e.name, e.description, e.get_bitstring(), new_value
)
)
if self.name == "CUSTOM_MAC":
bitarray_mac = self.convert_to_bitstring(new_value)
print_field(self, bitarray_mac)
super(EfuseMacField, self).save(new_value)
else:
# Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible,
# as it's written in the factory.
raise esptool.FatalError(f"Burning {self.name} is not supported")
# fmt: off
class EfuseKeyPurposeField(EfuseField):
# TODO: [ESP32H4] IDF-12268 need check
KEY_PURPOSES = [
("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use)
("ECDSA_KEY", 1, None, "Reverse", "need_rd_protect"), # ECDSA key
("XTS_AES_256_KEY_1", 2, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_1 (flash/PSRAM encryption)
("XTS_AES_256_KEY_2", 3, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_2 (flash/PSRAM encryption)
("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption)
("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode
("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode)
("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode)
("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode
("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest)
("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest)
("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest)
("KM_INIT_KEY", 12, None, None, "need_rd_protect"), # init key that is used for the generation of AES/ECDSA key
]
# fmt: on
KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES]
DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"]
def check_format(self, new_value_str):
# str convert to int: "XTS_AES_128_KEY" - > str(4)
# if int: 4 -> str(4)
raw_val = new_value_str
for purpose_name in self.KEY_PURPOSES:
if purpose_name[0] == new_value_str:
raw_val = str(purpose_name[1])
break
if raw_val.isdigit():
if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]:
raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val)
else:
raise esptool.FatalError("'%s' unknown name" % raw_val)
return raw_val
def need_reverse(self, new_key_purpose):
for key in self.KEY_PURPOSES:
if key[0] == new_key_purpose:
return key[3] == "Reverse"
def need_rd_protect(self, new_key_purpose):
for key in self.KEY_PURPOSES:
if key[0] == new_key_purpose:
return key[4] == "need_rd_protect"
def get(self, from_read=True):
for p in self.KEY_PURPOSES:
if p[1] == self.get_raw(from_read):
return p[0]
return "FORBIDDEN_STATE"
def get_name(self, raw_val):
for key in self.KEY_PURPOSES:
if key[1] == raw_val:
return key[0]
def save(self, new_value):
raw_val = int(self.check_format(str(new_value)))
return super(EfuseKeyPurposeField, self).save(raw_val)

View File

@@ -0,0 +1,169 @@
# This file describes eFuses fields and registers for ESP32-H4 chip
#
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
#
# SPDX-License-Identifier: GPL-2.0-or-later
import os
import yaml
from ..mem_definition_base import (
EfuseBlocksBase,
EfuseFieldsBase,
EfuseRegistersBase,
Field,
)
class EfuseDefineRegisters(EfuseRegistersBase):
EFUSE_MEM_SIZE = 0x01FC + 4
# EFUSE registers & command/conf values
DR_REG_EFUSE_BASE = 0x600B1800
EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE
EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020
EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1C8
EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1CC
EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1D4
EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D8
EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x190
EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x194
EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C
EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180
EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184
EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188
EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C
EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC
EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1F0
EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F4
EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F8
EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x198
EFUSE_WRITE_OP_CODE = 0x5A5A
EFUSE_READ_OP_CODE = 0x5AA5
EFUSE_PGM_CMD_MASK = 0x3
EFUSE_PGM_CMD = 0x2
EFUSE_READ_CMD = 0x1
BLOCK_ERRORS = [
# error_reg, err_num_mask, err_num_offs, fail_bit
(EFUSE_RD_REPEAT_ERR0_REG, None, None, None), # BLOCK0
(EFUSE_RD_RS_ERR0_REG, 0x7, 0, 3), # MAC_SPI_8M_0
(EFUSE_RD_RS_ERR0_REG, 0x7, 4, 7), # BLOCK_SYS_DATA
(EFUSE_RD_RS_ERR0_REG, 0x7, 8, 11), # BLOCK_USR_DATA
(EFUSE_RD_RS_ERR0_REG, 0x7, 12, 15), # BLOCK_KEY0
(EFUSE_RD_RS_ERR0_REG, 0x7, 16, 19), # BLOCK_KEY1
(EFUSE_RD_RS_ERR0_REG, 0x7, 20, 23), # BLOCK_KEY2
(EFUSE_RD_RS_ERR0_REG, 0x7, 24, 27), # BLOCK_KEY3
(EFUSE_RD_RS_ERR0_REG, 0x7, 28, 31), # BLOCK_KEY4
(EFUSE_RD_RS_ERR1_REG, 0x7, 0, 3), # BLOCK_KEY5
(EFUSE_RD_RS_ERR1_REG, 0x7, 4, 7), # BLOCK_SYS_DATA2
]
# EFUSE_WR_TIM_CONF2_REG
EFUSE_PWR_OFF_NUM_S = 0
EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S
# EFUSE_WR_TIM_CONF1_REG
EFUSE_PWR_ON_NUM_S = 8
EFUSE_PWR_ON_NUM_M = 0x0000FFFF << EFUSE_PWR_ON_NUM_S
# EFUSE_DAC_CONF_REG
EFUSE_DAC_CLK_DIV_S = 0
EFUSE_DAC_CLK_DIV_M = 0xFF << EFUSE_DAC_CLK_DIV_S
# EFUSE_DAC_CONF_REG
EFUSE_DAC_NUM_S = 9
EFUSE_DAC_NUM_M = 0xFF << EFUSE_DAC_NUM_S
class EfuseDefineBlocks(EfuseBlocksBase):
__base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE
__base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG
# List of efuse blocks
# fmt: off
BLOCKS = [
# Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose
("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None),
("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None),
("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None),
("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None),
("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"),
("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"),
("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"),
("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"),
("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"),
("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"),
("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None),
]
# fmt: on
def get_burn_block_data_names(self):
list_of_names = []
for block in self.BLOCKS:
blk = self.get(block)
if blk.name:
list_of_names.append(blk.name)
if blk.alias:
for alias in blk.alias:
list_of_names.append(alias)
return list_of_names
class EfuseDefineFields(EfuseFieldsBase):
def __init__(self, extend_efuse_table) -> None:
# List of efuse fields from TRM the chapter eFuse Controller.
self.EFUSES = []
self.KEYBLOCKS = []
# if BLK_VERSION_MINOR is 1, these efuse fields are in BLOCK2
self.BLOCK2_CALIBRATION_EFUSES = []
self.CALC = []
dir_name = os.path.dirname(os.path.abspath(__file__))
dir_name, file_name = os.path.split(dir_name)
file_name = file_name + ".yaml"
dir_name, _ = os.path.split(dir_name)
efuse_file = os.path.join(dir_name, "efuse_defs", file_name)
with open(f"{efuse_file}", "r") as r_file:
e_desc = yaml.safe_load(r_file)
super().__init__(e_desc, extend_efuse_table)
for i, efuse in enumerate(self.ALL_EFUSES):
if efuse.name in [
"BLOCK_USR_DATA",
"BLOCK_KEY0",
"BLOCK_KEY1",
"BLOCK_KEY2",
"BLOCK_KEY3",
"BLOCK_KEY4",
"BLOCK_KEY5",
"BLOCK_SYS_DATA2",
]:
if efuse.name == "BLOCK_USR_DATA":
efuse.bit_len = 256
efuse.type = "bytes:32"
self.KEYBLOCKS.append(efuse)
self.ALL_EFUSES[i] = None
elif efuse.category == "calibration":
self.BLOCK2_CALIBRATION_EFUSES.append(efuse)
self.ALL_EFUSES[i] = None
f = Field()
f.name = "MAC_EUI64"
f.block = 1
f.bit_len = 64
f.type = f"bytes:{f.bit_len // 8}"
f.category = "MAC"
f.class_type = "mac"
f.description = "calc MAC_EUI64 = MAC[0]:MAC[1]:MAC[2]:MAC_EXT[0]:MAC_EXT[1]:MAC[3]:MAC[4]:MAC[5]"
self.CALC.append(f)
for efuse in self.ALL_EFUSES:
if efuse is not None:
self.EFUSES.append(efuse)
self.ALL_EFUSES = []

View File

@@ -0,0 +1,389 @@
# This file includes the operations with eFuses for ESP32-H4 chip
#
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
#
# SPDX-License-Identifier: GPL-2.0-or-later
import argparse
import os # noqa: F401. It is used in IDF scripts
import traceback
import espsecure
import esptool
from . import fields
from .. import util
from ..base_operations import (
add_common_commands,
add_force_write_always,
burn_bit,
burn_block_data,
burn_efuse,
check_error,
dump,
read_protect_efuse,
summary,
write_protect_efuse,
)
def protect_options(p):
p.add_argument(
"--no-write-protect",
help="Disable write-protecting of the key. The key remains writable. "
"(The keys use the RS coding scheme that does not support "
"post-write data changes. Forced write can damage RS encoding bits.) "
"The write-protecting of keypurposes does not depend on the option, "
"it will be set anyway.",
action="store_true",
)
p.add_argument(
"--no-read-protect",
help="Disable read-protecting of the key. The key remains readable software."
"The key with keypurpose[USER, RESERVED and *_DIGEST] "
"will remain readable anyway. For the rest keypurposes the read-protection "
"will be defined the option (Read-protect by default).",
action="store_true",
)
def add_commands(subparsers, efuses):
add_common_commands(subparsers, efuses)
burn_key = subparsers.add_parser(
"burn_key", help="Burn the key block with the specified name"
)
protect_options(burn_key)
add_force_write_always(burn_key)
burn_key.add_argument(
"block",
help="Key block to burn",
action="append",
choices=efuses.BLOCKS_FOR_KEYS,
)
burn_key.add_argument(
"keyfile",
help="File containing 256 bits of binary key data. For the ECDSA_KEY purpose use PEM file.",
action="append",
type=argparse.FileType("rb"),
)
burn_key.add_argument(
"keypurpose",
help="Purpose to set.",
action="append",
choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME,
)
for _ in efuses.BLOCKS_FOR_KEYS:
burn_key.add_argument(
"block",
help="Key block to burn",
nargs="?",
action="append",
metavar="BLOCK",
choices=efuses.BLOCKS_FOR_KEYS,
)
burn_key.add_argument(
"keyfile",
help="File containing 256 bits of binary key data. For the ECDSA_KEY purpose use PEM file.",
nargs="?",
action="append",
metavar="KEYFILE",
type=argparse.FileType("rb"),
)
burn_key.add_argument(
"keypurpose",
help="Purpose to set.",
nargs="?",
action="append",
metavar="KEYPURPOSE",
choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME,
)
burn_key_digest = subparsers.add_parser(
"burn_key_digest",
help="Parse a RSA public key and burn the digest to key efuse block",
)
protect_options(burn_key_digest)
add_force_write_always(burn_key_digest)
burn_key_digest.add_argument(
"block",
help="Key block to burn",
action="append",
choices=efuses.BLOCKS_FOR_KEYS,
)
burn_key_digest.add_argument(
"keyfile",
help="Key file to digest (PEM format)",
action="append",
type=argparse.FileType("rb"),
)
burn_key_digest.add_argument(
"keypurpose",
help="Purpose to set.",
action="append",
choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES,
)
for _ in efuses.BLOCKS_FOR_KEYS:
burn_key_digest.add_argument(
"block",
help="Key block to burn",
nargs="?",
action="append",
metavar="BLOCK",
choices=efuses.BLOCKS_FOR_KEYS,
)
burn_key_digest.add_argument(
"keyfile",
help="Key file to digest (PEM format)",
nargs="?",
action="append",
metavar="KEYFILE",
type=argparse.FileType("rb"),
)
burn_key_digest.add_argument(
"keypurpose",
help="Purpose to set.",
nargs="?",
action="append",
metavar="KEYPURPOSE",
choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES,
)
p = subparsers.add_parser(
"set_flash_voltage",
help="Permanently set the internal flash voltage regulator "
"to either 1.8V, 3.3V or OFF. "
"This means GPIO45 can be high or low at reset without "
"changing the flash voltage.",
)
p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"])
p = subparsers.add_parser(
"burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3."
)
p.add_argument(
"mac",
help="Custom MAC Address to burn given in hexadecimal format with bytes "
"separated by colons (e.g. AA:CD:EF:01:02:03).",
type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"),
)
add_force_write_always(p)
p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.")
def burn_custom_mac(esp, efuses, args):
efuses["CUSTOM_MAC"].save(args.mac)
if not efuses.burn_all(check_batch_mode=True):
return
get_custom_mac(esp, efuses, args)
print("Successful")
def get_custom_mac(esp, efuses, args):
print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get()))
def set_flash_voltage(esp, efuses, args):
raise esptool.FatalError("set_flash_voltage is not supported!")
def adc_info(esp, efuses, args):
# fmt: off
# TODO: [ESP32H4] IDF-12268
print("Not supported yet")
# fmt: on
def burn_key(esp, efuses, args, digest=None):
if digest is None:
datafile_list = args.keyfile[
0 : len([name for name in args.keyfile if name is not None]) :
]
else:
datafile_list = digest[0 : len([name for name in digest if name is not None]) :]
efuses.force_write_always = args.force_write_always
block_name_list = args.block[
0 : len([name for name in args.block if name is not None]) :
]
keypurpose_list = args.keypurpose[
0 : len([name for name in args.keypurpose if name is not None]) :
]
util.check_duplicate_name_in_list(block_name_list)
if len(block_name_list) != len(datafile_list) or len(block_name_list) != len(
keypurpose_list
):
raise esptool.FatalError(
"The number of blocks (%d), datafile (%d) and keypurpose (%d) "
"should be the same."
% (len(block_name_list), len(datafile_list), len(keypurpose_list))
)
print("Burn keys to blocks:")
for block_name, datafile, keypurpose in zip(
block_name_list, datafile_list, keypurpose_list
):
efuse = None
for block in efuses.blocks:
if block_name == block.name or block_name in block.alias:
efuse = efuses[block.name]
if efuse is None:
raise esptool.FatalError("Unknown block name - %s" % (block_name))
num_bytes = efuse.bit_len // 8
block_num = efuses.get_index_block_by_name(block_name)
block = efuses.blocks[block_num]
if digest is None:
if keypurpose == "ECDSA_KEY":
sk = espsecure._load_ecdsa_signing_key(datafile)
data = sk.to_string()
if len(data) == 24:
# the private key is 24 bytes long for NIST192p, and 8 bytes of padding
data = b"\x00" * 8 + data
else:
data = datafile.read()
else:
data = datafile
print(" - %s" % (efuse.name), end=" ")
revers_msg = None
if efuses[block.key_purpose_name].need_reverse(keypurpose):
revers_msg = f"\tReversing byte order for {keypurpose} hardware peripheral"
data = data[::-1]
print("-> [%s]" % (util.hexify(data, " ")))
if revers_msg:
print(revers_msg)
if len(data) != num_bytes:
raise esptool.FatalError(
"Incorrect key file size %d. Key file must be %d bytes (%d bits) "
"of raw binary key data." % (len(data), num_bytes, num_bytes * 8)
)
if efuses[block.key_purpose_name].need_rd_protect(keypurpose):
read_protect = False if args.no_read_protect else True
else:
read_protect = False
write_protect = not args.no_write_protect
# using efuse instead of a block gives the advantage of checking it as the whole field.
efuse.save(data)
disable_wr_protect_key_purpose = False
if efuses[block.key_purpose_name].get() != keypurpose:
if efuses[block.key_purpose_name].is_writeable():
print(
"\t'%s': '%s' -> '%s'."
% (
block.key_purpose_name,
efuses[block.key_purpose_name].get(),
keypurpose,
)
)
efuses[block.key_purpose_name].save(keypurpose)
disable_wr_protect_key_purpose = True
else:
raise esptool.FatalError(
"It is not possible to change '%s' to '%s' "
"because write protection bit is set."
% (block.key_purpose_name, keypurpose)
)
else:
print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose))
if efuses[block.key_purpose_name].is_writeable():
disable_wr_protect_key_purpose = True
if disable_wr_protect_key_purpose:
print("\tDisabling write to '%s'." % block.key_purpose_name)
efuses[block.key_purpose_name].disable_write()
if read_protect:
print("\tDisabling read to key block")
efuse.disable_read()
if write_protect:
print("\tDisabling write to key block")
efuse.disable_write()
print("")
if not write_protect:
print("Keys will remain writeable (due to --no-write-protect)")
if args.no_read_protect:
print("Keys will remain readable (due to --no-read-protect)")
if not efuses.burn_all(check_batch_mode=True):
return
print("Successful")
def burn_key_digest(esp, efuses, args):
digest_list = []
datafile_list = args.keyfile[
0 : len([name for name in args.keyfile if name is not None]) :
]
block_list = args.block[
0 : len([block for block in args.block if block is not None]) :
]
for block_name, datafile in zip(block_list, datafile_list):
efuse = None
for block in efuses.blocks:
if block_name == block.name or block_name in block.alias:
efuse = efuses[block.name]
if efuse is None:
raise esptool.FatalError("Unknown block name - %s" % (block_name))
num_bytes = efuse.bit_len // 8
digest = espsecure._digest_sbv2_public_key(datafile)
if len(digest) != num_bytes:
raise esptool.FatalError(
"Incorrect digest size %d. Digest must be %d bytes (%d bits) "
"of raw binary key data." % (len(digest), num_bytes, num_bytes * 8)
)
digest_list.append(digest)
burn_key(esp, efuses, args, digest=digest_list)
def espefuse(esp, efuses, args, command):
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest="operation")
add_commands(subparsers, efuses)
try:
cmd_line_args = parser.parse_args(command.split())
except SystemExit:
traceback.print_stack()
raise esptool.FatalError('"{}" - incorrect command'.format(command))
if cmd_line_args.operation == "execute_scripts":
configfiles = cmd_line_args.configfiles
index = cmd_line_args.index
# copy arguments from args to cmd_line_args
vars(cmd_line_args).update(vars(args))
if cmd_line_args.operation == "execute_scripts":
cmd_line_args.configfiles = configfiles
cmd_line_args.index = index
if cmd_line_args.operation is None:
parser.print_help()
parser.exit(1)
operation_func = globals()[cmd_line_args.operation]
# each 'operation' is a module-level function of the same name
operation_func(esp, efuses, cmd_line_args)
def execute_scripts(esp, efuses, args):
efuses.batch_mode_cnt += 1
del args.operation
scripts = args.scripts
del args.scripts
for file in scripts:
with open(file.name, "r") as file:
exec(compile(file.read(), file.name, "exec"))
if args.debug:
for block in efuses.blocks:
data = block.get_bitstring(from_read=False)
block.print_block(data, "regs_for_burn", args.debug)
efuses.batch_mode_cnt -= 1
if not efuses.burn_all(check_batch_mode=True):
return
print("Successful")

View File

@@ -0,0 +1,105 @@
VER_NO: 709e8ea096e8a03a10006d40d5451a49
EFUSES:
WR_DIS : {show: y, blk : 0, word: 0, pos : 0, len : 32, start : 0, type : 'uint:32', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Disable programming of individual eFuses, rloc: EFUSE_RD_WR_DIS_REG, bloc: 'B0,B1,B2,B3'}
RD_DIS : {show: y, blk : 0, word: 1, pos : 0, len : 7, start : 32, type : 'uint:7', wr_dis : 0, rd_dis: null, alt : '', dict : '', desc: Disable reading from BlOCK4-10, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[6:0]', bloc: 'B4[6:0]'}
DIS_USB_JTAG : {show: y, blk : 0, word: 1, pos : 7, len : 1, start : 39, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the function of usb switch to jtag is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[7]', bloc: 'B4[7]'}
DIS_USB_SERIAL_JTAG : {show: y, blk : 0, word: 1, pos: 8, len : 1, start : 40, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether USB-Serial-JTAG is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[8]', bloc: 'B5[0]'}
DIS_FORCE_DOWNLOAD : {show: y, blk : 0, word: 1, pos: 9, len : 1, start : 41, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the function that forces chip into download mode is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[9]', bloc: 'B5[1]'}
SPI_DOWNLOAD_MSPI_DIS : {show: y, blk : 0, word: 1, pos: 10, len : 1, start : 42, type : bool, wr_dis : 17, rd_dis: null, alt : '', dict : '', desc: 'Represents whether SPI0 controller during boot_mode_download is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[10]', bloc: 'B5[2]'}
DIS_TWAI : {show: y, blk : 0, word: 1, pos: 11, len : 1, start : 43, type : bool, wr_dis : 2, rd_dis: null, alt : DIS_CAN, dict : '', desc: 'Represents whether TWAI function is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[11]', bloc: 'B5[3]'}
JTAG_SEL_ENABLE : {show: y, blk : 0, word: 1, pos: 12, len : 1, start : 44, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the selection between usb_to_jtag and pad_to_jtag through strapping gpio15 when both EFUSE_DIS_PAD_JTAG and EFUSE_DIS_USB_JTAG are equal to 0 is enabled or disabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[12]', bloc: 'B5[4]'}
DIS_PAD_JTAG : {show: y, blk : 0, word: 1, pos: 13, len : 1, start : 45, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether JTAG is disabled in the hard way(permanently). 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[13]', bloc: 'B5[5]'}
DIS_DOWNLOAD_MANUAL_ENCRYPT : {show: y, blk : 0, word: 1, pos: 14, len : 1, start : 46, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether flash encrypt function is disabled or enabled(except in SPI boot mode). 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[14]', bloc: 'B5[6]'}
RESERVED_RPT0_15_16 : {show: n, blk : 0, word: 1, pos: 15, len : 2, start : 47, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[16:15]', bloc: 'B5[7],B6[0]'}
RESERVED_RPT0_17 : {show: n, blk : 0, word: 1, pos: 17, len : 1, start : 49, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[17]', bloc: 'B6[1]'}
PVT_GLITCH_EN : {show: y, blk : 0, word: 1, pos: 18, len : 1, start : 50, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Represents whether to enable PVT power glitch monitor function, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[18]', bloc: 'B6[2]'}
PVT_GLITCH_CHARGE_RESET : {show: y, blk : 0, word: 1, pos: 19, len : 1, start : 51, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Represents whether to trigger reset or charge pump when PVT power glitch happened, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[19]', bloc: 'B6[3]'}
PVT_GLITCH_MODE : {show: y, blk : 0, word: 1, pos: 20, len : 2, start : 52, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: Use to configure glitch mode, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[21:20]', bloc: 'B6[5:4]'}
DIS_CORE1 : {show: y, blk : 0, word: 1, pos: 22, len : 1, start: 54, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Disable core1, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[22]', bloc: 'B6[6]'}
SPI_BOOT_CRYPT_CNT : {show: y, blk : 0, word: 1, pos: 23, len : 3, start : 55, type : 'uint:3', wr_dis : 4, rd_dis: null, alt : '', dict: '{0: "Disable", 1: "Enable", 3: "Disable", 7: "Enable"}', desc: Enables flash encryption when 1 or 3 bits are set and disables otherwise, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[25:23]', bloc: 'B6[7],B7[1:0]'}
SECURE_BOOT_KEY_REVOKE0 : {show: y, blk : 0, word: 1, pos: 26, len : 1, start : 58, type : bool, wr_dis : 5, rd_dis: null, alt : '', dict : '', desc: Revoke 1st secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[25]', bloc: 'B7[1]'}
SECURE_BOOT_KEY_REVOKE1 : {show: y, blk : 0, word: 1, pos: 27, len : 1, start : 59, type : bool, wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: Revoke 2nd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[26]', bloc: 'B7[2]'}
SECURE_BOOT_KEY_REVOKE2 : {show: y, blk : 0, word: 1, pos: 28, len : 1, start : 60, type : bool, wr_dis : 7, rd_dis: null, alt : '', dict : '', desc: Revoke 3rd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[27]', bloc: 'B7[3]'}
KEY_PURPOSE_0 : {show: y, blk : 0, word: 2, pos: 0, len : 5, start : 64, type : 'uint:5', wr_dis : 8, rd_dis: null, alt : KEY0_PURPOSE, dict : '', desc: Represents the purpose of Key0, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[4:0]', bloc: 'B8[4:0]'}
KEY_PURPOSE_1 : {show: y, blk : 0, word: 2, pos: 5, len : 5, start : 69, type : 'uint:5', wr_dis : 9, rd_dis: null, alt : KEY1_PURPOSE, dict : '', desc: Represents the purpose of Key1, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[9:5]', bloc: 'B8[7:5],B9[1:0]'}
KEY_PURPOSE_2 : {show: y, blk : 0, word: 2, pos: 10, len : 5, start : 74, type : 'uint:5', wr_dis : 10, rd_dis: null, alt : KEY2_PURPOSE, dict : '', desc: Represents the purpose of Key2, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[14:10]', bloc: 'B9[6:2]'}
KEY_PURPOSE_3 : {show: y, blk : 0, word: 2, pos: 15, len : 5, start: 79, type : 'uint:5', wr_dis : 11, rd_dis: null, alt : KEY3_PURPOSE, dict : '', desc: Represents the purpose of Key3, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[19:15]', bloc: 'B9[7],B10[3:0]'}
KEY_PURPOSE_4 : {show: y, blk : 0, word: 2, pos: 20, len : 5, start: 84, type : 'uint:5', wr_dis : 12, rd_dis: null, alt : KEY4_PURPOSE, dict : '', desc: Represents the purpose of Key4, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[24:20]', bloc: 'B10[7:4],B11[0]'}
KEY_PURPOSE_5 : {show: y, blk : 0, word: 2, pos: 25, len : 5, start: 89, type : 'uint:5', wr_dis : 13, rd_dis: null, alt : KEY5_PURPOSE, dict : '', desc: Represents the purpose of Key5, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[29:25]', bloc: 'B11[5:1]'}
SEC_DPA_LEVEL : {show: y, blk : 0, word: 2, pos: 30, len : 2, start: 94, type : 'uint:2', wr_dis : 14, rd_dis: null, alt : DPA_SEC_LEVEL, dict : '', desc: Represents the spa secure level by configuring the clock random divide mode, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[31:30]', bloc: 'B11[7:6]'}
XTS_DPA_PSEUDO_LEVEL : {show: y, blk : 0, word: 3, pos: 0, len : 2, start: 96, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[1:0]', bloc: 'B12[1:0]'}
XTS_DPA_CLK_ENABLE : {show: y, blk : 0, word: 3, pos: 2, len : 1, start: 98, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[2]', bloc: 'B12[2]'}
ECC_FORCE_CONST_TIME : {show: y, blk : 0, word: 3, pos: 3, len : 1, start: 99, type : bool, wr_dis: 19, rd_dis: null, alt : '', dict : '', desc: 'ecc force const time', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[3]', bloc: 'B12[3]'}
ECDSA_P384_ENABLE : {show: y, blk : 0, word: 3, pos: 4, len : 1, start: 100, type : bool, wr_dis: 19, rd_dis: null, alt : '', dict : '', desc: 'if the chip supports ECDSA P384', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[4]', bloc: 'B12[4]'}
SECURE_BOOT_EN : {show: y, blk : 0, word: 3, pos: 5, len : 1, start: 101, type : bool, wr_dis : 15, rd_dis: null, alt : '', dict : '', desc: 'Represents whether secure boot is enabled or disabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[5]', bloc: 'B12[5]'}
SECURE_BOOT_AGGRESSIVE_REVOKE : {show: y, blk : 0, word: 3, pos: 6, len : 1, start: 102, type : bool, wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: 'Represents whether revoking aggressive secure boot is enabled or disabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[6]', bloc: 'B12[6]'}
KM_DISABLE_DEPLOY_MODE : {show: y, blk : 0, word: 3, pos: 7, len : 5, start: 103, type : 'uint:5', wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: Represents whether the new key deployment of key manager is disabled, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[11:7]', bloc: 'B12[7],B13[3:0]'}
KM_RND_SWITCH_CYCLE : {show: y, blk : 0, word: 3, pos: 12, len : 2, start: 108, type : 'uint:2', wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: Represents the cycle at which the Key Manager switches random numbers, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[13:12]', bloc: 'B13[5:4]'}
KM_DEPLOY_ONLY_ONCE : {show: y, blk : 0, word: 3, pos: 14, len : 5, start: 110, type : 'uint:5', wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: Represents whether the corresponding key can be deployed only once, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[18:14]', bloc: 'B13[7:6],B14[2:0]'}
FORCE_USE_KEY_MANAGER_KEY : {show: y, blk : 0, word: 3, pos: 19, len : 5, start: 115, type : 'uint:5', wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: Represents whether the corresponding key must come from Key Manager, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[23:19]', bloc: 'B14[7:3]'}
FORCE_DISABLE_SW_INIT_KEY : {show: y, blk : 0, word: 3, pos: 24, len : 1, start: 120, type : bool, wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: Represents whether to disable the use of the initialization key written by softw, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[24]', bloc: 'B15[0]'}
KM_XTS_KEY_LENGTH_256 : {show: y, blk : 0, word: 3, pos: 25, len : 1, start: 121, type : bool, wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: Represents which key flash encryption uses, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[25]', bloc: 'B15[1]'}
LOCK_KM_KEY : {show: y, blk : 0, word: 3, pos: 26, len : 1, start: 122, type : bool, wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: Represents whether the keys in the Key Manager are locked after deployment, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[26]', bloc: 'B15[2]'}
FLASH_TPUW : {show: y, blk : 0, word: 3, pos: 27, len : 3, start: 123, type : 'uint:3', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Represents the flash waiting time after power-up; in unit of ms. When the value less than 15; the waiting time is the programmed value. Otherwise; the waiting time is 2 times the programmed value, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[29:27]', bloc: 'B15[5:3]'}
DIS_DOWNLOAD_MODE : {show: y, blk : 0, word: 3, pos: 31, len : 1, start: 127, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether Download mode is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[31]', bloc: 'B15[7]'}
DIS_DIRECT_BOOT : {show: y, blk : 0, word: 4, pos: 0, len : 1, start: 128, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether direct boot mode is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[0]', bloc: 'B16[0]'}
DIS_USB_SERIAL_JTAG_ROM_PRINT : {show: y, blk : 0, word: 4, pos: 1, len : 1, start: 129, type : bool, wr_dis : 18, rd_dis: null, alt : DIS_USB_PRINT, dict : '', desc: 'Represents whether print from USB-Serial-JTAG is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[1]', bloc: 'B16[1]'}
DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE: {show: y, blk : 0, word: 4, pos: 2, len : 1, start: 130, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the USB-Serial-JTAG download function is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[2]', bloc: 'B16[2]'}
ENABLE_SECURITY_DOWNLOAD : {show: y, blk : 0, word: 4, pos: 3, len : 1, start: 131, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether security download is enabled or disabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[3]', bloc: 'B16[3]'}
UART_PRINT_CONTROL : {show: y, blk : 0, word: 4, pos: 4, len : 2, start: 132, type : 'uint:2', wr_dis : 18, rd_dis: null, alt : '', dict: '{0: "Enable", 1: "Enable when GPIO8 is low at reset", 2: "Enable when GPIO8 is high at reset", 3: "Disable"}', desc: Set the default UARTboot message output mode, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[5:4]', bloc: 'B16[5:4]'}
FORCE_SEND_RESUME : {show: y, blk : 0, word: 4, pos: 6, len : 1, start: 134, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether ROM code is forced to send a resume command during SPI boot. 1: forced. 0:not forced', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[6]', bloc: 'B16[6]'}
SECURE_VERSION : {show: y, blk : 0, word: 4, pos: 7, len : 16, start: 135, type : 'uint:16', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Represents the version used by ESP-IDF anti-rollback feature, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[22:7]', bloc: 'B16[7],B17[7:0],B18[6:0]'}
HUK_GEN_STATE : {show: y, blk : 0, word: 4, pos: 23, len : 5, start: 151, type : 'uint:5', wr_dis : 19, rd_dis: null, alt : '', dict : '', desc: huk gen state, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[27:23]', bloc: 'B18[7],B19[3:0]'}
FLASH_LDO_EFUSE_SEL : {show: y, blk : 0, word: 4, pos: 28, len : 1, start: 156, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the HUK generate mode is valid', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[28]', bloc: 'B19[4]'}
USB_DREFH : {show: y, blk : 0, word: 5, pos: 0, len : 2, start: 160, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: USB device DREFH, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[1:0]', bloc: 'B20[1:0]'}
USB_DREFL : {show: y, blk : 0, word: 5, pos: 2, len : 2, start: 162, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: USB device DREFL, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[3:2]]', bloc: 'B20[3:2]'}
USB_EXCHG_PINS : {show: y, blk : 0, word: 5, pos: 4, len : 1, start: 164, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Represents whether the D+ and D- pins of USB_SERIAL_JTAG PHY is exchanged, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[4]]', bloc: 'B20[4]'}
USB_OTG_FS_DREFH : {show: y, blk : 0, word: 5, pos: 5, len : 2, start: 165, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: USB OTG FS DREFH, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[6:5]', bloc: 'B20[6:5]'}
USB_OTG_FS_DREFL : {show: y, blk : 0, word: 5, pos: 7, len : 2, start: 167, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: USB OTG FS DREFL, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[8:7]', bloc: 'B20[7],B21[0]'}
USB_OTG_FS_EXCHG_PINS : {show: y, blk : 0, word: 5, pos: 9, len : 1, start: 169, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: USB OTG FS EXCHG PINS, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[9]', bloc: 'B21[1]'}
USB_PHY_SEL : {show: y, blk : 0, word: 5, pos: 10, len : 1, start: 170, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: USB PHY SEL, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[10]', bloc: 'B21[2]'}
MAC : {show: y, blk : 1, word: 0, pos : 0, len : 48, start : 0, type : 'bytes:6', wr_dis : 20, rd_dis: null, alt : MAC_FACTORY, dict : '', desc: MAC address, rloc: EFUSE_RD_MAC_SPI_SYS_0_REG, bloc: 'B0,B1,B2,B3,B4,B5'}
MAC_EXT : {show: y, blk : 1, word: 1, pos: 16, len : 16, start : 48, type : 'bytes:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the extended bits of MAC address, rloc: 'EFUSE_RD_MAC_SPI_SYS_1_REG[31:16]', bloc: 'B6,B7'}
MAC_SPI_RESERVED : {show: n, blk : 1, word: 2, pos : 0, len : 14, start : 64, type : 'uint:14', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[13:0]', bloc: 'B8,B9[5:0]'}
SPI_PAD_CONF_1 : {show: n, blk : 1, word: 2, pos: 14, len : 18, start : 78, type : 'uint:18', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the first part of SPI_PAD_CONF, rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[31:14]', bloc: 'B9[7:6],B10,B11'}
SPI_PAD_CONF_2 : {show: n, blk : 1, word: 3, pos : 0, len : 18, start : 96, type : 'uint:18', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the second part of SPI_PAD_CONF, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[17:0]', bloc: 'B12,B13,B14[1:0]'}
WAFER_VERSION_MINOR : {show: y, blk : 1, word: 3, pos: 18, len : 4, start: 114, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[21:18]', bloc: 'B14[5:2]'}
WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 3, pos: 22, len : 2, start: 118, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[23:22]', bloc: 'B14[7:6]'}
PKG_VERSION : {show: y, blk : 1, word: 3, pos: 24, len : 3, start: 120, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Package version, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[26:24]', bloc: 'B15[2:0]'}
BLK_VERSION_MINOR : {show: y, blk : 1, word: 3, pos: 27, len : 3, start: 123, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MINOR of BLOCK2, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[29:27]', bloc: 'B15[5:3]'}
BLK_VERSION_MAJOR : {show: y, blk : 1, word: 3, pos: 30, len : 2, start: 126, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MAJOR of BLOCK2, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[31:30]', bloc: 'B15[7:6]'}
FLASH_CAP : {show: y, blk : 1, word: 4, pos : 0, len : 3, start: 128, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[2:0]', bloc: 'B16[2:0]'}
FLASH_TEMP : {show: y, blk : 1, word: 4, pos : 3, len : 2, start: 131, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[4:3]', bloc: 'B16[4:3]'}
FLASH_VENDOR : {show: y, blk : 1, word: 4, pos : 5, len : 3, start: 133, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[7:5]', bloc: 'B16[7:5]'}
RESERVED_1_136 : {show: n, blk : 1, word: 4, pos : 8, len : 24, start: 136, type : 'uint:24', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[31:8]', bloc: 'B17,B18,B19'}
SYS_DATA_PART0_2 : {show: n, blk : 1, word: 5, pos : 0, len : 32, start: 160, type : 'uint:32', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the second 32 bits of the zeroth part of system data, rloc: EFUSE_RD_MAC_SPI_SYS_5_REG, bloc: 'B20,B21,B22,B23'}
OPTIONAL_UNIQUE_ID : {show: y, blk : 2, word: 0, pos : 0, len: 128, start : 0, type: 'bytes:16', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Optional unique 128-bit ID, rloc: EFUSE_RD_SYS_PART1_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15'}
TEMP_CALIB : {show: y, blk : 2, word: 4, pos : 0, len : 9, start: 128, type : 'uint:9', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Temperature calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[8:0]', bloc: 'B16,B17[0]'}
OCODE : {show: y, blk : 2, word: 4, pos : 9, len : 8, start: 137, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC OCode, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[16:9]', bloc: 'B17[7:1],B18[0]'}
ADC1_INIT_CODE_ATTEN0 : {show: y, blk : 2, word: 4, pos: 17, len : 10, start: 145, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[26:17]', bloc: 'B18[7:1],B19[2:0]'}
ADC1_INIT_CODE_ATTEN1 : {show: y, blk : 2, word: 4, pos: 27, len : 10, start: 155, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten1, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[31:27]', bloc: 'B19[7:3],B20[4:0]'}
ADC1_INIT_CODE_ATTEN2 : {show: y, blk : 2, word: 5, pos : 5, len : 10, start: 165, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten2, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[14:5]', bloc: 'B20[7:5],B21[6:0]'}
ADC1_INIT_CODE_ATTEN3 : {show: y, blk : 2, word: 5, pos: 15, len : 10, start: 175, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten3, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[24:15]', bloc: 'B21[7],B22,B23[0]'}
ADC1_CAL_VOL_ATTEN0 : {show: y, blk : 2, word: 5, pos: 25, len : 10, start: 185, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[31:25]', bloc: 'B23[7:1],B24[2:0]'}
ADC1_CAL_VOL_ATTEN1 : {show: y, blk : 2, word: 6, pos : 3, len : 10, start: 195, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten1, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[12:3]', bloc: 'B24[7:3],B25[4:0]'}
ADC1_CAL_VOL_ATTEN2 : {show: y, blk : 2, word: 6, pos: 13, len : 10, start: 205, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten2, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[22:13]', bloc: 'B25[7:5],B26[6:0]'}
ADC1_CAL_VOL_ATTEN3 : {show: y, blk : 2, word: 6, pos: 23, len : 10, start: 215, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten3, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[31:23]', bloc: 'B26[7],B27,B28[0]'}
ADC1_INIT_CODE_ATTEN0_CH0 : {show: y, blk : 2, word: 7, pos : 1, len : 4, start: 225, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch0, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[4:1]', bloc: 'B28[4:1]'}
ADC1_INIT_CODE_ATTEN0_CH1 : {show: y, blk : 2, word: 7, pos : 5, len : 4, start: 229, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch1, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[8:5]', bloc: 'B28[7:5],B29[0]'}
ADC1_INIT_CODE_ATTEN0_CH2 : {show: y, blk : 2, word: 7, pos : 9, len : 4, start: 233, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch2, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[12:9]', bloc: 'B29[4:1]'}
ADC1_INIT_CODE_ATTEN0_CH3 : {show: y, blk : 2, word: 7, pos: 13, len : 4, start: 237, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch3, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[16:13]', bloc: 'B29[7:5],B30[0]'}
ADC1_INIT_CODE_ATTEN0_CH4 : {show: y, blk : 2, word: 7, pos: 17, len : 4, start: 241, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch4, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[20:17]', bloc: 'B30[4:1]'}
ADC1_INIT_CODE_ATTEN0_CH5 : {show: y, blk : 2, word: 7, pos: 21, len : 4, start: 245, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch5, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[24:21]', bloc: 'B30[7:5],B31[0]'}
ADC1_INIT_CODE_ATTEN0_CH6 : {show: y, blk : 2, word: 7, pos: 25, len : 4, start: 249, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch6, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[28:25]', bloc: 'B31[4:1]'}
RESERVED_2_253 : {show: n, blk : 2, word: 7, pos: 29, len : 3, start: 253, type : 'uint:3', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[31:29]', bloc: 'B31[7:5]'}
BLOCK_USR_DATA : {show: y, blk : 3, word: 0, pos : 0, len: 192, start : 0, type: 'bytes:24', wr_dis : 22, rd_dis: null, alt : USER_DATA, dict : '', desc: User data, rloc: EFUSE_RD_USR_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23'}
RESERVED_3_192 : {show: n, blk : 3, word: 6, pos : 0, len : 8, start: 192, type : 'uint:8', wr_dis : 22, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_USR_DATA6_REG[7:0]', bloc: B24}
CUSTOM_MAC : {show: y, blk : 3, word: 6, pos : 8, len : 48, start: 200, type : 'bytes:6', wr_dis : 22, rd_dis: null, alt: MAC_CUSTOM USER_DATA_MAC_CUSTOM, dict : '', desc: Custom MAC, rloc: 'EFUSE_RD_USR_DATA6_REG[31:8]', bloc: 'B25,B26,B27,B28,B29,B30'}
RESERVED_3_248 : {show: n, blk : 3, word: 7, pos: 24, len : 8, start: 248, type : 'uint:8', wr_dis : 22, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_USR_DATA7_REG[31:24]', bloc: B31}
BLOCK_KEY0 : {show: y, blk : 4, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 23, rd_dis : 0, alt : KEY0, dict : '', desc: Key0 or user data, rloc: EFUSE_RD_KEY0_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'}
BLOCK_KEY1 : {show: y, blk : 5, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 24, rd_dis : 1, alt : KEY1, dict : '', desc: Key1 or user data, rloc: EFUSE_RD_KEY1_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'}
BLOCK_KEY2 : {show: y, blk : 6, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 25, rd_dis : 2, alt : KEY2, dict : '', desc: Key2 or user data, rloc: EFUSE_RD_KEY2_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'}
BLOCK_KEY3 : {show: y, blk : 7, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 26, rd_dis : 3, alt : KEY3, dict : '', desc: Key3 or user data, rloc: EFUSE_RD_KEY3_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'}
BLOCK_KEY4 : {show: y, blk : 8, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 27, rd_dis : 4, alt : KEY4, dict : '', desc: Key4 or user data, rloc: EFUSE_RD_KEY4_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'}
BLOCK_KEY5 : {show: y, blk : 9, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 28, rd_dis : 5, alt : KEY5, dict : '', desc: Key5 or user data, rloc: EFUSE_RD_KEY5_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'}
BLOCK_SYS_DATA2 : {show: y, blk: 10, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 29, rd_dis : 6, alt : SYS_DATA_PART2, dict : '', desc: System data part 2 (reserved), rloc: EFUSE_RD_SYS_PART2_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'}

View File

@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton,
# SPDX-FileCopyrightText: 2014-2025 Fredrik Ahlberg, Angus Gratton,
# Espressif Systems (Shanghai) CO LTD, other contributors as noted.
#
# SPDX-License-Identifier: GPL-2.0-or-later
@@ -852,7 +852,10 @@ def main(argv=None, esp=None):
"setting --no-stub"
)
args.no_stub = True
elif esp.CHIP_NAME == "ESP32-H21": # TODO: [ESP32H21] IDF-11509
elif esp.CHIP_NAME in [
"ESP32-H21",
"ESP32-H4",
]: # TODO: [ESP32H21] IDF-11509 [ESP32H4] IDF-12271
print(
f"WARNING: Stub loader is not yet supported on {esp.CHIP_NAME}, "
"setting --no-stub"

View File

@@ -28,6 +28,7 @@ from .targets import (
ESP32H2BETA2ROM,
ESP32H2ROM,
ESP32H21ROM,
ESP32H4ROM,
ESP32P4ROM,
ESP32ROM,
ESP32S2ROM,
@@ -96,6 +97,7 @@ def LoadFirmwareImage(chip, image_file):
"esp32h2": ESP32H2FirmwareImage,
"esp32h21": ESP32H21FirmwareImage,
"esp32p4": ESP32P4FirmwareImage,
"esp32h4": ESP32H4FirmwareImage,
}[chip](f)
else: # Otherwise, ESP8266 so look at magic to determine the image type
magic = ord(f.read(1))
@@ -1180,6 +1182,13 @@ class ESP32C5BETA3FirmwareImage(ESP32C6FirmwareImage):
ESP32C5BETA3ROM.BOOTLOADER_IMAGE = ESP32C5BETA3FirmwareImage
class ESP32H4FirmwareImage(ESP32FirmwareImage):
"""ESP32H4 Firmware Image almost exactly the same as ESP32FirmwareImage"""
ROM_LOADER = ESP32H4ROM
ESP32H4ROM.BOOTLOADER_IMAGE = ESP32H4FirmwareImage
class ESP32P4FirmwareImage(ESP32FirmwareImage):

View File

@@ -10,6 +10,7 @@ from .esp32h2 import ESP32H2ROM
from .esp32h21 import ESP32H21ROM
from .esp32h2beta1 import ESP32H2BETA1ROM
from .esp32h2beta2 import ESP32H2BETA2ROM
from .esp32h4 import ESP32H4ROM
from .esp32p4 import ESP32P4ROM
from .esp32s2 import ESP32S2ROM
from .esp32s3 import ESP32S3ROM
@@ -35,6 +36,7 @@ CHIP_DEFS = {
"esp32h2": ESP32H2ROM,
"esp32h21": ESP32H21ROM,
"esp32p4": ESP32P4ROM,
"esp32h4": ESP32H4ROM,
}
CHIP_LIST = list(CHIP_DEFS.keys())

213
esptool/targets/esp32h4.py Normal file
View File

@@ -0,0 +1,213 @@
# SPDX-FileCopyrightText: 2025 Fredrik Ahlberg, Angus Gratton,
# Espressif Systems (Shanghai) CO LTD, other contributors as noted.
#
# SPDX-License-Identifier: GPL-2.0-or-later
import struct
from .esp32c3 import ESP32C3ROM
from ..loader import ESPLoader
from ..util import FatalError
class ESP32H4ROM(ESP32C3ROM):
CHIP_NAME = "ESP32-H4"
IMAGE_CHIP_ID = 28
IROM_MAP_START = 0x42000000
IROM_MAP_END = 0x42800000
DROM_MAP_START = 0x42800000
DROM_MAP_END = 0x43000000
BOOTLOADER_FLASH_OFFSET = 0x2000
SPI_REG_BASE = 0x60099000
SPI_USR_OFFS = 0x18
SPI_USR1_OFFS = 0x1C
SPI_USR2_OFFS = 0x20
SPI_MOSI_DLEN_OFFS = 0x24
SPI_MISO_DLEN_OFFS = 0x28
SPI_W0_OFFS = 0x58
UART_DATE_REG_ADDR = 0x60012000 + 0x7C
EFUSE_BASE = 0x600B1800
EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x044
MAC_EFUSE_REG = EFUSE_BASE + 0x044
EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address
EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34
EFUSE_PURPOSE_KEY0_SHIFT = 0
EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34
EFUSE_PURPOSE_KEY1_SHIFT = 5
EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x34
EFUSE_PURPOSE_KEY2_SHIFT = 10
EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x34
EFUSE_PURPOSE_KEY3_SHIFT = 15
EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x34
EFUSE_PURPOSE_KEY4_SHIFT = 20
EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x34
EFUSE_PURPOSE_KEY5_SHIFT = 25
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 14
EFUSE_SPI_BOOT_CRYPT_CNT_REG = EFUSE_BASE + 0x030
EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 23
EFUSE_SECURE_BOOT_EN_REG = EFUSE_BASE + 0x038
EFUSE_SECURE_BOOT_EN_MASK = 1 << 5
EFUSE_FORCE_USE_KM_KEY_REG = EFUSE_BASE + 0x038
EFUSE_FORCE_USE_KM_KEY_MASK = 0xF << 19
PURPOSE_VAL_XTS_AES128_KEY = 4
SUPPORTS_ENCRYPTED_FLASH = True
FLASH_ENCRYPTED_WRITE_ALIGN = 16
UARTDEV_BUF_NO = 0x4087F580 # Variable in ROM .bss which indicates the port in use
UARTDEV_BUF_NO_USB_JTAG_SERIAL = 3 # The above var when USB-JTAG/Serial is used
DR_REG_LP_WDT_BASE = 0x600B5400
RTC_CNTL_WDTCONFIG0_REG = DR_REG_LP_WDT_BASE + 0x0 # LP_WDT_RWDT_CONFIG0_REG
RTC_CNTL_WDTWPROTECT_REG = DR_REG_LP_WDT_BASE + 0x0018 # LP_WDT_RWDT_WPROTECT_REG
RTC_CNTL_SWD_CONF_REG = DR_REG_LP_WDT_BASE + 0x001C # LP_WDT_SWD_CONFIG_REG
RTC_CNTL_SWD_AUTO_FEED_EN = 1 << 18
RTC_CNTL_SWD_WPROTECT_REG = DR_REG_LP_WDT_BASE + 0x0020 # LP_WDT_SWD_WPROTECT_REG
RTC_CNTL_SWD_WKEY = 0x50D83AA1 # LP_WDT_SWD_WKEY, same as WDT key in this case
PCR_SYSCLK_CONF_REG = 0x60096110
PCR_SYSCLK_XTAL_FREQ_V = 0x7F << 24
PCR_SYSCLK_XTAL_FREQ_S = 24
FLASH_FREQUENCY = {
"48m": 0x0,
"24m": 0x0,
"16m": 0x1,
"12m": 0x2,
}
MEMORY_MAP = [
[0x00000000, 0x00010000, "PADDING"],
[0x42800000, 0x43000000, "DROM"],
[0x40800000, 0x40880000, "DRAM"],
[0x40800000, 0x40880000, "BYTE_ACCESSIBLE"],
[0x4004AC00, 0x40050000, "DROM_MASK"],
[0x40000000, 0x4004AC00, "IROM_MASK"],
[0x42000000, 0x42800000, "IROM"],
[0x40800000, 0x40880000, "IRAM"],
[0x50000000, 0x50004000, "RTC_IRAM"],
[0x50000000, 0x50004000, "RTC_DRAM"],
[0x600FE000, 0x60100000, "MEM_INTERNAL2"],
]
UF2_FAMILY_ID = 0x9E0BAA8A
# not alloc yet, return 0
def get_pkg_version(self):
return 0
def get_minor_chip_version(self):
return 0
def get_major_chip_version(self):
return 0
def get_chip_description(self):
chip_name = {
0: "ESP32-H4 (QFN40)",
}.get(self.get_pkg_version(), "unknown ESP32-H4")
major_rev = self.get_major_chip_version()
minor_rev = self.get_minor_chip_version()
return f"{chip_name} (revision v{major_rev}.{minor_rev})"
def get_chip_features(self):
return ["WiFi 6", "BT 5", "IEEE802.15.4"]
def get_crystal_freq(self):
# ESP32H4 XTAL is fixed to 32MHz
return 32
def change_baud(self, baud):
ESPLoader.change_baud(self, baud)
def read_mac(self, mac_type="BASE_MAC"):
"""Read MAC from EFUSE region"""
mac0 = self.read_reg(self.MAC_EFUSE_REG)
mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC
base_mac = struct.pack(">II", mac1, mac0)[2:]
ext_mac = struct.pack(">H", (mac1 >> 16) & 0xFFFF)
eui64 = base_mac[0:3] + ext_mac + base_mac[3:6]
# BASE MAC: 60:55:f9:f7:2c:a2
# EUI64 MAC: 60:55:f9:ff:fe:f7:2c:a2
# EXT_MAC: ff:fe
macs = {
"BASE_MAC": tuple(base_mac),
"EUI64": tuple(eui64),
"MAC_EXT": tuple(ext_mac),
}
return macs.get(mac_type, None)
def get_flash_crypt_config(self):
return None # doesn't exist on ESP32-H4
def get_secure_boot_enabled(self):
return (
self.read_reg(self.EFUSE_SECURE_BOOT_EN_REG)
& self.EFUSE_SECURE_BOOT_EN_MASK
)
def get_key_block_purpose(self, key_block):
if key_block < 0 or key_block > 5:
raise FatalError("Valid key block numbers must be in range 0-5")
reg, shift = [
(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT),
(self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT),
(self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT),
(self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT),
(self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT),
(self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT),
][key_block]
return (self.read_reg(reg) >> shift) & 0x1F
def is_flash_encryption_key_valid(self):
# Need to see an AES-128 key
purposes = [self.get_key_block_purpose(b) for b in range(6)]
return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes)
def check_spi_connection(self, spi_connection):
if not set(spi_connection).issubset(set(range(0, 40))):
raise FatalError("SPI Pin numbers must be in the range 0-39.")
if any([v for v in spi_connection if v in [13, 14]]):
print(
"WARNING: GPIO pins 13 and 14 are used by USB-Serial/JTAG, "
"consider using other pins for SPI flash connection."
)
class ESP32H4StubLoader(ESP32H4ROM):
"""Access class for ESP32H4 stub loader, runs on top of ROM.
(Basically the same as ESP32StubLoader, but different base class.
Can possibly be made into a mixin.)
"""
FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c
STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM
IS_STUB = True
def __init__(self, rom_loader):
self.secure_download_mode = rom_loader.secure_download_mode
self._port = rom_loader._port
self._trace_enabled = rom_loader._trace_enabled
self.cache = rom_loader.cache
self.flush_input() # resets _slip_reader
ESP32H4ROM.STUB_CLASS = ESP32H4StubLoader

View File

@@ -252,7 +252,8 @@ class TestReadCommands(EfuseTestCase):
# TODO: [ESP32H21] IDF-11506
@pytest.mark.skipif(arg_chip == "esp32h21", reason="Not supported yet")
# TODO: [ESP32H4] IDF-12268
@pytest.mark.skipif(arg_chip in ["esp32h21", "esp32h4"], reason="Not supported yet")
class TestReadProtectionCommands(EfuseTestCase):
def test_read_protect_efuse(self):
self.espefuse_py("read_protect_efuse -h")
@@ -394,7 +395,8 @@ class TestReadProtectionCommands(EfuseTestCase):
# TODO: [ESP32H21] IDF-11506
@pytest.mark.skipif(arg_chip == "esp32h21", reason="Not supported yet")
# TODO: [ESP32H4] IDF-12268
@pytest.mark.skipif(arg_chip in ["esp32h21", "esp32h4"], reason="Not supported yet")
class TestWriteProtectionCommands(EfuseTestCase):
def test_write_protect_efuse(self):
self.espefuse_py("write_protect_efuse -h")
@@ -455,6 +457,9 @@ class TestWriteProtectionCommands(EfuseTestCase):
@pytest.mark.skipif(arg_chip == "esp32p4", reason="No Custom MAC Address defined yet")
@pytest.mark.skipif(
arg_chip == "esp32h4", reason="Not supported yet"
) # TODO: [ESP32H4] IDF-12268
class TestBurnCustomMacCommands(EfuseTestCase):
def test_burn_custom_mac(self):
self.espefuse_py("burn_custom_mac -h")
@@ -675,6 +680,9 @@ class TestValueArgForBurnEfuseCommands(EfuseTestCase):
)
@pytest.mark.skipif(
arg_chip == "esp32h4", reason="Not supported yet"
) # TODO: [ESP32H4] IDF-12268
class TestBurnEfuseCommands(EfuseTestCase):
@pytest.mark.skipif(
arg_chip != "esp32",
@@ -723,11 +731,9 @@ class TestBurnEfuseCommands(EfuseTestCase):
self.espefuse_py("get_custom_mac", check_msg=f"aa:cd:ef:01:02:03 {crc_msg}")
# TODO: [ESP32H21] IDF-11506
# TODO: [ESP32H4] IDF-12268
@pytest.mark.skipif(
arg_chip
in [
"esp32h21",
],
arg_chip in ["esp32h21", "esp32h4"],
reason="No such eFuses, will be defined later",
)
def test_burn_efuse(self):
@@ -1876,7 +1882,8 @@ class TestByteOrderBurnKeyCommand(EfuseTestCase):
# TODO: [ESP32H21] IDF-11506
@pytest.mark.skipif(arg_chip == "esp32h21", reason="Not supported yet")
# TODO: [ESP32H4] IDF-12268
@pytest.mark.skipif(arg_chip in ["esp32h21", "esp32h4"], reason="Not supported yet")
class TestExecuteScriptsCommands(EfuseTestCase):
@classmethod
def setup_class(self):
@@ -1959,6 +1966,9 @@ class TestExecuteScriptsCommands(EfuseTestCase):
) in output
@pytest.mark.skipif(
arg_chip == "esp32h4", reason="Not supported yet"
) # TODO: [ESP32H4] IDF-12268
class TestMultipleCommands(EfuseTestCase):
def test_multiple_cmds_help(self):
if arg_chip == "esp32c2":
@@ -2104,6 +2114,9 @@ class TestKeyPurposes(EfuseTestCase):
)
@pytest.mark.skipif(
arg_chip == "esp32h4", reason="Not supported yet"
) # TODO: [ESP32H4] IDF-12268
class TestPostponedEfuses(EfuseTestCase):
def test_postpone_efuses(self):
if arg_chip == "esp32":
@@ -2130,6 +2143,9 @@ class TestPostponedEfuses(EfuseTestCase):
assert "Successful" in output
@pytest.mark.skipif(
arg_chip == "esp32h4", reason="Not supported yet"
) # TODO: [ESP32H4] IDF-12268
class TestCSVEfuseTable(EfuseTestCase):
def test_extend_efuse_table_with_csv_file(self):
csv_file = f"{IMAGES_DIR}/esp_efuse_custom_table.csv"