feat(core): 更新py-esptool (version: 4.8.1)
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
# Espressif Systems (Shanghai) CO LTD, other contributors as noted.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# PYTHON_ARGCOMPLETE_OK
|
||||
__all__ = [
|
||||
"chip_id",
|
||||
"detect_chip",
|
||||
@@ -28,7 +28,7 @@ __all__ = [
|
||||
"write_mem",
|
||||
]
|
||||
|
||||
__version__ = "4.7.0"
|
||||
__version__ = "4.8.1"
|
||||
|
||||
import argparse
|
||||
import inspect
|
||||
@@ -38,8 +38,8 @@ import sys
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from bin_image import intel_hex_to_bin
|
||||
from cmds import (
|
||||
from esptool.bin_image import intel_hex_to_bin
|
||||
from esptool.cmds import (
|
||||
DETECTED_FLASH_SIZES,
|
||||
chip_id,
|
||||
detect_chip,
|
||||
@@ -49,6 +49,7 @@ from cmds import (
|
||||
erase_flash,
|
||||
erase_region,
|
||||
flash_id,
|
||||
read_flash_sfdp,
|
||||
get_security_info,
|
||||
image_info,
|
||||
load_ram,
|
||||
@@ -65,15 +66,22 @@ from cmds import (
|
||||
write_flash_status,
|
||||
write_mem,
|
||||
)
|
||||
from config import load_config_file
|
||||
from loader import DEFAULT_CONNECT_ATTEMPTS, ESPLoader, list_ports
|
||||
from targets import CHIP_DEFS, CHIP_LIST, ESP32ROM
|
||||
from util import (
|
||||
from esptool.config import load_config_file
|
||||
from esptool.loader import (
|
||||
DEFAULT_CONNECT_ATTEMPTS,
|
||||
DEFAULT_OPEN_PORT_ATTEMPTS,
|
||||
StubFlasher,
|
||||
ESPLoader,
|
||||
list_ports,
|
||||
)
|
||||
from esptool.targets import CHIP_DEFS, CHIP_LIST, ESP32ROM
|
||||
from esptool.util import (
|
||||
FatalError,
|
||||
NotImplementedInROMError,
|
||||
flash_size_bytes,
|
||||
strip_chip_name,
|
||||
)
|
||||
from itertools import chain, cycle, repeat
|
||||
|
||||
import serial
|
||||
|
||||
@@ -123,6 +131,14 @@ def main(argv=None, esp=None):
|
||||
default=os.environ.get("ESPTOOL_BAUD", ESPLoader.ESP_ROM_BAUD),
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--port-filter",
|
||||
action="append",
|
||||
help="Serial port device filter, can be vid=NUMBER, pid=NUMBER, name=SUBSTRING",
|
||||
type=str,
|
||||
default=[],
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--before",
|
||||
help="What to do before connecting to the chip",
|
||||
@@ -145,6 +161,15 @@ def main(argv=None, esp=None):
|
||||
action="store_true",
|
||||
)
|
||||
|
||||
# --stub-version can be set with --no-stub so the tests wouldn't fail if this option is implied globally
|
||||
parser.add_argument(
|
||||
"--stub-version",
|
||||
default=os.environ.get("ESPTOOL_STUB_VERSION", StubFlasher.STUB_SUBDIRS[0]),
|
||||
choices=StubFlasher.STUB_SUBDIRS,
|
||||
# not a public option and is not subject to the semantic versioning policy
|
||||
help=argparse.SUPPRESS,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--trace",
|
||||
"-t",
|
||||
@@ -217,7 +242,12 @@ def main(argv=None, esp=None):
|
||||
default="0xFFFFFFFF",
|
||||
)
|
||||
|
||||
def add_spi_flash_subparsers(parent, allow_keep, auto_detect):
|
||||
def add_spi_flash_subparsers(
|
||||
parent: argparse.ArgumentParser,
|
||||
allow_keep: bool,
|
||||
auto_detect: bool,
|
||||
size_only: bool = False,
|
||||
):
|
||||
"""Add common parser arguments for SPI flash properties"""
|
||||
extra_keep_args = ["keep"] if allow_keep else []
|
||||
|
||||
@@ -234,33 +264,35 @@ def main(argv=None, esp=None):
|
||||
extra_fs_message = ""
|
||||
flash_sizes = []
|
||||
|
||||
parent.add_argument(
|
||||
"--flash_freq",
|
||||
"-ff",
|
||||
help="SPI Flash frequency",
|
||||
choices=extra_keep_args
|
||||
+ [
|
||||
"80m",
|
||||
"60m",
|
||||
"48m",
|
||||
"40m",
|
||||
"30m",
|
||||
"26m",
|
||||
"24m",
|
||||
"20m",
|
||||
"16m",
|
||||
"15m",
|
||||
"12m",
|
||||
],
|
||||
default=os.environ.get("ESPTOOL_FF", "keep" if allow_keep else None),
|
||||
)
|
||||
parent.add_argument(
|
||||
"--flash_mode",
|
||||
"-fm",
|
||||
help="SPI Flash mode",
|
||||
choices=extra_keep_args + ["qio", "qout", "dio", "dout"],
|
||||
default=os.environ.get("ESPTOOL_FM", "keep" if allow_keep else "qio"),
|
||||
)
|
||||
if not size_only:
|
||||
parent.add_argument(
|
||||
"--flash_freq",
|
||||
"-ff",
|
||||
help="SPI Flash frequency",
|
||||
choices=extra_keep_args
|
||||
+ [
|
||||
"80m",
|
||||
"60m",
|
||||
"48m",
|
||||
"40m",
|
||||
"30m",
|
||||
"26m",
|
||||
"24m",
|
||||
"20m",
|
||||
"16m",
|
||||
"15m",
|
||||
"12m",
|
||||
],
|
||||
default=os.environ.get("ESPTOOL_FF", "keep" if allow_keep else None),
|
||||
)
|
||||
parent.add_argument(
|
||||
"--flash_mode",
|
||||
"-fm",
|
||||
help="SPI Flash mode",
|
||||
choices=extra_keep_args + ["qio", "qout", "dio", "dout"],
|
||||
default=os.environ.get("ESPTOOL_FM", "keep" if allow_keep else "qio"),
|
||||
)
|
||||
|
||||
parent.add_argument(
|
||||
"--flash_size",
|
||||
"-fs",
|
||||
@@ -468,7 +500,7 @@ def main(argv=None, esp=None):
|
||||
parser_elf2image.add_argument(
|
||||
"--use_segments",
|
||||
help="If set, ELF segments will be used instead of ELF sections "
|
||||
"to genereate the image.",
|
||||
"to generate the image.",
|
||||
action="store_true",
|
||||
)
|
||||
parser_elf2image.add_argument(
|
||||
@@ -489,7 +521,7 @@ def main(argv=None, esp=None):
|
||||
"quantity. This will make the other segments invisible to the ROM "
|
||||
"loader. Use this argument with care because the ROM loader will load "
|
||||
"only the RAM segments although the other segments being present in "
|
||||
"the output.",
|
||||
"the output. Implies --dont-append-digest",
|
||||
action="store_true",
|
||||
default=None,
|
||||
)
|
||||
@@ -540,7 +572,9 @@ def main(argv=None, esp=None):
|
||||
parser_read_flash = subparsers.add_parser(
|
||||
"read_flash", help="Read SPI flash content"
|
||||
)
|
||||
add_spi_connection_arg(parser_read_flash)
|
||||
add_spi_flash_subparsers(
|
||||
parser_read_flash, allow_keep=True, auto_detect=True, size_only=True
|
||||
)
|
||||
parser_read_flash.add_argument("address", help="Start address", type=arg_auto_int)
|
||||
parser_read_flash.add_argument(
|
||||
"size",
|
||||
@@ -594,6 +628,14 @@ def main(argv=None, esp=None):
|
||||
type=arg_auto_size,
|
||||
)
|
||||
|
||||
parser_read_flash_sfdp = subparsers.add_parser(
|
||||
"read_flash_sfdp",
|
||||
help="Read SPI flash SFDP (Serial Flash Discoverable Parameters)",
|
||||
)
|
||||
add_spi_flash_subparsers(parser_read_flash_sfdp, allow_keep=True, auto_detect=True)
|
||||
parser_read_flash_sfdp.add_argument("addr", type=arg_auto_int)
|
||||
parser_read_flash_sfdp.add_argument("bytes", type=int)
|
||||
|
||||
parser_merge_bin = subparsers.add_parser(
|
||||
"merge_bin",
|
||||
help="Merge multiple raw binary files into a single file for later flashing",
|
||||
@@ -664,12 +706,40 @@ def main(argv=None, esp=None):
|
||||
for operation in subparsers.choices.keys():
|
||||
assert operation in globals(), "%s should be a module function" % operation
|
||||
|
||||
# Enable argcomplete only on Unix-like systems
|
||||
if sys.platform != "win32":
|
||||
try:
|
||||
import argcomplete
|
||||
|
||||
argcomplete.autocomplete(parser)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
argv = expand_file_arguments(argv or sys.argv[1:])
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
print("esptool.py v%s" % __version__)
|
||||
load_config_file(verbose=True)
|
||||
|
||||
StubFlasher.set_preferred_stub_subdir(args.stub_version)
|
||||
|
||||
# Parse filter arguments into separate lists
|
||||
args.filterVids = []
|
||||
args.filterPids = []
|
||||
args.filterNames = []
|
||||
for f in args.port_filter:
|
||||
kvp = f.split("=")
|
||||
if len(kvp) != 2:
|
||||
raise FatalError("Option --port-filter argument must consist of key=value")
|
||||
if kvp[0] == "vid":
|
||||
args.filterVids.append(arg_auto_int(kvp[1]))
|
||||
elif kvp[0] == "pid":
|
||||
args.filterPids.append(arg_auto_int(kvp[1]))
|
||||
elif kvp[0] == "name":
|
||||
args.filterNames.append(kvp[1])
|
||||
else:
|
||||
raise FatalError("Option --port-filter argument key not recognized")
|
||||
|
||||
# operation function can take 1 arg (args), 2 args (esp, arg)
|
||||
# or be a member function of the ESPLoader class.
|
||||
|
||||
@@ -705,10 +775,31 @@ def main(argv=None, esp=None):
|
||||
initial_baud = args.baud
|
||||
|
||||
if args.port is None:
|
||||
ser_list = get_port_list()
|
||||
ser_list = get_port_list(args.filterVids, args.filterPids, args.filterNames)
|
||||
print("Found %d serial ports" % len(ser_list))
|
||||
else:
|
||||
ser_list = [args.port]
|
||||
open_port_attempts = os.environ.get(
|
||||
"ESPTOOL_OPEN_PORT_ATTEMPTS", DEFAULT_OPEN_PORT_ATTEMPTS
|
||||
)
|
||||
try:
|
||||
open_port_attempts = int(open_port_attempts)
|
||||
except ValueError:
|
||||
raise SystemExit("Invalid value for ESPTOOL_OPEN_PORT_ATTEMPTS")
|
||||
if open_port_attempts != 1:
|
||||
if args.port is None or args.chip == "auto":
|
||||
print(
|
||||
"WARNING: The ESPTOOL_OPEN_PORT_ATTEMPTS (open_port_attempts) option can only be used with --port and --chip arguments."
|
||||
)
|
||||
else:
|
||||
esp = esp or connect_loop(
|
||||
args.port,
|
||||
initial_baud,
|
||||
args.chip,
|
||||
open_port_attempts,
|
||||
args.trace,
|
||||
args.before,
|
||||
)
|
||||
esp = esp or get_default_connected_device(
|
||||
ser_list,
|
||||
port=args.port,
|
||||
@@ -770,6 +861,13 @@ def main(argv=None, esp=None):
|
||||
"Keeping initial baud rate %d" % initial_baud
|
||||
)
|
||||
|
||||
def _define_spi_conn(spi_connection):
|
||||
"""Prepare SPI configuration string and value for flash_spi_attach()"""
|
||||
clk, q, d, hd, cs = spi_connection
|
||||
spi_config_txt = f"CLK:{clk}, Q:{q}, D:{d}, HD:{hd}, CS:{cs}"
|
||||
value = (hd << 24) | (cs << 18) | (d << 12) | (q << 6) | clk
|
||||
return spi_config_txt, value
|
||||
|
||||
# Override the common SPI flash parameter stuff if configured to do so
|
||||
if hasattr(args, "spi_connection") and args.spi_connection is not None:
|
||||
spi_config = args.spi_connection
|
||||
@@ -781,15 +879,26 @@ def main(argv=None, esp=None):
|
||||
esp.check_spi_connection(args.spi_connection)
|
||||
# Encode the pin numbers as a 32-bit integer with packed 6-bit values,
|
||||
# the same way the ESP ROM takes them
|
||||
clk, q, d, hd, cs = args.spi_connection
|
||||
spi_config = f"CLK:{clk}, Q:{q}, D:{d}, HD:{hd}, CS:{cs}"
|
||||
value = (hd << 24) | (cs << 18) | (d << 12) | (q << 6) | clk
|
||||
spi_config, value = _define_spi_conn(args.spi_connection)
|
||||
print(f"Configuring SPI flash mode ({spi_config})...")
|
||||
esp.flash_spi_attach(value)
|
||||
elif args.no_stub:
|
||||
print("Enabling default SPI flash mode...")
|
||||
# ROM loader doesn't enable flash unless we explicitly do it
|
||||
esp.flash_spi_attach(0)
|
||||
if esp.CHIP_NAME != "ESP32" or esp.secure_download_mode:
|
||||
print("Enabling default SPI flash mode...")
|
||||
# ROM loader doesn't enable flash unless we explicitly do it
|
||||
esp.flash_spi_attach(0)
|
||||
else:
|
||||
# ROM doesn't attach in-package flash chips
|
||||
spi_chip_pads = esp.get_chip_spi_pads()
|
||||
spi_config_txt, value = _define_spi_conn(spi_chip_pads)
|
||||
if spi_chip_pads != (0, 0, 0, 0, 0):
|
||||
print(
|
||||
"Attaching flash from eFuses' SPI pads configuration"
|
||||
f"({spi_config_txt})..."
|
||||
)
|
||||
else:
|
||||
print("Enabling default SPI flash mode...")
|
||||
esp.flash_spi_attach(value)
|
||||
|
||||
# XMC chip startup sequence
|
||||
XMC_VENDOR_ID = 0x20
|
||||
@@ -879,6 +988,10 @@ def main(argv=None, esp=None):
|
||||
flash_size = detect_flash_size(esp, args)
|
||||
elif args.flash_size == "keep":
|
||||
flash_size = detect_flash_size(esp, args=None)
|
||||
if not esp.IS_STUB:
|
||||
print(
|
||||
"WARNING: In case of failure, please set a specific --flash_size."
|
||||
)
|
||||
else:
|
||||
flash_size = args.flash_size
|
||||
|
||||
@@ -968,14 +1081,29 @@ def arg_auto_chunk_size(string: str) -> int:
|
||||
return num
|
||||
|
||||
|
||||
def get_port_list():
|
||||
def get_port_list(vids=[], pids=[], names=[]):
|
||||
if list_ports is None:
|
||||
raise FatalError(
|
||||
"Listing all serial ports is currently not available. "
|
||||
"Please try to specify the port when running esptool.py or update "
|
||||
"the pyserial package to the latest version"
|
||||
)
|
||||
return sorted(ports.device for ports in list_ports.comports())
|
||||
ports = []
|
||||
for port in list_ports.comports():
|
||||
if sys.platform == "darwin" and port.device.endswith(
|
||||
("Bluetooth-Incoming-Port", "wlan-debug")
|
||||
):
|
||||
continue
|
||||
if vids and (port.vid is None or port.vid not in vids):
|
||||
continue
|
||||
if pids and (port.pid is None or port.pid not in pids):
|
||||
continue
|
||||
if names and (
|
||||
port.name is None or all(name not in port.name for name in names)
|
||||
):
|
||||
continue
|
||||
ports.append(port.device)
|
||||
return sorted(ports)
|
||||
|
||||
|
||||
def expand_file_arguments(argv):
|
||||
@@ -1001,6 +1129,53 @@ def expand_file_arguments(argv):
|
||||
return argv
|
||||
|
||||
|
||||
def connect_loop(
|
||||
port: str,
|
||||
initial_baud: int,
|
||||
chip: str,
|
||||
max_retries: int,
|
||||
trace: bool = False,
|
||||
before: str = "default_reset",
|
||||
):
|
||||
chip_class = CHIP_DEFS[chip]
|
||||
esp = None
|
||||
print(f"Serial port {port}")
|
||||
|
||||
first = True
|
||||
ten_cycle = cycle(chain(repeat(False, 9), (True,)))
|
||||
retry_loop = chain(
|
||||
repeat(False, max_retries - 1), (True,) if max_retries else cycle((False,))
|
||||
)
|
||||
|
||||
for last, every_tenth in zip(retry_loop, ten_cycle):
|
||||
try:
|
||||
esp = chip_class(port, initial_baud, trace)
|
||||
if not first:
|
||||
# break the retrying line
|
||||
print("")
|
||||
esp.connect(before)
|
||||
return esp
|
||||
except (
|
||||
FatalError,
|
||||
serial.serialutil.SerialException,
|
||||
IOError,
|
||||
OSError,
|
||||
) as err:
|
||||
if esp and esp._port:
|
||||
esp._port.close()
|
||||
esp = None
|
||||
if first:
|
||||
print(err)
|
||||
print("Retrying failed connection", end="", flush=True)
|
||||
first = False
|
||||
if last:
|
||||
raise err
|
||||
if every_tenth:
|
||||
# print a dot every second
|
||||
print(".", end="", flush=True)
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
def get_default_connected_device(
|
||||
serial_list,
|
||||
port,
|
||||
|
||||
@@ -11,16 +11,19 @@ import os
|
||||
import re
|
||||
import struct
|
||||
import tempfile
|
||||
from typing import BinaryIO, Optional
|
||||
from typing import IO, Optional
|
||||
|
||||
from intelhex import IntelHex
|
||||
from intelhex import HexRecordError, IntelHex
|
||||
|
||||
from loader import ESPLoader
|
||||
from targets import (
|
||||
from .loader import ESPLoader
|
||||
from .targets import (
|
||||
ESP32C2ROM,
|
||||
ESP32C3ROM,
|
||||
ESP32C5ROM,
|
||||
ESP32C5BETA3ROM,
|
||||
ESP32C6BETAROM,
|
||||
ESP32C6ROM,
|
||||
ESP32C61ROM,
|
||||
ESP32H2BETA1ROM,
|
||||
ESP32H2BETA2ROM,
|
||||
ESP32H2ROM,
|
||||
@@ -31,7 +34,7 @@ from targets import (
|
||||
ESP32S3ROM,
|
||||
ESP8266ROM,
|
||||
)
|
||||
from util import FatalError, byte, pad_to
|
||||
from .util import FatalError, byte, pad_to
|
||||
|
||||
|
||||
def align_file_position(f, size):
|
||||
@@ -40,20 +43,24 @@ def align_file_position(f, size):
|
||||
f.seek(align, 1)
|
||||
|
||||
|
||||
def intel_hex_to_bin(file: BinaryIO, start_addr: Optional[int] = None) -> BinaryIO:
|
||||
def intel_hex_to_bin(file: IO[bytes], start_addr: Optional[int] = None) -> IO[bytes]:
|
||||
"""Convert IntelHex file to temp binary file with padding from start_addr
|
||||
If hex file was detected return temp bin file object; input file otherwise"""
|
||||
INTEL_HEX_MAGIC = b":"
|
||||
magic = file.read(1)
|
||||
file.seek(0)
|
||||
if magic == INTEL_HEX_MAGIC:
|
||||
ih = IntelHex()
|
||||
ih.loadhex(file.name)
|
||||
file.close()
|
||||
bin = tempfile.NamedTemporaryFile(suffix=".bin", delete=False)
|
||||
ih.tobinfile(bin, start=start_addr)
|
||||
return bin
|
||||
else:
|
||||
try:
|
||||
if magic == INTEL_HEX_MAGIC:
|
||||
ih = IntelHex()
|
||||
ih.loadhex(file.name)
|
||||
file.close()
|
||||
bin = tempfile.NamedTemporaryFile(suffix=".bin", delete=False)
|
||||
ih.tobinfile(bin, start=start_addr)
|
||||
return bin
|
||||
else:
|
||||
return file
|
||||
except (HexRecordError, UnicodeDecodeError):
|
||||
# file started with HEX magic but the rest was not according to the standard
|
||||
return file
|
||||
|
||||
|
||||
@@ -82,6 +89,9 @@ def LoadFirmwareImage(chip, image_file):
|
||||
"esp32h2beta2": ESP32H2BETA2FirmwareImage,
|
||||
"esp32c2": ESP32C2FirmwareImage,
|
||||
"esp32c6": ESP32C6FirmwareImage,
|
||||
"esp32c61": ESP32C61FirmwareImage,
|
||||
"esp32c5": ESP32C5FirmwareImage,
|
||||
"esp32c5beta3": ESP32C5BETA3FirmwareImage,
|
||||
"esp32h2": ESP32H2FirmwareImage,
|
||||
"esp32p4": ESP32P4FirmwareImage,
|
||||
}[chip](f)
|
||||
@@ -105,10 +115,11 @@ class ImageSegment(object):
|
||||
"""Wrapper class for a segment in an ESP image
|
||||
(very similar to a section in an ELFImage also)"""
|
||||
|
||||
def __init__(self, addr, data, file_offs=None):
|
||||
def __init__(self, addr, data, file_offs=None, flags=0):
|
||||
self.addr = addr
|
||||
self.data = data
|
||||
self.file_offs = file_offs
|
||||
self.flags = flags
|
||||
self.include_in_checksum = True
|
||||
if self.addr != 0:
|
||||
self.pad_to_alignment(
|
||||
@@ -157,8 +168,8 @@ class ELFSection(ImageSegment):
|
||||
"""Wrapper class for a section in an ELF image, has a section
|
||||
name as well as the common properties of an ImageSegment."""
|
||||
|
||||
def __init__(self, name, addr, data):
|
||||
super(ELFSection, self).__init__(addr, data)
|
||||
def __init__(self, name, addr, data, flags):
|
||||
super(ELFSection, self).__init__(addr, data, flags=flags)
|
||||
self.name = name.decode("utf-8")
|
||||
|
||||
def __repr__(self):
|
||||
@@ -168,6 +179,9 @@ class ELFSection(ImageSegment):
|
||||
class BaseFirmwareImage(object):
|
||||
SEG_HEADER_LEN = 8
|
||||
SHA256_DIGEST_LEN = 32
|
||||
ELF_FLAG_WRITE = 0x1
|
||||
ELF_FLAG_READ = 0x2
|
||||
ELF_FLAG_EXEC = 0x4
|
||||
|
||||
""" Base class with common firmware image functions """
|
||||
|
||||
@@ -342,6 +356,11 @@ class BaseFirmwareImage(object):
|
||||
irom_segment = self.get_irom_segment()
|
||||
return [s for s in self.segments if s != irom_segment]
|
||||
|
||||
def sort_segments(self):
|
||||
if not self.segments:
|
||||
return # nothing to sort
|
||||
self.segments = sorted(self.segments, key=lambda s: s.addr)
|
||||
|
||||
def merge_adjacent_segments(self):
|
||||
if not self.segments:
|
||||
return # nothing to merge
|
||||
@@ -358,6 +377,8 @@ class BaseFirmwareImage(object):
|
||||
elem.get_memory_type(self) == next_elem.get_memory_type(self),
|
||||
elem.include_in_checksum == next_elem.include_in_checksum,
|
||||
next_elem.addr == elem.addr + len(elem.data),
|
||||
next_elem.flags & self.ELF_FLAG_EXEC
|
||||
== elem.flags & self.ELF_FLAG_EXEC,
|
||||
)
|
||||
):
|
||||
# Merge any segment that ends where the next one starts,
|
||||
@@ -613,6 +634,7 @@ class ESP32FirmwareImage(BaseFirmwareImage):
|
||||
self.ram_only_header = ram_only_header
|
||||
|
||||
self.append_digest = append_digest
|
||||
self.data_length = None
|
||||
|
||||
if load_file is not None:
|
||||
start = load_file.tell()
|
||||
@@ -631,6 +653,7 @@ class ESP32FirmwareImage(BaseFirmwareImage):
|
||||
calc_digest = hashlib.sha256()
|
||||
calc_digest.update(load_file.read(end - start))
|
||||
self.calc_digest = calc_digest.digest() # TODO: decide what to do here?
|
||||
self.data_length = end - start
|
||||
|
||||
self.verify()
|
||||
|
||||
@@ -683,7 +706,10 @@ class ESP32FirmwareImage(BaseFirmwareImage):
|
||||
# So bootdesc will be at the very top of the binary at 0x20 offset
|
||||
# (in the first segment).
|
||||
for segment in ram_segments:
|
||||
if segment.name == ".dram0.bootdesc":
|
||||
if (
|
||||
isinstance(segment, ELFSection)
|
||||
and segment.name == ".dram0.bootdesc"
|
||||
):
|
||||
ram_segments.remove(segment)
|
||||
ram_segments.insert(0, segment)
|
||||
break
|
||||
@@ -736,15 +762,25 @@ class ESP32FirmwareImage(BaseFirmwareImage):
|
||||
flash_segments.reverse()
|
||||
for segment in flash_segments:
|
||||
pad_len = get_alignment_data_needed(segment)
|
||||
while pad_len > 0:
|
||||
pad_segment = ImageSegment(0, b"\x00" * pad_len, f.tell())
|
||||
self.save_segment(f, pad_segment)
|
||||
total_segments += 1
|
||||
pad_len = get_alignment_data_needed(segment)
|
||||
# write the flash segment
|
||||
assert (
|
||||
f.tell() + 8
|
||||
) % self.IROM_ALIGN == segment.addr % self.IROM_ALIGN
|
||||
# Some chips have a non-zero load offset (eg. 0x1000)
|
||||
# therefore we shift the ROM segments "-load_offset"
|
||||
# so it will be aligned properly after it is flashed
|
||||
align_min = (
|
||||
self.ROM_LOADER.BOOTLOADER_FLASH_OFFSET - self.SEG_HEADER_LEN
|
||||
)
|
||||
if pad_len < align_min:
|
||||
# in case pad_len does not fit minimum alignment,
|
||||
# pad it to next aligned boundary
|
||||
pad_len += self.IROM_ALIGN
|
||||
|
||||
pad_len -= self.ROM_LOADER.BOOTLOADER_FLASH_OFFSET
|
||||
pad_segment = ImageSegment(0, b"\x00" * pad_len, f.tell())
|
||||
self.save_segment(f, pad_segment)
|
||||
total_segments += 1
|
||||
# check the alignment
|
||||
assert (f.tell() + 8 + self.ROM_LOADER.BOOTLOADER_FLASH_OFFSET) % (
|
||||
self.IROM_ALIGN
|
||||
) == segment.addr % self.IROM_ALIGN
|
||||
# save the flash segment but not saving its checksum neither
|
||||
# saving the number of flash segments, since ROM bootloader
|
||||
# should "not see" them
|
||||
@@ -952,7 +988,7 @@ class ESP8266V3FirmwareImage(ESP32FirmwareImage):
|
||||
while len(flash_segments) > 0:
|
||||
segment = flash_segments[0]
|
||||
# remove 8 bytes empty data for insert segment header
|
||||
if segment.name == ".flash.rodata":
|
||||
if isinstance(segment, ELFSection) and segment.name == ".flash.rodata":
|
||||
segment.data = segment.data[8:]
|
||||
# write the flash segment
|
||||
checksum = self.save_segment(f, segment, checksum)
|
||||
@@ -1114,6 +1150,33 @@ class ESP32C6FirmwareImage(ESP32FirmwareImage):
|
||||
ESP32C6ROM.BOOTLOADER_IMAGE = ESP32C6FirmwareImage
|
||||
|
||||
|
||||
class ESP32C61FirmwareImage(ESP32C6FirmwareImage):
|
||||
"""ESP32C61 Firmware Image almost exactly the same as ESP32C6FirmwareImage"""
|
||||
|
||||
ROM_LOADER = ESP32C61ROM
|
||||
|
||||
|
||||
ESP32C61ROM.BOOTLOADER_IMAGE = ESP32C61FirmwareImage
|
||||
|
||||
|
||||
class ESP32C5FirmwareImage(ESP32C6FirmwareImage):
|
||||
"""ESP32C5 Firmware Image almost exactly the same as ESP32C6FirmwareImage"""
|
||||
|
||||
ROM_LOADER = ESP32C5ROM
|
||||
|
||||
|
||||
ESP32C5ROM.BOOTLOADER_IMAGE = ESP32C5FirmwareImage
|
||||
|
||||
|
||||
class ESP32C5BETA3FirmwareImage(ESP32C6FirmwareImage):
|
||||
"""ESP32C5BETA3 Firmware Image almost exactly the same as ESP32C6FirmwareImage"""
|
||||
|
||||
ROM_LOADER = ESP32C5BETA3ROM
|
||||
|
||||
|
||||
ESP32C5BETA3ROM.BOOTLOADER_IMAGE = ESP32C5BETA3FirmwareImage
|
||||
|
||||
|
||||
class ESP32P4FirmwareImage(ESP32FirmwareImage):
|
||||
"""ESP32P4 Firmware Image almost exactly the same as ESP32FirmwareImage"""
|
||||
|
||||
@@ -1222,16 +1285,16 @@ class ELFFile(object):
|
||||
name_offs, sec_type, _flags, lma, sec_offs, size = struct.unpack_from(
|
||||
"<LLLLLL", section_header[offs:]
|
||||
)
|
||||
return (name_offs, sec_type, lma, size, sec_offs)
|
||||
return (name_offs, sec_type, lma, size, sec_offs, _flags)
|
||||
|
||||
all_sections = [read_section_header(offs) for offs in section_header_offsets]
|
||||
prog_sections = [s for s in all_sections if s[1] in ELFFile.PROG_SEC_TYPES]
|
||||
nobits_secitons = [s for s in all_sections if s[1] == ELFFile.SEC_TYPE_NOBITS]
|
||||
|
||||
# search for the string table section
|
||||
if not (shstrndx * self.LEN_SEC_HEADER) in section_header_offsets:
|
||||
if (shstrndx * self.LEN_SEC_HEADER) not in section_header_offsets:
|
||||
raise FatalError("ELF file has no STRTAB section at shstrndx %d" % shstrndx)
|
||||
_, sec_type, _, sec_size, sec_offs = read_section_header(
|
||||
_, sec_type, _, sec_size, sec_offs, _ = read_section_header(
|
||||
shstrndx * self.LEN_SEC_HEADER
|
||||
)
|
||||
if sec_type != ELFFile.SEC_TYPE_STRTAB:
|
||||
@@ -1253,14 +1316,14 @@ class ELFFile(object):
|
||||
return f.read(size)
|
||||
|
||||
prog_sections = [
|
||||
ELFSection(lookup_string(n_offs), lma, read_data(offs, size))
|
||||
for (n_offs, _type, lma, size, offs) in prog_sections
|
||||
ELFSection(lookup_string(n_offs), lma, read_data(offs, size), flags=_flags)
|
||||
for (n_offs, _type, lma, size, offs, _flags) in prog_sections
|
||||
if lma != 0 and size > 0
|
||||
]
|
||||
self.sections = prog_sections
|
||||
self.nobits_sections = [
|
||||
ELFSection(lookup_string(n_offs), lma, b"")
|
||||
for (n_offs, _type, lma, size, offs) in nobits_secitons
|
||||
ELFSection(lookup_string(n_offs), lma, b"", flags=_flags)
|
||||
for (n_offs, _type, lma, size, offs, _flags) in nobits_secitons
|
||||
if lma != 0 and size > 0
|
||||
]
|
||||
|
||||
@@ -1293,7 +1356,7 @@ class ELFFile(object):
|
||||
_flags,
|
||||
_align,
|
||||
) = struct.unpack_from("<LLLLLLLL", segment_header[offs:])
|
||||
return (seg_type, lma, size, seg_offs)
|
||||
return (seg_type, lma, size, seg_offs, _flags)
|
||||
|
||||
all_segments = [read_segment_header(offs) for offs in segment_header_offsets]
|
||||
prog_segments = [s for s in all_segments if s[0] == ELFFile.SEG_TYPE_LOAD]
|
||||
@@ -1303,8 +1366,8 @@ class ELFFile(object):
|
||||
return f.read(size)
|
||||
|
||||
prog_segments = [
|
||||
ELFSection(b"PHDR", lma, read_data(offs, size))
|
||||
for (_type, lma, size, offs) in prog_segments
|
||||
ELFSection(b"PHDR", lma, read_data(offs, size), flags=_flags)
|
||||
for (_type, lma, size, offs, _flags) in prog_segments
|
||||
if lma != 0 and size > 0
|
||||
]
|
||||
self.segments = prog_segments
|
||||
|
||||
@@ -10,31 +10,33 @@ import struct
|
||||
import sys
|
||||
import time
|
||||
import zlib
|
||||
import itertools
|
||||
|
||||
from intelhex import IntelHex
|
||||
from serial import SerialException
|
||||
|
||||
from bin_image import ELFFile, ImageSegment, LoadFirmwareImage
|
||||
from bin_image import (
|
||||
from .bin_image import ELFFile, ImageSegment, LoadFirmwareImage
|
||||
from .bin_image import (
|
||||
ESP8266ROMFirmwareImage,
|
||||
ESP8266V2FirmwareImage,
|
||||
ESP8266V3FirmwareImage,
|
||||
)
|
||||
from loader import (
|
||||
from .loader import (
|
||||
DEFAULT_CONNECT_ATTEMPTS,
|
||||
DEFAULT_TIMEOUT,
|
||||
ERASE_WRITE_TIMEOUT_PER_MB,
|
||||
ESPLoader,
|
||||
timeout_per_mb,
|
||||
)
|
||||
from targets import CHIP_DEFS, CHIP_LIST, ROM_LIST
|
||||
from uf2_writer import UF2Writer
|
||||
from util import (
|
||||
from .targets import CHIP_DEFS, CHIP_LIST, ROM_LIST
|
||||
from .uf2_writer import UF2Writer
|
||||
from .util import (
|
||||
FatalError,
|
||||
NotImplementedInROMError,
|
||||
NotSupportedError,
|
||||
UnsupportedCommandError,
|
||||
)
|
||||
from util import (
|
||||
from .util import (
|
||||
div_roundup,
|
||||
flash_size_bytes,
|
||||
get_file_size,
|
||||
@@ -115,7 +117,7 @@ def detect_chip(
|
||||
else:
|
||||
err_msg = f"Unexpected chip ID value {chip_id}."
|
||||
except (UnsupportedCommandError, struct.error, FatalError) as e:
|
||||
# UnsupportedCommmanddError: ESP8266/ESP32 ROM
|
||||
# UnsupportedCommandError: ESP8266/ESP32 ROM
|
||||
# struct.error: ESP32-S2
|
||||
# FatalError: ESP8266/ESP32 STUB
|
||||
print(" Unsupported detection protocol, switching and trying again...")
|
||||
@@ -229,7 +231,7 @@ def detect_flash_size(esp, args=None):
|
||||
if flash_size is None:
|
||||
flash_size = "4MB"
|
||||
print(
|
||||
"Warning: Could not auto-detect Flash size "
|
||||
"WARNING: Could not auto-detect Flash size "
|
||||
f"(FlashID={flash_id:#x}, SizeID={size_id:#x}), defaulting to 4MB"
|
||||
)
|
||||
else:
|
||||
@@ -276,49 +278,60 @@ def _update_image_flash_params(esp, address, args, image):
|
||||
|
||||
# After the 8-byte header comes the extended header for chips others than ESP8266.
|
||||
# The 15th byte of the extended header indicates if the image is protected by
|
||||
# a SHA256 checksum. In that case we should not modify the header because
|
||||
# the checksum check would fail.
|
||||
sha_implies_keep = args.chip != "esp8266" and image[8 + 15] == 1
|
||||
|
||||
def print_keep_warning(arg_to_keep, arg_used):
|
||||
print(
|
||||
"Warning: Image file at {addr} is protected with a hash checksum, "
|
||||
"so not changing the flash {arg} setting. "
|
||||
"Use the --flash_{arg}=keep option instead of --flash_{arg}={arg_orig} "
|
||||
"in order to remove this warning, or use the --dont-append-digest option "
|
||||
"for the elf2image command in order to generate an image file "
|
||||
"without a hash checksum".format(
|
||||
addr=hex(address), arg=arg_to_keep, arg_orig=arg_used
|
||||
)
|
||||
)
|
||||
# a SHA256 checksum. In that case we recalculate the SHA digest after modifying the header.
|
||||
sha_appended = args.chip != "esp8266" and image[8 + 15] == 1
|
||||
|
||||
if args.flash_mode != "keep":
|
||||
new_flash_mode = FLASH_MODES[args.flash_mode]
|
||||
if flash_mode != new_flash_mode and sha_implies_keep:
|
||||
print_keep_warning("mode", args.flash_mode)
|
||||
else:
|
||||
flash_mode = new_flash_mode
|
||||
flash_mode = FLASH_MODES[args.flash_mode]
|
||||
|
||||
flash_freq = flash_size_freq & 0x0F
|
||||
if args.flash_freq != "keep":
|
||||
new_flash_freq = esp.parse_flash_freq_arg(args.flash_freq)
|
||||
if flash_freq != new_flash_freq and sha_implies_keep:
|
||||
print_keep_warning("frequency", args.flash_freq)
|
||||
else:
|
||||
flash_freq = new_flash_freq
|
||||
flash_freq = esp.parse_flash_freq_arg(args.flash_freq)
|
||||
|
||||
flash_size = flash_size_freq & 0xF0
|
||||
if args.flash_size != "keep":
|
||||
new_flash_size = esp.parse_flash_size_arg(args.flash_size)
|
||||
if flash_size != new_flash_size and sha_implies_keep:
|
||||
print_keep_warning("size", args.flash_size)
|
||||
else:
|
||||
flash_size = new_flash_size
|
||||
flash_size = esp.parse_flash_size_arg(args.flash_size)
|
||||
|
||||
flash_params = struct.pack(b"BB", flash_mode, flash_size + flash_freq)
|
||||
if flash_params != image[2:4]:
|
||||
print("Flash params set to 0x%04x" % struct.unpack(">H", flash_params))
|
||||
image = image[0:2] + flash_params + image[4:]
|
||||
|
||||
# recalculate the SHA digest if it was appended
|
||||
if sha_appended:
|
||||
# Since the changes are only made for images located in the bootloader offset,
|
||||
# we can assume that the image is always a bootloader image.
|
||||
# For merged binaries, we check the bootloader SHA when parameters are changed.
|
||||
image_object = esp.BOOTLOADER_IMAGE(io.BytesIO(image))
|
||||
# get the image header, extended header (if present) and data
|
||||
image_data_before_sha = image[: image_object.data_length]
|
||||
# get the image data after the SHA digest (primary for merged binaries)
|
||||
image_data_after_sha = image[
|
||||
(image_object.data_length + image_object.SHA256_DIGEST_LEN) :
|
||||
]
|
||||
|
||||
sha_digest_calculated = hashlib.sha256(image_data_before_sha).digest()
|
||||
image = bytes(
|
||||
itertools.chain(
|
||||
image_data_before_sha, sha_digest_calculated, image_data_after_sha
|
||||
)
|
||||
)
|
||||
|
||||
# get the SHA digest newly stored in the image and compare it to the calculated one
|
||||
image_stored_sha = image[
|
||||
image_object.data_length : image_object.data_length
|
||||
+ image_object.SHA256_DIGEST_LEN
|
||||
]
|
||||
|
||||
if hexify(sha_digest_calculated) == hexify(image_stored_sha):
|
||||
print("SHA digest in image updated")
|
||||
else:
|
||||
print(
|
||||
"WARNING: SHA recalculation for binary failed!\n"
|
||||
f"\tExpected calculated SHA: {hexify(sha_digest_calculated)}\n"
|
||||
f"\tSHA stored in binary: {hexify(image_stored_sha)}"
|
||||
)
|
||||
|
||||
return image
|
||||
|
||||
|
||||
@@ -465,19 +478,32 @@ def write_flash(esp, args):
|
||||
"Use --force to override the warning."
|
||||
)
|
||||
|
||||
# verify file sizes fit in flash
|
||||
flash_end = flash_size_bytes(
|
||||
detect_flash_size(esp) if args.flash_size == "keep" else args.flash_size
|
||||
set_flash_size = (
|
||||
flash_size_bytes(args.flash_size)
|
||||
if args.flash_size not in ["detect", "keep"]
|
||||
else None
|
||||
)
|
||||
if flash_end is not None: # Not in secure download mode
|
||||
if esp.secure_download_mode:
|
||||
flash_end = set_flash_size
|
||||
else: # Check against real flash chip size if not in SDM
|
||||
flash_end_str = detect_flash_size(esp)
|
||||
flash_end = flash_size_bytes(flash_end_str)
|
||||
if set_flash_size and set_flash_size > flash_end:
|
||||
print(
|
||||
f"WARNING: Set --flash_size {args.flash_size} "
|
||||
f"is larger than the available flash size of {flash_end_str}."
|
||||
)
|
||||
|
||||
# Verify file sizes fit in the set --flash_size, or real flash size if smaller
|
||||
flash_end = min(set_flash_size, flash_end) if set_flash_size else flash_end
|
||||
if flash_end is not None:
|
||||
for address, argfile in args.addr_filename:
|
||||
argfile.seek(0, os.SEEK_END)
|
||||
if address + argfile.tell() > flash_end:
|
||||
raise FatalError(
|
||||
"File %s (length %d) at offset %d "
|
||||
"will not fit in %d bytes of flash. "
|
||||
"Use --flash_size argument, or change flashing address."
|
||||
% (argfile.name, argfile.tell(), address, flash_end)
|
||||
f"File {argfile.name} (length {argfile.tell()}) at offset "
|
||||
f"{address} will not fit in {flash_end} bytes of flash. "
|
||||
"Change the --flash_size argument, or flashing address."
|
||||
)
|
||||
argfile.seek(0)
|
||||
|
||||
@@ -547,70 +573,118 @@ def write_flash(esp, args):
|
||||
print("Will flash %s uncompressed" % argfile.name)
|
||||
compress = False
|
||||
|
||||
if args.no_stub:
|
||||
print("Erasing flash...")
|
||||
image = pad_to(
|
||||
argfile.read(), esp.FLASH_ENCRYPTED_WRITE_ALIGN if encrypted else 4
|
||||
)
|
||||
image = argfile.read()
|
||||
|
||||
if len(image) == 0:
|
||||
print("WARNING: File %s is empty" % argfile.name)
|
||||
continue
|
||||
image = _update_image_flash_params(esp, address, args, image)
|
||||
|
||||
image = pad_to(image, esp.FLASH_ENCRYPTED_WRITE_ALIGN if encrypted else 4)
|
||||
|
||||
if args.no_stub:
|
||||
print("Erasing flash...")
|
||||
|
||||
# It is not possible to write to not aligned addresses without stub,
|
||||
# so there are added 0xFF (erase) bytes at the beginning of the image
|
||||
# to align it.
|
||||
bytes_over = address % esp.FLASH_SECTOR_SIZE
|
||||
address -= bytes_over
|
||||
image = b"\xFF" * bytes_over + image
|
||||
|
||||
if not esp.secure_download_mode and not esp.get_secure_boot_enabled():
|
||||
image = _update_image_flash_params(esp, address, args, image)
|
||||
else:
|
||||
print(
|
||||
"WARNING: Security features enabled, so not changing any flash settings."
|
||||
)
|
||||
calcmd5 = hashlib.md5(image).hexdigest()
|
||||
uncsize = len(image)
|
||||
if compress:
|
||||
uncimage = image
|
||||
image = zlib.compress(uncimage, 9)
|
||||
# Decompress the compressed binary a block at a time,
|
||||
# to dynamically calculate the timeout based on the real write size
|
||||
decompress = zlib.decompressobj()
|
||||
blocks = esp.flash_defl_begin(uncsize, len(image), address)
|
||||
else:
|
||||
blocks = esp.flash_begin(uncsize, address, begin_rom_encrypted=encrypted)
|
||||
argfile.seek(0) # in case we need it again
|
||||
seq = 0
|
||||
bytes_sent = 0 # bytes sent on wire
|
||||
bytes_written = 0 # bytes written to flash
|
||||
t = time.time()
|
||||
|
||||
timeout = DEFAULT_TIMEOUT
|
||||
|
||||
while len(image) > 0:
|
||||
print_overwrite(
|
||||
"Writing at 0x%08x... (%d %%)"
|
||||
% (address + bytes_written, 100 * (seq + 1) // blocks)
|
||||
)
|
||||
sys.stdout.flush()
|
||||
block = image[0 : esp.FLASH_WRITE_SIZE]
|
||||
if compress:
|
||||
# feeding each compressed block into the decompressor lets us
|
||||
# see block-by-block how much will be written
|
||||
block_uncompressed = len(decompress.decompress(block))
|
||||
bytes_written += block_uncompressed
|
||||
block_timeout = max(
|
||||
DEFAULT_TIMEOUT,
|
||||
timeout_per_mb(ERASE_WRITE_TIMEOUT_PER_MB, block_uncompressed),
|
||||
)
|
||||
if not esp.IS_STUB:
|
||||
timeout = (
|
||||
block_timeout # ROM code writes block to flash before ACKing
|
||||
)
|
||||
esp.flash_defl_block(block, seq, timeout=timeout)
|
||||
if esp.IS_STUB:
|
||||
# Stub ACKs when block is received,
|
||||
# then writes to flash while receiving the block after it
|
||||
timeout = block_timeout
|
||||
else:
|
||||
# Pad the last block
|
||||
block = block + b"\xff" * (esp.FLASH_WRITE_SIZE - len(block))
|
||||
if encrypted:
|
||||
esp.flash_encrypt_block(block, seq)
|
||||
original_image = image # Save the whole image in case retry is needed
|
||||
# Try again if reconnect was successful
|
||||
for attempt in range(1, esp.WRITE_FLASH_ATTEMPTS + 1):
|
||||
try:
|
||||
if compress:
|
||||
# Decompress the compressed binary a block at a time,
|
||||
# to dynamically calculate the timeout based on the real write size
|
||||
decompress = zlib.decompressobj()
|
||||
blocks = esp.flash_defl_begin(uncsize, len(image), address)
|
||||
else:
|
||||
esp.flash_block(block, seq)
|
||||
bytes_written += len(block)
|
||||
bytes_sent += len(block)
|
||||
image = image[esp.FLASH_WRITE_SIZE :]
|
||||
seq += 1
|
||||
blocks = esp.flash_begin(
|
||||
uncsize, address, begin_rom_encrypted=encrypted
|
||||
)
|
||||
argfile.seek(0) # in case we need it again
|
||||
seq = 0
|
||||
bytes_sent = 0 # bytes sent on wire
|
||||
bytes_written = 0 # bytes written to flash
|
||||
t = time.time()
|
||||
|
||||
timeout = DEFAULT_TIMEOUT
|
||||
|
||||
while len(image) > 0:
|
||||
print_overwrite(
|
||||
"Writing at 0x%08x... (%d %%)"
|
||||
% (address + bytes_written, 100 * (seq + 1) // blocks)
|
||||
)
|
||||
sys.stdout.flush()
|
||||
block = image[0 : esp.FLASH_WRITE_SIZE]
|
||||
if compress:
|
||||
# feeding each compressed block into the decompressor lets us
|
||||
# see block-by-block how much will be written
|
||||
block_uncompressed = len(decompress.decompress(block))
|
||||
bytes_written += block_uncompressed
|
||||
block_timeout = max(
|
||||
DEFAULT_TIMEOUT,
|
||||
timeout_per_mb(
|
||||
ERASE_WRITE_TIMEOUT_PER_MB, block_uncompressed
|
||||
),
|
||||
)
|
||||
if not esp.IS_STUB:
|
||||
timeout = block_timeout # ROM code writes block to flash before ACKing
|
||||
esp.flash_defl_block(block, seq, timeout=timeout)
|
||||
if esp.IS_STUB:
|
||||
# Stub ACKs when block is received,
|
||||
# then writes to flash while receiving the block after it
|
||||
timeout = block_timeout
|
||||
else:
|
||||
# Pad the last block
|
||||
block = block + b"\xff" * (esp.FLASH_WRITE_SIZE - len(block))
|
||||
if encrypted:
|
||||
esp.flash_encrypt_block(block, seq)
|
||||
else:
|
||||
esp.flash_block(block, seq)
|
||||
bytes_written += len(block)
|
||||
bytes_sent += len(block)
|
||||
image = image[esp.FLASH_WRITE_SIZE :]
|
||||
seq += 1
|
||||
break
|
||||
except SerialException:
|
||||
if attempt == esp.WRITE_FLASH_ATTEMPTS or encrypted:
|
||||
# Already retried once or encrypted mode is disabled because of security reasons
|
||||
raise
|
||||
print("\nLost connection, retrying...")
|
||||
esp._port.close()
|
||||
print("Waiting for the chip to reconnect", end="")
|
||||
for _ in range(DEFAULT_CONNECT_ATTEMPTS):
|
||||
try:
|
||||
time.sleep(1)
|
||||
esp._port.open()
|
||||
print() # Print new line which was suppressed by print(".")
|
||||
esp.connect()
|
||||
if esp.IS_STUB:
|
||||
# Hack to bypass the stub overwrite check
|
||||
esp.IS_STUB = False
|
||||
# Reflash stub because chip was reset
|
||||
esp = esp.run_stub()
|
||||
image = original_image
|
||||
break
|
||||
except SerialException:
|
||||
print(".", end="")
|
||||
sys.stdout.flush()
|
||||
else:
|
||||
raise # Reconnect limit reached
|
||||
|
||||
if esp.IS_STUB:
|
||||
# Stub only writes each block to flash after 'ack'ing the receive,
|
||||
@@ -645,7 +719,7 @@ def write_flash(esp, args):
|
||||
print("Flash md5: %s" % res)
|
||||
print(
|
||||
"MD5 of 0xFF is %s"
|
||||
% (hashlib.md5(b"\xFF" * uncsize).hexdigest())
|
||||
% (hashlib.md5(b"\xff" * uncsize).hexdigest())
|
||||
)
|
||||
raise FatalError("MD5 of file does not match data in flash!")
|
||||
else:
|
||||
@@ -793,7 +867,7 @@ def image_info(args):
|
||||
format_str = "{:7} {:#07x} {:#010x} {:#010x} {}"
|
||||
app_desc = None
|
||||
bootloader_desc = None
|
||||
for idx, seg in enumerate(image.segments, start=1):
|
||||
for idx, seg in enumerate(image.segments):
|
||||
segs = seg.get_memory_type(image)
|
||||
seg_name = ", ".join(segs)
|
||||
if "DROM" in segs: # The DROM segment starts with the esp_app_desc_t struct
|
||||
@@ -815,9 +889,11 @@ def image_info(args):
|
||||
print(
|
||||
"Checksum: {:#02x} ({})".format(
|
||||
image.checksum,
|
||||
"valid"
|
||||
if image.checksum == calc_checksum
|
||||
else "invalid - calculated {:02x}".format(calc_checksum),
|
||||
(
|
||||
"valid"
|
||||
if image.checksum == calc_checksum
|
||||
else "invalid - calculated {:02x}".format(calc_checksum)
|
||||
),
|
||||
)
|
||||
)
|
||||
try:
|
||||
@@ -892,8 +968,7 @@ def image_info(args):
|
||||
ESP8266V2FirmwareImage.IMAGE_V2_MAGIC,
|
||||
]:
|
||||
raise FatalError(
|
||||
"This is not a valid image "
|
||||
"(invalid magic number: {:#x})".format(magic)
|
||||
f"This is not a valid image (invalid magic number: {magic:#x})"
|
||||
)
|
||||
|
||||
if args.chip == "auto":
|
||||
@@ -940,9 +1015,11 @@ def image_info(args):
|
||||
print(
|
||||
"Checksum: {:02x} ({})".format(
|
||||
image.checksum,
|
||||
"valid"
|
||||
if image.checksum == calc_checksum
|
||||
else "invalid - calculated {:02x}".format(calc_checksum),
|
||||
(
|
||||
"valid"
|
||||
if image.checksum == calc_checksum
|
||||
else "invalid - calculated {:02x}".format(calc_checksum)
|
||||
),
|
||||
)
|
||||
)
|
||||
try:
|
||||
@@ -982,8 +1059,6 @@ def elf2image(args):
|
||||
args.chip = "esp8266"
|
||||
|
||||
print("Creating {} image...".format(args.chip))
|
||||
if args.ram_only_header:
|
||||
print("ROM segments hidden - only RAM segments are visible to the ROM loader!")
|
||||
|
||||
if args.chip != "esp8266":
|
||||
image = CHIP_DEFS[args.chip].BOOTLOADER_IMAGE()
|
||||
@@ -995,7 +1070,10 @@ def elf2image(args):
|
||||
image.min_rev_full = args.min_rev_full
|
||||
image.max_rev_full = args.max_rev_full
|
||||
image.ram_only_header = args.ram_only_header
|
||||
image.append_digest = args.append_digest
|
||||
if image.ram_only_header:
|
||||
image.append_digest = False
|
||||
else:
|
||||
image.append_digest = args.append_digest
|
||||
elif args.version == "1": # ESP8266
|
||||
image = ESP8266ROMFirmwareImage()
|
||||
elif args.version == "2":
|
||||
@@ -1019,6 +1097,13 @@ def elf2image(args):
|
||||
image.elf_sha256 = e.sha256()
|
||||
image.elf_sha256_offset = args.elf_sha256_offset
|
||||
|
||||
if args.ram_only_header:
|
||||
print(
|
||||
"Image has only RAM segments visible. "
|
||||
"ROM segments are hidden and SHA256 digest is not appended."
|
||||
)
|
||||
image.sort_segments()
|
||||
|
||||
before = len(image.segments)
|
||||
image.merge_adjacent_segments()
|
||||
if len(image.segments) != before:
|
||||
@@ -1090,7 +1175,7 @@ def run(esp, args):
|
||||
esp.run()
|
||||
|
||||
|
||||
def flash_id(esp, args):
|
||||
def detect_flash_id(esp):
|
||||
flash_id = esp.flash_id()
|
||||
print("Manufacturer: %02x" % (flash_id & 0xFF))
|
||||
flid_lowbyte = (flash_id >> 16) & 0xFF
|
||||
@@ -1098,11 +1183,27 @@ def flash_id(esp, args):
|
||||
print(
|
||||
"Detected flash size: %s" % (DETECTED_FLASH_SIZES.get(flid_lowbyte, "Unknown"))
|
||||
)
|
||||
|
||||
|
||||
def flash_id(esp, args):
|
||||
detect_flash_id(esp)
|
||||
flash_type = esp.flash_type()
|
||||
flash_type_dict = {0: "quad (4 data lines)", 1: "octal (8 data lines)"}
|
||||
flash_type_str = flash_type_dict.get(flash_type)
|
||||
if flash_type_str:
|
||||
print(f"Flash type set in eFuse: {flash_type_str}")
|
||||
esp.get_flash_voltage()
|
||||
|
||||
|
||||
def read_flash_sfdp(esp, args):
|
||||
detect_flash_id(esp)
|
||||
|
||||
sfdp = esp.read_spiflash_sfdp(args.addr, args.bytes * 8)
|
||||
print(f"SFDP[{args.addr}..{args.addr+args.bytes-1}]: ", end="")
|
||||
for i in range(args.bytes):
|
||||
print(f"{sfdp&0xff:02X} ", end="")
|
||||
sfdp = sfdp >> 8
|
||||
print()
|
||||
|
||||
|
||||
def read_flash(esp, args):
|
||||
@@ -1218,7 +1319,16 @@ def get_security_info(esp, args):
|
||||
print(title)
|
||||
print("=" * len(title))
|
||||
print("Flags: {:#010x} ({})".format(si["flags"], bin(si["flags"])))
|
||||
print("Key Purposes: {}".format(si["key_purposes"]))
|
||||
if esp.KEY_PURPOSES:
|
||||
print(f"Key Purposes: {si['key_purposes']}")
|
||||
desc = "\n ".join(
|
||||
[
|
||||
f"BLOCK_KEY{key_num} - {esp.KEY_PURPOSES.get(purpose, 'UNKNOWN')}"
|
||||
for key_num, purpose in enumerate(si["key_purposes"])
|
||||
if key_num <= esp.EFUSE_MAX_KEY
|
||||
]
|
||||
)
|
||||
print(f" {desc}")
|
||||
if si["chip_id"] is not None and si["api_version"] is not None:
|
||||
print("Chip ID: {}".format(si["chip_id"]))
|
||||
print("API Version: {}".format(si["api_version"]))
|
||||
@@ -1270,7 +1380,7 @@ def get_security_info(esp, args):
|
||||
hard_dis_jtag = get_security_flag_status("HARD_DIS_JTAG", flags)
|
||||
soft_dis_jtag = get_security_flag_status("SOFT_DIS_JTAG", flags)
|
||||
if hard_dis_jtag:
|
||||
print("JTAG: Permenantly Disabled")
|
||||
print("JTAG: Permanently Disabled")
|
||||
elif soft_dis_jtag:
|
||||
print("JTAG: Software Access Disabled")
|
||||
if get_security_flag_status("DIS_USB", flags):
|
||||
@@ -1323,7 +1433,7 @@ def merge_bin(args):
|
||||
|
||||
def pad_to(flash_offs):
|
||||
# account for output file offset if there is any
|
||||
of.write(b"\xFF" * (flash_offs - args.target_offset - of.tell()))
|
||||
of.write(b"\xff" * (flash_offs - args.target_offset - of.tell()))
|
||||
|
||||
for addr, argfile in input_files:
|
||||
pad_to(addr)
|
||||
@@ -1338,6 +1448,13 @@ def merge_bin(args):
|
||||
)
|
||||
elif args.format == "hex":
|
||||
out = IntelHex()
|
||||
if len(input_files) == 1:
|
||||
print(
|
||||
"WARNING: Only one input file specified, output may include "
|
||||
"additional padding if input file was previously merged. "
|
||||
"Please refer to the documentation for more information: "
|
||||
"https://docs.espressif.com/projects/esptool/en/latest/esptool/basic-commands.html#hex-output-format" # noqa E501
|
||||
)
|
||||
for addr, argfile in input_files:
|
||||
ihex = IntelHex()
|
||||
image = argfile.read()
|
||||
|
||||
@@ -19,6 +19,7 @@ CONFIG_OPTIONS = [
|
||||
"connect_attempts",
|
||||
"write_block_attempts",
|
||||
"reset_delay",
|
||||
"open_port_attempts",
|
||||
"custom_reset_sequence",
|
||||
]
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,39 +0,0 @@
|
||||
# Copyright (c) 2016-2018, Alexander Belchenko
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms,
|
||||
# with or without modification, are permitted provided
|
||||
# that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain
|
||||
# the above copyright notice, this list of conditions
|
||||
# and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce
|
||||
# the above copyright notice, this list of conditions
|
||||
# and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of the author nor the names
|
||||
# of its contributors may be used to endorse
|
||||
# or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
|
||||
# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Welcome to IntelHex Python library.")
|
||||
print()
|
||||
print("The intelhex package has some executable points:")
|
||||
print(" python -m intelhex.test -- easy way to run unit tests.")
|
||||
print(" python -m intelhex.bench -- run benchmarks.")
|
||||
@@ -1,3 +0,0 @@
|
||||
# IntelHex library version information
|
||||
version_info = (2, 3, 0)
|
||||
version_str = '.'.join([str(i) for i in version_info])
|
||||
@@ -1,360 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# (c) Alexander Belchenko, 2007, 2009
|
||||
|
||||
# [2013/08] NOTE: This file is keeping for historical reasons.
|
||||
# It may or may not work actually with current version of intelhex,
|
||||
# and most likely it requires some fixes here and there.
|
||||
|
||||
"""Benchmarking.
|
||||
|
||||
Run each test 3 times and get median value.
|
||||
Using 10K array as base test time.
|
||||
|
||||
Each other test compared with base with next formula::
|
||||
|
||||
Tc * Nb
|
||||
q = ---------
|
||||
Tb * Nc
|
||||
|
||||
Here:
|
||||
|
||||
* Tc - execution time of current test
|
||||
* Tb - execution time of base
|
||||
* Nb - array size of base (10K)
|
||||
* Nc - array size of current test
|
||||
|
||||
If resulting value is ``q <= 1.0`` it's the best possible result,
|
||||
i.e. time increase proportionally to array size.
|
||||
"""
|
||||
|
||||
import gc
|
||||
import sys
|
||||
import time
|
||||
|
||||
import intelhex
|
||||
from intelhex.compat import StringIO, range_g
|
||||
|
||||
def median(values):
|
||||
"""Return median value for the list of values.
|
||||
@param values: list of values for processing.
|
||||
@return: median value.
|
||||
"""
|
||||
values.sort()
|
||||
n = int(len(values) / 2)
|
||||
return values[n]
|
||||
|
||||
def run_test(func, fobj):
|
||||
"""Run func with argument fobj and measure execution time.
|
||||
@param func: function for test
|
||||
@param fobj: data for test
|
||||
@return: execution time
|
||||
"""
|
||||
gc.disable()
|
||||
try:
|
||||
begin = time.time()
|
||||
func(fobj)
|
||||
end = time.time()
|
||||
finally:
|
||||
gc.enable()
|
||||
return end - begin
|
||||
|
||||
def run_readtest_N_times(func, hexstr, n):
|
||||
"""Run each test N times.
|
||||
@param func: function for test
|
||||
@param hexstr: string with content of hex file to read
|
||||
@param n: times to repeat.
|
||||
@return: (median time, times list)
|
||||
"""
|
||||
assert n > 0
|
||||
times = []
|
||||
for i in range_g(n):
|
||||
sio = StringIO(hexstr)
|
||||
times.append(run_test(func, sio))
|
||||
sio.close()
|
||||
t = median(times)
|
||||
return t, times
|
||||
|
||||
def run_writetest_N_times(func, n):
|
||||
"""Run each test N times.
|
||||
@param func: function for test
|
||||
@param n: times to repeat.
|
||||
@return: (median time, times list)
|
||||
"""
|
||||
assert n > 0
|
||||
times = []
|
||||
for i in range_g(n):
|
||||
sio = StringIO()
|
||||
times.append(run_test(func, sio))
|
||||
sio.close()
|
||||
t = median(times)
|
||||
return t, times
|
||||
|
||||
def time_coef(tc, nc, tb, nb):
|
||||
"""Return time coefficient relative to base numbers.
|
||||
@param tc: current test time
|
||||
@param nc: current test data size
|
||||
@param tb: base test time
|
||||
@param nb: base test data size
|
||||
@return: time coef.
|
||||
"""
|
||||
tc = float(tc)
|
||||
nc = float(nc)
|
||||
tb = float(tb)
|
||||
nb = float(nb)
|
||||
q = (tc * nb) / (tb * nc)
|
||||
return q
|
||||
|
||||
def get_test_data(n1, offset, n2):
|
||||
"""Create test data on given pattern.
|
||||
@param n1: size of first part of array at base address 0.
|
||||
@param offset: offset for second part of array.
|
||||
@param n2: size of second part of array at given offset.
|
||||
@return: (overall size, hex file, IntelHex object)
|
||||
"""
|
||||
# make IntelHex object
|
||||
ih = intelhex.IntelHex()
|
||||
addr = 0
|
||||
for i in range_g(n1):
|
||||
ih[addr] = addr % 256
|
||||
addr += 1
|
||||
addr += offset
|
||||
for i in range_g(n2):
|
||||
ih[addr] = addr % 256
|
||||
addr += 1
|
||||
# make hex file
|
||||
sio = StringIO()
|
||||
ih.write_hex_file(sio)
|
||||
hexstr = sio.getvalue()
|
||||
sio.close()
|
||||
#
|
||||
return n1+n2, hexstr, ih
|
||||
|
||||
def get_base_50K():
|
||||
return get_test_data(50000, 0, 0)
|
||||
|
||||
def get_250K():
|
||||
return get_test_data(250000, 0, 0)
|
||||
|
||||
def get_100K_100K():
|
||||
return get_test_data(100000, 1000000, 100000)
|
||||
|
||||
def get_0_100K():
|
||||
return get_test_data(0, 1000000, 100000)
|
||||
|
||||
def get_1M():
|
||||
return get_test_data(1000000, 0, 0)
|
||||
|
||||
|
||||
class Measure(object):
|
||||
"""Measure execution time helper."""
|
||||
|
||||
data_set = [
|
||||
# (data name, getter)
|
||||
('base 50K', get_base_50K), # first should be base numbers
|
||||
('250K', get_250K),
|
||||
('1M', get_1M),
|
||||
('100K+100K', get_100K_100K),
|
||||
('0+100K', get_0_100K),
|
||||
]
|
||||
|
||||
def __init__(self, n=3, read=True, write=True):
|
||||
self.n = n
|
||||
self.read = read
|
||||
self.write = write
|
||||
self.results = []
|
||||
|
||||
def measure_one(self, data):
|
||||
"""Do measuring of read and write operations.
|
||||
@param data: 3-tuple from get_test_data
|
||||
@return: (time readhex, time writehex)
|
||||
"""
|
||||
_unused, hexstr, ih = data
|
||||
tread, twrite = 0.0, 0.0
|
||||
if self.read:
|
||||
tread = run_readtest_N_times(intelhex.IntelHex, hexstr, self.n)[0]
|
||||
if self.write:
|
||||
twrite = run_writetest_N_times(ih.write_hex_file, self.n)[0]
|
||||
return tread, twrite
|
||||
|
||||
def measure_all(self):
|
||||
for name, getter in self.data_set:
|
||||
data = getter()
|
||||
times = self.measure_one(data)
|
||||
self.results.append((name, times, data[0]))
|
||||
|
||||
def print_report(self, to_file=None):
|
||||
if to_file is None:
|
||||
to_file = sys.stdout
|
||||
|
||||
base_title, base_times, base_n = self.results[0]
|
||||
base_read, base_write = base_times
|
||||
read_report = ['%-10s\t%7.3f' % (base_title, base_read)]
|
||||
write_report = ['%-10s\t%7.3f' % (base_title, base_write)]
|
||||
|
||||
for item in self.results[1:]:
|
||||
cur_title, cur_times, cur_n = item
|
||||
cur_read, cur_write = cur_times
|
||||
if self.read:
|
||||
qread = time_coef(cur_read, cur_n,
|
||||
base_read, base_n)
|
||||
read_report.append('%-10s\t%7.3f\t%7.3f' % (cur_title,
|
||||
cur_read,
|
||||
qread))
|
||||
if self.write:
|
||||
qwrite = time_coef(cur_write, cur_n,
|
||||
base_write, base_n)
|
||||
write_report.append('%-10s\t%7.3f\t%7.3f' % (cur_title,
|
||||
cur_write,
|
||||
qwrite))
|
||||
if self.read:
|
||||
to_file.write('Read operation:\n')
|
||||
to_file.write('\n'.join(read_report))
|
||||
to_file.write('\n\n')
|
||||
if self.write:
|
||||
to_file.write('Write operation:\n')
|
||||
to_file.write('\n'.join(write_report))
|
||||
to_file.write('\n\n')
|
||||
|
||||
|
||||
HELP = """\
|
||||
Usage: python _bench.py [OPTIONS]
|
||||
|
||||
Options:
|
||||
-h this help
|
||||
-n N repeat tests N times
|
||||
-r run only tests for read operation
|
||||
-w run only tests for write operation
|
||||
|
||||
If option -r or -w is not specified then all tests will be run.
|
||||
"""
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
"""Main function to run benchmarks.
|
||||
@param argv: command-line arguments.
|
||||
@return: exit code (0 is OK).
|
||||
"""
|
||||
import getopt
|
||||
|
||||
# default values
|
||||
test_read = None
|
||||
test_write = None
|
||||
n = 3 # number of repeat
|
||||
|
||||
if argv is None:
|
||||
argv = sys.argv[1:]
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(argv, 'hn:rw', [])
|
||||
|
||||
for o,a in opts:
|
||||
if o == '-h':
|
||||
print(HELP)
|
||||
return 0
|
||||
elif o == '-n':
|
||||
n = int(a)
|
||||
elif o == '-r':
|
||||
test_read = True
|
||||
elif o == '-w':
|
||||
test_write = True
|
||||
|
||||
if args:
|
||||
raise getopt.GetoptError('Arguments are not used.')
|
||||
except getopt.GetoptError:
|
||||
msg = sys.exc_info()[1] # current exception
|
||||
txt = str(msg)
|
||||
print(txt)
|
||||
return 1
|
||||
|
||||
if (test_read, test_write) == (None, None):
|
||||
test_read = test_write = True
|
||||
|
||||
m = Measure(n, test_read, test_write)
|
||||
m.measure_all()
|
||||
m.print_report()
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
|
||||
|
||||
"""
|
||||
|
||||
Some Results
|
||||
************
|
||||
|
||||
|
||||
21/04/2007 revno.40
|
||||
Python 2.5 @ Windows XP, Intel Celeron M CPU 430 @ 1.73GHz
|
||||
|
||||
Read operation:
|
||||
base 10K 0.031
|
||||
100K 0.360 1.161
|
||||
1M 3.500 1.129
|
||||
100K+100K 0.719 1.160
|
||||
0+100K 0.360 1.161
|
||||
|
||||
Write operation:
|
||||
base 10K 0.031
|
||||
100K 0.297 0.958
|
||||
1M 2.953 0.953
|
||||
100K+100K 1.328 2.142
|
||||
0+100K 0.312 1.006
|
||||
|
||||
|
||||
21/04/2007 revno.46
|
||||
Python 2.5 @ Windows XP, Intel Celeron M CPU 430 @ 1.73GHz
|
||||
|
||||
Read operation:
|
||||
base 10K 0.016
|
||||
100K 0.203 1.269
|
||||
1M 2.000 1.250
|
||||
100K+100K 0.422 1.319
|
||||
0+100K 0.203 1.269
|
||||
|
||||
Write operation:
|
||||
base 10K 0.031
|
||||
100K 0.297 0.958
|
||||
1M 2.969 0.958
|
||||
100K+100K 1.328 2.142
|
||||
0+100K 0.312 1.006
|
||||
|
||||
|
||||
22/04/2007 revno.48
|
||||
Python 2.5 @ Windows XP, Intel Celeron M CPU 430 @ 1.73GHz
|
||||
|
||||
Read operation:
|
||||
base 10K 0.016
|
||||
100K 0.187 1.169
|
||||
1M 1.891 1.182
|
||||
100K+100K 0.406 1.269
|
||||
0+100K 0.188 1.175
|
||||
|
||||
Write operation:
|
||||
base 10K 0.031
|
||||
100K 0.296 0.955
|
||||
1M 2.969 0.958
|
||||
100K+100K 1.328 2.142
|
||||
0+100K 0.312 1.006
|
||||
|
||||
|
||||
19/08/2008 revno.72
|
||||
Python 2.5.2 @ Windows XP, Intel Celeron M CPU 430 @ 1.73GHz
|
||||
|
||||
Read operation:
|
||||
base 10K 0.016
|
||||
100K 0.171 1.069
|
||||
1M 1.734 1.084
|
||||
100K+100K 0.375 1.172
|
||||
0+100K 0.172 1.075
|
||||
|
||||
Write operation:
|
||||
base 10K 0.016
|
||||
100K 0.156 0.975
|
||||
1M 1.532 0.957
|
||||
100K+100K 0.344 1.075
|
||||
0+100K 0.156 0.975
|
||||
|
||||
"""
|
||||
@@ -1,160 +0,0 @@
|
||||
# Copyright (c) 2011, Bernhard Leiner
|
||||
# Copyright (c) 2013-2018 Alexander Belchenko
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms,
|
||||
# with or without modification, are permitted provided
|
||||
# that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain
|
||||
# the above copyright notice, this list of conditions
|
||||
# and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce
|
||||
# the above copyright notice, this list of conditions
|
||||
# and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of the author nor the names
|
||||
# of its contributors may be used to endorse
|
||||
# or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
|
||||
# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
'''Compatibility functions for python 2 and 3.
|
||||
|
||||
@author Bernhard Leiner (bleiner AT gmail com)
|
||||
@author Alexander Belchenko (alexander belchenko AT gmail com)
|
||||
'''
|
||||
|
||||
__docformat__ = "javadoc"
|
||||
|
||||
|
||||
import sys, array
|
||||
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
# Python 3
|
||||
Python = 3
|
||||
|
||||
def asbytes(s):
|
||||
if isinstance(s, bytes):
|
||||
return s
|
||||
return s.encode('latin1')
|
||||
def asstr(s):
|
||||
if isinstance(s, str):
|
||||
return s
|
||||
return s.decode('latin1')
|
||||
|
||||
# for python >= 3.2 use 'tobytes', otherwise 'tostring'
|
||||
array_tobytes = array.array.tobytes if sys.version_info[1] >= 2 else array.array.tostring
|
||||
|
||||
IntTypes = (int,)
|
||||
StrType = str
|
||||
UnicodeType = str
|
||||
|
||||
range_g = range # range generator
|
||||
def range_l(*args): # range list
|
||||
return list(range(*args))
|
||||
|
||||
def dict_keys(dikt): # dict keys list
|
||||
return list(dikt.keys())
|
||||
def dict_keys_g(dikt): # dict keys generator
|
||||
return dikt.keys()
|
||||
def dict_items_g(dikt): # dict items generator
|
||||
return dikt.items()
|
||||
|
||||
from io import StringIO, BytesIO
|
||||
|
||||
def get_binary_stdout():
|
||||
return sys.stdout.buffer
|
||||
|
||||
def get_binary_stdin():
|
||||
return sys.stdin.buffer
|
||||
|
||||
else:
|
||||
# Python 2
|
||||
Python = 2
|
||||
|
||||
asbytes = str
|
||||
asstr = str
|
||||
|
||||
array_tobytes = array.array.tostring
|
||||
|
||||
IntTypes = (int, long)
|
||||
StrType = basestring
|
||||
UnicodeType = unicode
|
||||
|
||||
#range_g = xrange # range generator
|
||||
def range_g(*args):
|
||||
# we want to use xrange here but on python 2 it does not work with long ints
|
||||
try:
|
||||
return xrange(*args)
|
||||
except OverflowError:
|
||||
start = 0
|
||||
stop = 0
|
||||
step = 1
|
||||
n = len(args)
|
||||
if n == 1:
|
||||
stop = args[0]
|
||||
elif n == 2:
|
||||
start, stop = args
|
||||
elif n == 3:
|
||||
start, stop, step = args
|
||||
else:
|
||||
raise TypeError('wrong number of arguments in range_g call!')
|
||||
if step == 0:
|
||||
raise ValueError('step cannot be zero')
|
||||
if step > 0:
|
||||
def up(start, stop, step):
|
||||
while start < stop:
|
||||
yield start
|
||||
start += step
|
||||
return up(start, stop, step)
|
||||
else:
|
||||
def down(start, stop, step):
|
||||
while start > stop:
|
||||
yield start
|
||||
start += step
|
||||
return down(start, stop, step)
|
||||
|
||||
range_l = range # range list
|
||||
|
||||
def dict_keys(dikt): # dict keys list
|
||||
return dikt.keys()
|
||||
def dict_keys_g(dikt): # dict keys generator
|
||||
return dikt.keys()
|
||||
def dict_items_g(dikt): # dict items generator
|
||||
return dikt.items()
|
||||
|
||||
from cStringIO import StringIO
|
||||
BytesIO = StringIO
|
||||
|
||||
import os
|
||||
def _force_stream_binary(stream):
|
||||
"""Force binary mode for stream on Windows."""
|
||||
if os.name == 'nt':
|
||||
f_fileno = getattr(stream, 'fileno', None)
|
||||
if f_fileno:
|
||||
fileno = f_fileno()
|
||||
if fileno >= 0:
|
||||
import msvcrt
|
||||
msvcrt.setmode(fileno, os.O_BINARY)
|
||||
return stream
|
||||
|
||||
def get_binary_stdout():
|
||||
return _force_stream_binary(sys.stdout)
|
||||
|
||||
def get_binary_stdin():
|
||||
return _force_stream_binary(sys.stdin)
|
||||
@@ -1,64 +0,0 @@
|
||||
# Recursive version sys.getsizeof(). Extendable with custom handlers.
|
||||
# Code from http://code.activestate.com/recipes/577504/
|
||||
# Created by Raymond Hettinger on Fri, 17 Dec 2010 (MIT)
|
||||
|
||||
import sys
|
||||
from itertools import chain
|
||||
from collections import deque
|
||||
try:
|
||||
from reprlib import repr
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
def total_size(o, handlers={}, verbose=False):
|
||||
""" Returns the approximate memory footprint an object and all of its contents.
|
||||
|
||||
Automatically finds the contents of the following builtin containers and
|
||||
their subclasses: tuple, list, deque, dict, set and frozenset.
|
||||
To search other containers, add handlers to iterate over their contents:
|
||||
|
||||
handlers = {SomeContainerClass: iter,
|
||||
OtherContainerClass: OtherContainerClass.get_elements}
|
||||
|
||||
"""
|
||||
dict_handler = lambda d: chain.from_iterable(d.items())
|
||||
all_handlers = {tuple: iter,
|
||||
list: iter,
|
||||
deque: iter,
|
||||
dict: dict_handler,
|
||||
set: iter,
|
||||
frozenset: iter,
|
||||
}
|
||||
all_handlers.update(handlers) # user handlers take precedence
|
||||
seen = set() # track which object id's have already been seen
|
||||
default_size = sys.getsizeof(0) # estimate sizeof object without __sizeof__
|
||||
|
||||
def sizeof(o):
|
||||
if id(o) in seen: # do not double count the same object
|
||||
return 0
|
||||
seen.add(id(o))
|
||||
s = sys.getsizeof(o, default_size)
|
||||
|
||||
if verbose:
|
||||
print(s, type(o), repr(o))#, file=stderr)
|
||||
|
||||
for typ, handler in all_handlers.items():
|
||||
if isinstance(o, typ):
|
||||
s += sum(map(sizeof, handler(o)))
|
||||
break
|
||||
return s
|
||||
|
||||
return sizeof(o)
|
||||
|
||||
|
||||
##### Example call #####
|
||||
|
||||
if __name__ == '__main__':
|
||||
#d = dict(a=1, b=2, c=3, d=[4,5,6,7], e='a string of chars')
|
||||
print("dict 3 elements")
|
||||
d = {0:0xFF, 1:0xEE, 2:0xCC}
|
||||
print(total_size(d, verbose=True))
|
||||
|
||||
#print("array 3 elements")
|
||||
#import array
|
||||
#print(total_size(array.array('B', b'\x01\x02\x03')))
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,9 +13,11 @@ import string
|
||||
import struct
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional
|
||||
|
||||
from config import load_config_file
|
||||
from reset import (
|
||||
|
||||
from .config import load_config_file
|
||||
from .reset import (
|
||||
ClassicReset,
|
||||
CustomReset,
|
||||
DEFAULT_RESET_DELAY,
|
||||
@@ -23,8 +25,8 @@ from reset import (
|
||||
USBJTAGSerialReset,
|
||||
UnixTightReset,
|
||||
)
|
||||
from util import FatalError, NotImplementedInROMError, UnsupportedCommandError
|
||||
from util import byte, hexify, mask_to_shift, pad_to, strip_chip_name
|
||||
from .util import FatalError, NotImplementedInROMError, UnsupportedCommandError
|
||||
from .util import byte, hexify, mask_to_shift, pad_to, strip_chip_name
|
||||
|
||||
try:
|
||||
import serial
|
||||
@@ -59,7 +61,7 @@ except ImportError:
|
||||
print(
|
||||
"The installed version (%s) of pyserial appears to be too old for esptool.py "
|
||||
"(Python interpreter %s). Check the README for installation instructions."
|
||||
% (sys.VERSION, sys.executable)
|
||||
% (serial.VERSION, sys.executable)
|
||||
)
|
||||
raise
|
||||
except Exception:
|
||||
@@ -96,14 +98,8 @@ DEFAULT_SERIAL_WRITE_TIMEOUT = cfg.getfloat("serial_write_timeout", 10)
|
||||
DEFAULT_CONNECT_ATTEMPTS = cfg.getint("connect_attempts", 7)
|
||||
# Number of times to try writing a data block
|
||||
WRITE_BLOCK_ATTEMPTS = cfg.getint("write_block_attempts", 3)
|
||||
|
||||
STUBS_DIR = os.path.join(os.path.dirname(__file__), "targets", "stub_flasher")
|
||||
|
||||
|
||||
def get_stub_json_path(chip_name):
|
||||
chip_name = strip_chip_name(chip_name)
|
||||
chip_name = chip_name.replace("esp", "")
|
||||
return os.path.join(STUBS_DIR, f"stub_flasher_{chip_name}.json")
|
||||
# Number of times to try opening the serial port
|
||||
DEFAULT_OPEN_PORT_ATTEMPTS = cfg.getint("open_port_attempts", 1)
|
||||
|
||||
|
||||
def timeout_per_mb(seconds_per_mb, size_bytes):
|
||||
@@ -155,8 +151,12 @@ def esp32s3_or_newer_function_only(func):
|
||||
|
||||
|
||||
class StubFlasher:
|
||||
def __init__(self, json_path):
|
||||
with open(json_path) as json_file:
|
||||
STUB_DIR = os.path.join(os.path.dirname(__file__), "targets", "stub_flasher")
|
||||
# directories will be searched in the order of STUB_SUBDIRS
|
||||
STUB_SUBDIRS = ["1", "2"]
|
||||
|
||||
def __init__(self, chip_name):
|
||||
with open(self.get_json_path(chip_name)) as json_file:
|
||||
stub = json.load(json_file)
|
||||
|
||||
self.text = base64.b64decode(stub["text"])
|
||||
@@ -172,6 +172,26 @@ class StubFlasher:
|
||||
|
||||
self.bss_start = stub.get("bss_start")
|
||||
|
||||
def get_json_path(self, chip_name):
|
||||
chip_name = strip_chip_name(chip_name)
|
||||
for i, subdir in enumerate(self.STUB_SUBDIRS):
|
||||
json_path = os.path.join(self.STUB_DIR, subdir, f"{chip_name}.json")
|
||||
if os.path.exists(json_path):
|
||||
if i:
|
||||
print(
|
||||
f"Warning: Stub version {self.STUB_SUBDIRS[0]} doesn't exist, using {subdir} instead"
|
||||
)
|
||||
|
||||
return json_path
|
||||
else:
|
||||
raise FileNotFoundError(f"Stub flasher JSON file for {chip_name} not found")
|
||||
|
||||
@classmethod
|
||||
def set_preferred_stub_subdir(cls, subdir):
|
||||
if subdir in cls.STUB_SUBDIRS:
|
||||
cls.STUB_SUBDIRS.remove(subdir)
|
||||
cls.STUB_SUBDIRS.insert(0, subdir)
|
||||
|
||||
|
||||
class ESPLoader(object):
|
||||
"""Base class providing access to ESP ROM & software stub bootloaders.
|
||||
@@ -179,12 +199,15 @@ class ESPLoader(object):
|
||||
|
||||
Don't instantiate this base class directly, either instantiate a subclass or
|
||||
call cmds.detect_chip() which will interrogate the chip and return the
|
||||
appropriate subclass instance.
|
||||
appropriate subclass instance. You can also use a context manager as
|
||||
"with detect_chip() as esp:" to ensure the serial port is closed when done.
|
||||
|
||||
"""
|
||||
|
||||
CHIP_NAME = "Espressif device"
|
||||
IS_STUB = False
|
||||
STUB_CLASS: Optional[object] = None
|
||||
BOOTLOADER_IMAGE: Optional[object] = None
|
||||
|
||||
DEFAULT_PORT = "/dev/ttyUSB0"
|
||||
|
||||
@@ -201,7 +224,7 @@ class ESPLoader(object):
|
||||
ESP_WRITE_REG = 0x09
|
||||
ESP_READ_REG = 0x0A
|
||||
|
||||
# Some comands supported by ESP32 and later chips ROM bootloader (or -8266 w/ stub)
|
||||
# Some commands supported by ESP32 and later chips ROM bootloader (or -8266 w/ stub)
|
||||
ESP_SPI_SET_PARAMS = 0x0B
|
||||
ESP_SPI_ATTACH = 0x0D
|
||||
ESP_READ_FLASH_SLOW = 0x0E # ROM only, much slower than the stub flash read
|
||||
@@ -245,6 +268,9 @@ class ESPLoader(object):
|
||||
|
||||
UART_DATE_REG_ADDR = 0x60000078
|
||||
|
||||
# Whether the SPI peripheral sends from MSB of 32-bit register, or the MSB of valid LSB bits.
|
||||
SPI_ADDR_REG_MSB = True
|
||||
|
||||
# This ROM address has a different value on each chip model
|
||||
CHIP_DETECT_MAGIC_REG_ADDR = 0x40001000
|
||||
|
||||
@@ -273,11 +299,15 @@ class ESPLoader(object):
|
||||
# Chip IDs that are no longer supported by esptool
|
||||
UNSUPPORTED_CHIPS = {6: "ESP32-S3(beta 3)"}
|
||||
|
||||
# Number of attempts to write flash data
|
||||
WRITE_FLASH_ATTEMPTS = 2
|
||||
|
||||
def __init__(self, port=DEFAULT_PORT, baud=ESP_ROM_BAUD, trace_enabled=False):
|
||||
"""Base constructor for ESPLoader bootloader interaction
|
||||
|
||||
Don't call this constructor, either instantiate a specific
|
||||
ROM class directly, or use cmds.detect_chip().
|
||||
ROM class directly, or use cmds.detect_chip(). You can use the with
|
||||
statement to ensure the serial port is closed when done.
|
||||
|
||||
This base class has all of the instance methods for bootloader
|
||||
functionality supported across various chips & stub
|
||||
@@ -300,7 +330,17 @@ class ESPLoader(object):
|
||||
|
||||
if isinstance(port, str):
|
||||
try:
|
||||
self._port = serial.serial_for_url(port)
|
||||
self._port = serial.serial_for_url(
|
||||
port, exclusive=True, do_not_open=True
|
||||
)
|
||||
if sys.platform == "win32":
|
||||
# When opening a port on Windows,
|
||||
# the RTS/DTR (active low) lines
|
||||
# need to be set to False (pulled high)
|
||||
# to avoid unwanted chip reset
|
||||
self._port.rts = False
|
||||
self._port.dtr = False
|
||||
self._port.open()
|
||||
except serial.serialutil.SerialException as e:
|
||||
port_issues = [
|
||||
[ # does not exist error
|
||||
@@ -316,10 +356,7 @@ class ESPLoader(object):
|
||||
port_issues.append(
|
||||
[ # permission denied error
|
||||
re.compile(r"Permission denied", re.IGNORECASE),
|
||||
(
|
||||
"Try to add user into dialout group: "
|
||||
"sudo usermod -a -G dialout $USER"
|
||||
),
|
||||
("Try to add user into dialout or uucp group."),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -351,6 +388,12 @@ class ESPLoader(object):
|
||||
# need to set the property back to None or it will continue to fail
|
||||
self._port.write_timeout = None
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
self._port.close()
|
||||
|
||||
@property
|
||||
def serial_port(self):
|
||||
return self._port.port
|
||||
@@ -499,7 +542,7 @@ class ESPLoader(object):
|
||||
|
||||
# ROM bootloaders send some non-zero "val" response. The flasher stub sends 0.
|
||||
# If we receive 0 then it probably indicates that the chip wasn't or couldn't be
|
||||
# reseted properly and esptool is talking to the flasher stub.
|
||||
# reset properly and esptool is talking to the flasher stub.
|
||||
self.sync_stub_detected = val == 0
|
||||
|
||||
for _ in range(7):
|
||||
@@ -665,6 +708,15 @@ class ESPLoader(object):
|
||||
"Connection may fail if the chip is not in bootloader "
|
||||
"or flasher stub mode.",
|
||||
)
|
||||
|
||||
if self._port.name.startswith("socket:"):
|
||||
mode = "no_reset" # not possible to toggle DTR/RTS over a TCP socket
|
||||
print(
|
||||
"Note: It's not possible to reset the chip over a TCP socket. "
|
||||
"Automatic resetting to bootloader has been disabled, "
|
||||
"reset the chip manually."
|
||||
)
|
||||
|
||||
print("Connecting...", end="")
|
||||
sys.stdout.flush()
|
||||
last_error = None
|
||||
@@ -701,7 +753,7 @@ class ESPLoader(object):
|
||||
|
||||
if not detecting:
|
||||
try:
|
||||
from targets import ROM_LIST
|
||||
from .targets import ROM_LIST
|
||||
|
||||
# check the date code registers match what we expect to see
|
||||
chip_magic_value = self.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR)
|
||||
@@ -788,11 +840,14 @@ class ESPLoader(object):
|
||||
"""Start downloading an application image to RAM"""
|
||||
# check we're not going to overwrite a running stub with this data
|
||||
if self.IS_STUB:
|
||||
stub = StubFlasher(get_stub_json_path(self.CHIP_NAME))
|
||||
stub = StubFlasher(self.CHIP_NAME)
|
||||
load_start = offset
|
||||
load_end = offset + size
|
||||
for stub_start, stub_end in [
|
||||
(stub.bss_start, stub.data_start + len(stub.data)), # DRAM = bss+data
|
||||
(
|
||||
stub.bss_start or stub.data_start,
|
||||
stub.data_start + len(stub.data),
|
||||
), # DRAM = bss+data
|
||||
(stub.text_start, stub.text_start + len(stub.text)), # IRAM
|
||||
]:
|
||||
if load_start < stub_end and load_end > stub_start:
|
||||
@@ -993,7 +1048,7 @@ class ESPLoader(object):
|
||||
|
||||
def run_stub(self, stub=None):
|
||||
if stub is None:
|
||||
stub = StubFlasher(get_stub_json_path(self.CHIP_NAME))
|
||||
stub = StubFlasher(self.CHIP_NAME)
|
||||
|
||||
if self.sync_stub_detected:
|
||||
print("Stub is already running. No upload is necessary.")
|
||||
@@ -1349,7 +1404,9 @@ class ESPLoader(object):
|
||||
self.write_reg(
|
||||
SPI_USR2_REG, (7 << SPI_USR2_COMMAND_LEN_SHIFT) | spiflash_command
|
||||
)
|
||||
if addr and addr_len > 0:
|
||||
if addr_len > 0:
|
||||
if self.SPI_ADDR_REG_MSB:
|
||||
addr = addr << (32 - addr_len)
|
||||
self.write_reg(SPI_ADDR_REG, addr)
|
||||
if data_bits == 0:
|
||||
self.write_reg(SPI_W0_REG, 0) # clear data register before we read it
|
||||
@@ -1454,7 +1511,12 @@ class ESPLoader(object):
|
||||
# See the self.XTAL_CLK_DIVIDER parameter for this factor.
|
||||
uart_div = self.read_reg(self.UART_CLKDIV_REG) & self.UART_CLKDIV_MASK
|
||||
est_xtal = (self._port.baudrate * uart_div) / 1e6 / self.XTAL_CLK_DIVIDER
|
||||
norm_xtal = 40 if est_xtal > 33 else 26
|
||||
if est_xtal > 45:
|
||||
norm_xtal = 48
|
||||
elif est_xtal > 33:
|
||||
norm_xtal = 40
|
||||
else:
|
||||
norm_xtal = 26
|
||||
if abs(norm_xtal - est_xtal) > 1:
|
||||
print(
|
||||
"WARNING: Detected crystal freq %.2fMHz is quite different to "
|
||||
@@ -1636,12 +1698,14 @@ class HexFormatter(object):
|
||||
while len(s) > 0:
|
||||
line = s[:16]
|
||||
ascii_line = "".join(
|
||||
c
|
||||
if (
|
||||
c == " "
|
||||
or (c in string.printable and c not in string.whitespace)
|
||||
(
|
||||
c
|
||||
if (
|
||||
c == " "
|
||||
or (c in string.printable and c not in string.whitespace)
|
||||
)
|
||||
else "."
|
||||
)
|
||||
else "."
|
||||
for c in line.decode("ascii", "replace")
|
||||
)
|
||||
s = s[16:]
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import errno
|
||||
import os
|
||||
import struct
|
||||
import time
|
||||
|
||||
from util import FatalError
|
||||
from .util import FatalError, PrintOnce
|
||||
|
||||
# Used for resetting into bootloader on Unix-like systems
|
||||
if os.name != "nt":
|
||||
@@ -26,11 +27,41 @@ DEFAULT_RESET_DELAY = 0.05 # default time to wait before releasing boot pin aft
|
||||
|
||||
|
||||
class ResetStrategy(object):
|
||||
print_once = PrintOnce()
|
||||
|
||||
def __init__(self, port, reset_delay=DEFAULT_RESET_DELAY):
|
||||
self.port = port
|
||||
self.reset_delay = reset_delay
|
||||
|
||||
def __call__():
|
||||
def __call__(self):
|
||||
"""
|
||||
On targets with USB modes, the reset process can cause the port to
|
||||
disconnect / reconnect during reset.
|
||||
This will retry reconnections on ports that
|
||||
drop out during the reset sequence.
|
||||
"""
|
||||
for retry in reversed(range(3)):
|
||||
try:
|
||||
if not self.port.isOpen():
|
||||
self.port.open()
|
||||
self.reset()
|
||||
break
|
||||
except OSError as e:
|
||||
# ENOTTY for TIOCMSET; EINVAL for TIOCMGET
|
||||
if e.errno in [errno.ENOTTY, errno.EINVAL]:
|
||||
self.print_once(
|
||||
"WARNING: Chip was NOT reset. Setting RTS/DTR lines is not "
|
||||
f"supported for port '{self.port.name}'. Set --before and --after "
|
||||
"arguments to 'no_reset' and switch to bootloader manually to "
|
||||
"avoid this warning."
|
||||
)
|
||||
break
|
||||
elif not retry:
|
||||
raise
|
||||
self.port.close()
|
||||
time.sleep(0.5)
|
||||
|
||||
def reset(self):
|
||||
pass
|
||||
|
||||
def _setDTR(self, state):
|
||||
@@ -63,7 +94,7 @@ class ClassicReset(ResetStrategy):
|
||||
Classic reset sequence, sets DTR and RTS lines sequentially.
|
||||
"""
|
||||
|
||||
def __call__(self):
|
||||
def reset(self):
|
||||
self._setDTR(False) # IO0=HIGH
|
||||
self._setRTS(True) # EN=LOW, chip in reset
|
||||
time.sleep(0.1)
|
||||
@@ -79,7 +110,7 @@ class UnixTightReset(ResetStrategy):
|
||||
which allows setting DTR and RTS lines at the same time.
|
||||
"""
|
||||
|
||||
def __call__(self):
|
||||
def reset(self):
|
||||
self._setDTRandRTS(False, False)
|
||||
self._setDTRandRTS(True, True)
|
||||
self._setDTRandRTS(False, True) # IO0=HIGH & EN=LOW, chip in reset
|
||||
@@ -96,7 +127,7 @@ class USBJTAGSerialReset(ResetStrategy):
|
||||
is connecting via its USB-JTAG-Serial peripheral.
|
||||
"""
|
||||
|
||||
def __call__(self):
|
||||
def reset(self):
|
||||
self._setRTS(False)
|
||||
self._setDTR(False) # Idle
|
||||
time.sleep(0.1)
|
||||
@@ -117,13 +148,13 @@ class HardReset(ResetStrategy):
|
||||
Can be used to reset out of the bootloader or to restart a running app.
|
||||
"""
|
||||
|
||||
def __init__(self, port, uses_usb_otg=False):
|
||||
def __init__(self, port, uses_usb=False):
|
||||
super().__init__(port)
|
||||
self.uses_usb_otg = uses_usb_otg
|
||||
self.uses_usb = uses_usb
|
||||
|
||||
def __call__(self):
|
||||
def reset(self):
|
||||
self._setRTS(True) # EN->LOW
|
||||
if self.uses_usb_otg:
|
||||
if self.uses_usb:
|
||||
# Give the chip some time to come out of reset,
|
||||
# to be able to handle further DTR/RTS transitions
|
||||
time.sleep(0.2)
|
||||
@@ -162,7 +193,7 @@ class CustomReset(ResetStrategy):
|
||||
"U": "self._setDTRandRTS({})",
|
||||
}
|
||||
|
||||
def __call__(self):
|
||||
def reset(self):
|
||||
exec(self.constructed_strategy)
|
||||
|
||||
def __init__(self, port, seq_str):
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
from .esp32 import ESP32ROM
|
||||
from .esp32c2 import ESP32C2ROM
|
||||
from .esp32c3 import ESP32C3ROM
|
||||
from .esp32c5 import ESP32C5ROM
|
||||
from .esp32c5beta3 import ESP32C5BETA3ROM
|
||||
from .esp32c6 import ESP32C6ROM
|
||||
from .esp32c61 import ESP32C61ROM
|
||||
from .esp32c6beta import ESP32C6BETAROM
|
||||
from .esp32h2 import ESP32H2ROM
|
||||
from .esp32h2beta1 import ESP32H2BETA1ROM
|
||||
@@ -25,6 +28,9 @@ CHIP_DEFS = {
|
||||
"esp32h2beta2": ESP32H2BETA2ROM,
|
||||
"esp32c2": ESP32C2ROM,
|
||||
"esp32c6": ESP32C6ROM,
|
||||
"esp32c61": ESP32C61ROM,
|
||||
"esp32c5": ESP32C5ROM,
|
||||
"esp32c5beta3": ESP32C5BETA3ROM,
|
||||
"esp32h2": ESP32H2ROM,
|
||||
"esp32p4": ESP32P4ROM,
|
||||
}
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
|
||||
import struct
|
||||
import time
|
||||
from typing import Dict, Optional
|
||||
|
||||
from loader import ESPLoader
|
||||
from util import FatalError, NotSupportedError
|
||||
from ..loader import ESPLoader
|
||||
from ..util import FatalError, NotSupportedError
|
||||
|
||||
|
||||
class ESP32ROM(ESPLoader):
|
||||
@@ -36,6 +37,9 @@ class ESP32ROM(ESPLoader):
|
||||
SPI_MISO_DLEN_OFFS = 0x2C
|
||||
EFUSE_RD_REG_BASE = 0x3FF5A000
|
||||
|
||||
EFUSE_BLK0_RDATA3_REG_OFFS = EFUSE_RD_REG_BASE + 0x00C
|
||||
EFUSE_BLK0_RDATA5_REG_OFFS = EFUSE_RD_REG_BASE + 0x014
|
||||
|
||||
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE + 0x18
|
||||
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 7 # EFUSE_RD_DISABLE_DL_ENCRYPT
|
||||
|
||||
@@ -46,6 +50,11 @@ class ESP32ROM(ESPLoader):
|
||||
EFUSE_RD_ABS_DONE_0_MASK = 1 << 4
|
||||
EFUSE_RD_ABS_DONE_1_MASK = 1 << 5
|
||||
|
||||
EFUSE_VDD_SPI_REG = EFUSE_RD_REG_BASE + 0x10
|
||||
VDD_SPI_XPD = 1 << 14 # XPD_SDIO_REG
|
||||
VDD_SPI_TIEH = 1 << 15 # XPD_SDIO_TIEH
|
||||
VDD_SPI_FORCE = 1 << 16 # XPD_SDIO_FORCE
|
||||
|
||||
DR_REG_SYSCON_BASE = 0x3FF66000
|
||||
APB_CTL_DATE_ADDR = DR_REG_SYSCON_BASE + 0x7C
|
||||
APB_CTL_DATE_V = 0x1
|
||||
@@ -61,6 +70,17 @@ class ESP32ROM(ESPLoader):
|
||||
TIMERS_RTC_CALI_VALUE = 0x01FFFFFF
|
||||
TIMERS_RTC_CALI_VALUE_S = 7
|
||||
|
||||
GPIO_STRAP_REG = 0x3FF44038
|
||||
GPIO_STRAP_VDDSPI_MASK = 1 << 5 # GPIO_STRAP_VDDSDIO
|
||||
|
||||
RTC_CNTL_SDIO_CONF_REG = 0x3FF48074
|
||||
RTC_CNTL_XPD_SDIO_REG = 1 << 31
|
||||
RTC_CNTL_DREFH_SDIO_M = 3 << 29
|
||||
RTC_CNTL_DREFM_SDIO_M = 3 << 27
|
||||
RTC_CNTL_DREFL_SDIO_M = 3 << 25
|
||||
RTC_CNTL_SDIO_FORCE = 1 << 22
|
||||
RTC_CNTL_SDIO_PD_EN = 1 << 21
|
||||
|
||||
FLASH_SIZES = {
|
||||
"1MB": 0x00,
|
||||
"2MB": 0x10,
|
||||
@@ -105,6 +125,8 @@ class ESP32ROM(ESPLoader):
|
||||
|
||||
UF2_FAMILY_ID = 0x1C5F21B0
|
||||
|
||||
KEY_PURPOSES: Dict[int, str] = {}
|
||||
|
||||
""" Try to read the BLOCK1 (encryption key) and check if it is valid """
|
||||
|
||||
def is_flash_encryption_key_valid(self):
|
||||
@@ -121,7 +143,7 @@ class ESP32ROM(ESPLoader):
|
||||
# When ESP32 has not generated AES/encryption key in BLOCK1,
|
||||
# the contents will be readable and 0.
|
||||
# If the flash encryption is enabled it is expected to have a valid
|
||||
# non-zero key. We break out on first occurance of non-zero value
|
||||
# non-zero key. We break out on first occurrence of non-zero value
|
||||
key_word = [0] * 7
|
||||
for i in range(len(key_word)):
|
||||
key_word[i] = self.read_efuse(14 + i)
|
||||
@@ -205,11 +227,11 @@ class ESP32ROM(ESPLoader):
|
||||
major_rev = self.get_major_chip_version()
|
||||
minor_rev = self.get_minor_chip_version()
|
||||
rev3 = major_rev == 3
|
||||
single_core = self.read_efuse(3) & (1 << 0) # CHIP_VER DIS_APP_CPU
|
||||
sc = self.read_efuse(3) & (1 << 0) # single core, CHIP_VER DIS_APP_CPU
|
||||
|
||||
chip_name = {
|
||||
0: "ESP32-S0WDQ6" if single_core else "ESP32-D0WDQ6",
|
||||
1: "ESP32-S0WD" if single_core else "ESP32-D0WD",
|
||||
0: "ESP32-S0WDQ6" if sc else "ESP32-D0WDQ6-V3" if rev3 else "ESP32-D0WDQ6",
|
||||
1: "ESP32-S0WD" if sc else "ESP32-D0WD-V3" if rev3 else "ESP32-D0WD",
|
||||
2: "ESP32-D2WD",
|
||||
4: "ESP32-U4WDH",
|
||||
5: "ESP32-PICO-V3" if rev3 else "ESP32-PICO-D4",
|
||||
@@ -217,10 +239,6 @@ class ESP32ROM(ESPLoader):
|
||||
7: "ESP32-D0WDR2-V3",
|
||||
}.get(pkg_version, "unknown ESP32")
|
||||
|
||||
# ESP32-D0WD-V3, ESP32-D0WDQ6-V3
|
||||
if chip_name.startswith("ESP32-D0WD") and rev3:
|
||||
chip_name += "-V3"
|
||||
|
||||
return f"{chip_name} (revision v{major_rev}.{minor_rev})"
|
||||
|
||||
def get_chip_features(self):
|
||||
@@ -269,13 +287,30 @@ class ESP32ROM(ESPLoader):
|
||||
coding_scheme = word6 & 0x3
|
||||
features += [
|
||||
"Coding Scheme %s"
|
||||
% {0: "None", 1: "3/4", 2: "Repeat (UNSUPPORTED)", 3: "Invalid"}[
|
||||
coding_scheme
|
||||
]
|
||||
% {
|
||||
0: "None",
|
||||
1: "3/4",
|
||||
2: "Repeat (UNSUPPORTED)",
|
||||
3: "None (may contain encoding data)",
|
||||
}[coding_scheme]
|
||||
]
|
||||
|
||||
return features
|
||||
|
||||
def get_chip_spi_pads(self):
|
||||
"""Read chip spi pad config
|
||||
return: clk, q, d, hd, cd
|
||||
"""
|
||||
efuse_blk0_rdata5 = self.read_reg(self.EFUSE_BLK0_RDATA5_REG_OFFS)
|
||||
spi_pad_clk = efuse_blk0_rdata5 & 0x1F
|
||||
spi_pad_q = (efuse_blk0_rdata5 >> 5) & 0x1F
|
||||
spi_pad_d = (efuse_blk0_rdata5 >> 10) & 0x1F
|
||||
spi_pad_cs = (efuse_blk0_rdata5 >> 15) & 0x1F
|
||||
|
||||
efuse_blk0_rdata3_reg = self.read_reg(self.EFUSE_BLK0_RDATA3_REG_OFFS)
|
||||
spi_pad_hd = (efuse_blk0_rdata3_reg >> 4) & 0x1F
|
||||
return spi_pad_clk, spi_pad_q, spi_pad_d, spi_pad_hd, spi_pad_cs
|
||||
|
||||
def read_efuse(self, n):
|
||||
"""Read the nth word of the ESP3x EFUSE region."""
|
||||
return self.read_reg(self.EFUSE_RD_REG_BASE + (4 * n))
|
||||
@@ -295,32 +330,63 @@ class ESP32ROM(ESPLoader):
|
||||
def get_erase_size(self, offset, size):
|
||||
return size
|
||||
|
||||
def _get_efuse_flash_voltage(self) -> Optional[str]:
|
||||
efuse = self.read_reg(self.EFUSE_VDD_SPI_REG)
|
||||
# check efuse setting
|
||||
if efuse & (self.VDD_SPI_FORCE | self.VDD_SPI_XPD | self.VDD_SPI_TIEH):
|
||||
return "3.3V"
|
||||
elif efuse & (self.VDD_SPI_FORCE | self.VDD_SPI_XPD):
|
||||
return "1.8V"
|
||||
elif efuse & self.VDD_SPI_FORCE:
|
||||
return "OFF"
|
||||
return None
|
||||
|
||||
def _get_rtc_cntl_flash_voltage(self) -> Optional[str]:
|
||||
reg = self.read_reg(self.RTC_CNTL_SDIO_CONF_REG)
|
||||
# check if override is set in RTC_CNTL_SDIO_CONF_REG
|
||||
if reg & self.RTC_CNTL_SDIO_FORCE:
|
||||
if reg & self.RTC_CNTL_DREFH_SDIO_M:
|
||||
return "1.9V"
|
||||
elif reg & self.RTC_CNTL_XPD_SDIO_REG:
|
||||
return "1.8V"
|
||||
else:
|
||||
return "OFF"
|
||||
return None
|
||||
|
||||
def get_flash_voltage(self):
|
||||
"""Get flash voltage setting and print it to the console."""
|
||||
voltage = self._get_rtc_cntl_flash_voltage()
|
||||
source = "RTC_CNTL"
|
||||
if not voltage:
|
||||
voltage = self._get_efuse_flash_voltage()
|
||||
source = "eFuse"
|
||||
if not voltage:
|
||||
strap_reg = self.read_reg(self.GPIO_STRAP_REG)
|
||||
strap_reg &= self.GPIO_STRAP_VDDSPI_MASK
|
||||
voltage = "1.8V" if strap_reg else "3.3V"
|
||||
source = "a strapping pin"
|
||||
print(f"Flash voltage set by {source} to {voltage}")
|
||||
|
||||
def override_vddsdio(self, new_voltage):
|
||||
new_voltage = new_voltage.upper()
|
||||
if new_voltage not in self.OVERRIDE_VDDSDIO_CHOICES:
|
||||
raise FatalError(
|
||||
"The only accepted VDDSDIO overrides are '1.8V', '1.9V' and 'OFF'"
|
||||
f"The only accepted VDDSDIO overrides are {', '.join(self.OVERRIDE_VDDSDIO_CHOICES)}"
|
||||
)
|
||||
RTC_CNTL_SDIO_CONF_REG = 0x3FF48074
|
||||
RTC_CNTL_XPD_SDIO_REG = 1 << 31
|
||||
RTC_CNTL_DREFH_SDIO_M = 3 << 29
|
||||
RTC_CNTL_DREFM_SDIO_M = 3 << 27
|
||||
RTC_CNTL_DREFL_SDIO_M = 3 << 25
|
||||
# RTC_CNTL_SDIO_TIEH = (1 << 23)
|
||||
# not used here, setting TIEH=1 would set 3.3V output,
|
||||
# RTC_CNTL_SDIO_TIEH is not used here, setting TIEH=1 would set 3.3V output,
|
||||
# not safe for esptool.py to do
|
||||
RTC_CNTL_SDIO_FORCE = 1 << 22
|
||||
RTC_CNTL_SDIO_PD_EN = 1 << 21
|
||||
|
||||
reg_val = RTC_CNTL_SDIO_FORCE # override efuse setting
|
||||
reg_val |= RTC_CNTL_SDIO_PD_EN
|
||||
reg_val = self.RTC_CNTL_SDIO_FORCE # override efuse setting
|
||||
reg_val |= self.RTC_CNTL_SDIO_PD_EN
|
||||
if new_voltage != "OFF":
|
||||
reg_val |= RTC_CNTL_XPD_SDIO_REG # enable internal LDO
|
||||
reg_val |= self.RTC_CNTL_XPD_SDIO_REG # enable internal LDO
|
||||
if new_voltage == "1.9V":
|
||||
reg_val |= (
|
||||
RTC_CNTL_DREFH_SDIO_M | RTC_CNTL_DREFM_SDIO_M | RTC_CNTL_DREFL_SDIO_M
|
||||
self.RTC_CNTL_DREFH_SDIO_M
|
||||
| self.RTC_CNTL_DREFM_SDIO_M
|
||||
| self.RTC_CNTL_DREFL_SDIO_M
|
||||
) # boost voltage
|
||||
self.write_reg(RTC_CNTL_SDIO_CONF_REG, reg_val)
|
||||
self.write_reg(self.RTC_CNTL_SDIO_CONF_REG, reg_val)
|
||||
print("VDDSDIO regulator set to %s" % new_voltage)
|
||||
|
||||
def read_flash_slow(self, offset, length, progress_fn):
|
||||
@@ -329,11 +395,17 @@ class ESP32ROM(ESPLoader):
|
||||
data = b""
|
||||
while len(data) < length:
|
||||
block_len = min(BLOCK_LEN, length - len(data))
|
||||
r = self.check_command(
|
||||
"read flash block",
|
||||
self.ESP_READ_FLASH_SLOW,
|
||||
struct.pack("<II", offset + len(data), block_len),
|
||||
)
|
||||
try:
|
||||
r = self.check_command(
|
||||
"read flash block",
|
||||
self.ESP_READ_FLASH_SLOW,
|
||||
struct.pack("<II", offset + len(data), block_len),
|
||||
)
|
||||
except FatalError:
|
||||
print(
|
||||
"Hint: Consider specifying flash size using '--flash_size' argument"
|
||||
)
|
||||
raise
|
||||
if len(r) < block_len:
|
||||
raise FatalError(
|
||||
"Expected %d byte block, got %d bytes. Serial errors?"
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
|
||||
import struct
|
||||
import time
|
||||
from typing import Dict
|
||||
|
||||
from .esp32c3 import ESP32C3ROM
|
||||
from loader import ESPLoader
|
||||
from util import FatalError
|
||||
from ..loader import ESPLoader
|
||||
from ..util import FatalError
|
||||
|
||||
|
||||
class ESP32C2ROM(ESP32C3ROM):
|
||||
@@ -20,8 +21,8 @@ class ESP32C2ROM(ESP32C3ROM):
|
||||
DROM_MAP_START = 0x3C000000
|
||||
DROM_MAP_END = 0x3C400000
|
||||
|
||||
# Magic value for ESP32C2 ECO0 and ECO1 respectively
|
||||
CHIP_DETECT_MAGIC_VALUE = [0x6F51306F, 0x7C41A06F]
|
||||
# Magic value for ESP32C2 ECO0 , ECO1 and ECO4 respectively
|
||||
CHIP_DETECT_MAGIC_VALUE = [0x6F51306F, 0x7C41A06F, 0x0C21E06F]
|
||||
|
||||
EFUSE_BASE = 0x60008800
|
||||
EFUSE_BLOCK2_ADDR = EFUSE_BASE + 0x040
|
||||
@@ -64,6 +65,8 @@ class ESP32C2ROM(ESP32C3ROM):
|
||||
|
||||
UF2_FAMILY_ID = 0x2B88D29C
|
||||
|
||||
KEY_PURPOSES: Dict[int, str] = {}
|
||||
|
||||
def get_pkg_version(self):
|
||||
num_word = 1
|
||||
return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 22) & 0x07
|
||||
@@ -146,7 +149,7 @@ class ESP32C2ROM(ESP32C3ROM):
|
||||
# When chip has not generated AES/encryption key in BLOCK3,
|
||||
# the contents will be readable and 0.
|
||||
# If the flash encryption is enabled it is expected to have a valid
|
||||
# non-zero key. We break out on first occurance of non-zero value
|
||||
# non-zero key. We break out on first occurrence of non-zero value
|
||||
key_word = [0] * 7 if key_len_256 else [0] * 3
|
||||
for i in range(len(key_word)):
|
||||
key_word[i] = self.read_reg(self.EFUSE_BLOCK_KEY0_REG + i * 4)
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import struct
|
||||
from typing import Dict
|
||||
|
||||
from .esp32 import ESP32ROM
|
||||
from loader import ESPLoader
|
||||
from util import FatalError, NotImplementedInROMError
|
||||
from ..loader import ESPLoader
|
||||
from ..util import FatalError, NotImplementedInROMError
|
||||
|
||||
|
||||
class ESP32C3ROM(ESP32ROM):
|
||||
@@ -27,6 +28,8 @@ class ESP32C3ROM(ESP32ROM):
|
||||
SPI_MISO_DLEN_OFFS = 0x28
|
||||
SPI_W0_OFFS = 0x58
|
||||
|
||||
SPI_ADDR_REG_MSB = False
|
||||
|
||||
BOOTLOADER_FLASH_OFFSET = 0x0
|
||||
|
||||
# Magic values for ESP32-C3 eco 1+2, eco 3, eco 6, and eco 7 respectively
|
||||
@@ -99,6 +102,20 @@ class ESP32C3ROM(ESP32ROM):
|
||||
|
||||
UF2_FAMILY_ID = 0xD42BA06C
|
||||
|
||||
EFUSE_MAX_KEY = 5
|
||||
KEY_PURPOSES: Dict[int, str] = {
|
||||
0: "USER/EMPTY",
|
||||
1: "RESERVED",
|
||||
4: "XTS_AES_128_KEY",
|
||||
5: "HMAC_DOWN_ALL",
|
||||
6: "HMAC_DOWN_JTAG",
|
||||
7: "HMAC_DOWN_DIGITAL_SIGNATURE",
|
||||
8: "HMAC_UP",
|
||||
9: "SECURE_BOOT_DIGEST0",
|
||||
10: "SECURE_BOOT_DIGEST1",
|
||||
11: "SECURE_BOOT_DIGEST2",
|
||||
}
|
||||
|
||||
def get_pkg_version(self):
|
||||
num_word = 3
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x07
|
||||
@@ -152,6 +169,9 @@ class ESP32C3ROM(ESP32ROM):
|
||||
# ESP32C3 XTAL is fixed to 40MHz
|
||||
return 40
|
||||
|
||||
def get_flash_voltage(self):
|
||||
pass # not supported on ESP32-C3
|
||||
|
||||
def override_vddsdio(self, new_voltage):
|
||||
raise NotImplementedInROMError(
|
||||
"VDD_SDIO overrides are not supported for ESP32-C3"
|
||||
@@ -176,8 +196,10 @@ class ESP32C3ROM(ESP32ROM):
|
||||
)
|
||||
|
||||
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")
|
||||
if key_block < 0 or key_block > self.EFUSE_MAX_KEY:
|
||||
raise FatalError(
|
||||
f"Valid key block numbers must be in range 0-{self.EFUSE_MAX_KEY}"
|
||||
)
|
||||
|
||||
reg, shift = [
|
||||
(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT),
|
||||
@@ -191,7 +213,9 @@ class ESP32C3ROM(ESP32ROM):
|
||||
|
||||
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)]
|
||||
purposes = [
|
||||
self.get_key_block_purpose(b) for b in range(self.EFUSE_MAX_KEY + 1)
|
||||
]
|
||||
|
||||
return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes)
|
||||
|
||||
|
||||
190
tools/python/esptool/targets/esp32c5.py
Normal file
190
tools/python/esptool/targets/esp32c5.py
Normal file
@@ -0,0 +1,190 @@
|
||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import struct
|
||||
import time
|
||||
from typing import Dict
|
||||
|
||||
from .esp32c6 import ESP32C6ROM
|
||||
from ..loader import ESPLoader
|
||||
from ..reset import HardReset
|
||||
from ..util import FatalError
|
||||
|
||||
|
||||
class ESP32C5ROM(ESP32C6ROM):
|
||||
CHIP_NAME = "ESP32-C5"
|
||||
IMAGE_CHIP_ID = 23
|
||||
|
||||
EFUSE_BASE = 0x600B4800
|
||||
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 = 24
|
||||
EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34
|
||||
EFUSE_PURPOSE_KEY1_SHIFT = 28
|
||||
EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38
|
||||
EFUSE_PURPOSE_KEY2_SHIFT = 0
|
||||
EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38
|
||||
EFUSE_PURPOSE_KEY3_SHIFT = 4
|
||||
EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38
|
||||
EFUSE_PURPOSE_KEY4_SHIFT = 8
|
||||
EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38
|
||||
EFUSE_PURPOSE_KEY5_SHIFT = 12
|
||||
|
||||
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE
|
||||
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20
|
||||
|
||||
EFUSE_SPI_BOOT_CRYPT_CNT_REG = EFUSE_BASE + 0x034
|
||||
EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18
|
||||
|
||||
EFUSE_SECURE_BOOT_EN_REG = EFUSE_BASE + 0x038
|
||||
EFUSE_SECURE_BOOT_EN_MASK = 1 << 20
|
||||
|
||||
IROM_MAP_START = 0x42000000
|
||||
IROM_MAP_END = 0x42800000
|
||||
DROM_MAP_START = 0x42800000
|
||||
DROM_MAP_END = 0x43000000
|
||||
|
||||
PCR_SYSCLK_CONF_REG = 0x60096110
|
||||
PCR_SYSCLK_XTAL_FREQ_V = 0x7F << 24
|
||||
PCR_SYSCLK_XTAL_FREQ_S = 24
|
||||
|
||||
UARTDEV_BUF_NO = 0x4085F51C # Variable in ROM .bss which indicates the port in use
|
||||
|
||||
# Magic value for ESP32C5
|
||||
CHIP_DETECT_MAGIC_VALUE = [0x1101406F]
|
||||
|
||||
FLASH_FREQUENCY = {
|
||||
"80m": 0xF,
|
||||
"40m": 0x0,
|
||||
"20m": 0x2,
|
||||
}
|
||||
|
||||
MEMORY_MAP = [
|
||||
[0x00000000, 0x00010000, "PADDING"],
|
||||
[0x42800000, 0x43000000, "DROM"],
|
||||
[0x40800000, 0x40860000, "DRAM"],
|
||||
[0x40800000, 0x40860000, "BYTE_ACCESSIBLE"],
|
||||
[0x4003A000, 0x40040000, "DROM_MASK"],
|
||||
[0x40000000, 0x4003A000, "IROM_MASK"],
|
||||
[0x42000000, 0x42800000, "IROM"],
|
||||
[0x40800000, 0x40860000, "IRAM"],
|
||||
[0x50000000, 0x50004000, "RTC_IRAM"],
|
||||
[0x50000000, 0x50004000, "RTC_DRAM"],
|
||||
[0x600FE000, 0x60100000, "MEM_INTERNAL2"],
|
||||
]
|
||||
|
||||
UF2_FAMILY_ID = 0xF71C0343
|
||||
|
||||
EFUSE_MAX_KEY = 5
|
||||
KEY_PURPOSES: Dict[int, str] = {
|
||||
0: "USER/EMPTY",
|
||||
1: "ECDSA_KEY",
|
||||
2: "XTS_AES_256_KEY_1",
|
||||
3: "XTS_AES_256_KEY_2",
|
||||
4: "XTS_AES_128_KEY",
|
||||
5: "HMAC_DOWN_ALL",
|
||||
6: "HMAC_DOWN_JTAG",
|
||||
7: "HMAC_DOWN_DIGITAL_SIGNATURE",
|
||||
8: "HMAC_UP",
|
||||
9: "SECURE_BOOT_DIGEST0",
|
||||
10: "SECURE_BOOT_DIGEST1",
|
||||
11: "SECURE_BOOT_DIGEST2",
|
||||
12: "KM_INIT_KEY",
|
||||
}
|
||||
|
||||
def get_pkg_version(self):
|
||||
num_word = 2
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 26) & 0x07
|
||||
|
||||
def get_minor_chip_version(self):
|
||||
num_word = 2
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x0F
|
||||
|
||||
def get_major_chip_version(self):
|
||||
num_word = 2
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 4) & 0x03
|
||||
|
||||
def get_chip_description(self):
|
||||
chip_name = {
|
||||
0: "ESP32-C5",
|
||||
}.get(self.get_pkg_version(), "unknown ESP32-C5")
|
||||
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_crystal_freq(self):
|
||||
# The crystal detection algorithm of ESP32/ESP8266
|
||||
# works for ESP32-C5 as well.
|
||||
return ESPLoader.get_crystal_freq(self)
|
||||
|
||||
def get_crystal_freq_rom_expect(self):
|
||||
return (
|
||||
self.read_reg(self.PCR_SYSCLK_CONF_REG) & self.PCR_SYSCLK_XTAL_FREQ_V
|
||||
) >> self.PCR_SYSCLK_XTAL_FREQ_S
|
||||
|
||||
def hard_reset(self):
|
||||
print("Hard resetting via RTS pin...")
|
||||
HardReset(self._port, self.uses_usb_jtag_serial())()
|
||||
|
||||
def change_baud(self, baud):
|
||||
if not self.IS_STUB:
|
||||
crystal_freq_rom_expect = self.get_crystal_freq_rom_expect()
|
||||
crystal_freq_detect = self.get_crystal_freq()
|
||||
print(
|
||||
f"ROM expects crystal freq: {crystal_freq_rom_expect} MHz, detected {crystal_freq_detect} MHz"
|
||||
)
|
||||
baud_rate = baud
|
||||
# If detect the XTAL is 48MHz, but the ROM code expects it to be 40MHz
|
||||
if crystal_freq_detect == 48 and crystal_freq_rom_expect == 40:
|
||||
baud_rate = baud * 40 // 48
|
||||
# If detect the XTAL is 40MHz, but the ROM code expects it to be 48MHz
|
||||
elif crystal_freq_detect == 40 and crystal_freq_rom_expect == 48:
|
||||
baud_rate = baud * 48 // 40
|
||||
else:
|
||||
ESPLoader.change_baud(self, baud_rate)
|
||||
return
|
||||
|
||||
print(f"Changing baud rate to {baud_rate}")
|
||||
self.command(self.ESP_CHANGE_BAUDRATE, struct.pack("<II", baud_rate, 0))
|
||||
print("Changed.")
|
||||
self._set_port_baudrate(baud)
|
||||
time.sleep(0.05) # get rid of garbage sent during baud rate change
|
||||
self.flush_input()
|
||||
else:
|
||||
ESPLoader.change_baud(self, baud)
|
||||
|
||||
def check_spi_connection(self, spi_connection):
|
||||
if not set(spi_connection).issubset(set(range(0, 29))):
|
||||
raise FatalError("SPI Pin numbers must be in the range 0-28.")
|
||||
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 ESP32C5StubLoader(ESP32C5ROM):
|
||||
"""Access class for ESP32C5 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
|
||||
|
||||
|
||||
ESP32C5ROM.STUB_CLASS = ESP32C5StubLoader
|
||||
129
tools/python/esptool/targets/esp32c5beta3.py
Normal file
129
tools/python/esptool/targets/esp32c5beta3.py
Normal file
@@ -0,0 +1,129 @@
|
||||
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import struct
|
||||
import time
|
||||
from typing import Dict
|
||||
|
||||
from .esp32c6 import ESP32C6ROM
|
||||
from ..loader import ESPLoader
|
||||
|
||||
|
||||
class ESP32C5BETA3ROM(ESP32C6ROM):
|
||||
CHIP_NAME = "ESP32-C5(beta3)"
|
||||
IMAGE_CHIP_ID = 17
|
||||
|
||||
IROM_MAP_START = 0x41000000
|
||||
IROM_MAP_END = 0x41800000
|
||||
DROM_MAP_START = 0x41000000
|
||||
DROM_MAP_END = 0x41800000
|
||||
|
||||
# Magic value for ESP32C5(beta3)
|
||||
CHIP_DETECT_MAGIC_VALUE = [0xE10D8082]
|
||||
|
||||
FLASH_FREQUENCY = {
|
||||
"80m": 0xF,
|
||||
"40m": 0x0,
|
||||
"20m": 0x2,
|
||||
}
|
||||
|
||||
MEMORY_MAP = [
|
||||
[0x00000000, 0x00010000, "PADDING"],
|
||||
[0x41800000, 0x42000000, "DROM"],
|
||||
[0x40800000, 0x40880000, "DRAM"],
|
||||
[0x40800000, 0x40880000, "BYTE_ACCESSIBLE"],
|
||||
[0x4004A000, 0x40050000, "DROM_MASK"],
|
||||
[0x40000000, 0x4004A000, "IROM_MASK"],
|
||||
[0x41000000, 0x41800000, "IROM"],
|
||||
[0x40800000, 0x40880000, "IRAM"],
|
||||
[0x50000000, 0x50004000, "RTC_IRAM"],
|
||||
[0x50000000, 0x50004000, "RTC_DRAM"],
|
||||
[0x600FE000, 0x60100000, "MEM_INTERNAL2"],
|
||||
]
|
||||
|
||||
EFUSE_MAX_KEY = 5
|
||||
KEY_PURPOSES: Dict[int, str] = {
|
||||
0: "USER/EMPTY",
|
||||
1: "ECDSA_KEY",
|
||||
2: "XTS_AES_256_KEY_1",
|
||||
3: "XTS_AES_256_KEY_2",
|
||||
4: "XTS_AES_128_KEY",
|
||||
5: "HMAC_DOWN_ALL",
|
||||
6: "HMAC_DOWN_JTAG",
|
||||
7: "HMAC_DOWN_DIGITAL_SIGNATURE",
|
||||
8: "HMAC_UP",
|
||||
9: "SECURE_BOOT_DIGEST0",
|
||||
10: "SECURE_BOOT_DIGEST1",
|
||||
11: "SECURE_BOOT_DIGEST2",
|
||||
12: "KM_INIT_KEY",
|
||||
}
|
||||
|
||||
def get_pkg_version(self):
|
||||
num_word = 2
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 26) & 0x07
|
||||
|
||||
def get_minor_chip_version(self):
|
||||
num_word = 2
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x0F
|
||||
|
||||
def get_major_chip_version(self):
|
||||
num_word = 2
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 4) & 0x03
|
||||
|
||||
def get_chip_description(self):
|
||||
chip_name = {
|
||||
0: "ESP32-C5 beta3 (QFN40)",
|
||||
}.get(self.get_pkg_version(), "unknown ESP32-C5 beta3")
|
||||
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_crystal_freq(self):
|
||||
# The crystal detection algorithm of ESP32/ESP8266
|
||||
# works for ESP32-C5 beta3 as well.
|
||||
return ESPLoader.get_crystal_freq(self)
|
||||
|
||||
def change_baud(self, baud):
|
||||
rom_with_48M_XTAL = not self.IS_STUB and self.get_crystal_freq() == 48
|
||||
if rom_with_48M_XTAL:
|
||||
# The code is copied over from ESPLoader.change_baud().
|
||||
# Probably this is just a temporary solution until the next chip revision.
|
||||
|
||||
# The ROM code thinks it uses a 40 MHz XTAL. Recompute the baud rate
|
||||
# in order to trick the ROM code to set the correct baud rate for
|
||||
# a 48 MHz XTAL.
|
||||
false_rom_baud = baud * 40 // 48
|
||||
|
||||
print(f"Changing baud rate to {baud}")
|
||||
self.command(
|
||||
self.ESP_CHANGE_BAUDRATE, struct.pack("<II", false_rom_baud, 0)
|
||||
)
|
||||
print("Changed.")
|
||||
self._set_port_baudrate(baud)
|
||||
time.sleep(0.05) # get rid of garbage sent during baud rate change
|
||||
self.flush_input()
|
||||
else:
|
||||
ESPLoader.change_baud(self, baud)
|
||||
|
||||
|
||||
class ESP32C5BETA3StubLoader(ESP32C5BETA3ROM):
|
||||
"""Access class for ESP32C5BETA3 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
|
||||
|
||||
|
||||
ESP32C5BETA3ROM.STUB_CLASS = ESP32C5BETA3StubLoader
|
||||
@@ -6,7 +6,7 @@
|
||||
import struct
|
||||
|
||||
from .esp32c3 import ESP32C3ROM
|
||||
from util import FatalError, NotImplementedInROMError
|
||||
from ..util import FatalError, NotImplementedInROMError
|
||||
|
||||
|
||||
class ESP32C6ROM(ESP32C3ROM):
|
||||
@@ -161,8 +161,10 @@ class ESP32C6ROM(ESP32C3ROM):
|
||||
)
|
||||
|
||||
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")
|
||||
if key_block < 0 or key_block > self.EFUSE_MAX_KEY:
|
||||
raise FatalError(
|
||||
f"Valid key block numbers must be in range 0-{self.EFUSE_MAX_KEY}"
|
||||
)
|
||||
|
||||
reg, shift = [
|
||||
(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT),
|
||||
@@ -176,7 +178,9 @@ class ESP32C6ROM(ESP32C3ROM):
|
||||
|
||||
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)]
|
||||
purposes = [
|
||||
self.get_key_block_purpose(b) for b in range(self.EFUSE_MAX_KEY + 1)
|
||||
]
|
||||
|
||||
return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes)
|
||||
|
||||
|
||||
144
tools/python/esptool/targets/esp32c61.py
Normal file
144
tools/python/esptool/targets/esp32c61.py
Normal file
@@ -0,0 +1,144 @@
|
||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import struct
|
||||
from typing import Dict
|
||||
|
||||
from .esp32c6 import ESP32C6ROM
|
||||
|
||||
|
||||
class ESP32C61ROM(ESP32C6ROM):
|
||||
CHIP_NAME = "ESP32-C61"
|
||||
IMAGE_CHIP_ID = 20
|
||||
|
||||
# Magic value for ESP32C61
|
||||
CHIP_DETECT_MAGIC_VALUE = [0x33F0206F, 0x2421606F]
|
||||
|
||||
UART_DATE_REG_ADDR = 0x60000000 + 0x7C
|
||||
|
||||
EFUSE_BASE = 0x600B4800
|
||||
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 = 4
|
||||
EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x34
|
||||
EFUSE_PURPOSE_KEY2_SHIFT = 8
|
||||
EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x34
|
||||
EFUSE_PURPOSE_KEY3_SHIFT = 12
|
||||
EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x34
|
||||
EFUSE_PURPOSE_KEY4_SHIFT = 16
|
||||
EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x34
|
||||
EFUSE_PURPOSE_KEY5_SHIFT = 20
|
||||
|
||||
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE
|
||||
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20
|
||||
|
||||
EFUSE_SPI_BOOT_CRYPT_CNT_REG = EFUSE_BASE + 0x030
|
||||
EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 23
|
||||
|
||||
EFUSE_SECURE_BOOT_EN_REG = EFUSE_BASE + 0x034
|
||||
EFUSE_SECURE_BOOT_EN_MASK = 1 << 26
|
||||
|
||||
FLASH_FREQUENCY = {
|
||||
"80m": 0xF,
|
||||
"40m": 0x0,
|
||||
"20m": 0x2,
|
||||
}
|
||||
|
||||
MEMORY_MAP = [
|
||||
[0x00000000, 0x00010000, "PADDING"],
|
||||
[0x41800000, 0x42000000, "DROM"],
|
||||
[0x40800000, 0x40860000, "DRAM"],
|
||||
[0x40800000, 0x40860000, "BYTE_ACCESSIBLE"],
|
||||
[0x4004AC00, 0x40050000, "DROM_MASK"],
|
||||
[0x40000000, 0x4004AC00, "IROM_MASK"],
|
||||
[0x41000000, 0x41800000, "IROM"],
|
||||
[0x40800000, 0x40860000, "IRAM"],
|
||||
[0x50000000, 0x50004000, "RTC_IRAM"],
|
||||
[0x50000000, 0x50004000, "RTC_DRAM"],
|
||||
[0x600FE000, 0x60100000, "MEM_INTERNAL2"],
|
||||
]
|
||||
|
||||
UF2_FAMILY_ID = 0x77D850C4
|
||||
|
||||
EFUSE_MAX_KEY = 5
|
||||
KEY_PURPOSES: Dict[int, str] = {
|
||||
0: "USER/EMPTY",
|
||||
1: "ECDSA_KEY",
|
||||
2: "XTS_AES_256_KEY_1",
|
||||
3: "XTS_AES_256_KEY_2",
|
||||
4: "XTS_AES_128_KEY",
|
||||
5: "HMAC_DOWN_ALL",
|
||||
6: "HMAC_DOWN_JTAG",
|
||||
7: "HMAC_DOWN_DIGITAL_SIGNATURE",
|
||||
8: "HMAC_UP",
|
||||
9: "SECURE_BOOT_DIGEST0",
|
||||
10: "SECURE_BOOT_DIGEST1",
|
||||
11: "SECURE_BOOT_DIGEST2",
|
||||
12: "KM_INIT_KEY",
|
||||
13: "XTS_AES_256_KEY_1_PSRAM",
|
||||
14: "XTS_AES_256_KEY_2_PSRAM",
|
||||
15: "XTS_AES_128_KEY_PSRAM",
|
||||
}
|
||||
|
||||
def get_pkg_version(self):
|
||||
num_word = 2
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 26) & 0x07
|
||||
|
||||
def get_minor_chip_version(self):
|
||||
num_word = 2
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x0F
|
||||
|
||||
def get_major_chip_version(self):
|
||||
num_word = 2
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 4) & 0x03
|
||||
|
||||
def get_chip_description(self):
|
||||
chip_name = {
|
||||
0: "ESP32-C61",
|
||||
}.get(self.get_pkg_version(), "unknown ESP32-C61")
|
||||
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"]
|
||||
|
||||
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:]
|
||||
# BASE MAC: 60:55:f9:f7:2c:a2
|
||||
macs = {
|
||||
"BASE_MAC": tuple(base_mac),
|
||||
}
|
||||
return macs.get(mac_type, None)
|
||||
|
||||
|
||||
class ESP32C61StubLoader(ESP32C61ROM):
|
||||
"""Access class for ESP32C61 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
|
||||
|
||||
|
||||
ESP32C61ROM.STUB_CLASS = ESP32C61StubLoader
|
||||
@@ -3,8 +3,10 @@
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
from typing import Dict
|
||||
|
||||
from .esp32c6 import ESP32C6ROM
|
||||
from util import FatalError
|
||||
from ..util import FatalError
|
||||
|
||||
|
||||
class ESP32H2ROM(ESP32C6ROM):
|
||||
@@ -32,6 +34,22 @@ class ESP32H2ROM(ESP32C6ROM):
|
||||
|
||||
UF2_FAMILY_ID = 0x332726F6
|
||||
|
||||
EFUSE_MAX_KEY = 5
|
||||
KEY_PURPOSES: Dict[int, str] = {
|
||||
0: "USER/EMPTY",
|
||||
1: "ECDSA_KEY",
|
||||
2: "XTS_AES_256_KEY_1",
|
||||
3: "XTS_AES_256_KEY_2",
|
||||
4: "XTS_AES_128_KEY",
|
||||
5: "HMAC_DOWN_ALL",
|
||||
6: "HMAC_DOWN_JTAG",
|
||||
7: "HMAC_DOWN_DIGITAL_SIGNATURE",
|
||||
8: "HMAC_UP",
|
||||
9: "SECURE_BOOT_DIGEST0",
|
||||
10: "SECURE_BOOT_DIGEST1",
|
||||
11: "SECURE_BOOT_DIGEST2",
|
||||
}
|
||||
|
||||
def get_pkg_version(self):
|
||||
num_word = 4
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x07
|
||||
|
||||
@@ -4,9 +4,12 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import struct
|
||||
from typing import Dict
|
||||
|
||||
from .esp32c3 import ESP32C3ROM
|
||||
from util import FatalError, NotImplementedInROMError
|
||||
from ..util import FatalError, NotImplementedInROMError
|
||||
|
||||
from typing import List
|
||||
|
||||
|
||||
class ESP32H2BETA1ROM(ESP32C3ROM):
|
||||
@@ -66,7 +69,7 @@ class ESP32H2BETA1ROM(ESP32C3ROM):
|
||||
|
||||
FLASH_ENCRYPTED_WRITE_ALIGN = 16
|
||||
|
||||
MEMORY_MAP = []
|
||||
MEMORY_MAP: List = []
|
||||
|
||||
FLASH_FREQUENCY = {
|
||||
"48m": 0xF,
|
||||
@@ -75,6 +78,21 @@ class ESP32H2BETA1ROM(ESP32C3ROM):
|
||||
"12m": 0x2,
|
||||
}
|
||||
|
||||
EFUSE_MAX_KEY = 5
|
||||
KEY_PURPOSES: Dict[int, str] = {
|
||||
0: "USER/EMPTY",
|
||||
1: "ECDSA_KEY",
|
||||
2: "RESERVED",
|
||||
4: "XTS_AES_128_KEY",
|
||||
5: "HMAC_DOWN_ALL",
|
||||
6: "HMAC_DOWN_JTAG",
|
||||
7: "HMAC_DOWN_DIGITAL_SIGNATURE",
|
||||
8: "HMAC_UP",
|
||||
9: "SECURE_BOOT_DIGEST0",
|
||||
10: "SECURE_BOOT_DIGEST1",
|
||||
11: "SECURE_BOOT_DIGEST2",
|
||||
}
|
||||
|
||||
def get_pkg_version(self):
|
||||
num_word = 4
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x07
|
||||
@@ -119,8 +137,10 @@ class ESP32H2BETA1ROM(ESP32C3ROM):
|
||||
return None # doesn't exist on ESP32-H2
|
||||
|
||||
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")
|
||||
if key_block < 0 or key_block > self.EFUSE_MAX_KEY:
|
||||
raise FatalError(
|
||||
f"Valid key block numbers must be in range 0-{self.EFUSE_MAX_KEY}"
|
||||
)
|
||||
|
||||
reg, shift = [
|
||||
(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT),
|
||||
@@ -134,7 +154,9 @@ class ESP32H2BETA1ROM(ESP32C3ROM):
|
||||
|
||||
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)]
|
||||
purposes = [
|
||||
self.get_key_block_purpose(b) for b in range(self.EFUSE_MAX_KEY + 1)
|
||||
]
|
||||
|
||||
return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes)
|
||||
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import struct
|
||||
from typing import Dict
|
||||
|
||||
from .esp32 import ESP32ROM
|
||||
from loader import ESPLoader
|
||||
from util import FatalError, NotImplementedInROMError
|
||||
from ..loader import ESPLoader
|
||||
from ..util import FatalError, NotImplementedInROMError
|
||||
|
||||
|
||||
class ESP32P4ROM(ESP32ROM):
|
||||
@@ -21,7 +22,7 @@ class ESP32P4ROM(ESP32ROM):
|
||||
|
||||
BOOTLOADER_FLASH_OFFSET = 0x2000 # First 2 sectors are reserved for FE purposes
|
||||
|
||||
CHIP_DETECT_MAGIC_VALUE = [0x0]
|
||||
CHIP_DETECT_MAGIC_VALUE = [0x0, 0x0ADDBAD0]
|
||||
|
||||
UART_DATE_REG_ADDR = 0x500CA000 + 0x8C
|
||||
|
||||
@@ -37,6 +38,8 @@ class ESP32P4ROM(ESP32ROM):
|
||||
SPI_MISO_DLEN_OFFS = 0x28
|
||||
SPI_W0_OFFS = 0x58
|
||||
|
||||
SPI_ADDR_REG_MSB = False
|
||||
|
||||
EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address
|
||||
|
||||
EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34
|
||||
@@ -85,17 +88,34 @@ class ESP32P4ROM(ESP32ROM):
|
||||
|
||||
UF2_FAMILY_ID = 0x3D308E94
|
||||
|
||||
EFUSE_MAX_KEY = 5
|
||||
KEY_PURPOSES: Dict[int, str] = {
|
||||
0: "USER/EMPTY",
|
||||
1: "ECDSA_KEY",
|
||||
2: "XTS_AES_256_KEY_1",
|
||||
3: "XTS_AES_256_KEY_2",
|
||||
4: "XTS_AES_128_KEY",
|
||||
5: "HMAC_DOWN_ALL",
|
||||
6: "HMAC_DOWN_JTAG",
|
||||
7: "HMAC_DOWN_DIGITAL_SIGNATURE",
|
||||
8: "HMAC_UP",
|
||||
9: "SECURE_BOOT_DIGEST0",
|
||||
10: "SECURE_BOOT_DIGEST1",
|
||||
11: "SECURE_BOOT_DIGEST2",
|
||||
12: "KM_INIT_KEY",
|
||||
}
|
||||
|
||||
def get_pkg_version(self):
|
||||
# ESP32P4 TODO
|
||||
return 0
|
||||
num_word = 2
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 20) & 0x07
|
||||
|
||||
def get_minor_chip_version(self):
|
||||
# ESP32P4 TODO
|
||||
return 0
|
||||
num_word = 2
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x0F
|
||||
|
||||
def get_major_chip_version(self):
|
||||
# ESP32P4 TODO
|
||||
return 0
|
||||
num_word = 2
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 4) & 0x03
|
||||
|
||||
def get_chip_description(self):
|
||||
chip_name = {
|
||||
@@ -112,6 +132,9 @@ class ESP32P4ROM(ESP32ROM):
|
||||
# ESP32P4 XTAL is fixed to 40MHz
|
||||
return 40
|
||||
|
||||
def get_flash_voltage(self):
|
||||
pass # not supported on ESP32-P4
|
||||
|
||||
def override_vddsdio(self, new_voltage):
|
||||
raise NotImplementedInROMError(
|
||||
"VDD_SDIO overrides are not supported for ESP32-P4"
|
||||
@@ -136,8 +159,10 @@ class ESP32P4ROM(ESP32ROM):
|
||||
)
|
||||
|
||||
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")
|
||||
if key_block < 0 or key_block > self.EFUSE_MAX_KEY:
|
||||
raise FatalError(
|
||||
f"Valid key block numbers must be in range 0-{self.EFUSE_MAX_KEY}"
|
||||
)
|
||||
|
||||
reg, shift = [
|
||||
(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT),
|
||||
@@ -151,7 +176,9 @@ class ESP32P4ROM(ESP32ROM):
|
||||
|
||||
def is_flash_encryption_key_valid(self):
|
||||
# Need to see either an AES-128 key or two AES-256 keys
|
||||
purposes = [self.get_key_block_purpose(b) for b in range(6)]
|
||||
purposes = [
|
||||
self.get_key_block_purpose(b) for b in range(self.EFUSE_MAX_KEY + 1)
|
||||
]
|
||||
|
||||
if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes):
|
||||
return True
|
||||
@@ -170,7 +197,13 @@ class ESP32P4ROM(ESP32ROM):
|
||||
# self.disable_watchdogs()
|
||||
|
||||
def check_spi_connection(self, spi_connection):
|
||||
pass # TODO: Define GPIOs for --spi-connection
|
||||
if not set(spi_connection).issubset(set(range(0, 55))):
|
||||
raise FatalError("SPI Pin numbers must be in the range 0-54.")
|
||||
if any([v for v in spi_connection if v in [24, 25]]):
|
||||
print(
|
||||
"WARNING: GPIO pins 24 and 25 are used by USB-Serial/JTAG, "
|
||||
"consider using other pins for SPI flash connection."
|
||||
)
|
||||
|
||||
|
||||
class ESP32P4StubLoader(ESP32P4ROM):
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
|
||||
import os
|
||||
import struct
|
||||
from typing import Dict
|
||||
|
||||
from .esp32 import ESP32ROM
|
||||
from loader import ESPLoader
|
||||
from reset import HardReset
|
||||
from util import FatalError, NotImplementedInROMError
|
||||
from ..loader import ESPLoader
|
||||
from ..reset import HardReset
|
||||
from ..util import FatalError, NotImplementedInROMError
|
||||
|
||||
|
||||
class ESP32S2ROM(ESP32ROM):
|
||||
@@ -31,6 +32,8 @@ class ESP32S2ROM(ESP32ROM):
|
||||
SPI_MISO_DLEN_OFFS = 0x28
|
||||
SPI_W0_OFFS = 0x58
|
||||
|
||||
SPI_ADDR_REG_MSB = False
|
||||
|
||||
MAC_EFUSE_REG = 0x3F41A044 # ESP32-S2 has special block for MAC efuses
|
||||
|
||||
UART_CLKDIV_REG = 0x3F400014
|
||||
@@ -81,6 +84,7 @@ class ESP32S2ROM(ESP32ROM):
|
||||
|
||||
GPIO_STRAP_REG = 0x3F404038
|
||||
GPIO_STRAP_SPI_BOOT_MASK = 0x8 # Not download mode
|
||||
GPIO_STRAP_VDDSPI_MASK = 1 << 4
|
||||
RTC_CNTL_OPTION1_REG = 0x3F408128
|
||||
RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1 # Is download mode forced over USB?
|
||||
|
||||
@@ -99,8 +103,29 @@ class ESP32S2ROM(ESP32ROM):
|
||||
[0x50000000, 0x50002000, "RTC_DATA"],
|
||||
]
|
||||
|
||||
EFUSE_VDD_SPI_REG = EFUSE_BASE + 0x34
|
||||
VDD_SPI_XPD = 1 << 4
|
||||
VDD_SPI_TIEH = 1 << 5
|
||||
VDD_SPI_FORCE = 1 << 6
|
||||
|
||||
UF2_FAMILY_ID = 0xBFDD4EEE
|
||||
|
||||
EFUSE_MAX_KEY = 5
|
||||
KEY_PURPOSES: Dict[int, str] = {
|
||||
0: "USER/EMPTY",
|
||||
1: "RESERVED",
|
||||
2: "XTS_AES_256_KEY_1",
|
||||
3: "XTS_AES_256_KEY_2",
|
||||
4: "XTS_AES_128_KEY",
|
||||
5: "HMAC_DOWN_ALL",
|
||||
6: "HMAC_DOWN_JTAG",
|
||||
7: "HMAC_DOWN_DIGITAL_SIGNATURE",
|
||||
8: "HMAC_UP",
|
||||
9: "SECURE_BOOT_DIGEST0",
|
||||
10: "SECURE_BOOT_DIGEST1",
|
||||
11: "SECURE_BOOT_DIGEST2",
|
||||
}
|
||||
|
||||
def get_pkg_version(self):
|
||||
num_word = 4
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x0F
|
||||
@@ -183,6 +208,9 @@ class ESP32S2ROM(ESP32ROM):
|
||||
# ESP32-S2 XTAL is fixed to 40MHz
|
||||
return 40
|
||||
|
||||
def _get_rtc_cntl_flash_voltage(self):
|
||||
return None # not supported on ESP32-S2
|
||||
|
||||
def override_vddsdio(self, new_voltage):
|
||||
raise NotImplementedInROMError(
|
||||
"VDD_SDIO overrides are not supported for ESP32-S2"
|
||||
@@ -215,8 +243,10 @@ class ESP32S2ROM(ESP32ROM):
|
||||
)
|
||||
|
||||
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")
|
||||
if key_block < 0 or key_block > self.EFUSE_MAX_KEY:
|
||||
raise FatalError(
|
||||
f"Valid key block numbers must be in range 0-{self.EFUSE_MAX_KEY}"
|
||||
)
|
||||
|
||||
reg, shift = [
|
||||
(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT),
|
||||
@@ -230,7 +260,9 @@ class ESP32S2ROM(ESP32ROM):
|
||||
|
||||
def is_flash_encryption_key_valid(self):
|
||||
# Need to see either an AES-128 key or two AES-256 keys
|
||||
purposes = [self.get_key_block_purpose(b) for b in range(6)]
|
||||
purposes = [
|
||||
self.get_key_block_purpose(b) for b in range(self.EFUSE_MAX_KEY + 1)
|
||||
]
|
||||
|
||||
if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes):
|
||||
return True
|
||||
@@ -266,15 +298,12 @@ class ESP32S2ROM(ESP32ROM):
|
||||
strap_reg & self.GPIO_STRAP_SPI_BOOT_MASK == 0
|
||||
and force_dl_reg & self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK == 0
|
||||
):
|
||||
print(
|
||||
"WARNING: {} chip was placed into download mode using GPIO0.\n"
|
||||
"esptool.py can not exit the download mode over USB. "
|
||||
"To run the app, reset the chip manually.\n"
|
||||
"To suppress this note, set --after option to 'no_reset'.".format(
|
||||
self.get_chip_description()
|
||||
)
|
||||
raise SystemExit(
|
||||
f"Error: {self.get_chip_description()} chip was placed into download "
|
||||
"mode using GPIO0.\nesptool.py can not exit the download mode over "
|
||||
"USB. To run the app, reset the chip manually.\n"
|
||||
"To suppress this note, set --after option to 'no_reset'."
|
||||
)
|
||||
raise SystemExit(1)
|
||||
|
||||
def hard_reset(self):
|
||||
uses_usb_otg = self.uses_usb_otg()
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
|
||||
import os
|
||||
import struct
|
||||
from typing import Dict
|
||||
|
||||
from .esp32 import ESP32ROM
|
||||
from loader import ESPLoader
|
||||
from reset import HardReset
|
||||
from util import FatalError, NotImplementedInROMError
|
||||
from ..loader import ESPLoader
|
||||
from ..reset import HardReset
|
||||
from ..util import FatalError, NotImplementedInROMError
|
||||
|
||||
|
||||
class ESP32S3ROM(ESP32ROM):
|
||||
@@ -34,6 +35,8 @@ class ESP32S3ROM(ESP32ROM):
|
||||
SPI_MISO_DLEN_OFFS = 0x28
|
||||
SPI_W0_OFFS = 0x58
|
||||
|
||||
SPI_ADDR_REG_MSB = False
|
||||
|
||||
BOOTLOADER_FLASH_OFFSET = 0x0
|
||||
|
||||
SUPPORTS_ENCRYPTED_FLASH = True
|
||||
@@ -95,6 +98,7 @@ class ESP32S3ROM(ESP32ROM):
|
||||
|
||||
GPIO_STRAP_REG = 0x60004038
|
||||
GPIO_STRAP_SPI_BOOT_MASK = 0x8 # Not download mode
|
||||
GPIO_STRAP_VDDSPI_MASK = 1 << 4
|
||||
RTC_CNTL_OPTION1_REG = 0x6000812C
|
||||
RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1 # Is download mode forced over USB?
|
||||
|
||||
@@ -115,8 +119,29 @@ class ESP32S3ROM(ESP32ROM):
|
||||
[0x50000000, 0x50002000, "RTC_DATA"],
|
||||
]
|
||||
|
||||
EFUSE_VDD_SPI_REG = EFUSE_BASE + 0x34
|
||||
VDD_SPI_XPD = 1 << 4
|
||||
VDD_SPI_TIEH = 1 << 5
|
||||
VDD_SPI_FORCE = 1 << 6
|
||||
|
||||
UF2_FAMILY_ID = 0xC47E5767
|
||||
|
||||
EFUSE_MAX_KEY = 5
|
||||
KEY_PURPOSES: Dict[int, str] = {
|
||||
0: "USER/EMPTY",
|
||||
1: "RESERVED",
|
||||
2: "XTS_AES_256_KEY_1",
|
||||
3: "XTS_AES_256_KEY_2",
|
||||
4: "XTS_AES_128_KEY",
|
||||
5: "HMAC_DOWN_ALL",
|
||||
6: "HMAC_DOWN_JTAG",
|
||||
7: "HMAC_DOWN_DIGITAL_SIGNATURE",
|
||||
8: "HMAC_UP",
|
||||
9: "SECURE_BOOT_DIGEST0",
|
||||
10: "SECURE_BOOT_DIGEST1",
|
||||
11: "SECURE_BOOT_DIGEST2",
|
||||
}
|
||||
|
||||
def get_pkg_version(self):
|
||||
num_word = 3
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x07
|
||||
@@ -221,8 +246,10 @@ class ESP32S3ROM(ESP32ROM):
|
||||
return None # doesn't exist on ESP32-S3
|
||||
|
||||
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")
|
||||
if key_block < 0 or key_block > self.EFUSE_MAX_KEY:
|
||||
raise FatalError(
|
||||
f"Valid key block numbers must be in range 0-{self.EFUSE_MAX_KEY}"
|
||||
)
|
||||
|
||||
reg, shift = [
|
||||
(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT),
|
||||
@@ -236,7 +263,9 @@ class ESP32S3ROM(ESP32ROM):
|
||||
|
||||
def is_flash_encryption_key_valid(self):
|
||||
# Need to see either an AES-128 key or two AES-256 keys
|
||||
purposes = [self.get_key_block_purpose(b) for b in range(6)]
|
||||
purposes = [
|
||||
self.get_key_block_purpose(b) for b in range(self.EFUSE_MAX_KEY + 1)
|
||||
]
|
||||
|
||||
if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes):
|
||||
return True
|
||||
@@ -251,6 +280,9 @@ class ESP32S3ROM(ESP32ROM):
|
||||
& self.EFUSE_SECURE_BOOT_EN_MASK
|
||||
)
|
||||
|
||||
def _get_rtc_cntl_flash_voltage(self):
|
||||
return None # not supported on ESP32-S3
|
||||
|
||||
def override_vddsdio(self, new_voltage):
|
||||
raise NotImplementedInROMError(
|
||||
"VDD_SDIO overrides are not supported for ESP32-S3"
|
||||
@@ -328,21 +360,28 @@ class ESP32S3ROM(ESP32ROM):
|
||||
strap_reg & self.GPIO_STRAP_SPI_BOOT_MASK == 0
|
||||
and force_dl_reg & self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK == 0
|
||||
):
|
||||
print(
|
||||
"WARNING: {} chip was placed into download mode using GPIO0.\n"
|
||||
"esptool.py can not exit the download mode over USB. "
|
||||
"To run the app, reset the chip manually.\n"
|
||||
"To suppress this note, set --after option to 'no_reset'.".format(
|
||||
self.get_chip_description()
|
||||
)
|
||||
raise SystemExit(
|
||||
f"Error: {self.get_chip_description()} chip was placed into download "
|
||||
"mode using GPIO0.\nesptool.py can not exit the download mode over "
|
||||
"USB. To run the app, reset the chip manually.\n"
|
||||
"To suppress this note, set --after option to 'no_reset'."
|
||||
)
|
||||
raise SystemExit(1)
|
||||
|
||||
def hard_reset(self):
|
||||
uses_usb_otg = self.uses_usb_otg()
|
||||
if uses_usb_otg:
|
||||
self._check_if_can_reset()
|
||||
|
||||
try:
|
||||
# Clear force download boot mode to avoid the chip being stuck in download mode after reset
|
||||
# workaround for issue: https://github.com/espressif/arduino-esp32/issues/6762
|
||||
self.write_reg(
|
||||
self.RTC_CNTL_OPTION1_REG, 0, self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK
|
||||
)
|
||||
except Exception:
|
||||
# Skip if response was not valid and proceed to reset; e.g. when monitoring while resetting
|
||||
pass
|
||||
|
||||
print("Hard resetting via RTS pin...")
|
||||
HardReset(self._port, uses_usb_otg)()
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
from loader import ESPLoader
|
||||
from util import FatalError, NotSupportedError
|
||||
from ..loader import ESPLoader
|
||||
from ..util import FatalError, NotSupportedError
|
||||
|
||||
|
||||
class ESP8266ROM(ESPLoader):
|
||||
@@ -169,12 +169,18 @@ class ESP8266ROM(ESPLoader):
|
||||
else:
|
||||
return (num_sectors - head_sectors) * sector_size
|
||||
|
||||
def get_flash_voltage(self):
|
||||
pass # not supported on ESP8266
|
||||
|
||||
def override_vddsdio(self, new_voltage):
|
||||
raise NotSupportedError(self, "Overriding VDDSDIO")
|
||||
|
||||
def check_spi_connection(self, spi_connection):
|
||||
raise NotSupportedError(self, "Setting --spi-connection")
|
||||
|
||||
def get_secure_boot_enabled(self):
|
||||
return False # ESP8266 doesn't have security features
|
||||
|
||||
|
||||
class ESP8266StubLoader(ESP8266ROM):
|
||||
"""Access class for ESP8266 stub loader, runs on top of ROM."""
|
||||
|
||||
3
tools/python/esptool/targets/stub_flasher/1/README.md
Normal file
3
tools/python/esptool/targets/stub_flasher/1/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Licensing
|
||||
|
||||
The binaries in JSON format distributed in this directory are released as Free Software under GNU General Public License Version 2 or later. They were released at https://github.com/espressif/esptool-legacy-flasher-stub/releases/tag/v1.3.0 from where the sources can be obtained.
|
||||
8
tools/python/esptool/targets/stub_flasher/1/esp32c3.json
Normal file
8
tools/python/esptool/targets/stub_flasher/1/esp32c3.json
Normal file
File diff suppressed because one or more lines are too long
8
tools/python/esptool/targets/stub_flasher/1/esp32c5.json
Normal file
8
tools/python/esptool/targets/stub_flasher/1/esp32c5.json
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"entry": 1082131910,
|
||||
"text": "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",
|
||||
"text_start": 1082130432,
|
||||
"data": "DACEQO4IgEA6CYBAkgmAQGAKgEDMCoBAegqAQLYHgEAcCoBAXAqAQKYJgEBmB4BA2gmAQGYHgEDICIBADAmAQDoJgECSCYBA2giAQCAIgEBQCIBA1giAQCQNgEA6CYBA5AuAQNgMgECyBoBAAg2AQLIGgECyBoBAsgaAQLIGgECyBoBAsgaAQLIGgECyBoBAgAuAQLIGgEAADIBA2AyAQA==",
|
||||
"data_start": 1082469288,
|
||||
"bss_start": 1082392576
|
||||
}
|
||||
8
tools/python/esptool/targets/stub_flasher/1/esp32c6.json
Normal file
8
tools/python/esptool/targets/stub_flasher/1/esp32c6.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
8
tools/python/esptool/targets/stub_flasher/1/esp32h2.json
Normal file
8
tools/python/esptool/targets/stub_flasher/1/esp32h2.json
Normal file
File diff suppressed because one or more lines are too long
8
tools/python/esptool/targets/stub_flasher/1/esp32p4.json
Normal file
8
tools/python/esptool/targets/stub_flasher/1/esp32p4.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"entry": 1341195918,
|
||||
"text": "QREixCbCBsa3Jw1QEUc3BPVP2Mu3JA1QEwQEANxAkYuR57JAIkSSREEBgoCIQBxAE3X1D4KX3bcBEbenDFBOxoOphwBKyDcJ9U8mylLEBs4izLekDFB9WhMJCQDATBN09D8N4PJAYkQjqDQBQknSRLJJIkoFYYKAiECDJwkAE3X1D4KXfRTjGUT/yb8TBwAMlEGqh2MY5QCFR4XGI6AFAHlVgoAFR2OH5gAJRmONxgB9VYKAQgUTB7ANQYVjlecCiUecwfW3kwbADWMW1QCYwRMFAAyCgJMG0A19VWOV1wCYwRMFsA2CgLc19k9BEZOFRboGxmE/Y0UFBrc39k+Th8exA6cHCAPWRwgTdfUPkwYWAMIGwYIjktcIMpcjAKcAA9dHCJFnk4cHBGMe9wI3t/VPEwfHsaFnupcDpgcIt/b1T7c39k+Th8exk4bGtWMf5gAjpscII6DXCCOSBwghoPlX4wb1/LJAQQGCgCOm1wgjoOcI3bc31whQfEudi/X/N8cIUHxLnYv1/4KAQREGxt03t9cIUCOmBwI3BwAImMOYQ33/yFeyQBNF9f8FiUEBgoBBEQbG2T993TcHAEC31whQmMM31whQHEP9/7JAQQGCgEERIsQ3hPVPkwcEAUrAA6kHAQbGJsJjCgkERTc5xb1HEwQEAYFEY9YnAQREvYiTtBQAfTeFPxxENwaAABOXxwCZ4DcGAAG39v8AdY+31ghQ2MKQwphCff9BR5HgBUczCelAupcjKCQBHMSyQCJEkkQCSUEBgoABEQbOIswlNzcE9E9sABMFxP6XAM//54Ag86qHBUWV57JHk/cHID7GiTc31whQHEe3BkAAEwXE/tWPHMeyRZcAz//ngKDwMzWgAPJAYkQFYYKAQRG3h/VPBsaThwcBBUcjgOcAE9fFAJjHBWd9F8zDyMf5jTqVqpWxgYzLI6oHAEE3GcETBVAMskBBAYKAAREizDeE9U+TBwQBJsrER07GBs5KyKqJEwQEAWPzlQCuhKnAAylEACaZE1nJABxIY1XwABxEY175ArU9fd1IQCaGzoWXAM//54Cg4xN19Q8BxZMHQAxcyFxAppdcwFxEhY9cxPJAYkTSREJJskkFYYKAaTVtv0ERBsaXAM//54BA1gNFhQGyQGkVEzUVAEEBgoBBEQbGxTcRwRlFskBBARcDz/9nAOPPQREGxibCIsSqhJcAz//ngADNdT8NyTcH9U+TBgcAg9dGABMEBwCFB8IHwYMjkvYAkwYADGOG1AATB+ADY3X3AG03IxIEALJAIkSSREEBgoBBEQbGEwcADGMa5QATBbANRTcTBcANskBBAVm/EwewDeMb5f5xNxMF0A31t0ERIsQmwgbGKoSzBLUAYxeUALJAIkSSREEBgoADRQQABQRNP+23NXEmy07H/XKFaf10Is1KyVLFVsMGz5OEhPoWkZOHCQemlxgIs4TnACqJJoUuhJcAz//ngOAZk4cJBxgIBWq6l7OKR0Ex5AVnfXWTBYX6kwcHBxMFhfkUCKqXM4XXAJMHBweul7OF1wAqxpcAz//ngKAWMkXBRZU3AUWFYhaR+kBqRNpESkm6SSpKmkoNYYKAooljc4oAhWlOhtaFSoWXAM//54CgyRN19Q8B7U6G1oUmhZcAz//ngOARTpkzBDRBUbcTBTAGVb8TBQAMSb0xcf1yBWdO11LVVtNezwbfIt0m20rZWtFizWbLaslux/13FpETBwcHPpccCLqXPsYjqgf4qokuirKKtosNNZMHAAIZwbcHAgA+hZcAz//ngIAKhWdj5VcTBWR9eRMJifqTBwQHypcYCDOJ5wBKhZcAz//ngAAJfXsTDDv5kwyL+RMHBAeTBwQHFAhil+aXgUQzDNcAs4zXAFJNY3xNCWPxpANBqJk/ooUIAY01uTcihgwBSoWXAM//54DgBKKZopRj9UQDs4ekQWPxdwMzBJpAY/OKAFaEIoYMAU6FlwDP/+eA4LgTdfUPVd0CzAFEeV2NTaMJAQBihZcAz//ngKCnffkDRTEB5oVZPGNPBQDj4o3+hWeThwcHopcYCLqX2pcjiqf4BQTxt+MVpf2RR+MF9PYFZ311kwcHB5MFhfoTBYX5FAiqlzOF1wCTBwcHrpezhdcAKsaXAM//54AA+3E9MkXBRWUzUT3dObcHAgAZ4ZMHAAI+hZcAz//ngAD4hWIWkfpQalTaVEpZulkqWppaClv6S2pM2kxKTbpNKWGCgLdXQUkZcZOH94QBRYbeotym2srYztbS1NbS2tDezuLM5srqyO7GPs6XAM//54DgoHkxBcU3R9hQt2cRUBMHF6qYzyOgBwAjrAcAmNPYT7cGBABVj9jPI6AHArcH9U83N/ZPk4cHABMHx7ohoCOgBwCRB+Pt5/7VM5FFaAjFOfE7t7f1T5OHx7EhZz6XIyD3CLcH8U83CfVPk4eHDiMg+QC3OfZPKTmTicmxEwkJAGMFBRC3Zw1QEwcQArjPhUVFRZcAz//ngKDmtwXxTwFGk4UFAEVFlwDP/+eAoOe3Jw1QEUeYyzcFAgCXAM//54Dg5rcHDlCIX4FFt4T1T3GJYRUTNRUAlwDP/+eAYKXBZ/0XEwcAEIVmQWa3BQABAUWThAQBtwr1Tw1qlwDP/+eAIJsTiwoBJpqDp8kI9d+Dq8kIhUcjpgkIIwLxAoPHGwAJRyMT4QKjAvECAtRNR2OB5whRR2OP5wYpR2Of5wCDxzsAA8crAKIH2Y8RR2OW5wCDp4sAnEM+1NE5oUVIEMU2g8c7AAPHKwCiB9mPEWdBB2N09wQTBbANqTYTBcANkTYTBeAOPT5dMUG3twXxTwFGk4WFAxVFlwDP/+eAoNg3pwxQXEcTBQACk+cXEFzHMbfJRyMT8QJNtwPHGwDRRmPn5gKFRmPm5gABTBME8A+FqHkXE3f3D8lG4+jm/rc29k8KB5OGBrs2lxhDAoeTBgcDk/b2DxFG42nW/BMH9wITd/cPjUZj6+YItzb2TwoHk4bGvzaXGEMChxMHQAJjl+cQAtQdRAFFcTwBReU0ATH9PqFFSBB9FCE2dfQBTAFEE3X0D8E8E3X8D+k0zTbjHgTqg8cbAElHY2v3MAlH43b36vUXk/f3Dz1H42D36jc39k+KBxMHx8C6l5xDgocFRJ3rcBCBRQFFl/DO/+eAoHcd4dFFaBBtNAFEMagFRIHvl/DO/+eAIH0zNKAAKaAhR2OF5wAFRAFMYbcDrIsAA6TLALNnjADSB/X30TBl9cFsIpz9HH19MwWMQF3cs3eVAZXjwWwzBYxAY+aMAv18MwWMQF3QMYGX8M7/54DAeV35ZpT1tzGBl/DO/+eAwHhd8WqU0bdBgZfwzv/ngAB4WfkzBJRBwbchR+OK5/ABTBMEAAw5t0FHzb9BRwVE453n9oOlywADpYsAOTy5v0FHBUTjk+f2A6cLAZFnY+7nHoOlSwEDpYsA7/C/hz2/QUcFROOT5/SDpwsBEWdjbvccA6fLAIOlSwEDpYsAM4TnAu/wP4UjrAQAIySKsDm3A8cEAGMHBxQDp4sAwRcTBAAMYxP3AMBIAUeTBvAOY0b3AoPHWwADx0sAAUyiB9mPA8drAEIHXY+Dx3sA4gfZj+OC9uYTBBAMsb0zhusAA0aGAQUHsY7ht4PHBAD9y9xEY5EHFsBII4AEAEW9YUdjlucCg6fLAQOniwGDpksBA6YLAYOlywADpYsAl/DO/+eAgGgqjDM0oAAxtQFMBUQZtRFHBUTjm+fmtxcOUPRfZXd9FwVm+Y7RjgOliwCThQcI9N+UQfmO0Y6UwZOFRwiUQfmO0Y6UwbRfgUV1j1GPuN+X8M7/54AgaxG9E/f3AOMRB+qT3EcAE4SLAAFMfV3jcZzbSESX8M7/54AgThhEVEAQQPmOYwenARxCE0f3/32P2Y4UwgUMQQTZvxFHhbVBRwVE45Tn3oOniwADp0sBIyb5ACMk6QBdu4MliQDBF5Hlic8BTBMEYAyxswMnyQBjZvcGE/c3AOMVB+IDKMkAAUYBRzMF6ECzhuUAY2n3AOMBBtIjJqkAIyTZABm7M4brABBOEQeQwgVG6b8hRwVE457n1gMkyQAZwBMEgAwjJgkAIyQJADM0gACNswFMEwQgDNWxAUwTBIAM8bkBTBMEkAzRuRMHIA1jg+cMEwdADeOY57gDxDsAg8crACIEXYyX8M7/54AATgOsxABBFGNzhAEijOMGDLbAQGKUMYCcSGNV8ACcRGNb9Arv8O/Rdd3IQGKGk4WLAZfwzv/ngABKAcWTB0AM3MjcQOKX3MDcRLOHh0HcxJfwzv/ngOBIDbYJZRMFBXEDrMsAA6SLAJfwzv/ngKA4t6cMUNhLtwYAAcEWk1dHARIHdY+9i9mPs4eHAwFFs9WHApfwzv/ngAA6EwWAPpfwzv/ngEA10byDpksBA6YLAYOlywADpYsA7/DP/n28g8U7AIPHKwAThYsBogXdjcEV7/DP21207/Avyz2/A8Q7AIPHKwATjIsBIgRdjNxEQRTN45FHhUtj/4cIkweQDNzIrbwDpw0AItAFSLOH7EA+1oMnirBjc/QADUhCxjrE7/CvxiJHMkg3hfVP4oV8EJOGCgEQEBMFhQKX8M7/54BgNze39U+TCAcBglcDp4iwg6UNAB2MHY8+nLJXI6TosKqLvpUjoL0Ak4cKAZ2NAcWhZ2OX9QBahe/wb9EjoG0BCcTcRJnD409w92PfCwCTB3AMvbeFS7c99k+3jPVPk43NupOMDAHpv+OaC5zcROOHB5yTB4AMqbeDp4sA45AHnO/wD9YJZRMFBXGX8M7/54CgIpfwzv/ngKAnTbIDpMsA4w4EmO/wz9MTBYA+l/DO/+eAgCAClFmy9lBmVNZURlm2WSZalloGW/ZLZkzWTEZNtk0JYYKAAAA=",
|
||||
"text_start": 1341194240,
|
||||
"data": "EAD1TwYK8U9WCvFPrgrxT4QL8U/wC/FPngvxT9QI8U9AC/FPgAvxT8IK8U+ECPFP9grxT4QI8U/gCfFPJgrxT1YK8U+uCvFP8gnxTzgJ8U9oCfFP7gnxT0AO8U9WCvFPCA3xTwAO8U/EB/FPJA7xT8QH8U/EB/FPxAfxT8QH8U/EB/FPxAfxT8QH8U/EB/FPpAzxT8QH8U8mDfFPAA7xTw==",
|
||||
"data_start": 1341533100,
|
||||
"bss_start": 1341456384
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
201
tools/python/esptool/targets/stub_flasher/2/LICENSE-APACHE
Normal file
201
tools/python/esptool/targets/stub_flasher/2/LICENSE-APACHE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
25
tools/python/esptool/targets/stub_flasher/2/LICENSE-MIT
Normal file
25
tools/python/esptool/targets/stub_flasher/2/LICENSE-MIT
Normal file
@@ -0,0 +1,25 @@
|
||||
Copyright 2022 esp-rs
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
3
tools/python/esptool/targets/stub_flasher/2/README.md
Normal file
3
tools/python/esptool/targets/stub_flasher/2/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Licensing
|
||||
|
||||
The binaries in JSON format distributed in this directory are dual licensed under the Apache License Version 2.0 or the MIT license. They were released at https://github.com/esp-rs/esp-flasher-stub/releases/tag/v0.3.0 from where the sources can be obtained.
|
||||
1
tools/python/esptool/targets/stub_flasher/2/esp32.json
Normal file
1
tools/python/esptool/targets/stub_flasher/2/esp32.json
Normal file
File diff suppressed because one or more lines are too long
1
tools/python/esptool/targets/stub_flasher/2/esp32c2.json
Normal file
1
tools/python/esptool/targets/stub_flasher/2/esp32c2.json
Normal file
File diff suppressed because one or more lines are too long
1
tools/python/esptool/targets/stub_flasher/2/esp32c3.json
Normal file
1
tools/python/esptool/targets/stub_flasher/2/esp32c3.json
Normal file
File diff suppressed because one or more lines are too long
1
tools/python/esptool/targets/stub_flasher/2/esp32c6.json
Normal file
1
tools/python/esptool/targets/stub_flasher/2/esp32c6.json
Normal file
File diff suppressed because one or more lines are too long
1
tools/python/esptool/targets/stub_flasher/2/esp32h2.json
Normal file
1
tools/python/esptool/targets/stub_flasher/2/esp32h2.json
Normal file
File diff suppressed because one or more lines are too long
1
tools/python/esptool/targets/stub_flasher/2/esp32s2.json
Normal file
1
tools/python/esptool/targets/stub_flasher/2/esp32s2.json
Normal file
File diff suppressed because one or more lines are too long
1
tools/python/esptool/targets/stub_flasher/2/esp32s3.json
Normal file
1
tools/python/esptool/targets/stub_flasher/2/esp32s3.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"entry": 1341195718,
|
||||
"text": "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",
|
||||
"text_start": 1341194240,
|
||||
"data": "DAD1T+4I8U86CfFPkgnxT2gK8U/UCvFPggrxT7YH8U8kCvFPZArxT6YJ8U9mB/FP2gnxT2YH8U/ICPFPDAnxTzoJ8U+SCfFP2gjxTyAI8U9QCPFP1gjxTywN8U86CfFP7AvxT+AM8U+yBvFPCg3xT7IG8U+yBvFPsgbxT7IG8U+yBvFPsgbxT7IG8U+yBvFPiAvxT7IG8U8IDPFP4AzxTw==",
|
||||
"data_start": 1341533096,
|
||||
"bss_start": 1341456384
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Code was originally licensed under Apache 2.0 before the release of ESP-IDF v5.2
|
||||
@@ -9,7 +7,7 @@ import os
|
||||
import struct
|
||||
from typing import List
|
||||
|
||||
from util import div_roundup
|
||||
from esptool.util import div_roundup
|
||||
|
||||
|
||||
class UF2Writer(object):
|
||||
|
||||
@@ -99,6 +99,20 @@ def get_file_size(path_to_file):
|
||||
return file_size
|
||||
|
||||
|
||||
class PrintOnce:
|
||||
"""
|
||||
Class for printing messages just once. Can be useful when running in a loop
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.already_printed = False
|
||||
|
||||
def __call__(self, text) -> None:
|
||||
if not self.already_printed:
|
||||
print(text)
|
||||
self.already_printed = True
|
||||
|
||||
|
||||
class FatalError(RuntimeError):
|
||||
"""
|
||||
Wrapper class for runtime errors that aren't caused by internal bugs, but by
|
||||
@@ -128,6 +142,12 @@ class FatalError(RuntimeError):
|
||||
0x109: "CRC or checksum was invalid",
|
||||
0x10A: "Version was invalid",
|
||||
0x10B: "MAC address was invalid",
|
||||
0x6001: "Flash operation failed",
|
||||
0x6002: "Flash operation timed out",
|
||||
0x6003: "Flash not initialised properly",
|
||||
0x6004: "Operation not supported by the host SPI bus",
|
||||
0x6005: "Operation not supported by the flash chip",
|
||||
0x6006: "Can't write, protection enabled",
|
||||
# Flasher stub error codes
|
||||
0xC000: "Bad data length",
|
||||
0xC100: "Bad data checksum",
|
||||
|
||||
Reference in New Issue
Block a user