Skip to content

Commit

Permalink
feat(espefuse): Adds incompatible eFuse settings check for S3
Browse files Browse the repository at this point in the history
  • Loading branch information
KonstantinKondrashov authored and radimkarnis committed Jun 18, 2024
1 parent 1059ec7 commit c244843
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 0 deletions.
4 changes: 4 additions & 0 deletions docs/en/espefuse/burn-efuse-cmd.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ Positional arguments:
- ``eFuse name``
- ``value``

Optional arguments:

* ``--force``. Suppress an error to burn eFuses. The tool checks for incompatible eFuse states to prevent them from burning and potentially **bricking the chip**. Use this flag only if you are sure. This will suppress the eFuse incompatibility error.

It can be list of eFuse names and values (like EFUSE_NAME1 1 EFUSE_NAME2 7 EFUSE_NAME3 10 etc.).

New values can be a numeric value in decimal or hex (with "0x" prefix). eFuse bits can only be burned from 0 to 1, attempting to set any back to 0 will have no effect. Most eFuses have a limited bit width (many are only 1-bit flags). Longer eFuses (MAC addresses, keys) can be set with this command, but it's better to use a specific command (``burn_custom_mac``, ``burn_key``) for a specific field.
Expand Down
4 changes: 4 additions & 0 deletions espefuse/efuse/base_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,10 @@ def get_block_errors(self, block_num):
"""Returns (error count, failure boolean flag)"""
return self.blocks[block_num].num_errors, self.blocks[block_num].fail

def is_efuses_incompatible_for_burn(self):
# Overwrite this function for a specific target if you want to check if a certain eFuse(s) can be burned.
return False


class EfuseFieldBase(EfuseProtectBase):
def __init__(self, parent, param):
Expand Down
13 changes: 13 additions & 0 deletions espefuse/efuse/base_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ def check_efuse_name(efuse_name, efuse_list):
+ [name for e in efuses.efuses for name in e.alt_names if name != ""],
efuses=efuses,
)
burn.add_argument(
"--force",
help="Suppress an error to burn eFuses",
action="store_true",
)

read_protect_efuse = subparsers.add_parser(
"read_protect_efuse",
Expand Down Expand Up @@ -480,6 +485,14 @@ def print_attention(blocked_efuses_after_burn):
)
print(" espefuse/esptool will not work.")

if efuses.is_efuses_incompatible_for_burn():
if args.force:
print("Ignore incompatible eFuse settings.")
else:
raise esptool.FatalError(
"Incompatible eFuse settings detected, abort. (use --force flag to skip it)."
)

if not efuses.burn_all(check_batch_mode=True):
return

Expand Down
22 changes: 22 additions & 0 deletions espefuse/efuse/esp32s3/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,28 @@ def summary(self):
output = "Flash voltage (VDD_SPI) set to 3.3V by efuse."
return output

def is_efuses_incompatible_for_burn(self):
# getting chip version: self._esp.get_chip_revision()
if (
(
self["DIS_USB_JTAG"].get()
and self["DIS_USB_SERIAL_JTAG"].get(from_read=False)
)
or (
self["DIS_USB_JTAG"].get(from_read=False)
and self["DIS_USB_SERIAL_JTAG"].get()
)
or (
self["DIS_USB_JTAG"].get(from_read=False)
and self["DIS_USB_SERIAL_JTAG"].get(from_read=False)
)
):
print(
"DIS_USB_JTAG and DIS_USB_SERIAL_JTAG cannot be set together due to a bug in the ROM bootloader!"
)
return True
return False


class EfuseField(base_fields.EfuseFieldBase):
@staticmethod
Expand Down
22 changes: 22 additions & 0 deletions espefuse/efuse/esp32s3beta2/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,28 @@ def summary(self):
output = "Flash voltage (VDD_SPI) set to 3.3V by efuse."
return output

def is_efuses_incompatible_for_burn(self):
# getting chip version: self._esp.get_chip_revision()
if (
(
self["DIS_USB_JTAG"].get(from_read=True)
and self["DIS_USB_SERIAL_JTAG"].get(from_read=False)
)
or (
self["DIS_USB_JTAG"].get(from_read=False)
and self["DIS_USB_SERIAL_JTAG"].get(from_read=True)
)
or (
self["DIS_USB_JTAG"].get(from_read=False)
and self["DIS_USB_SERIAL_JTAG"].get(from_read=False)
)
):
print(
"DIS_USB_JTAG and DIS_USB_SERIAL_JTAG cannot be set together due to a bug in the ROM bootloader"
)
return True
return False


class EfuseField(base_fields.EfuseFieldBase):
@staticmethod
Expand Down
18 changes: 18 additions & 0 deletions test/test_espefuse.py
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,24 @@ def test_burn_efuse_with_34_coding_scheme2(self):
ADC2_TP_HIGH 45"
)

@pytest.mark.skipif(
arg_chip != "esp32s3",
reason="Currently S3 only has this efuse incompatibility check",
)
def test_burn_efuse_incompatibility_check(self):
self.espefuse_py(
"burn_efuse DIS_USB_JTAG 1 DIS_USB_SERIAL_JTAG 1",
check_msg="Incompatible eFuse settings detected, abort",
ret_code=2,
)
self.espefuse_py("burn_efuse DIS_USB_JTAG 1")
self.espefuse_py(
"burn_efuse DIS_USB_SERIAL_JTAG 1",
check_msg="Incompatible eFuse settings detected, abort",
ret_code=2,
)
self.espefuse_py("burn_efuse DIS_USB_SERIAL_JTAG 1 --force")


class TestBurnKeyCommands(EfuseTestCase):
@pytest.mark.skipif(arg_chip != "esp32", reason="ESP32-only")
Expand Down

0 comments on commit c244843

Please sign in to comment.