Update: 更新esptool ( version: 4.7.0 )
This commit is contained in:
@@ -28,7 +28,7 @@ __all__ = [
|
||||
"write_mem",
|
||||
]
|
||||
|
||||
__version__ = "4.6.2"
|
||||
__version__ = "4.7.0"
|
||||
|
||||
import argparse
|
||||
import inspect
|
||||
@@ -38,6 +38,7 @@ import sys
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from bin_image import intel_hex_to_bin
|
||||
from cmds import (
|
||||
DETECTED_FLASH_SIZES,
|
||||
chip_id,
|
||||
@@ -176,16 +177,18 @@ def main(argv=None, esp=None):
|
||||
parent.add_argument(
|
||||
"--spi-connection",
|
||||
"-sc",
|
||||
help="ESP32-only argument. Override default SPI Flash connection. "
|
||||
help="Override default SPI Flash connection. "
|
||||
"Value can be SPI, HSPI or a comma-separated list of 5 I/O numbers "
|
||||
"to use for SPI flash (CLK,Q,D,HD,CS).",
|
||||
"to use for SPI flash (CLK,Q,D,HD,CS). Not supported with ESP8266.",
|
||||
action=SpiConnectionAction,
|
||||
)
|
||||
|
||||
parser_load_ram = subparsers.add_parser(
|
||||
"load_ram", help="Download an image to RAM and execute"
|
||||
)
|
||||
parser_load_ram.add_argument("filename", help="Firmware image")
|
||||
parser_load_ram.add_argument(
|
||||
"filename", help="Firmware image", action=AutoHex2BinAction
|
||||
)
|
||||
|
||||
parser_dump_mem = subparsers.add_parser(
|
||||
"dump_mem", help="Dump arbitrary memory to disk"
|
||||
@@ -357,7 +360,9 @@ def main(argv=None, esp=None):
|
||||
parser_image_info = subparsers.add_parser(
|
||||
"image_info", help="Dump headers from a binary file (bootloader or application)"
|
||||
)
|
||||
parser_image_info.add_argument("filename", help="Image file to parse")
|
||||
parser_image_info.add_argument(
|
||||
"filename", help="Image file to parse", action=AutoHex2BinAction
|
||||
)
|
||||
parser_image_info.add_argument(
|
||||
"--version",
|
||||
"-v",
|
||||
@@ -477,6 +482,17 @@ def main(argv=None, esp=None):
|
||||
"must be aligned to. Value 0xFF is used for padding, similar to erase_flash",
|
||||
default=None,
|
||||
)
|
||||
parser_elf2image.add_argument(
|
||||
"--ram-only-header",
|
||||
help="Order segments of the output so IRAM and DRAM are placed at the "
|
||||
"beginning and force the main header segment number to RAM segments "
|
||||
"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.",
|
||||
action="store_true",
|
||||
default=None,
|
||||
)
|
||||
|
||||
add_spi_flash_subparsers(parser_elf2image, allow_keep=False, auto_detect=False)
|
||||
|
||||
@@ -587,18 +603,36 @@ def main(argv=None, esp=None):
|
||||
"--output", "-o", help="Output filename", type=str, required=True
|
||||
)
|
||||
parser_merge_bin.add_argument(
|
||||
"--format", "-f", help="Format of the output file", choices="raw", default="raw"
|
||||
) # for future expansion
|
||||
"--format",
|
||||
"-f",
|
||||
help="Format of the output file",
|
||||
choices=["raw", "uf2", "hex"],
|
||||
default="raw",
|
||||
)
|
||||
uf2_group = parser_merge_bin.add_argument_group("UF2 format")
|
||||
uf2_group.add_argument(
|
||||
"--chunk-size",
|
||||
help="Specify the used data part of the 512 byte UF2 block. "
|
||||
"A common value is 256. By default the largest possible value will be used.",
|
||||
default=None,
|
||||
type=arg_auto_chunk_size,
|
||||
)
|
||||
uf2_group.add_argument(
|
||||
"--md5-disable",
|
||||
help="Disable MD5 checksum in UF2 output",
|
||||
action="store_true",
|
||||
)
|
||||
add_spi_flash_subparsers(parser_merge_bin, allow_keep=True, auto_detect=False)
|
||||
|
||||
parser_merge_bin.add_argument(
|
||||
raw_group = parser_merge_bin.add_argument_group("RAW format")
|
||||
raw_group.add_argument(
|
||||
"--target-offset",
|
||||
"-t",
|
||||
help="Target offset where the output file will be flashed",
|
||||
type=arg_auto_int,
|
||||
default=0,
|
||||
)
|
||||
parser_merge_bin.add_argument(
|
||||
raw_group.add_argument(
|
||||
"--fill-flash-size",
|
||||
help="If set, the final binary file will be padded with FF "
|
||||
"bytes up to this flash size.",
|
||||
@@ -736,14 +770,22 @@ def main(argv=None, esp=None):
|
||||
"Keeping initial baud rate %d" % initial_baud
|
||||
)
|
||||
|
||||
# override common SPI flash parameter stuff if configured to do so
|
||||
# Override the common SPI flash parameter stuff if configured to do so
|
||||
if hasattr(args, "spi_connection") and args.spi_connection is not None:
|
||||
if esp.CHIP_NAME != "ESP32":
|
||||
raise FatalError(
|
||||
"Chip %s does not support --spi-connection option." % esp.CHIP_NAME
|
||||
)
|
||||
print("Configuring SPI flash mode...")
|
||||
esp.flash_spi_attach(args.spi_connection)
|
||||
spi_config = args.spi_connection
|
||||
if args.spi_connection == "SPI":
|
||||
value = 0
|
||||
elif args.spi_connection == "HSPI":
|
||||
value = 1
|
||||
else:
|
||||
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
|
||||
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
|
||||
@@ -812,6 +854,15 @@ def main(argv=None, esp=None):
|
||||
"Try checking the chip connections or removing "
|
||||
"any other hardware connected to IOs."
|
||||
)
|
||||
if (
|
||||
hasattr(args, "spi_connection")
|
||||
and args.spi_connection is not None
|
||||
):
|
||||
print(
|
||||
"Some GPIO pins might be used by other peripherals, "
|
||||
"try using another --spi-connection combination."
|
||||
)
|
||||
|
||||
except FatalError as e:
|
||||
raise FatalError(f"Unable to verify flash chip connection ({e}).")
|
||||
|
||||
@@ -834,7 +885,11 @@ def main(argv=None, esp=None):
|
||||
if flash_size is not None: # Secure download mode
|
||||
esp.flash_set_parameters(flash_size_bytes(flash_size))
|
||||
# Check if stub supports chosen flash size
|
||||
if esp.IS_STUB and flash_size in ("32MB", "64MB", "128MB"):
|
||||
if (
|
||||
esp.IS_STUB
|
||||
and esp.CHIP_NAME != "ESP32-S3"
|
||||
and flash_size_bytes(flash_size) > 16 * 1024 * 1024
|
||||
):
|
||||
print(
|
||||
"WARNING: Flasher stub doesn't fully support flash size larger "
|
||||
"than 16MB, in case of failure use --no-stub."
|
||||
@@ -858,7 +913,7 @@ def main(argv=None, esp=None):
|
||||
args.size = flash_size_bytes(size_str)
|
||||
|
||||
if esp.IS_STUB and hasattr(args, "address") and hasattr(args, "size"):
|
||||
if args.address + args.size > 0x1000000:
|
||||
if esp.CHIP_NAME != "ESP32-S3" and args.address + args.size > 0x1000000:
|
||||
print(
|
||||
"WARNING: Flasher stub doesn't fully support flash size larger "
|
||||
"than 16MB, in case of failure use --no-stub."
|
||||
@@ -906,6 +961,13 @@ def arg_auto_size(x):
|
||||
return x if x == "all" else arg_auto_int(x)
|
||||
|
||||
|
||||
def arg_auto_chunk_size(string: str) -> int:
|
||||
num = int(string, 0)
|
||||
if num & 3 != 0:
|
||||
raise argparse.ArgumentTypeError("Chunk size should be a 4-byte aligned number")
|
||||
return num
|
||||
|
||||
|
||||
def get_port_list():
|
||||
if list_ports is None:
|
||||
raise FatalError(
|
||||
@@ -934,7 +996,7 @@ def expand_file_arguments(argv):
|
||||
else:
|
||||
new_args.append(arg)
|
||||
if expanded:
|
||||
print("esptool %s" % (" ".join(new_args[1:])))
|
||||
print(f"esptool.py {' '.join(new_args)}")
|
||||
return new_args
|
||||
return argv
|
||||
|
||||
@@ -978,42 +1040,44 @@ class SpiConnectionAction(argparse.Action):
|
||||
"""
|
||||
|
||||
def __call__(self, parser, namespace, value, option_string=None):
|
||||
if value.upper() == "SPI":
|
||||
value = 0
|
||||
elif value.upper() == "HSPI":
|
||||
value = 1
|
||||
if value.upper() in ["SPI", "HSPI"]:
|
||||
values = value.upper()
|
||||
elif "," in value:
|
||||
values = value.split(",")
|
||||
if len(values) != 5:
|
||||
raise argparse.ArgumentError(
|
||||
self,
|
||||
"%s is not a valid list of comma-separate pin numbers. "
|
||||
"Must be 5 numbers - CLK,Q,D,HD,CS." % value,
|
||||
f"{value} is not a valid list of comma-separate pin numbers. "
|
||||
"Must be 5 numbers - CLK,Q,D,HD,CS.",
|
||||
)
|
||||
try:
|
||||
values = tuple(int(v, 0) for v in values)
|
||||
except ValueError:
|
||||
raise argparse.ArgumentError(
|
||||
self,
|
||||
"%s is not a valid argument. All pins must be numeric values"
|
||||
% values,
|
||||
f"{values} is not a valid argument. "
|
||||
"All pins must be numeric values",
|
||||
)
|
||||
if any([v for v in values if v > 33 or v < 0]):
|
||||
raise argparse.ArgumentError(
|
||||
self, "Pin numbers must be in the range 0-33."
|
||||
)
|
||||
# encode the pin numbers as a 32-bit integer with packed 6-bit values,
|
||||
# the same way ESP32 ROM takes them
|
||||
# TODO: make this less ESP32 ROM specific somehow...
|
||||
clk, q, d, hd, cs = values
|
||||
value = (hd << 24) | (cs << 18) | (d << 12) | (q << 6) | clk
|
||||
else:
|
||||
raise argparse.ArgumentError(
|
||||
self,
|
||||
"%s is not a valid spi-connection value. "
|
||||
"Values are SPI, HSPI, or a sequence of 5 pin numbers CLK,Q,D,HD,CS)."
|
||||
% value,
|
||||
f"{value} is not a valid spi-connection value. "
|
||||
"Values are SPI, HSPI, or a sequence of 5 pin numbers - CLK,Q,D,HD,CS.",
|
||||
)
|
||||
setattr(namespace, self.dest, values)
|
||||
|
||||
|
||||
class AutoHex2BinAction(argparse.Action):
|
||||
"""Custom parser class for auto conversion of input files from hex to bin"""
|
||||
|
||||
def __call__(self, parser, namespace, value, option_string=None):
|
||||
try:
|
||||
with open(value, "rb") as f:
|
||||
# if hex file was detected replace hex file with converted temp bin
|
||||
# otherwise keep the original file
|
||||
value = intel_hex_to_bin(f).name
|
||||
except IOError as e:
|
||||
raise argparse.ArgumentError(self, e)
|
||||
setattr(namespace, self.dest, value)
|
||||
|
||||
|
||||
@@ -1045,6 +1109,8 @@ class AddrFilenamePairAction(argparse.Action):
|
||||
"Must be pairs of an address "
|
||||
"and the binary filename to write there",
|
||||
)
|
||||
# check for intel hex files and convert them to bin
|
||||
argfile = intel_hex_to_bin(argfile, address)
|
||||
pairs.append((address, argfile))
|
||||
|
||||
# Sort the addresses and check for overlapping
|
||||
|
||||
@@ -10,6 +10,10 @@ import io
|
||||
import os
|
||||
import re
|
||||
import struct
|
||||
import tempfile
|
||||
from typing import BinaryIO, Optional
|
||||
|
||||
from intelhex import IntelHex
|
||||
|
||||
from loader import ESPLoader
|
||||
from targets import (
|
||||
@@ -20,6 +24,7 @@ from targets import (
|
||||
ESP32H2BETA1ROM,
|
||||
ESP32H2BETA2ROM,
|
||||
ESP32H2ROM,
|
||||
ESP32P4ROM,
|
||||
ESP32ROM,
|
||||
ESP32S2ROM,
|
||||
ESP32S3BETA2ROM,
|
||||
@@ -35,6 +40,23 @@ def align_file_position(f, size):
|
||||
f.seek(align, 1)
|
||||
|
||||
|
||||
def intel_hex_to_bin(file: BinaryIO, start_addr: Optional[int] = None) -> BinaryIO:
|
||||
"""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:
|
||||
return file
|
||||
|
||||
|
||||
def LoadFirmwareImage(chip, image_file):
|
||||
"""
|
||||
Load a firmware image. Can be for any supported SoC.
|
||||
@@ -61,6 +83,7 @@ def LoadFirmwareImage(chip, image_file):
|
||||
"esp32c2": ESP32C2FirmwareImage,
|
||||
"esp32c6": ESP32C6FirmwareImage,
|
||||
"esp32h2": ESP32H2FirmwareImage,
|
||||
"esp32p4": ESP32P4FirmwareImage,
|
||||
}[chip](f)
|
||||
else: # Otherwise, ESP8266 so look at magic to determine the image type
|
||||
magic = ord(f.read(1))
|
||||
@@ -248,6 +271,20 @@ class BaseFirmwareImage(object):
|
||||
if checksum is not None:
|
||||
return ESPLoader.checksum(segment_data, checksum)
|
||||
|
||||
def save_flash_segment(self, f, segment, checksum=None):
|
||||
"""
|
||||
Save the next segment to the image file, return next checksum value if provided
|
||||
"""
|
||||
if self.ROM_LOADER.CHIP_NAME == "ESP32":
|
||||
# Work around a bug in ESP-IDF 2nd stage bootloader, that it didn't map the
|
||||
# last MMU page, if an IROM/DROM segment was < 0x24 bytes
|
||||
# over the page boundary.
|
||||
segment_end_pos = f.tell() + len(segment.data) + self.SEG_HEADER_LEN
|
||||
segment_len_remainder = segment_end_pos % self.IROM_ALIGN
|
||||
if segment_len_remainder < 0x24:
|
||||
segment.data += b"\x00" * (0x24 - segment_len_remainder)
|
||||
return self.save_segment(f, segment, checksum)
|
||||
|
||||
def read_checksum(self, f):
|
||||
"""Return ESPLoader checksum from end of just-read image"""
|
||||
# Skip the padding. The checksum is stored in the last byte so that the
|
||||
@@ -555,7 +592,7 @@ class ESP32FirmwareImage(BaseFirmwareImage):
|
||||
|
||||
IROM_ALIGN = 65536
|
||||
|
||||
def __init__(self, load_file=None, append_digest=True):
|
||||
def __init__(self, load_file=None, append_digest=True, ram_only_header=False):
|
||||
super(ESP32FirmwareImage, self).__init__()
|
||||
self.secure_pad = None
|
||||
self.flash_mode = 0
|
||||
@@ -573,6 +610,7 @@ class ESP32FirmwareImage(BaseFirmwareImage):
|
||||
self.min_rev = 0
|
||||
self.min_rev_full = 0
|
||||
self.max_rev_full = 0
|
||||
self.ram_only_header = ram_only_header
|
||||
|
||||
self.append_digest = append_digest
|
||||
|
||||
@@ -632,10 +670,10 @@ class ESP32FirmwareImage(BaseFirmwareImage):
|
||||
if not self.is_flash_addr(s.addr)
|
||||
]
|
||||
|
||||
# Patch to support 761 union bus memmap // TODO: ESPTOOL-512
|
||||
# Patch to support ESP32-C6 union bus memmap
|
||||
# move ".flash.appdesc" segment to the top of the flash segment
|
||||
for segment in flash_segments:
|
||||
if segment.name == ".flash.appdesc":
|
||||
if isinstance(segment, ELFSection) and segment.name == ".flash.appdesc":
|
||||
flash_segments.remove(segment)
|
||||
flash_segments.insert(0, segment)
|
||||
break
|
||||
@@ -685,33 +723,61 @@ class ESP32FirmwareImage(BaseFirmwareImage):
|
||||
pad_len += self.IROM_ALIGN
|
||||
return pad_len
|
||||
|
||||
# try to fit each flash segment on a 64kB aligned boundary
|
||||
# by padding with parts of the non-flash segments...
|
||||
while len(flash_segments) > 0:
|
||||
segment = flash_segments[0]
|
||||
pad_len = get_alignment_data_needed(segment)
|
||||
if pad_len > 0: # need to pad
|
||||
if len(ram_segments) > 0 and pad_len > self.SEG_HEADER_LEN:
|
||||
pad_segment = ram_segments[0].split_image(pad_len)
|
||||
if len(ram_segments[0].data) == 0:
|
||||
ram_segments.pop(0)
|
||||
else:
|
||||
pad_segment = ImageSegment(0, b"\x00" * pad_len, f.tell())
|
||||
checksum = self.save_segment(f, pad_segment, checksum)
|
||||
if self.ram_only_header:
|
||||
# write RAM segments first in order to get only RAM segments quantity
|
||||
# and checksum (ROM bootloader will only care for RAM segments and its
|
||||
# correct checksums)
|
||||
for segment in ram_segments:
|
||||
checksum = self.save_segment(f, segment, checksum)
|
||||
total_segments += 1
|
||||
else:
|
||||
self.append_checksum(f, checksum)
|
||||
|
||||
# reversing to match the same section order from linker script
|
||||
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
|
||||
checksum = self.save_flash_segment(f, segment, checksum)
|
||||
flash_segments.pop(0)
|
||||
# save the flash segment but not saving its checksum neither
|
||||
# saving the number of flash segments, since ROM bootloader
|
||||
# should "not see" them
|
||||
self.save_flash_segment(f, segment)
|
||||
total_segments += 1
|
||||
else: # not self.ram_only_header
|
||||
# try to fit each flash segment on a 64kB aligned boundary
|
||||
# by padding with parts of the non-flash segments...
|
||||
while len(flash_segments) > 0:
|
||||
segment = flash_segments[0]
|
||||
pad_len = get_alignment_data_needed(segment)
|
||||
if pad_len > 0: # need to pad
|
||||
if len(ram_segments) > 0 and pad_len > self.SEG_HEADER_LEN:
|
||||
pad_segment = ram_segments[0].split_image(pad_len)
|
||||
if len(ram_segments[0].data) == 0:
|
||||
ram_segments.pop(0)
|
||||
else:
|
||||
pad_segment = ImageSegment(0, b"\x00" * pad_len, f.tell())
|
||||
checksum = self.save_segment(f, pad_segment, checksum)
|
||||
total_segments += 1
|
||||
else:
|
||||
# write the flash segment
|
||||
assert (
|
||||
f.tell() + 8
|
||||
) % self.IROM_ALIGN == segment.addr % self.IROM_ALIGN
|
||||
checksum = self.save_flash_segment(f, segment, checksum)
|
||||
flash_segments.pop(0)
|
||||
total_segments += 1
|
||||
|
||||
# flash segments all written, so write any remaining RAM segments
|
||||
for segment in ram_segments:
|
||||
checksum = self.save_segment(f, segment, checksum)
|
||||
total_segments += 1
|
||||
# flash segments all written, so write any remaining RAM segments
|
||||
for segment in ram_segments:
|
||||
checksum = self.save_segment(f, segment, checksum)
|
||||
total_segments += 1
|
||||
|
||||
if self.secure_pad:
|
||||
# pad the image so that after signing it will end on a a 64KB boundary.
|
||||
@@ -743,8 +809,9 @@ class ESP32FirmwareImage(BaseFirmwareImage):
|
||||
checksum = self.save_segment(f, pad_segment, checksum)
|
||||
total_segments += 1
|
||||
|
||||
# done writing segments
|
||||
self.append_checksum(f, checksum)
|
||||
if not self.ram_only_header:
|
||||
# done writing segments
|
||||
self.append_checksum(f, checksum)
|
||||
image_length = f.tell()
|
||||
|
||||
if self.secure_pad:
|
||||
@@ -753,7 +820,12 @@ class ESP32FirmwareImage(BaseFirmwareImage):
|
||||
# kinda hacky: go back to the initial header and write the new segment count
|
||||
# that includes padding segments. This header is not checksummed
|
||||
f.seek(1)
|
||||
f.write(bytes([total_segments]))
|
||||
if self.ram_only_header:
|
||||
# Update the header with the RAM segments quantity as it should be
|
||||
# visible by the ROM bootloader
|
||||
f.write(bytes([len(ram_segments)]))
|
||||
else:
|
||||
f.write(bytes([total_segments]))
|
||||
|
||||
if self.append_digest:
|
||||
# calculate the SHA256 of the whole file and append it
|
||||
@@ -771,19 +843,6 @@ class ESP32FirmwareImage(BaseFirmwareImage):
|
||||
with open(filename, "wb") as real_file:
|
||||
real_file.write(f.getvalue())
|
||||
|
||||
def save_flash_segment(self, f, segment, checksum=None):
|
||||
"""
|
||||
Save the next segment to the image file, return next checksum value if provided
|
||||
"""
|
||||
segment_end_pos = f.tell() + len(segment.data) + self.SEG_HEADER_LEN
|
||||
segment_len_remainder = segment_end_pos % self.IROM_ALIGN
|
||||
if segment_len_remainder < 0x24:
|
||||
# Work around a bug in ESP-IDF 2nd stage bootloader, that it didn't map the
|
||||
# last MMU page, if an IROM/DROM segment was < 0x24 bytes
|
||||
# over the page boundary.
|
||||
segment.data += b"\x00" * (0x24 - segment_len_remainder)
|
||||
return self.save_segment(f, segment, checksum)
|
||||
|
||||
def load_extended_header(self, load_file):
|
||||
def split_byte(n):
|
||||
return (n & 0x0F, (n >> 4) & 0x0F)
|
||||
@@ -1055,6 +1114,15 @@ class ESP32C6FirmwareImage(ESP32FirmwareImage):
|
||||
ESP32C6ROM.BOOTLOADER_IMAGE = ESP32C6FirmwareImage
|
||||
|
||||
|
||||
class ESP32P4FirmwareImage(ESP32FirmwareImage):
|
||||
"""ESP32P4 Firmware Image almost exactly the same as ESP32FirmwareImage"""
|
||||
|
||||
ROM_LOADER = ESP32P4ROM
|
||||
|
||||
|
||||
ESP32P4ROM.BOOTLOADER_IMAGE = ESP32P4FirmwareImage
|
||||
|
||||
|
||||
class ESP32H2FirmwareImage(ESP32C6FirmwareImage):
|
||||
"""ESP32H2 Firmware Image almost exactly the same as ESP32FirmwareImage"""
|
||||
|
||||
@@ -1067,6 +1135,7 @@ ESP32H2ROM.BOOTLOADER_IMAGE = ESP32H2FirmwareImage
|
||||
class ELFFile(object):
|
||||
SEC_TYPE_PROGBITS = 0x01
|
||||
SEC_TYPE_STRTAB = 0x03
|
||||
SEC_TYPE_NOBITS = 0x08 # e.g. .bss section
|
||||
SEC_TYPE_INITARRAY = 0x0E
|
||||
SEC_TYPE_FINIARRAY = 0x0F
|
||||
|
||||
@@ -1157,6 +1226,7 @@ class ELFFile(object):
|
||||
|
||||
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:
|
||||
@@ -1188,6 +1258,11 @@ class ELFFile(object):
|
||||
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
|
||||
if lma != 0 and size > 0
|
||||
]
|
||||
|
||||
def _read_segments(self, f, segment_header_offs, segment_header_count, shstrndx):
|
||||
f.seek(segment_header_offs)
|
||||
|
||||
@@ -11,6 +11,8 @@ import sys
|
||||
import time
|
||||
import zlib
|
||||
|
||||
from intelhex import IntelHex
|
||||
|
||||
from bin_image import ELFFile, ImageSegment, LoadFirmwareImage
|
||||
from bin_image import (
|
||||
ESP8266ROMFirmwareImage,
|
||||
@@ -25,6 +27,7 @@ from loader import (
|
||||
timeout_per_mb,
|
||||
)
|
||||
from targets import CHIP_DEFS, CHIP_LIST, ROM_LIST
|
||||
from uf2_writer import UF2Writer
|
||||
from util import (
|
||||
FatalError,
|
||||
NotImplementedInROMError,
|
||||
@@ -96,7 +99,7 @@ def detect_chip(
|
||||
print("Detecting chip type...", end="")
|
||||
chip_id = detect_port.get_chip_id()
|
||||
for cls in [
|
||||
n for n in ROM_LIST if n.CHIP_NAME not in ("ESP8266", "ESP32", "ESP32S2")
|
||||
n for n in ROM_LIST if n.CHIP_NAME not in ("ESP8266", "ESP32", "ESP32-S2")
|
||||
]:
|
||||
# cmd not supported on ESP8266 and ESP32 + ESP32-S2 doesn't return chip_id
|
||||
if chip_id == cls.IMAGE_CHIP_ID:
|
||||
@@ -466,7 +469,7 @@ def write_flash(esp, args):
|
||||
flash_end = flash_size_bytes(
|
||||
detect_flash_size(esp) if args.flash_size == "keep" else args.flash_size
|
||||
)
|
||||
if flash_end is not None: # Secure download mode
|
||||
if flash_end is not None: # Not in secure download mode
|
||||
for address, argfile in args.addr_filename:
|
||||
argfile.seek(0, os.SEEK_END)
|
||||
if address + argfile.tell() > flash_end:
|
||||
@@ -979,6 +982,8 @@ 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()
|
||||
@@ -989,6 +994,7 @@ def elf2image(args):
|
||||
image.min_rev = args.min_rev
|
||||
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
|
||||
elif args.version == "1": # ESP8266
|
||||
image = ESP8266ROMFirmwareImage()
|
||||
@@ -1106,9 +1112,9 @@ def read_flash(esp, args):
|
||||
|
||||
def flash_progress(progress, length):
|
||||
msg = "%d (%d %%)" % (progress, progress * 100.0 / length)
|
||||
# padding = "\b" * len(msg)
|
||||
# if progress == length:
|
||||
padding = "\n"
|
||||
padding = "\b" * len(msg)
|
||||
if progress == length:
|
||||
padding = "\n"
|
||||
sys.stdout.write(msg + padding)
|
||||
sys.stdout.flush()
|
||||
|
||||
@@ -1180,15 +1186,95 @@ def write_flash_status(esp, args):
|
||||
print(("After flash status: " + fmt) % esp.read_status(args.bytes))
|
||||
|
||||
|
||||
# The following mapping was taken from the ROM code
|
||||
# This mapping is same across all targets in the ROM
|
||||
SECURITY_INFO_FLAG_MAP = {
|
||||
"SECURE_BOOT_EN": (1 << 0),
|
||||
"SECURE_BOOT_AGGRESSIVE_REVOKE": (1 << 1),
|
||||
"SECURE_DOWNLOAD_ENABLE": (1 << 2),
|
||||
"SECURE_BOOT_KEY_REVOKE0": (1 << 3),
|
||||
"SECURE_BOOT_KEY_REVOKE1": (1 << 4),
|
||||
"SECURE_BOOT_KEY_REVOKE2": (1 << 5),
|
||||
"SOFT_DIS_JTAG": (1 << 6),
|
||||
"HARD_DIS_JTAG": (1 << 7),
|
||||
"DIS_USB": (1 << 8),
|
||||
"DIS_DOWNLOAD_DCACHE": (1 << 9),
|
||||
"DIS_DOWNLOAD_ICACHE": (1 << 10),
|
||||
}
|
||||
|
||||
|
||||
# Get the status of respective security flag
|
||||
def get_security_flag_status(flag_name, flags_value):
|
||||
try:
|
||||
return (flags_value & SECURITY_INFO_FLAG_MAP[flag_name]) != 0
|
||||
except KeyError:
|
||||
raise ValueError(f"Invalid flag name: {flag_name}")
|
||||
|
||||
|
||||
def get_security_info(esp, args):
|
||||
si = esp.get_security_info()
|
||||
# TODO: better display
|
||||
print()
|
||||
title = "Security Information:"
|
||||
print(title)
|
||||
print("=" * len(title))
|
||||
print("Flags: {:#010x} ({})".format(si["flags"], bin(si["flags"])))
|
||||
print("Flash_Crypt_Cnt: {:#x}".format(si["flash_crypt_cnt"]))
|
||||
print("Key_Purposes: {}".format(si["key_purposes"]))
|
||||
print("Key Purposes: {}".format(si["key_purposes"]))
|
||||
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"]))
|
||||
print("Chip ID: {}".format(si["chip_id"]))
|
||||
print("API Version: {}".format(si["api_version"]))
|
||||
|
||||
flags = si["flags"]
|
||||
|
||||
if get_security_flag_status("SECURE_BOOT_EN", flags):
|
||||
print("Secure Boot: Enabled")
|
||||
if get_security_flag_status("SECURE_BOOT_AGGRESSIVE_REVOKE", flags):
|
||||
print("Secure Boot Aggressive key revocation: Enabled")
|
||||
|
||||
revoked_keys = []
|
||||
for i, key in enumerate(
|
||||
[
|
||||
"SECURE_BOOT_KEY_REVOKE0",
|
||||
"SECURE_BOOT_KEY_REVOKE1",
|
||||
"SECURE_BOOT_KEY_REVOKE2",
|
||||
]
|
||||
):
|
||||
if get_security_flag_status(key, flags):
|
||||
revoked_keys.append(i)
|
||||
|
||||
if len(revoked_keys) > 0:
|
||||
print("Secure Boot Key Revocation Status:\n")
|
||||
for i in revoked_keys:
|
||||
print(f"\tSecure Boot Key{i} is Revoked\n")
|
||||
|
||||
else:
|
||||
print("Secure Boot: Disabled")
|
||||
|
||||
flash_crypt_cnt = bin(si["flash_crypt_cnt"])
|
||||
if (flash_crypt_cnt.count("1") % 2) != 0:
|
||||
print("Flash Encryption: Enabled")
|
||||
else:
|
||||
print("Flash Encryption: Disabled")
|
||||
|
||||
CRYPT_CNT_STRING = "SPI Boot Crypt Count (SPI_BOOT_CRYPT_CNT)"
|
||||
if esp.CHIP_NAME == "esp32":
|
||||
CRYPT_CNT_STRING = "Flash Crypt Count (FLASH_CRYPT_CNT)"
|
||||
|
||||
print(f"{CRYPT_CNT_STRING}: {si['flash_crypt_cnt']:#x}")
|
||||
|
||||
if get_security_flag_status("DIS_DOWNLOAD_DCACHE", flags):
|
||||
print("Dcache in UART download mode: Disabled")
|
||||
|
||||
if get_security_flag_status("DIS_DOWNLOAD_ICACHE", flags):
|
||||
print("Icache in UART download mode: Disabled")
|
||||
|
||||
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")
|
||||
elif soft_dis_jtag:
|
||||
print("JTAG: Software Access Disabled")
|
||||
if get_security_flag_status("DIS_USB", flags):
|
||||
print("USB Access: Disabled")
|
||||
|
||||
|
||||
def merge_bin(args):
|
||||
@@ -1198,9 +1284,9 @@ def merge_bin(args):
|
||||
msg = (
|
||||
"Please specify the chip argument"
|
||||
if args.chip == "auto"
|
||||
else "Invalid chip choice: '{}'".format(args.chip)
|
||||
else f"Invalid chip choice: '{args.chip}'"
|
||||
)
|
||||
msg = msg + " (choose from {})".format(", ".join(CHIP_LIST))
|
||||
msg = f"{msg} (choose from {', '.join(CHIP_LIST)})"
|
||||
raise FatalError(msg)
|
||||
|
||||
# sort the files by offset.
|
||||
@@ -1211,31 +1297,57 @@ def merge_bin(args):
|
||||
first_addr = input_files[0][0]
|
||||
if first_addr < args.target_offset:
|
||||
raise FatalError(
|
||||
"Output file target offset is 0x%x. Input file offset 0x%x is before this."
|
||||
% (args.target_offset, first_addr)
|
||||
f"Output file target offset is {args.target_offset:#x}. "
|
||||
f"Input file offset {first_addr:#x} is before this."
|
||||
)
|
||||
|
||||
if args.format != "raw":
|
||||
raise FatalError(
|
||||
"This version of esptool only supports the 'raw' output format"
|
||||
if args.format == "uf2":
|
||||
with UF2Writer(
|
||||
chip_class.UF2_FAMILY_ID,
|
||||
args.output,
|
||||
args.chunk_size,
|
||||
md5_enabled=not args.md5_disable,
|
||||
) as writer:
|
||||
for addr, argfile in input_files:
|
||||
print(f"Adding {argfile.name} at {addr:#x}")
|
||||
image = argfile.read()
|
||||
image = _update_image_flash_params(chip_class, addr, args, image)
|
||||
writer.add_file(addr, image)
|
||||
print(
|
||||
f"Wrote {os.path.getsize(args.output):#x} bytes to file {args.output}, "
|
||||
f"ready to be flashed with any ESP USB Bridge"
|
||||
)
|
||||
|
||||
with open(args.output, "wb") as of:
|
||||
elif args.format == "raw":
|
||||
with open(args.output, "wb") as of:
|
||||
|
||||
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()))
|
||||
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()))
|
||||
|
||||
for addr, argfile in input_files:
|
||||
pad_to(addr)
|
||||
image = argfile.read()
|
||||
image = _update_image_flash_params(chip_class, addr, args, image)
|
||||
of.write(image)
|
||||
if args.fill_flash_size:
|
||||
pad_to(flash_size_bytes(args.fill_flash_size))
|
||||
print(
|
||||
f"Wrote {of.tell():#x} bytes to file {args.output}, "
|
||||
f"ready to flash to offset {args.target_offset:#x}"
|
||||
)
|
||||
elif args.format == "hex":
|
||||
out = IntelHex()
|
||||
for addr, argfile in input_files:
|
||||
pad_to(addr)
|
||||
ihex = IntelHex()
|
||||
image = argfile.read()
|
||||
image = _update_image_flash_params(chip_class, addr, args, image)
|
||||
of.write(image)
|
||||
if args.fill_flash_size:
|
||||
pad_to(flash_size_bytes(args.fill_flash_size))
|
||||
ihex.frombytes(image, addr)
|
||||
out.merge(ihex)
|
||||
out.write_hex_file(args.output)
|
||||
print(
|
||||
"Wrote 0x%x bytes to file %s, ready to flash to offset 0x%x"
|
||||
% (of.tell(), args.output, args.target_offset)
|
||||
f"Wrote {os.path.getsize(args.output):#x} bytes to file {args.output}, "
|
||||
f"ready to flash to offset {args.target_offset:#x}"
|
||||
)
|
||||
|
||||
|
||||
|
||||
1372
tools/python/esptool/intelhex/__init__.py
Normal file
1372
tools/python/esptool/intelhex/__init__.py
Normal file
File diff suppressed because it is too large
Load Diff
39
tools/python/esptool/intelhex/__main__.py
Normal file
39
tools/python/esptool/intelhex/__main__.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# 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.")
|
||||
3
tools/python/esptool/intelhex/__version__.py
Normal file
3
tools/python/esptool/intelhex/__version__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
# IntelHex library version information
|
||||
version_info = (2, 3, 0)
|
||||
version_str = '.'.join([str(i) for i in version_info])
|
||||
360
tools/python/esptool/intelhex/bench.py
Normal file
360
tools/python/esptool/intelhex/bench.py
Normal file
@@ -0,0 +1,360 @@
|
||||
#!/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
|
||||
|
||||
"""
|
||||
160
tools/python/esptool/intelhex/compat.py
Normal file
160
tools/python/esptool/intelhex/compat.py
Normal file
@@ -0,0 +1,160 @@
|
||||
# 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)
|
||||
64
tools/python/esptool/intelhex/getsizeof.py
Normal file
64
tools/python/esptool/intelhex/getsizeof.py
Normal file
@@ -0,0 +1,64 @@
|
||||
# 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')))
|
||||
1799
tools/python/esptool/intelhex/test.py
Normal file
1799
tools/python/esptool/intelhex/test.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -170,6 +170,8 @@ class StubFlasher:
|
||||
self.data = None
|
||||
self.data_start = None
|
||||
|
||||
self.bss_start = stub.get("bss_start")
|
||||
|
||||
|
||||
class ESPLoader(object):
|
||||
"""Base class providing access to ESP ROM & software stub bootloaders.
|
||||
@@ -184,8 +186,6 @@ class ESPLoader(object):
|
||||
CHIP_NAME = "Espressif device"
|
||||
IS_STUB = False
|
||||
|
||||
FPGA_SLOW_BOOT = False
|
||||
|
||||
DEFAULT_PORT = "/dev/ttyUSB0"
|
||||
|
||||
USES_RFC2217 = False
|
||||
@@ -301,8 +301,39 @@ class ESPLoader(object):
|
||||
if isinstance(port, str):
|
||||
try:
|
||||
self._port = serial.serial_for_url(port)
|
||||
except serial.serialutil.SerialException:
|
||||
raise FatalError(f"Could not open {port}, the port doesn't exist")
|
||||
except serial.serialutil.SerialException as e:
|
||||
port_issues = [
|
||||
[ # does not exist error
|
||||
re.compile(r"Errno 2|FileNotFoundError", re.IGNORECASE),
|
||||
"Check if the port is correct and ESP connected",
|
||||
],
|
||||
[ # busy port error
|
||||
re.compile(r"Access is denied", re.IGNORECASE),
|
||||
"Check if the port is not used by another task",
|
||||
],
|
||||
]
|
||||
if sys.platform.startswith("linux"):
|
||||
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"
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
hint_msg = ""
|
||||
for port_issue in port_issues:
|
||||
if port_issue[0].search(str(e)):
|
||||
hint_msg = f"\nHint: {port_issue[1]}\n"
|
||||
break
|
||||
|
||||
raise FatalError(
|
||||
f"Could not open {port}, the port is busy or doesn't exist."
|
||||
f"\n({e})\n"
|
||||
f"{hint_msg}"
|
||||
)
|
||||
else:
|
||||
self._port = port
|
||||
self._slip_reader = slip_reader(self._port, self.trace)
|
||||
@@ -597,7 +628,7 @@ class ESPLoader(object):
|
||||
|
||||
# This FPGA delay is for Espressif internal use
|
||||
if (
|
||||
self.FPGA_SLOW_BOOT
|
||||
self.CHIP_NAME == "ESP32"
|
||||
and os.environ.get("ESPTOOL_ENV_FPGA", "").strip() == "1"
|
||||
):
|
||||
delay = extra_delay = 7
|
||||
@@ -651,8 +682,17 @@ class ESPLoader(object):
|
||||
print("") # end 'Connecting...' line
|
||||
|
||||
if last_error is not None:
|
||||
additional_msg = ""
|
||||
if self.CHIP_NAME == "ESP32-C2" and self._port.baudrate < 115200:
|
||||
additional_msg = (
|
||||
"\nNote: Please set a higher baud rate (--baud)"
|
||||
" if ESP32-C2 doesn't connect"
|
||||
" (at least 115200 Bd is recommended)."
|
||||
)
|
||||
|
||||
raise FatalError(
|
||||
"Failed to connect to {}: {}"
|
||||
f"{additional_msg}"
|
||||
"\nFor troubleshooting steps visit: "
|
||||
"https://docs.espressif.com/projects/esptool/en/latest/troubleshooting.html".format( # noqa E501
|
||||
self.CHIP_NAME, last_error
|
||||
@@ -751,17 +791,17 @@ class ESPLoader(object):
|
||||
stub = StubFlasher(get_stub_json_path(self.CHIP_NAME))
|
||||
load_start = offset
|
||||
load_end = offset + size
|
||||
for start, end in [
|
||||
(stub.data_start, stub.data_start + len(stub.data)),
|
||||
(stub.text_start, stub.text_start + len(stub.text)),
|
||||
for stub_start, stub_end in [
|
||||
(stub.bss_start, stub.data_start + len(stub.data)), # DRAM = bss+data
|
||||
(stub.text_start, stub.text_start + len(stub.text)), # IRAM
|
||||
]:
|
||||
if load_start < end and load_end > start:
|
||||
if load_start < stub_end and load_end > stub_start:
|
||||
raise FatalError(
|
||||
"Software loader is resident at 0x%08x-0x%08x. "
|
||||
"Can't load binary at overlapping address range 0x%08x-0x%08x. "
|
||||
"Either change binary loading address, or use the --no-stub "
|
||||
"option to disable the software loader."
|
||||
% (start, end, load_start, load_end)
|
||||
% (stub_start, stub_end, load_start, load_end)
|
||||
)
|
||||
|
||||
return self.check_command(
|
||||
|
||||
@@ -6,6 +6,7 @@ from .esp32c6beta import ESP32C6BETAROM
|
||||
from .esp32h2 import ESP32H2ROM
|
||||
from .esp32h2beta1 import ESP32H2BETA1ROM
|
||||
from .esp32h2beta2 import ESP32H2BETA2ROM
|
||||
from .esp32p4 import ESP32P4ROM
|
||||
from .esp32s2 import ESP32S2ROM
|
||||
from .esp32s3 import ESP32S3ROM
|
||||
from .esp32s3beta2 import ESP32S3BETA2ROM
|
||||
@@ -25,6 +26,7 @@ CHIP_DEFS = {
|
||||
"esp32c2": ESP32C2ROM,
|
||||
"esp32c6": ESP32C6ROM,
|
||||
"esp32h2": ESP32H2ROM,
|
||||
"esp32p4": ESP32P4ROM,
|
||||
}
|
||||
|
||||
CHIP_LIST = list(CHIP_DEFS.keys())
|
||||
|
||||
@@ -17,8 +17,6 @@ class ESP32ROM(ESPLoader):
|
||||
IMAGE_CHIP_ID = 0
|
||||
IS_STUB = False
|
||||
|
||||
FPGA_SLOW_BOOT = True
|
||||
|
||||
CHIP_DETECT_MAGIC_VALUE = [0x00F01D83]
|
||||
|
||||
IROM_MAP_START = 0x400D0000
|
||||
@@ -105,6 +103,8 @@ class ESP32ROM(ESPLoader):
|
||||
|
||||
FLASH_ENCRYPTED_WRITE_ALIGN = 32
|
||||
|
||||
UF2_FAMILY_ID = 0x1C5F21B0
|
||||
|
||||
""" Try to read the BLOCK1 (encryption key) and check if it is valid """
|
||||
|
||||
def is_flash_encryption_key_valid(self):
|
||||
@@ -281,7 +281,7 @@ class ESP32ROM(ESPLoader):
|
||||
return self.read_reg(self.EFUSE_RD_REG_BASE + (4 * n))
|
||||
|
||||
def chip_id(self):
|
||||
raise NotSupportedError(self, "chip_id")
|
||||
raise NotSupportedError(self, "Function chip_id")
|
||||
|
||||
def read_mac(self, mac_type="BASE_MAC"):
|
||||
"""Read MAC from EFUSE region"""
|
||||
@@ -361,6 +361,7 @@ class ESP32ROM(ESPLoader):
|
||||
return rom_calculated_freq
|
||||
|
||||
def change_baud(self, baud):
|
||||
assert self.CHIP_NAME == "ESP32", "This workaround should only apply to ESP32"
|
||||
# It's a workaround to avoid esp32 CK_8M frequency drift.
|
||||
rom_calculated_freq = self.get_rom_cal_crystal_freq()
|
||||
valid_freq = 40000000 if rom_calculated_freq > 33000000 else 26000000
|
||||
@@ -373,6 +374,11 @@ class ESP32ROM(ESPLoader):
|
||||
time.sleep(0.05) # get rid of garbage sent during baud rate change
|
||||
self.flush_input()
|
||||
|
||||
def check_spi_connection(self, spi_connection):
|
||||
# Pins 30, 31 do not exist
|
||||
if not set(spi_connection).issubset(set(range(0, 30)) | set((32, 33))):
|
||||
raise FatalError("SPI Pin numbers must be in the range 0-29, 32, or 33.")
|
||||
|
||||
|
||||
class ESP32StubLoader(ESP32ROM):
|
||||
"""Access class for ESP32 stub loader, runs on top of ROM."""
|
||||
|
||||
@@ -8,6 +8,7 @@ import time
|
||||
|
||||
from .esp32c3 import ESP32C3ROM
|
||||
from loader import ESPLoader
|
||||
from util import FatalError
|
||||
|
||||
|
||||
class ESP32C2ROM(ESP32C3ROM):
|
||||
@@ -61,6 +62,8 @@ class ESP32C2ROM(ESP32C3ROM):
|
||||
[0x4037C000, 0x403C0000, "IRAM"],
|
||||
]
|
||||
|
||||
UF2_FAMILY_ID = 0x2B88D29C
|
||||
|
||||
def get_pkg_version(self):
|
||||
num_word = 1
|
||||
return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 22) & 0x07
|
||||
@@ -82,6 +85,16 @@ class ESP32C2ROM(ESP32C3ROM):
|
||||
num_word = 1
|
||||
return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 20) & 0x3
|
||||
|
||||
def get_flash_cap(self):
|
||||
# ESP32-C2 doesn't have eFuse field FLASH_CAP.
|
||||
# Can't get info about the flash chip.
|
||||
return 0
|
||||
|
||||
def get_flash_vendor(self):
|
||||
# ESP32-C2 doesn't have eFuse field FLASH_VENDOR.
|
||||
# Can't get info about the flash chip.
|
||||
return ""
|
||||
|
||||
def get_crystal_freq(self):
|
||||
# The crystal detection algorithm of ESP32/ESP8266 works for ESP32-C2 as well.
|
||||
return ESPLoader.get_crystal_freq(self)
|
||||
@@ -110,7 +123,7 @@ class ESP32C2ROM(ESP32C3ROM):
|
||||
|
||||
def _post_connect(self):
|
||||
# ESP32C2 ECO0 is no longer supported by the flasher stub
|
||||
if self.get_chip_revision() == 0:
|
||||
if not self.secure_download_mode and self.get_chip_revision() == 0:
|
||||
self.stub_is_disabled = True
|
||||
self.IS_STUB = False
|
||||
|
||||
@@ -142,6 +155,10 @@ class ESP32C2ROM(ESP32C3ROM):
|
||||
return True
|
||||
return False
|
||||
|
||||
def check_spi_connection(self, spi_connection):
|
||||
if not set(spi_connection).issubset(set(range(0, 21))):
|
||||
raise FatalError("SPI Pin numbers must be in the range 0-20.")
|
||||
|
||||
|
||||
class ESP32C2StubLoader(ESP32C2ROM):
|
||||
"""Access class for ESP32C2 stub loader, runs on top of ROM.
|
||||
|
||||
@@ -14,8 +14,6 @@ class ESP32C3ROM(ESP32ROM):
|
||||
CHIP_NAME = "ESP32-C3"
|
||||
IMAGE_CHIP_ID = 5
|
||||
|
||||
FPGA_SLOW_BOOT = False
|
||||
|
||||
IROM_MAP_START = 0x42000000
|
||||
IROM_MAP_END = 0x42800000
|
||||
DROM_MAP_START = 0x3C000000
|
||||
@@ -31,8 +29,8 @@ class ESP32C3ROM(ESP32ROM):
|
||||
|
||||
BOOTLOADER_FLASH_OFFSET = 0x0
|
||||
|
||||
# Magic value for ESP32C3 eco 1+2 and ESP32C3 eco3 respectivly
|
||||
CHIP_DETECT_MAGIC_VALUE = [0x6921506F, 0x1B31506F]
|
||||
# Magic values for ESP32-C3 eco 1+2, eco 3, eco 6, and eco 7 respectively
|
||||
CHIP_DETECT_MAGIC_VALUE = [0x6921506F, 0x1B31506F, 0x4881606F, 0x4361606F]
|
||||
|
||||
UART_DATE_REG_ADDR = 0x60000000 + 0x7C
|
||||
|
||||
@@ -99,6 +97,8 @@ class ESP32C3ROM(ESP32ROM):
|
||||
[0x600FE000, 0x60100000, "MEM_INTERNAL2"],
|
||||
]
|
||||
|
||||
UF2_FAMILY_ID = 0xD42BA06C
|
||||
|
||||
def get_pkg_version(self):
|
||||
num_word = 3
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x07
|
||||
@@ -114,16 +114,39 @@ class ESP32C3ROM(ESP32ROM):
|
||||
num_word = 5
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x03
|
||||
|
||||
def get_flash_cap(self):
|
||||
num_word = 3
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 27) & 0x07
|
||||
|
||||
def get_flash_vendor(self):
|
||||
num_word = 4
|
||||
vendor_id = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x07
|
||||
return {1: "XMC", 2: "GD", 3: "FM", 4: "TT", 5: "ZBIT"}.get(vendor_id, "")
|
||||
|
||||
def get_chip_description(self):
|
||||
chip_name = {
|
||||
0: "ESP32-C3",
|
||||
0: "ESP32-C3 (QFN32)",
|
||||
1: "ESP8685 (QFN28)",
|
||||
2: "ESP32-C3 AZ (QFN32)",
|
||||
3: "ESP8686 (QFN24)",
|
||||
}.get(self.get_pkg_version(), "unknown ESP32-C3")
|
||||
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", "BLE"]
|
||||
features = ["WiFi", "BLE"]
|
||||
|
||||
flash = {
|
||||
0: None,
|
||||
1: "Embedded Flash 4MB",
|
||||
2: "Embedded Flash 2MB",
|
||||
3: "Embedded Flash 1MB",
|
||||
4: "Embedded Flash 8MB",
|
||||
}.get(self.get_flash_cap(), "Unknown Embedded Flash")
|
||||
if flash is not None:
|
||||
features += [flash + f" ({self.get_flash_vendor()})"]
|
||||
return features
|
||||
|
||||
def get_crystal_freq(self):
|
||||
# ESP32C3 XTAL is fixed to 40MHz
|
||||
@@ -205,6 +228,15 @@ class ESP32C3ROM(ESP32ROM):
|
||||
if not self.sync_stub_detected: # Don't run if stub is reused
|
||||
self.disable_watchdogs()
|
||||
|
||||
def check_spi_connection(self, spi_connection):
|
||||
if not set(spi_connection).issubset(set(range(0, 22))):
|
||||
raise FatalError("SPI Pin numbers must be in the range 0-21.")
|
||||
if any([v for v in spi_connection if v in [18, 19]]):
|
||||
print(
|
||||
"WARNING: GPIO pins 18 and 19 are used by USB-Serial/JTAG, "
|
||||
"consider using other pins for SPI flash connection."
|
||||
)
|
||||
|
||||
|
||||
class ESP32C3StubLoader(ESP32C3ROM):
|
||||
"""Access class for ESP32C3 stub loader, runs on top of ROM.
|
||||
|
||||
@@ -13,8 +13,6 @@ class ESP32C6ROM(ESP32C3ROM):
|
||||
CHIP_NAME = "ESP32-C6"
|
||||
IMAGE_CHIP_ID = 13
|
||||
|
||||
FPGA_SLOW_BOOT = False
|
||||
|
||||
IROM_MAP_START = 0x42000000
|
||||
IROM_MAP_END = 0x42800000
|
||||
DROM_MAP_START = 0x42800000
|
||||
@@ -101,6 +99,8 @@ class ESP32C6ROM(ESP32C3ROM):
|
||||
[0x600FE000, 0x60100000, "MEM_INTERNAL2"],
|
||||
]
|
||||
|
||||
UF2_FAMILY_ID = 0x540DDF62
|
||||
|
||||
def get_pkg_version(self):
|
||||
num_word = 3
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x07
|
||||
@@ -180,6 +180,15 @@ class ESP32C6ROM(ESP32C3ROM):
|
||||
|
||||
return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes)
|
||||
|
||||
def check_spi_connection(self, spi_connection):
|
||||
if not set(spi_connection).issubset(set(range(0, 31))):
|
||||
raise FatalError("SPI Pin numbers must be in the range 0-30.")
|
||||
if any([v for v in spi_connection if v in [12, 13]]):
|
||||
print(
|
||||
"WARNING: GPIO pins 12 and 13 are used by USB-Serial/JTAG, "
|
||||
"consider using other pins for SPI flash connection."
|
||||
)
|
||||
|
||||
|
||||
class ESP32C6StubLoader(ESP32C6ROM):
|
||||
"""Access class for ESP32C6 stub loader, runs on top of ROM.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
from .esp32c6 import ESP32C6ROM
|
||||
from util import FatalError
|
||||
|
||||
|
||||
class ESP32H2ROM(ESP32C6ROM):
|
||||
@@ -29,6 +30,8 @@ class ESP32H2ROM(ESP32C6ROM):
|
||||
"12m": 0x2,
|
||||
}
|
||||
|
||||
UF2_FAMILY_ID = 0x332726F6
|
||||
|
||||
def get_pkg_version(self):
|
||||
num_word = 4
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x07
|
||||
@@ -56,6 +59,15 @@ class ESP32H2ROM(ESP32C6ROM):
|
||||
# ESP32H2 XTAL is fixed to 32MHz
|
||||
return 32
|
||||
|
||||
def check_spi_connection(self, spi_connection):
|
||||
if not set(spi_connection).issubset(set(range(0, 28))):
|
||||
raise FatalError("SPI Pin numbers must be in the range 0-27.")
|
||||
if any([v for v in spi_connection if v in [26, 27]]):
|
||||
print(
|
||||
"WARNING: GPIO pins 26 and 27 are used by USB-Serial/JTAG, "
|
||||
"consider using other pins for SPI flash connection."
|
||||
)
|
||||
|
||||
|
||||
class ESP32H2StubLoader(ESP32H2ROM):
|
||||
"""Access class for ESP32H2 stub loader, runs on top of ROM.
|
||||
|
||||
195
tools/python/esptool/targets/esp32p4.py
Normal file
195
tools/python/esptool/targets/esp32p4.py
Normal file
@@ -0,0 +1,195 @@
|
||||
# SPDX-FileCopyrightText: 2023 Fredrik Ahlberg, Angus Gratton,
|
||||
# Espressif Systems (Shanghai) CO LTD, other contributors as noted.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import struct
|
||||
|
||||
from .esp32 import ESP32ROM
|
||||
from loader import ESPLoader
|
||||
from util import FatalError, NotImplementedInROMError
|
||||
|
||||
|
||||
class ESP32P4ROM(ESP32ROM):
|
||||
CHIP_NAME = "ESP32-P4"
|
||||
IMAGE_CHIP_ID = 18
|
||||
|
||||
IROM_MAP_START = 0x40000000
|
||||
IROM_MAP_END = 0x4C000000
|
||||
DROM_MAP_START = 0x40000000
|
||||
DROM_MAP_END = 0x4C000000
|
||||
|
||||
BOOTLOADER_FLASH_OFFSET = 0x2000 # First 2 sectors are reserved for FE purposes
|
||||
|
||||
CHIP_DETECT_MAGIC_VALUE = [0x0]
|
||||
|
||||
UART_DATE_REG_ADDR = 0x500CA000 + 0x8C
|
||||
|
||||
EFUSE_BASE = 0x5012D000
|
||||
EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x044
|
||||
MAC_EFUSE_REG = EFUSE_BASE + 0x044
|
||||
|
||||
SPI_REG_BASE = 0x5008D000 # SPIMEM1
|
||||
SPI_USR_OFFS = 0x18
|
||||
SPI_USR1_OFFS = 0x1C
|
||||
SPI_USR2_OFFS = 0x20
|
||||
SPI_MOSI_DLEN_OFFS = 0x24
|
||||
SPI_MISO_DLEN_OFFS = 0x28
|
||||
SPI_W0_OFFS = 0x58
|
||||
|
||||
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
|
||||
|
||||
PURPOSE_VAL_XTS_AES256_KEY_1 = 2
|
||||
PURPOSE_VAL_XTS_AES256_KEY_2 = 3
|
||||
PURPOSE_VAL_XTS_AES128_KEY = 4
|
||||
|
||||
SUPPORTS_ENCRYPTED_FLASH = True
|
||||
|
||||
FLASH_ENCRYPTED_WRITE_ALIGN = 16
|
||||
|
||||
MEMORY_MAP = [
|
||||
[0x00000000, 0x00010000, "PADDING"],
|
||||
[0x40000000, 0x4C000000, "DROM"],
|
||||
[0x4FF00000, 0x4FFA0000, "DRAM"],
|
||||
[0x4FF00000, 0x4FFA0000, "BYTE_ACCESSIBLE"],
|
||||
[0x4FC00000, 0x4FC20000, "DROM_MASK"],
|
||||
[0x4FC00000, 0x4FC20000, "IROM_MASK"],
|
||||
[0x40000000, 0x4C000000, "IROM"],
|
||||
[0x4FF00000, 0x4FFA0000, "IRAM"],
|
||||
[0x50108000, 0x50110000, "RTC_IRAM"],
|
||||
[0x50108000, 0x50110000, "RTC_DRAM"],
|
||||
[0x600FE000, 0x60100000, "MEM_INTERNAL2"],
|
||||
]
|
||||
|
||||
UF2_FAMILY_ID = 0x3D308E94
|
||||
|
||||
def get_pkg_version(self):
|
||||
# ESP32P4 TODO
|
||||
return 0
|
||||
|
||||
def get_minor_chip_version(self):
|
||||
# ESP32P4 TODO
|
||||
return 0
|
||||
|
||||
def get_major_chip_version(self):
|
||||
# ESP32P4 TODO
|
||||
return 0
|
||||
|
||||
def get_chip_description(self):
|
||||
chip_name = {
|
||||
0: "ESP32-P4",
|
||||
}.get(self.get_pkg_version(), "unknown ESP32-P4")
|
||||
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 ["High-Performance MCU"]
|
||||
|
||||
def get_crystal_freq(self):
|
||||
# ESP32P4 XTAL is fixed to 40MHz
|
||||
return 40
|
||||
|
||||
def override_vddsdio(self, new_voltage):
|
||||
raise NotImplementedInROMError(
|
||||
"VDD_SDIO overrides are not supported for ESP32-P4"
|
||||
)
|
||||
|
||||
def read_mac(self, mac_type="BASE_MAC"):
|
||||
"""Read MAC from EFUSE region"""
|
||||
if mac_type != "BASE_MAC":
|
||||
return None
|
||||
mac0 = self.read_reg(self.MAC_EFUSE_REG)
|
||||
mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC
|
||||
bitstring = struct.pack(">II", mac1, mac0)[2:]
|
||||
return tuple(bitstring)
|
||||
|
||||
def get_flash_crypt_config(self):
|
||||
return None # doesn't exist on ESP32-P4
|
||||
|
||||
def get_secure_boot_enabled(self):
|
||||
return (
|
||||
self.read_reg(self.EFUSE_SECURE_BOOT_EN_REG)
|
||||
& self.EFUSE_SECURE_BOOT_EN_MASK
|
||||
)
|
||||
|
||||
def get_key_block_purpose(self, key_block):
|
||||
if key_block < 0 or key_block > 5:
|
||||
raise FatalError("Valid key block numbers must be in range 0-5")
|
||||
|
||||
reg, shift = [
|
||||
(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT),
|
||||
(self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT),
|
||||
(self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT),
|
||||
(self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT),
|
||||
(self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT),
|
||||
(self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT),
|
||||
][key_block]
|
||||
return (self.read_reg(reg) >> shift) & 0xF
|
||||
|
||||
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)]
|
||||
|
||||
if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes):
|
||||
return True
|
||||
|
||||
return any(p == self.PURPOSE_VAL_XTS_AES256_KEY_1 for p in purposes) and any(
|
||||
p == self.PURPOSE_VAL_XTS_AES256_KEY_2 for p in purposes
|
||||
)
|
||||
|
||||
def change_baud(self, baud):
|
||||
ESPLoader.change_baud(self, baud)
|
||||
|
||||
def _post_connect(self):
|
||||
pass
|
||||
# TODO: Disable watchdogs when USB modes are supported in the stub
|
||||
# if not self.sync_stub_detected: # Don't run if stub is reused
|
||||
# self.disable_watchdogs()
|
||||
|
||||
def check_spi_connection(self, spi_connection):
|
||||
pass # TODO: Define GPIOs for --spi-connection
|
||||
|
||||
|
||||
class ESP32P4StubLoader(ESP32P4ROM):
|
||||
"""Access class for ESP32P4 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
|
||||
|
||||
|
||||
ESP32P4ROM.STUB_CLASS = ESP32P4StubLoader
|
||||
@@ -16,8 +16,6 @@ class ESP32S2ROM(ESP32ROM):
|
||||
CHIP_NAME = "ESP32-S2"
|
||||
IMAGE_CHIP_ID = 2
|
||||
|
||||
FPGA_SLOW_BOOT = False
|
||||
|
||||
IROM_MAP_START = 0x40080000
|
||||
IROM_MAP_END = 0x40B80000
|
||||
DROM_MAP_START = 0x3F000000
|
||||
@@ -101,6 +99,8 @@ class ESP32S2ROM(ESP32ROM):
|
||||
[0x50000000, 0x50002000, "RTC_DATA"],
|
||||
]
|
||||
|
||||
UF2_FAMILY_ID = 0xBFDD4EEE
|
||||
|
||||
def get_pkg_version(self):
|
||||
num_word = 4
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x0F
|
||||
@@ -120,10 +120,16 @@ class ESP32S2ROM(ESP32ROM):
|
||||
num_word = 3
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x0F
|
||||
|
||||
def get_flash_cap(self):
|
||||
return self.get_flash_version()
|
||||
|
||||
def get_psram_version(self):
|
||||
num_word = 3
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 28) & 0x0F
|
||||
|
||||
def get_psram_cap(self):
|
||||
return self.get_psram_version()
|
||||
|
||||
def get_block2_version(self):
|
||||
# BLK_VERSION_MINOR
|
||||
num_word = 4
|
||||
@@ -137,7 +143,7 @@ class ESP32S2ROM(ESP32ROM):
|
||||
102: "ESP32-S2FNR2",
|
||||
100: "ESP32-S2R2",
|
||||
}.get(
|
||||
self.get_flash_version() + self.get_psram_version() * 100,
|
||||
self.get_flash_cap() + self.get_psram_cap() * 100,
|
||||
"unknown ESP32-S2",
|
||||
)
|
||||
major_rev = self.get_major_chip_version()
|
||||
@@ -154,14 +160,14 @@ class ESP32S2ROM(ESP32ROM):
|
||||
0: "No Embedded Flash",
|
||||
1: "Embedded Flash 2MB",
|
||||
2: "Embedded Flash 4MB",
|
||||
}.get(self.get_flash_version(), "Unknown Embedded Flash")
|
||||
}.get(self.get_flash_cap(), "Unknown Embedded Flash")
|
||||
features += [flash_version]
|
||||
|
||||
psram_version = {
|
||||
0: "No Embedded PSRAM",
|
||||
1: "Embedded PSRAM 2MB",
|
||||
2: "Embedded PSRAM 4MB",
|
||||
}.get(self.get_psram_version(), "Unknown Embedded PSRAM")
|
||||
}.get(self.get_psram_cap(), "Unknown Embedded PSRAM")
|
||||
features += [psram_version]
|
||||
|
||||
block2_version = {
|
||||
@@ -281,6 +287,15 @@ class ESP32S2ROM(ESP32ROM):
|
||||
def change_baud(self, baud):
|
||||
ESPLoader.change_baud(self, baud)
|
||||
|
||||
def check_spi_connection(self, spi_connection):
|
||||
if not set(spi_connection).issubset(set(range(0, 22)) | set(range(26, 47))):
|
||||
raise FatalError("SPI Pin numbers must be in the range 0-21, or 26-46.")
|
||||
if any([v for v in spi_connection if v in [19, 20]]):
|
||||
print(
|
||||
"WARNING: GPIO pins 19 and 20 are used by USB-OTG, "
|
||||
"consider using other pins for SPI flash connection."
|
||||
)
|
||||
|
||||
|
||||
class ESP32S2StubLoader(ESP32S2ROM):
|
||||
"""Access class for ESP32-S2 stub loader, runs on top of ROM.
|
||||
|
||||
@@ -19,8 +19,6 @@ class ESP32S3ROM(ESP32ROM):
|
||||
|
||||
CHIP_DETECT_MAGIC_VALUE = [0x9]
|
||||
|
||||
FPGA_SLOW_BOOT = False
|
||||
|
||||
IROM_MAP_START = 0x42000000
|
||||
IROM_MAP_END = 0x44000000
|
||||
DROM_MAP_START = 0x3C000000
|
||||
@@ -117,6 +115,8 @@ class ESP32S3ROM(ESP32ROM):
|
||||
[0x50000000, 0x50002000, "RTC_DATA"],
|
||||
]
|
||||
|
||||
UF2_FAMILY_ID = 0xC47E5767
|
||||
|
||||
def get_pkg_version(self):
|
||||
num_word = 3
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x07
|
||||
@@ -165,10 +165,53 @@ class ESP32S3ROM(ESP32ROM):
|
||||
def get_chip_description(self):
|
||||
major_rev = self.get_major_chip_version()
|
||||
minor_rev = self.get_minor_chip_version()
|
||||
return f"{self.CHIP_NAME} (revision v{major_rev}.{minor_rev})"
|
||||
pkg_version = self.get_pkg_version()
|
||||
|
||||
chip_name = {
|
||||
0: "ESP32-S3 (QFN56)",
|
||||
1: "ESP32-S3-PICO-1 (LGA56)",
|
||||
}.get(pkg_version, "unknown ESP32-S3")
|
||||
|
||||
return f"{chip_name} (revision v{major_rev}.{minor_rev})"
|
||||
|
||||
def get_flash_cap(self):
|
||||
num_word = 3
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 27) & 0x07
|
||||
|
||||
def get_flash_vendor(self):
|
||||
num_word = 4
|
||||
vendor_id = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x07
|
||||
return {1: "XMC", 2: "GD", 3: "FM", 4: "TT", 5: "BY"}.get(vendor_id, "")
|
||||
|
||||
def get_psram_cap(self):
|
||||
num_word = 4
|
||||
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 3) & 0x03
|
||||
|
||||
def get_psram_vendor(self):
|
||||
num_word = 4
|
||||
vendor_id = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 7) & 0x03
|
||||
return {1: "AP_3v3", 2: "AP_1v8"}.get(vendor_id, "")
|
||||
|
||||
def get_chip_features(self):
|
||||
return ["WiFi", "BLE"]
|
||||
features = ["WiFi", "BLE"]
|
||||
|
||||
flash = {
|
||||
0: None,
|
||||
1: "Embedded Flash 8MB",
|
||||
2: "Embedded Flash 4MB",
|
||||
}.get(self.get_flash_cap(), "Unknown Embedded Flash")
|
||||
if flash is not None:
|
||||
features += [flash + f" ({self.get_flash_vendor()})"]
|
||||
|
||||
psram = {
|
||||
0: None,
|
||||
1: "Embedded PSRAM 8MB",
|
||||
2: "Embedded PSRAM 2MB",
|
||||
}.get(self.get_psram_cap(), "Unknown Embedded PSRAM")
|
||||
if psram is not None:
|
||||
features += [psram + f" ({self.get_psram_vendor()})"]
|
||||
|
||||
return features
|
||||
|
||||
def get_crystal_freq(self):
|
||||
# ESP32S3 XTAL is fixed to 40MHz
|
||||
@@ -306,6 +349,17 @@ class ESP32S3ROM(ESP32ROM):
|
||||
def change_baud(self, baud):
|
||||
ESPLoader.change_baud(self, baud)
|
||||
|
||||
def check_spi_connection(self, spi_connection):
|
||||
if not set(spi_connection).issubset(set(range(0, 22)) | set(range(26, 49))):
|
||||
raise FatalError("SPI Pin numbers must be in the range 0-21, or 26-48.")
|
||||
if spi_connection[3] > 46: # hd_gpio_num must be <= SPI_GPIO_NUM_LIMIT (46)
|
||||
raise FatalError("SPI HD Pin number must be <= 46.")
|
||||
if any([v for v in spi_connection if v in [19, 20]]):
|
||||
print(
|
||||
"WARNING: GPIO pins 19 and 20 are used by USB-Serial/JTAG and USB-OTG, "
|
||||
"consider using other pins for SPI flash connection."
|
||||
)
|
||||
|
||||
|
||||
class ESP32S3StubLoader(ESP32S3ROM):
|
||||
"""Access class for ESP32S3 stub loader, runs on top of ROM.
|
||||
|
||||
@@ -14,11 +14,6 @@ class ESP32S3BETA2ROM(ESP32S3ROM):
|
||||
|
||||
EFUSE_BASE = 0x6001A000 # BLOCK0 read base address
|
||||
|
||||
def get_chip_description(self):
|
||||
major_rev = self.get_major_chip_version()
|
||||
minor_rev = self.get_minor_chip_version()
|
||||
return f"{self.CHIP_NAME} (revision v{major_rev}.{minor_rev})"
|
||||
|
||||
|
||||
class ESP32S3BETA2StubLoader(ESP32S3BETA2ROM):
|
||||
"""Access class for ESP32S3 stub loader, runs on top of ROM.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
from loader import ESPLoader
|
||||
from util import FatalError, NotImplementedInROMError
|
||||
from util import FatalError, NotSupportedError
|
||||
|
||||
|
||||
class ESP8266ROM(ESPLoader):
|
||||
@@ -60,6 +60,8 @@ class ESP8266ROM(ESPLoader):
|
||||
[0x40201010, 0x402E1010, "IROM"],
|
||||
]
|
||||
|
||||
UF2_FAMILY_ID = 0x7EAB61ED
|
||||
|
||||
def get_efuses(self):
|
||||
# Return the 128 bits of ESP8266 efuse as a single Python integer
|
||||
result = self.read_reg(0x3FF0005C) << 96
|
||||
@@ -168,9 +170,10 @@ class ESP8266ROM(ESPLoader):
|
||||
return (num_sectors - head_sectors) * sector_size
|
||||
|
||||
def override_vddsdio(self, new_voltage):
|
||||
raise NotImplementedInROMError(
|
||||
"Overriding VDDSDIO setting only applies to ESP32"
|
||||
)
|
||||
raise NotSupportedError(self, "Overriding VDDSDIO")
|
||||
|
||||
def check_spi_connection(self, spi_connection):
|
||||
raise NotSupportedError(self, "Setting --spi-connection")
|
||||
|
||||
|
||||
class ESP8266StubLoader(ESP8266ROM):
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"entry": 1074521560,
|
||||
"text": "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",
|
||||
"entry": 1074521580,
|
||||
"text": "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",
|
||||
"text_start": 1074520064,
|
||||
"data": "DMD8P9jnC0Br6AtAA+0LQPLoC0CL6AtA8ugLQFHpC0Ae6gtAkOoLQDnqC0CB5wtAtukLQBDqC0B06QtAtOoLQJ7pC0C06gtAWegLQLboC0Dy6AtAUekLQHHoC0Bk6wtAxewLQKTmC0Dn7AtApOYLQKTmC0Ck5gtApOYLQKTmC0Ck5gtApOYLQKTmC0AL6wtApOYLQOXrC0DF7AtA",
|
||||
"data_start": 1073605544
|
||||
"data": "DMD8P+znC0B/6AtAZ+0LQAbpC0Cf6AtABukLQGXpC0CC6gtA9OoLQJ3qC0CV5wtAGuoLQHTqC0CI6QtAGOsLQLDpC0AY6wtAbegLQMroC0AG6QtAZekLQIXoC0DI6wtAKe0LQLjmC0BL7QtAuOYLQLjmC0C45gtAuOYLQLjmC0C45gtAuOYLQLjmC0Bv6wtAuOYLQEnsC0Ap7QtA",
|
||||
"data_start": 1073605544,
|
||||
"bss_start": 1073528832
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"entry": 1077413304,
|
||||
"text": "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",
|
||||
"text": "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",
|
||||
"text_start": 1077411840,
|
||||
"data": "DEDKP+AIOEAsCThAhAk4QCgKOECUCjhAQgo4QKgHOEDkCThAJAo4QJgJOEBYBzhAzAk4QFgHOEC6CDhA/gg4QCwJOECECThAzAg4QBIIOEBCCDhAyAg4QOwMOEAsCThArAs4QKAMOECkBjhAygw4QKQGOECkBjhApAY4QKQGOECkBjhApAY4QKQGOECkBjhASAs4QKQGOEDICzhAoAw4QA==",
|
||||
"data_start": 1070295976
|
||||
"data": "DEDKP+AIOEAsCThAhAk4QFIKOEC+CjhAbAo4QKgHOEAOCjhATgo4QJgJOEBYBzhAzAk4QFgHOEC6CDhA/gg4QCwJOECECThAzAg4QBIIOEBCCDhAyAg4QBYNOEAsCThA1gs4QMoMOECkBjhA9Aw4QKQGOECkBjhApAY4QKQGOECkBjhApAY4QKQGOECkBjhAcgs4QKQGOEDyCzhAygw4QA==",
|
||||
"data_start": 1070295976,
|
||||
"bss_start": 1070219264
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"entry": 1077413318,
|
||||
"text": "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",
|
||||
"text": "ARG3BwBgTsaDqYcASsg3Scg/JspSxAbOIsy3BABgfVoTCQkAwEwTdPQ/DeDyQGJEI6g0AUJJ0kSySSJKBWGCgIhAgycJABN19Q+Cl30U4xlE/8m/EwcADJRBqodjGOUAhUeFxiOgBQB5VYKABUdjh+YACUZjjcYAfVWCgEIFEwewDUGFY5XnAolHnMH1t5MGwA1jFtUAmMETBQAMgoCTBtANfVVjldcAmMETBbANgoC3dck/QRGThQW6BsZhP2NFBQa3d8k/k4eHsQOnBwgD1kcIE3X1D5MGFgDCBsGCI5LXCDKXIwCnAAPXRwiRZ5OHBwRjHvcCN/fIPxMHh7GhZ7qXA6YHCLc2yT+3d8k/k4eHsZOGhrVjH+YAI6bHCCOg1wgjkgcIIaD5V+MG9fyyQEEBgoAjptcII6DnCN23NycAYHxLnYv1/zc3AGB8S52L9f+CgEERBsbdN7cnAGAjpgcCNwcACJjDmEN9/8hXskATRfX/BYlBAYKAQREGxtk/fd03BwBAtycAYJjDNycAYBxD/f+yQEEBgoBBESLEN8TIP5MHxABKwAOpBwEGxibCYwoJBEU3OcW9RxMExACBRGPWJwEERL2Ik7QUAH03hT8cRDcGgAATl8cAmeA3BgABt/b/AHWPtyYAYNjCkMKYQn3/QUeR4AVHMwnpQLqXIygkARzEskAiRJJEAklBAYKAQREGxhMHAAxjEOUCEwWwDZcAyP/ngMDjEwXADbJAQQEXA8j/ZwDD4hMHsA3jGOX+lwDI/+eAwOETBdANxbdBESLEJsIGxiqEswS1AGMXlACyQCJEkkRBAYKAA0UEAAUERTfttxMFAAwXA8j/ZwBD3jVxJstOx/1yhWn9dCLNSslSxVbDBs+ThIT6FpGThwkHppcYCLOE5wAqiSaFLoSXAMj/54AgNpOHCQcYCAVqupezikdBMeQFZ311kwWF+pMHBwcTBYX5FAiqlzOF1wCTBwcHrpezhdcAKsaXAMj/54DgMjJFwUWhPwFFhWIWkfpAakTaREpJukkqSppKDWGCgKKJY3OKAIVpTobWhUqFlwDI/+eA4OATdfUPAe1OhtaFJoWXAMj/54AgLk6ZMwQ0QVG3EwUwBlW/MXH9ck7XUtVW017PBt8i3SbbStla0WLNZstqyW7HqokWkRMFAAIuirKKtosCypcAyP/ngOAohWdj4FcThWR9dBMEhPqThwQHopcYCDOE5wAihZcAyP/ngGAnfXsTDDv5kwyL+ROHBAeThwQHFAhil+aXAUkzDNcAs4zXAFJNY3xNCWNxqQNBqFU1poUIAaU9cT0mhgwBIoWXAMj/54BAI6aZJpljdUkDswepQWPxdwOzBCpBY/OaANaEJoYMAU6FlwDI/+eAQNITdfUPVd0CzIFEeV2NTaMJAQBihZcAyP/ngADEffkDRTEB5oUFMWNPBQDj4p3+hWeThwcHppcYCLqX2pcjiqf4hQTxt+MVpf2RR+OF9PYFZ311kwcHB5MFhfoTBYX5FAiqlzOF1wCTBwcHrpezhdcAKsaXAMj/54BgGe0zMkXBRX07zTMTBQAClwDI/+eAABeFYhaR+lBqVNpUSlm6WSpamloKW/pLakzaTEpNuk0pYYKAAREGziLMnTk3BM4/bAATBUT/lwDI/+eAQMiqhwVFleeyR5P3ByA+xkE5NycAYBxHtwZAABMFRP/VjxzHskWXAMj/54DAxTM1oADyQGJEBWGCgEERt8fIPwbGk4fHAAVHI4DnABPXxQCYxwVnfRfMw8jH+Y06laqVsYGMyyOqBwBBNxnBEwVQDLJAQQGCgAERIsw3xMg/kwfEACbKxEdOxgbOSsiqiRMExABj85UAroSpwAMpRAAmmRNZyQAcSGNV8AAcRGNe+QLpNn3dSEAmhs6FlwDI/+eAQLkTdfUPAcWTB0AMXMhcQKaXXMBcRIWPXMTyQGJE0kRCSbJJBWGCgOE+bb+3V0FJGXGTh/eEAUU+zobeotym2srYztbS1NbS2tDezuLM5srqyO7GlwDI/+eAoKy3R8g/N3fJP5OHBwATB4e6Y+XnFK0xkUVoCD05jTG398g/k4eHsSFnPpcjIPcItwU4QLcHOECThwcLAUaThQUAN0nIPxVFIyD5AJcAyP/ngAD8NwcAYFxHEwUAArd5yT+T5xcQXMeXAMj/54DA+pcAyP/ngEALt0cAYJxfk4mJsRMJCQAJ5fGL4RcTtRcAgUWXAMj/54CgrcFnt8TIP/0XEwcAEIVmQWa3BQABAUWThMQAt0rIPw1qlwDI/+eAIKgTi8oAJpqDp8kI9d+Dq8kIhUcjpgkIIwLxAoPHGwAJRyMT4QKjAvECAtRNR2OL5wZRR2OJ5wYpR2Of5wCDxzsAA8crAKIH2Y8RR2OW5wCDp4sAnEM+1KU2oUVIEDU+g8c7AAPHKwCiB9mPEWdBB2N+9wITBbANlwDI/+eAAJMTBcANlwDI/+eAQJITBeAOlwDI/+eAgJElNr23I6AHAJEHRb3JRyMT8QJ9twPHGwDRRmPn5gKFRmPm5gABTBME8A+dqHkXE3f3D8lG4+jm/rd2yT8KB5OGxro2lxhDAoeTBgcDk/b2DxFG42nW/BMH9wITd/cPjUZj7uYIt3bJPwoHk4aGvzaXGEMChxMHQAJjmucQAtQdRAFFlwDI/+eA4IgBRSU8aTxhPKFFSBB9FK00ffABTAFEE3X0DwU0E3X8Dyk8tTzjEQTsg8cbAElHY2X3MAlH43n36vUXk/f3Dz1H42P36jd3yT+KBxMHh8C6l5xDgocFRJ3rcBCBRQFFl7DM/+eA4JMd4dFFaBAxNAFEMagFRIHvlwDI/+eAAI0zNKAAKaAhR2OF5wAFRAFMYbcDrIsAA6TLALNnjADSB/X3sTFl9cFsIpz9HH19MwWMQF3cs3eVAZXjwWwzBYxAY+aMAv18MwWMQF3QMYGXAMj/54AgiF35ZpT1tzGBlwDI/+eAIIdd8WqU0bdBgZcAyP/ngOCFWfkzBJRBwbchR+OK5/ABTBMEAAw5t0FHzb9BRwVE453n9oOlywADpYsAcTK5v0FHBUTjk+f2A6cLAZFnY+jnHoOlSwEDpYsACTGBt0FHBUTjlOf0g6cLARFnY2n3HAOnywCDpUsBA6WLADOE5wLxPiOsBAAjJIqwCb8DxwQAYwMHFAOniwDBFxMEAAxjE/cAwEgBR5MG8A5jRvcCg8dbAAPHSwABTKIH2Y8Dx2sAQgddj4PHewDiB9mP44T25hMEEAyFtTOG6wADRoYBBQexjuG3g8cEAP3H3ERjnQcUwEgjgAQAVb1hR2OW5wKDp8sBA6eLAYOmSwEDpgsBg6XLAAOliwCX8Mf/54AgdiqMMzSgAAG9AUwFRCm1EUcFROOd5+a3lwBgtF9ld30XBWb5jtGOA6WLALTftFeBRfmO0Y601/Rf+Y7RjvTf9FN1j1GP+NOX8Mf/54BAdAW1E/f3AOMXB+qT3EcAE4SLAAFMfV3jd5zbSESX8Mf/54BAYBhEVEAQQPmOYwenARxCE0f3/32P2Y4UwgUMQQTZvxFHtbVBRwVE45rn3oOniwADp0sBIyT5ACMi6QDJs4MlSQDBF5Hlic8BTBMEYAyhuwMniQBjZvcGE/c3AOMbB+IDKIkAAUYBRzMF6ECzhuUAY2n3AOMHBtIjJKkAIyLZAA2zM4brABBOEQeQwgVG6b8hRwVE45Tn2AMkiQAZwBMEgAwjJAkAIyIJADM0gAC9swFMEwQgDMW5AUwTBIAM5bEBTBMEkAzFsRMHIA1jg+cMEwdADeOR57oDxDsAg8crACIEXYyX8Mf/54CgXgOsxABBFGNzhAEijOMPDLbAQGKUMYCcSGNV8ACcRGNa9Arv8A/gdd3IQGKGk4WLAZfwx//ngKBaAcWTB0AM3MjcQOKX3MDcRLOHh0HcxJfwx//ngIBZFb4JZRMFBXEDrMsAA6SLAJfwx//ngMBKtwcAYNhLtwYAAcEWk1dHARIHdY+9i9mPs4eHAwFFs9WHApfwx//ngKBLEwWAPpfwx//ngGBH3bSDpksBA6YLAYOlywADpYsA7/AP9sG8g8U7AIPHKwAThYsBogXdjcEVgTptvO/wb9mBtwPEOwCDxysAE4yLASIEXYzcREEUxeORR4VLY/6HCJMHkAzcyHm0A6cNACLQBUizh+xAPtaDJ4qwY3P0AA1IQsY6xO/w79QiRzJIN8XIP+KFfBCThsoAEBATBUUCl/DH/+eAoEg398g/kwjHAIJXA6eIsIOlDQAdjB2PPpyyVyOk6LCqi76VI6C9AJOHygCdjQHFoWdjlvUAWoV1MCOgbQEJxNxEmcPjQHD5Y98LAJMHcAyFv4VLt33JP7fMyD+TjY26k4zMAOm/45ULntxE44IHnpMHgAyxt4OniwDjmwecAUWX8Mf/54CAOAllEwUFcZfwx//ngKA0l/DH/+eAIDhNugOkywDjBgSaAUWX8Mf/54DgNRMFgD6X8Mf/54AgMgKUQbr2UGZU1lRGWbZZJlqWWgZb9ktmTNZMRk22TQlhgoA=",
|
||||
"text_start": 1077411840,
|
||||
"data": "DEDIP/gIOEBECThAnAk4QEAKOECsCjhAWgo4QMAHOED8CThAPAo4QLAJOEBwBzhA5Ak4QHAHOEDSCDhAFgk4QEQJOECcCThA5Ag4QCoIOEBaCDhA4Ag4QAQNOEBECThAxAs4QLgMOEC8BjhA4gw4QLwGOEC8BjhAvAY4QLwGOEC8BjhAvAY4QLwGOEC8BjhAYAs4QLwGOEDgCzhAuAw4QA==",
|
||||
"data_start": 1070164904
|
||||
"data": "DEDIP/gIOEBECThAnAk4QGoKOEDWCjhAhAo4QMAHOEAmCjhAZgo4QLAJOEBwBzhA5Ak4QHAHOEDSCDhAFgk4QEQJOECcCThA5Ag4QCoIOEBaCDhA4Ag4QC4NOEBECThA7gs4QOIMOEC8BjhADA04QLwGOEC8BjhAvAY4QLwGOEC8BjhAvAY4QLwGOEC8BjhAigs4QLwGOEAKDDhA4gw4QA==",
|
||||
"data_start": 1070164904,
|
||||
"bss_start": 1070088192
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"entry": 1077413318,
|
||||
"text": "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",
|
||||
"text": "ARG3BwBgTsaDqYcASsg3Scg/JspSxAbOIsy3BABgfVoTCQkAwEwTdPQ/DeDyQGJEI6g0AUJJ0kSySSJKBWGCgIhAgycJABN19Q+Cl30U4xlE/8m/EwcADJRBqodjGOUAhUeFxiOgBQB5VYKABUdjh+YACUZjjcYAfVWCgEIFEwewDUGFY5XnAolHnMH1t5MGwA1jFtUAmMETBQAMgoCTBtANfVVjldcAmMETBbANgoC3dck/QRGThQW6BsZhP2NFBQa3d8k/k4eHsQOnBwgD1kcIE3X1D5MGFgDCBsGCI5LXCDKXIwCnAAPXRwiRZ5OHBwRjHvcCN/fIPxMHh7GhZ7qXA6YHCLc2yT+3d8k/k4eHsZOGhrVjH+YAI6bHCCOg1wgjkgcIIaD5V+MG9fyyQEEBgoAjptcII6DnCN23NycAYHxLnYv1/zc3AGB8S52L9f+CgEERBsbdN7cnAGAjpgcCNwcACJjDmEN9/8hXskATRfX/BYlBAYKAQREGxtk/fd03BwBAtycAYJjDNycAYBxD/f+yQEEBgoBBESLEN8TIP5MHxABKwAOpBwEGxibCYwoJBEU3OcW9RxMExACBRGPWJwEERL2Ik7QUAH03hT8cRDcGgAATl8cAmeA3BgABt/b/AHWPtyYAYNjCkMKYQn3/QUeR4AVHMwnpQLqXIygkARzEskAiRJJEAklBAYKAQREGxhMHAAxjEOUCEwWwDZcAyP/ngMDjEwXADbJAQQEXA8j/ZwDD4hMHsA3jGOX+lwDI/+eAwOETBdANxbdBESLEJsIGxiqEswS1AGMXlACyQCJEkkRBAYKAA0UEAAUERTfttxMFAAwXA8j/ZwBD3jVxJstOx/1yhWn9dCLNSslSxVbDBs+ThIT6FpGThwkHppcYCLOE5wAqiSaFLoSXAMj/54DgNpOHCQcYCAVqupezikdBMeQFZ311kwWF+pMHBwcTBYX5FAiqlzOF1wCTBwcHrpezhdcAKsaXAMj/54CgMzJFwUWhPwFFhWIWkfpAakTaREpJukkqSppKDWGCgKKJY3OKAIVpTobWhUqFlwDI/+eA4OATdfUPAe1OhtaFJoWXAMj/54DgLk6ZMwQ0QVG3EwUwBlW/MXH9ck7XUtVW017PBt8i3SbbStla0WLNZstqyW7HqokWkRMFAAIuirKKtosCypcAyP/ngKAphWdj4FcThWR9dBMEhPqThwQHopcYCDOE5wAihZcAyP/ngCAofXsTDDv5kwyL+ROHBAeThwQHFAhil+aXAUkzDNcAs4zXAFJNY3xNCWNxqQNBqFU1poUIAaU9cT0mhgwBIoWXAMj/54AAJKaZJpljdUkDswepQWPxdwOzBCpBY/OaANaEJoYMAU6FlwDI/+eAQNITdfUPVd0CzIFEeV2NTaMJAQBihZcAyP/ngADEffkDRTEB5oUFMWNPBQDj4p3+hWeThwcHppcYCLqX2pcjiqf4hQTxt+MVpf2RR+OF9PYFZ311kwcHB5MFhfoTBYX5FAiqlzOF1wCTBwcHrpezhdcAKsaXAMj/54AgGu0zMkXBRX07zTMTBQAClwDI/+eAwBeFYhaR+lBqVNpUSlm6WSpamloKW/pLakzaTEpNuk0pYYKAAREGziLMnTk3BM4/bAATBQT/lwDI/+eAQMiqhwVFleeyR5P3ByA+xkE5NycAYBxHtwZAABMFBP/VjxzHskWXAMj/54DAxTM1oADyQGJEBWGCgEERt8fIPwbGk4fHAAVHI4DnABPXxQCYxwVnfRfMw8jH+Y06laqVsYGMyyOqBwBBNxnBEwVQDLJAQQGCgAERIsw3xMg/kwfEACbKxEdOxgbOSsiqiRMExABj85UAroSpwAMpRAAmmRNZyQAcSGNV8AAcRGNe+QLpNn3dSEAmhs6FlwDI/+eAQLkTdfUPAcWTB0AMXMhcQKaXXMBcRIWPXMTyQGJE0kRCSbJJBWGCgOE+bb+3V0FJGXGTh/eEAUU+zobeotym2srYztbS1NbS2tDezuLM5srqyO7GlwDI/+eAoKy3R8g/N3fJP5OHBwATB4e6Y+XnFK0xkUVoCD05jTG398g/k4eHsSFnPpcjIPcItwU4QLcHOECThwcLAUaThQUAN0nIPxVFIyD5AJcAyP/ngMD8NwcAYFxHEwUAArd5yT+T5xcQXMeXAMj/54CA+5cAyP/ngAAMt0cAYJxfk4mJsRMJCQAJ5fGL4RcTtRcAgUWXAMj/54CgrcFnt8TIP/0XEwcAEIVmQWa3BQABAUWThMQAt0rIPw1qlwDI/+eAIKgTi8oAJpqDp8kI9d+Dq8kIhUcjpgkIIwLxAoPHGwAJRyMT4QKjAvECAtRNR2OL5wZRR2OJ5wYpR2Of5wCDxzsAA8crAKIH2Y8RR2OW5wCDp4sAnEM+1KU2oUVIEDU+g8c7AAPHKwCiB9mPEWdBB2N+9wITBbANlwDI/+eAAJMTBcANlwDI/+eAQJITBeAOlwDI/+eAgJElNr23I6AHAJEHRb3JRyMT8QJ9twPHGwDRRmPn5gKFRmPm5gABTBME8A+dqHkXE3f3D8lG4+jm/rd2yT8KB5OGxro2lxhDAoeTBgcDk/b2DxFG42nW/BMH9wITd/cPjUZj7uYIt3bJPwoHk4aGvzaXGEMChxMHQAJjmucQAtQdRAFFlwDI/+eA4IgBRSU8aTxhPKFFSBB9FK00ffABTAFEE3X0DwU0E3X8Dyk8tTzjEQTsg8cbAElHY2X3MAlH43n36vUXk/f3Dz1H42P36jd3yT+KBxMHh8C6l5xDgocFRJ3rcBCBRQFFlyDJ/+eA4Icd4dFFaBAxNAFEMagFRIHvlwDI/+eAAI0zNKAAKaAhR2OF5wAFRAFMYbcDrIsAA6TLALNnjADSB/X3sTFl9cFsIpz9HH19MwWMQF3cs3eVAZXjwWwzBYxAY+aMAv18MwWMQF3QMYGXAMj/54AgiF35ZpT1tzGBlwDI/+eAIIdd8WqU0bdBgZcAyP/ngOCFWfkzBJRBwbchR+OK5/ABTBMEAAw5t0FHzb9BRwVE453n9oOlywADpYsAcTK5v0FHBUTjk+f2A6cLAZFnY+jnHoOlSwEDpYsACTGBt0FHBUTjlOf0g6cLARFnY2n3HAOnywCDpUsBA6WLADOE5wLxPiOsBAAjJIqwCb8DxwQAYwMHFAOniwDBFxMEAAxjE/cAwEgBR5MG8A5jRvcCg8dbAAPHSwABTKIH2Y8Dx2sAQgddj4PHewDiB9mP44T25hMEEAyFtTOG6wADRoYBBQexjuG3g8cEAP3H3ERjnQcUwEgjgAQAVb1hR2OW5wKDp8sBA6eLAYOmSwEDpgsBg6XLAAOliwCX8Mf/54AgdiqMMzSgAAG9AUwFRCm1EUcFROOd5+a3lwBgtEtld30XBWb5jtGOA6WLALTL9EOBRfmO0Y70w/RL+Y7RjvTLtEN1j1GPuMOX8Mf/54BAdAW1E/f3AOMXB+qT3EcAE4SLAAFMfV3jd5zbSESX8Mf/54BAYBhEVEAQQPmOYwenARxCE0f3/32P2Y4UwgUMQQTZvxFHtbVBRwVE45rn3oOniwADp0sBIyT5ACMi6QDJs4MlSQDBF5Hlic8BTBMEYAyhuwMniQBjZvcGE/c3AOMbB+IDKIkAAUYBRzMF6ECzhuUAY2n3AOMHBtIjJKkAIyLZAA2zM4brABBOEQeQwgVG6b8hRwVE45Tn2AMkiQAZwBMEgAwjJAkAIyIJADM0gAC9swFMEwQgDMW5AUwTBIAM5bEBTBMEkAzFsRMHIA1jg+cMEwdADeOR57oDxDsAg8crACIEXYyX8Mf/54CgXgOsxABBFGNzhAEijOMPDLbAQGKUMYCcSGNV8ACcRGNa9Arv8A/gdd3IQGKGk4WLAZfwx//ngKBaAcWTB0AM3MjcQOKX3MDcRLOHh0HcxJfwx//ngIBZFb4JZRMFBXEDrMsAA6SLAJfwx//ngMBKtwcAYNhLtwYAAcEWk1dHARIHdY+9i9mPs4eHAwFFs9WHApfwx//ngKBLEwWAPpfwx//ngGBH3bSDpksBA6YLAYOlywADpYsA7/AP9sG8g8U7AIPHKwAThYsBogXdjcEVgTptvO/wb9mBtwPEOwCDxysAE4yLASIEXYzcREEUxeORR4VLY/6HCJMHkAzcyHm0A6cNACLQBUizh+xAPtaDJ4qwY3P0AA1IQsY6xO/w79QiRzJIN8XIP+KFfBCThsoAEBATBUUCl/DH/+eAoEg398g/kwjHAIJXA6eIsIOlDQAdjB2PPpyyVyOk6LCqi76VI6C9AJOHygCdjQHFoWdjlvUAWoV1MCOgbQEJxNxEmcPjQHD5Y98LAJMHcAyFv4VLt33JP7fMyD+TjY26k4zMAOm/45ULntxE44IHnpMHgAyxt4OniwDjmwecAUWX8Mf/54CAOAllEwUFcZfwx//ngKA0l/DH/+eAIDhNugOkywDjBgSaAUWX8Mf/54DgNRMFgD6X8Mf/54AgMgKUQbr2UGZU1lRGWbZZJlqWWgZb9ktmTNZMRk22TQlhgoA=",
|
||||
"text_start": 1077411840,
|
||||
"data": "DEDIP/gIOEBECThAnAk4QEAKOECsCjhAWgo4QMAHOED8CThAPAo4QLAJOEBwBzhA5Ak4QHAHOEDSCDhAFgk4QEQJOECcCThA5Ag4QCoIOEBaCDhA4Ag4QAQNOEBECThAxAs4QLgMOEC8BjhA4gw4QLwGOEC8BjhAvAY4QLwGOEC8BjhAvAY4QLwGOEC8BjhAYAs4QLwGOEDgCzhAuAw4QA==",
|
||||
"data_start": 1070164904
|
||||
"data": "DEDIP/gIOEBECThAnAk4QGoKOEDWCjhAhAo4QMAHOEAmCjhAZgo4QLAJOEBwBzhA5Ak4QHAHOEDSCDhAFgk4QEQJOECcCThA5Ag4QCoIOEBaCDhA4Ag4QC4NOEBECThA7gs4QOIMOEC8BjhADA04QLwGOEC8BjhAvAY4QLwGOEC8BjhAvAY4QLwGOEC8BjhAigs4QLwGOEAKDDhA4gw4QA==",
|
||||
"data_start": 1070164904,
|
||||
"bss_start": 1070088192
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"entry": 1077413318,
|
||||
"text": "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",
|
||||
"text": "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",
|
||||
"text_start": 1077411840,
|
||||
"data": "DEDIP/gIOEBECThAnAk4QEAKOECsCjhAWgo4QMAHOED8CThAPAo4QLAJOEBwBzhA5Ak4QHAHOEDSCDhAFgk4QEQJOECcCThA5Ag4QCoIOEBaCDhA4Ag4QAQNOEBECThAxAs4QLgMOEC8BjhA4gw4QLwGOEC8BjhAvAY4QLwGOEC8BjhAvAY4QLwGOEC8BjhAYAs4QLwGOEDgCzhAuAw4QA==",
|
||||
"data_start": 1070164904
|
||||
"data": "DEDIP/gIOEBECThAnAk4QGoKOEDWCjhAhAo4QMAHOEAmCjhAZgo4QLAJOEBwBzhA5Ak4QHAHOEDSCDhAFgk4QEQJOECcCThA5Ag4QCoIOEBaCDhA4Ag4QC4NOEBECThA7gs4QOIMOEC8BjhADA04QLwGOEC8BjhAvAY4QLwGOEC8BjhAvAY4QLwGOEC8BjhAigs4QLwGOEAKDDhA4gw4QA==",
|
||||
"data_start": 1070164904,
|
||||
"bss_start": 1070088192
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
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
96
tools/python/esptool/uf2_writer.py
Normal file
96
tools/python/esptool/uf2_writer.py
Normal file
@@ -0,0 +1,96 @@
|
||||
#!/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
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import struct
|
||||
from typing import List
|
||||
|
||||
from util import div_roundup
|
||||
|
||||
|
||||
class UF2Writer(object):
|
||||
# The UF2 format is described here: https://github.com/microsoft/uf2
|
||||
UF2_BLOCK_SIZE = 512
|
||||
# max value of CHUNK_SIZE reduced by optional parts. Currently, MD5_PART only.
|
||||
UF2_DATA_SIZE = 476
|
||||
UF2_MD5_PART_SIZE = 24
|
||||
UF2_FIRST_MAGIC = 0x0A324655
|
||||
UF2_SECOND_MAGIC = 0x9E5D5157
|
||||
UF2_FINAL_MAGIC = 0x0AB16F30
|
||||
UF2_FLAG_FAMILYID_PRESENT = 0x00002000
|
||||
UF2_FLAG_MD5_PRESENT = 0x00004000
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
chip_id: int,
|
||||
output_file: os.PathLike,
|
||||
chunk_size: int,
|
||||
md5_enabled: bool = True,
|
||||
) -> None:
|
||||
if not md5_enabled:
|
||||
self.UF2_MD5_PART_SIZE = 0
|
||||
self.UF2_FLAG_MD5_PRESENT = 0x00000000
|
||||
self.md5_enabled = md5_enabled
|
||||
self.chip_id = chip_id
|
||||
self.CHUNK_SIZE = (
|
||||
self.UF2_DATA_SIZE - self.UF2_MD5_PART_SIZE
|
||||
if chunk_size is None
|
||||
else chunk_size
|
||||
)
|
||||
self.f = open(output_file, "wb")
|
||||
|
||||
def __enter__(self) -> "UF2Writer":
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type: str, exc_val: int, exc_tb: List) -> None:
|
||||
if self.f:
|
||||
self.f.close()
|
||||
|
||||
@staticmethod
|
||||
def _to_uint32(num: int) -> bytes:
|
||||
return struct.pack("<I", num)
|
||||
|
||||
def _write_block(
|
||||
self, addr: int, chunk: bytes, len_chunk: int, block_no: int, blocks: int
|
||||
) -> None:
|
||||
assert len_chunk > 0
|
||||
assert len_chunk <= self.CHUNK_SIZE
|
||||
assert block_no < blocks
|
||||
block = struct.pack(
|
||||
"<IIIIIIII",
|
||||
self.UF2_FIRST_MAGIC,
|
||||
self.UF2_SECOND_MAGIC,
|
||||
self.UF2_FLAG_FAMILYID_PRESENT | self.UF2_FLAG_MD5_PRESENT,
|
||||
addr,
|
||||
len_chunk,
|
||||
block_no,
|
||||
blocks,
|
||||
self.chip_id,
|
||||
)
|
||||
block += chunk
|
||||
|
||||
if self.md5_enabled:
|
||||
md5_part = struct.pack("<II", addr, len_chunk)
|
||||
md5_part += hashlib.md5(chunk).digest()
|
||||
assert len(md5_part) == self.UF2_MD5_PART_SIZE
|
||||
|
||||
block += md5_part
|
||||
block += b"\x00" * (self.UF2_DATA_SIZE - self.UF2_MD5_PART_SIZE - len_chunk)
|
||||
block += self._to_uint32(self.UF2_FINAL_MAGIC)
|
||||
assert len(block) == self.UF2_BLOCK_SIZE
|
||||
self.f.write(block)
|
||||
|
||||
def add_file(self, addr: int, image: bytes) -> None:
|
||||
blocks = div_roundup(len(image), self.CHUNK_SIZE)
|
||||
chunks = [
|
||||
image[i : i + self.CHUNK_SIZE]
|
||||
for i in range(0, len(image), self.CHUNK_SIZE)
|
||||
]
|
||||
for i, chunk in enumerate(chunks):
|
||||
len_chunk = len(chunk)
|
||||
self._write_block(addr, chunk, len_chunk, i, blocks)
|
||||
addr += len_chunk
|
||||
@@ -34,6 +34,8 @@ def flash_size_bytes(size):
|
||||
"""Given a flash size of the type passed in args.flash_size
|
||||
(ie 512KB or 1MB) then return the size in bytes.
|
||||
"""
|
||||
if size is None:
|
||||
return None
|
||||
if "MB" in size:
|
||||
return int(size[: size.index("MB")]) * 1024 * 1024
|
||||
elif "KB" in size:
|
||||
@@ -66,7 +68,7 @@ def print_overwrite(message, last_line=False):
|
||||
If output is not a TTY (for example redirected a pipe),
|
||||
no overwriting happens and this function is the same as print().
|
||||
"""
|
||||
if sys.stdout.isatty():
|
||||
if hasattr(sys.stdout, "isatty") and sys.stdout.isatty():
|
||||
print("\r%s" % message, end="\n" if last_line else "")
|
||||
else:
|
||||
print(message)
|
||||
@@ -165,7 +167,7 @@ class NotSupportedError(FatalError):
|
||||
def __init__(self, esp, function_name):
|
||||
FatalError.__init__(
|
||||
self,
|
||||
"Function %s is not supported for %s." % (function_name, esp.CHIP_NAME),
|
||||
f"{function_name} is not supported by {esp.CHIP_NAME}.",
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user