esptool.py
   1  #!/usr/bin/env python
   2  #
   3  # SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, Espressif Systems (Shanghai) CO LTD, other contributors as noted.
   4  #
   5  # SPDX-License-Identifier: GPL-2.0-or-later
   6  
   7  from __future__ import division, print_function
   8  
   9  import argparse
  10  import base64
  11  import binascii
  12  import copy
  13  import hashlib
  14  import inspect
  15  import io
  16  import itertools
  17  import os
  18  import re
  19  import shlex
  20  import string
  21  import struct
  22  import sys
  23  import time
  24  import zlib
  25  
  26  try:
  27      import serial
  28  except ImportError:
  29      print("Pyserial is not installed for %s. Check the README for installation instructions." % (sys.executable))
  30      raise
  31  
  32  # check 'serial' is 'pyserial' and not 'serial' https://github.com/espressif/esptool/issues/269
  33  try:
  34      if "serialization" in serial.__doc__ and "deserialization" in serial.__doc__:
  35          raise ImportError("""
  36  esptool.py depends on pyserial, but there is a conflict with a currently installed package named 'serial'.
  37  
  38  You may be able to work around this by 'pip uninstall serial; pip install pyserial' \
  39  but this may break other installed Python software that depends on 'serial'.
  40  
  41  There is no good fix for this right now, apart from configuring virtualenvs. \
  42  See https://github.com/espressif/esptool/issues/269#issuecomment-385298196 for discussion of the underlying issue(s).""")
  43  except TypeError:
  44      pass  # __doc__ returns None for pyserial
  45  
  46  try:
  47      import serial.tools.list_ports as list_ports
  48  except ImportError:
  49      print("The installed version (%s) of pyserial appears to be too old for esptool.py (Python interpreter %s). "
  50            "Check the README for installation instructions." % (sys.VERSION, sys.executable))
  51      raise
  52  except Exception:
  53      if sys.platform == "darwin":
  54          # swallow the exception, this is a known issue in pyserial+macOS Big Sur preview ref https://github.com/espressif/esptool/issues/540
  55          list_ports = None
  56      else:
  57          raise
  58  
  59  
  60  __version__ = "3.3.3"
  61  
  62  MAX_UINT32 = 0xffffffff
  63  MAX_UINT24 = 0xffffff
  64  
  65  DEFAULT_TIMEOUT = 3                   # timeout for most flash operations
  66  START_FLASH_TIMEOUT = 20              # timeout for starting flash (may perform erase)
  67  CHIP_ERASE_TIMEOUT = 120              # timeout for full chip erase
  68  MAX_TIMEOUT = CHIP_ERASE_TIMEOUT * 2  # longest any command can run
  69  SYNC_TIMEOUT = 0.1                    # timeout for syncing with bootloader
  70  MD5_TIMEOUT_PER_MB = 8                # timeout (per megabyte) for calculating md5sum
  71  ERASE_REGION_TIMEOUT_PER_MB = 30      # timeout (per megabyte) for erasing a region
  72  ERASE_WRITE_TIMEOUT_PER_MB = 40       # timeout (per megabyte) for erasing and writing data
  73  MEM_END_ROM_TIMEOUT = 0.05            # special short timeout for ESP_MEM_END, as it may never respond
  74  DEFAULT_SERIAL_WRITE_TIMEOUT = 10     # timeout for serial port write
  75  DEFAULT_CONNECT_ATTEMPTS = 7          # default number of times to try connection
  76  WRITE_BLOCK_ATTEMPTS = 3              # number of times to try writing a data block
  77  
  78  SUPPORTED_CHIPS = ['esp8266', 'esp32', 'esp32s2', 'esp32s3beta2', 'esp32s3', 'esp32c3', 'esp32c6beta', 'esp32h2beta1', 'esp32h2beta2', 'esp32c2']
  79  
  80  
  81  def timeout_per_mb(seconds_per_mb, size_bytes):
  82      """ Scales timeouts which are size-specific """
  83      result = seconds_per_mb * (size_bytes / 1e6)
  84      if result < DEFAULT_TIMEOUT:
  85          return DEFAULT_TIMEOUT
  86      return result
  87  
  88  
  89  def _chip_to_rom_loader(chip):
  90      return {
  91          'esp8266': ESP8266ROM,
  92          'esp32': ESP32ROM,
  93          'esp32s2': ESP32S2ROM,
  94          'esp32s3beta2': ESP32S3BETA2ROM,
  95          'esp32s3': ESP32S3ROM,
  96          'esp32c3': ESP32C3ROM,
  97          'esp32c6beta': ESP32C6BETAROM,
  98          'esp32h2beta1': ESP32H2BETA1ROM,
  99          'esp32h2beta2': ESP32H2BETA2ROM,
 100          'esp32c2': ESP32C2ROM,
 101      }[chip]
 102  
 103  
 104  def get_default_connected_device(serial_list, port, connect_attempts, initial_baud, chip='auto', trace=False,
 105                                   before='default_reset'):
 106      _esp = None
 107      for each_port in reversed(serial_list):
 108          print("Serial port %s" % each_port)
 109          try:
 110              if chip == 'auto':
 111                  _esp = ESPLoader.detect_chip(each_port, initial_baud, before, trace,
 112                                               connect_attempts)
 113              else:
 114                  chip_class = _chip_to_rom_loader(chip)
 115                  _esp = chip_class(each_port, initial_baud, trace)
 116                  _esp.connect(before, connect_attempts)
 117              break
 118          except (FatalError, OSError) as err:
 119              if port is not None:
 120                  raise
 121              print("%s failed to connect: %s" % (each_port, err))
 122              if _esp and _esp._port:
 123                  _esp._port.close()
 124              _esp = None
 125      return _esp
 126  
 127  
 128  DETECTED_FLASH_SIZES = {
 129      0x12: "256KB",
 130      0x13: "512KB",
 131      0x14: "1MB",
 132      0x15: "2MB",
 133      0x16: "4MB",
 134      0x17: "8MB",
 135      0x18: "16MB",
 136      0x19: "32MB",
 137      0x1A: "64MB",
 138      0x1B: "128MB",
 139      0x1C: "256MB",
 140      0x20: "64MB",
 141      0x21: "128MB",
 142      0x22: "256MB",
 143      0x32: "256KB",
 144      0x33: "512KB",
 145      0x34: "1MB",
 146      0x35: "2MB",
 147      0x36: "4MB",
 148      0x37: "8MB",
 149      0x38: "16MB",
 150      0x39: "32MB",
 151      0x3A: "64MB",
 152  }
 153  
 154  
 155  def check_supported_function(func, check_func):
 156      """
 157      Decorator implementation that wraps a check around an ESPLoader
 158      bootloader function to check if it's supported.
 159  
 160      This is used to capture the multidimensional differences in
 161      functionality between the ESP8266 & ESP32 (and later chips) ROM loaders, and the
 162      software stub that runs on these. Not possible to do this cleanly
 163      via inheritance alone.
 164      """
 165      def inner(*args, **kwargs):
 166          obj = args[0]
 167          if check_func(obj):
 168              return func(*args, **kwargs)
 169          else:
 170              raise NotImplementedInROMError(obj, func)
 171      return inner
 172  
 173  
 174  def esp8266_function_only(func):
 175      """ Attribute for a function only supported on ESP8266 """
 176      return check_supported_function(func, lambda o: o.CHIP_NAME == "ESP8266")
 177  
 178  
 179  def stub_function_only(func):
 180      """ Attribute for a function only supported in the software stub loader """
 181      return check_supported_function(func, lambda o: o.IS_STUB)
 182  
 183  
 184  def stub_and_esp32_function_only(func):
 185      """ Attribute for a function only supported by software stubs or ESP32 and later chips ROM """
 186      return check_supported_function(func, lambda o: o.IS_STUB or isinstance(o, ESP32ROM))
 187  
 188  
 189  def esp32s3_or_newer_function_only(func):
 190      """ Attribute for a function only supported by ESP32S3 and later chips ROM """
 191      return check_supported_function(func, lambda o: isinstance(o, ESP32S3ROM) or isinstance(o, ESP32C3ROM))
 192  
 193  
 194  PYTHON2 = sys.version_info[0] < 3  # True if on pre-Python 3
 195  
 196  # Function to return nth byte of a bitstring
 197  # Different behaviour on Python 2 vs 3
 198  if PYTHON2:
 199      def byte(bitstr, index):
 200          return ord(bitstr[index])
 201  else:
 202      def byte(bitstr, index):
 203          return bitstr[index]
 204  
 205  # Provide a 'basestring' class on Python 3
 206  try:
 207      basestring
 208  except NameError:
 209      basestring = str
 210  
 211  
 212  def print_overwrite(message, last_line=False):
 213      """ Print a message, overwriting the currently printed line.
 214  
 215      If last_line is False, don't append a newline at the end (expecting another subsequent call will overwrite this one.)
 216  
 217      After a sequence of calls with last_line=False, call once with last_line=True.
 218  
 219      If output is not a TTY (for example redirected a pipe), no overwriting happens and this function is the same as print().
 220      """
 221      if sys.stdout.isatty():
 222          print("\r%s" % message, end='\n' if last_line else '')
 223      else:
 224          print(message)
 225  
 226  
 227  def _mask_to_shift(mask):
 228      """ Return the index of the least significant bit in the mask """
 229      shift = 0
 230      while mask & 0x1 == 0:
 231          shift += 1
 232          mask >>= 1
 233      return shift
 234  
 235  
 236  class ESPLoader(object):
 237      """ Base class providing access to ESP ROM & software stub bootloaders.
 238      Subclasses provide ESP8266 & ESP32 Family specific functionality.
 239  
 240      Don't instantiate this base class directly, either instantiate a subclass or
 241      call ESPLoader.detect_chip() which will interrogate the chip and return the
 242      appropriate subclass instance.
 243  
 244      """
 245      CHIP_NAME = "Espressif device"
 246      IS_STUB = False
 247  
 248      FPGA_SLOW_BOOT = False
 249  
 250      DEFAULT_PORT = "/dev/ttyUSB0"
 251  
 252      USES_RFC2217 = False
 253  
 254      # Commands supported by ESP8266 ROM bootloader
 255      ESP_FLASH_BEGIN = 0x02
 256      ESP_FLASH_DATA  = 0x03
 257      ESP_FLASH_END   = 0x04
 258      ESP_MEM_BEGIN   = 0x05
 259      ESP_MEM_END     = 0x06
 260      ESP_MEM_DATA    = 0x07
 261      ESP_SYNC        = 0x08
 262      ESP_WRITE_REG   = 0x09
 263      ESP_READ_REG    = 0x0a
 264  
 265      # Some comands supported by ESP32 and later chips ROM bootloader (or -8266 w/ stub)
 266      ESP_SPI_SET_PARAMS = 0x0B
 267      ESP_SPI_ATTACH     = 0x0D
 268      ESP_READ_FLASH_SLOW  = 0x0e  # ROM only, much slower than the stub flash read
 269      ESP_CHANGE_BAUDRATE = 0x0F
 270      ESP_FLASH_DEFL_BEGIN = 0x10
 271      ESP_FLASH_DEFL_DATA  = 0x11
 272      ESP_FLASH_DEFL_END   = 0x12
 273      ESP_SPI_FLASH_MD5    = 0x13
 274  
 275      # Commands supported by ESP32-S2 and later chips ROM bootloader only
 276      ESP_GET_SECURITY_INFO = 0x14
 277  
 278      # Some commands supported by stub only
 279      ESP_ERASE_FLASH = 0xD0
 280      ESP_ERASE_REGION = 0xD1
 281      ESP_READ_FLASH = 0xD2
 282      ESP_RUN_USER_CODE = 0xD3
 283  
 284      # Flash encryption encrypted data command
 285      ESP_FLASH_ENCRYPT_DATA = 0xD4
 286  
 287      # Response code(s) sent by ROM
 288      ROM_INVALID_RECV_MSG = 0x05   # response if an invalid message is received
 289  
 290      # Maximum block sized for RAM and Flash writes, respectively.
 291      ESP_RAM_BLOCK   = 0x1800
 292  
 293      FLASH_WRITE_SIZE = 0x400
 294  
 295      # Default baudrate. The ROM auto-bauds, so we can use more or less whatever we want.
 296      ESP_ROM_BAUD    = 115200
 297  
 298      # First byte of the application image
 299      ESP_IMAGE_MAGIC = 0xe9
 300  
 301      # Initial state for the checksum routine
 302      ESP_CHECKSUM_MAGIC = 0xef
 303  
 304      # Flash sector size, minimum unit of erase.
 305      FLASH_SECTOR_SIZE = 0x1000
 306  
 307      UART_DATE_REG_ADDR = 0x60000078
 308  
 309      CHIP_DETECT_MAGIC_REG_ADDR = 0x40001000  # This ROM address has a different value on each chip model
 310  
 311      UART_CLKDIV_MASK = 0xFFFFF
 312  
 313      # Memory addresses
 314      IROM_MAP_START = 0x40200000
 315      IROM_MAP_END = 0x40300000
 316  
 317      # The number of bytes in the UART response that signify command status
 318      STATUS_BYTES_LENGTH = 2
 319  
 320      # Response to ESP_SYNC might indicate that flasher stub is running instead of the ROM bootloader
 321      sync_stub_detected = False
 322  
 323      # Device PIDs
 324      USB_JTAG_SERIAL_PID = 0x1001
 325  
 326      # Chip IDs that are no longer supported by esptool
 327      UNSUPPORTED_CHIPS = {6: "ESP32-S3(beta 3)"}
 328  
 329      def __init__(self, port=DEFAULT_PORT, baud=ESP_ROM_BAUD, trace_enabled=False):
 330          """Base constructor for ESPLoader bootloader interaction
 331  
 332          Don't call this constructor, either instantiate ESP8266ROM
 333          or ESP32ROM, or use ESPLoader.detect_chip().
 334  
 335          This base class has all of the instance methods for bootloader
 336          functionality supported across various chips & stub
 337          loaders. Subclasses replace the functions they don't support
 338          with ones which throw NotImplementedInROMError().
 339  
 340          """
 341          self.secure_download_mode = False  # flag is set to True if esptool detects the ROM is in Secure Download Mode
 342          self.stub_is_disabled = False  # flag is set to True if esptool detects conditions which require the stub to be disabled
 343  
 344          if isinstance(port, basestring):
 345              self._port = serial.serial_for_url(port)
 346          else:
 347              self._port = port
 348          self._slip_reader = slip_reader(self._port, self.trace)
 349          # setting baud rate in a separate step is a workaround for
 350          # CH341 driver on some Linux versions (this opens at 9600 then
 351          # sets), shouldn't matter for other platforms/drivers. See
 352          # https://github.com/espressif/esptool/issues/44#issuecomment-107094446
 353          self._set_port_baudrate(baud)
 354          self._trace_enabled = trace_enabled
 355          # set write timeout, to prevent esptool blocked at write forever.
 356          try:
 357              self._port.write_timeout = DEFAULT_SERIAL_WRITE_TIMEOUT
 358          except NotImplementedError:
 359              # no write timeout for RFC2217 ports
 360              # need to set the property back to None or it will continue to fail
 361              self._port.write_timeout = None
 362  
 363      @property
 364      def serial_port(self):
 365          return self._port.port
 366  
 367      def _set_port_baudrate(self, baud):
 368          try:
 369              self._port.baudrate = baud
 370          except IOError:
 371              raise FatalError("Failed to set baud rate %d. The driver may not support this rate." % baud)
 372  
 373      @staticmethod
 374      def detect_chip(port=DEFAULT_PORT, baud=ESP_ROM_BAUD, connect_mode='default_reset', trace_enabled=False,
 375                      connect_attempts=DEFAULT_CONNECT_ATTEMPTS):
 376          """ Use serial access to detect the chip type.
 377  
 378          First, get_security_info command is sent to detect the ID of the chip
 379          (supported only by ESP32-C3 and later, works even in the Secure Download Mode).
 380          If this fails, we reconnect and fall-back to reading the magic number.
 381          It's mapped at a specific ROM address and has a different value on each chip model.
 382          This way we can use one memory read and compare it to the magic number for each chip type.
 383  
 384          This routine automatically performs ESPLoader.connect() (passing
 385          connect_mode parameter) as part of querying the chip.
 386          """
 387          inst = None
 388          detect_port = ESPLoader(port, baud, trace_enabled=trace_enabled)
 389          if detect_port.serial_port.startswith("rfc2217:"):
 390              detect_port.USES_RFC2217 = True
 391          detect_port.connect(connect_mode, connect_attempts, detecting=True)
 392          try:
 393              print('Detecting chip type...', end='')
 394              res = detect_port.check_command('get security info', ESPLoader.ESP_GET_SECURITY_INFO, b'')
 395              res = struct.unpack("<IBBBBBBBBI", res[:16])  # 4b flags, 1b flash_crypt_cnt, 7*1b key_purposes, 4b chip_id
 396              chip_id = res[9]  # 2/4 status bytes invariant
 397  
 398              for cls in [ESP32S3BETA2ROM, ESP32S3ROM, ESP32C3ROM, ESP32C6BETAROM, ESP32H2BETA1ROM, ESP32C2ROM, ESP32H2BETA2ROM]:
 399                  if chip_id == cls.IMAGE_CHIP_ID:
 400                      inst = cls(detect_port._port, baud, trace_enabled=trace_enabled)
 401                      inst._post_connect()
 402                      try:
 403                          inst.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR)  # Dummy read to check Secure Download mode
 404                      except UnsupportedCommandError:
 405                          inst.secure_download_mode = True
 406          except (UnsupportedCommandError, struct.error, FatalError) as e:
 407              # UnsupportedCmdErr: ESP8266/ESP32 ROM | struct.err: ESP32-S2 | FatalErr: ESP8266/ESP32 STUB
 408              print(" Unsupported detection protocol, switching and trying again...")
 409              try:
 410                  # ESP32/ESP8266 are reset after an unsupported command, need to connect again (not needed on ESP32-S2)
 411                  if not isinstance(e, struct.error):
 412                      detect_port.connect(connect_mode, connect_attempts, detecting=True, warnings=False)
 413                  print('Detecting chip type...', end='')
 414                  sys.stdout.flush()
 415                  chip_magic_value = detect_port.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR)
 416  
 417                  for cls in [ESP8266ROM, ESP32ROM, ESP32S2ROM, ESP32S3BETA2ROM, ESP32S3ROM,
 418                              ESP32C3ROM, ESP32C6BETAROM, ESP32H2BETA1ROM, ESP32C2ROM, ESP32H2BETA2ROM]:
 419                      if chip_magic_value in cls.CHIP_DETECT_MAGIC_VALUE:
 420                          inst = cls(detect_port._port, baud, trace_enabled=trace_enabled)
 421                          inst._post_connect()
 422                          inst.check_chip_id()
 423              except UnsupportedCommandError:
 424                  raise FatalError("Unsupported Command Error received. Probably this means Secure Download Mode is enabled, "
 425                                   "autodetection will not work. Need to manually specify the chip.")
 426          finally:
 427              if inst is not None:
 428                  print(' %s' % inst.CHIP_NAME, end='')
 429                  if detect_port.sync_stub_detected:
 430                      inst = inst.STUB_CLASS(inst)
 431                      inst.sync_stub_detected = True
 432                  print('')  # end line
 433                  return inst
 434          raise FatalError("Unexpected CHIP magic value 0x%08x. Failed to autodetect chip type." % (chip_magic_value))
 435  
 436      """ Read a SLIP packet from the serial port """
 437      def read(self):
 438          return next(self._slip_reader)
 439  
 440      """ Write bytes to the serial port while performing SLIP escaping """
 441      def write(self, packet):
 442          buf = b'\xc0' \
 443                + (packet.replace(b'\xdb', b'\xdb\xdd').replace(b'\xc0', b'\xdb\xdc')) \
 444                + b'\xc0'
 445          self.trace("Write %d bytes: %s", len(buf), HexFormatter(buf))
 446          self._port.write(buf)
 447  
 448      def trace(self, message, *format_args):
 449          if self._trace_enabled:
 450              now = time.time()
 451              try:
 452  
 453                  delta = now - self._last_trace
 454              except AttributeError:
 455                  delta = 0.0
 456              self._last_trace = now
 457              prefix = "TRACE +%.3f " % delta
 458              print(prefix + (message % format_args))
 459  
 460      """ Calculate checksum of a blob, as it is defined by the ROM """
 461      @staticmethod
 462      def checksum(data, state=ESP_CHECKSUM_MAGIC):
 463          for b in data:
 464              if type(b) is int:  # python 2/3 compat
 465                  state ^= b
 466              else:
 467                  state ^= ord(b)
 468  
 469          return state
 470  
 471      """ Send a request and read the response """
 472      def command(self, op=None, data=b"", chk=0, wait_response=True, timeout=DEFAULT_TIMEOUT):
 473          saved_timeout = self._port.timeout
 474          new_timeout = min(timeout, MAX_TIMEOUT)
 475          if new_timeout != saved_timeout:
 476              self._port.timeout = new_timeout
 477  
 478          try:
 479              if op is not None:
 480                  self.trace("command op=0x%02x data len=%s wait_response=%d timeout=%.3f data=%s",
 481                             op, len(data), 1 if wait_response else 0, timeout, HexFormatter(data))
 482                  pkt = struct.pack(b'<BBHI', 0x00, op, len(data), chk) + data
 483                  self.write(pkt)
 484  
 485              if not wait_response:
 486                  return
 487  
 488              # tries to get a response until that response has the
 489              # same operation as the request or a retries limit has
 490              # exceeded. This is needed for some esp8266s that
 491              # reply with more sync responses than expected.
 492              for retry in range(100):
 493                  p = self.read()
 494                  if len(p) < 8:
 495                      continue
 496                  (resp, op_ret, len_ret, val) = struct.unpack('<BBHI', p[:8])
 497                  if resp != 1:
 498                      continue
 499                  data = p[8:]
 500  
 501                  if op is None or op_ret == op:
 502                      return val, data
 503                  if byte(data, 0) != 0 and byte(data, 1) == self.ROM_INVALID_RECV_MSG:
 504                      self.flush_input()  # Unsupported read_reg can result in more than one error response for some reason
 505                      raise UnsupportedCommandError(self, op)
 506  
 507          finally:
 508              if new_timeout != saved_timeout:
 509                  self._port.timeout = saved_timeout
 510  
 511          raise FatalError("Response doesn't match request")
 512  
 513      def check_command(self, op_description, op=None, data=b'', chk=0, timeout=DEFAULT_TIMEOUT):
 514          """
 515          Execute a command with 'command', check the result code and throw an appropriate
 516          FatalError if it fails.
 517  
 518          Returns the "result" of a successful command.
 519          """
 520          val, data = self.command(op, data, chk, timeout=timeout)
 521  
 522          # things are a bit weird here, bear with us
 523  
 524          # the status bytes are the last 2/4 bytes in the data (depending on chip)
 525          if len(data) < self.STATUS_BYTES_LENGTH:
 526              raise FatalError("Failed to %s. Only got %d byte status response." % (op_description, len(data)))
 527          status_bytes = data[-self.STATUS_BYTES_LENGTH:]
 528          # we only care if the first one is non-zero. If it is, the second byte is a reason.
 529          if byte(status_bytes, 0) != 0:
 530              raise FatalError.WithResult('Failed to %s' % op_description, status_bytes)
 531  
 532          # if we had more data than just the status bytes, return it as the result
 533          # (this is used by the md5sum command, maybe other commands?)
 534          if len(data) > self.STATUS_BYTES_LENGTH:
 535              return data[:-self.STATUS_BYTES_LENGTH]
 536          else:  # otherwise, just return the 'val' field which comes from the reply header (this is used by read_reg)
 537              return val
 538  
 539      def flush_input(self):
 540          self._port.flushInput()
 541          self._slip_reader = slip_reader(self._port, self.trace)
 542  
 543      def sync(self):
 544          val, _ = self.command(self.ESP_SYNC, b'\x07\x07\x12\x20' + 32 * b'\x55',
 545                                timeout=SYNC_TIMEOUT)
 546  
 547          # ROM bootloaders send some non-zero "val" response. The flasher stub sends 0. If we receive 0 then it
 548          # probably indicates that the chip wasn't or couldn't be reseted properly and esptool is talking to the
 549          # flasher stub.
 550          self.sync_stub_detected = val == 0
 551  
 552          for _ in range(7):
 553              val, _ = self.command()
 554              self.sync_stub_detected &= val == 0
 555  
 556      def _setDTR(self, state):
 557          self._port.setDTR(state)
 558  
 559      def _setRTS(self, state):
 560          self._port.setRTS(state)
 561          # Work-around for adapters on Windows using the usbser.sys driver:
 562          # generate a dummy change to DTR so that the set-control-line-state
 563          # request is sent with the updated RTS state and the same DTR state
 564          self._port.setDTR(self._port.dtr)
 565  
 566      def _get_pid(self):
 567          if list_ports is None:
 568              print("\nListing all serial ports is currently not available. Can't get device PID.")
 569              return
 570          active_port = self._port.port
 571  
 572          # Pyserial only identifies regular ports, URL handlers are not supported
 573          if not active_port.lower().startswith(("com", "/dev/")):
 574              print("\nDevice PID identification is only supported on COM and /dev/ serial ports.")
 575              return
 576          # Return the real path if the active port is a symlink
 577          if active_port.startswith("/dev/") and os.path.islink(active_port):
 578              active_port = os.path.realpath(active_port)
 579  
 580          # The "cu" (call-up) device has to be used for outgoing communication on MacOS
 581          if sys.platform == "darwin" and "tty" in active_port:
 582              active_port = [active_port, active_port.replace("tty", "cu")]
 583          ports = list_ports.comports()
 584          for p in ports:
 585              if p.device in active_port:
 586                  return p.pid
 587          print("\nFailed to get PID of a device on {}, using standard reset sequence.".format(active_port))
 588  
 589      def bootloader_reset(self, usb_jtag_serial=False, extra_delay=False):
 590          """ Issue a reset-to-bootloader, with USB-JTAG-Serial custom reset sequence option
 591          """
 592          # RTS = either CH_PD/EN or nRESET (both active low = chip in reset)
 593          # DTR = GPIO0 (active low = boot to flasher)
 594          #
 595          # DTR & RTS are active low signals,
 596          # ie True = pin @ 0V, False = pin @ VCC.
 597          if usb_jtag_serial:
 598              # Custom reset sequence, which is required when the device
 599              # is connecting via its USB-JTAG-Serial peripheral
 600              self._setRTS(False)
 601              self._setDTR(False)  # Idle
 602              time.sleep(0.1)
 603              self._setDTR(True)  # Set IO0
 604              self._setRTS(False)
 605              time.sleep(0.1)
 606              self._setRTS(True)  # Reset. Note dtr/rts calls inverted so we go through (1,1) instead of (0,0)
 607              self._setDTR(False)
 608              self._setRTS(True)  # Extra RTS set for RTS as Windows only propagates DTR on RTS setting
 609              time.sleep(0.1)
 610              self._setDTR(False)
 611              self._setRTS(False)
 612          else:
 613              # This fpga delay is for Espressif internal use
 614              fpga_delay = True if self.FPGA_SLOW_BOOT and os.environ.get("ESPTOOL_ENV_FPGA", "").strip() == "1" else False
 615              delay = 7 if fpga_delay else 0.5 if extra_delay else 0.05  # 0.5 needed for ESP32 rev0 and rev1
 616  
 617              self._setDTR(False)  # IO0=HIGH
 618              self._setRTS(True)   # EN=LOW, chip in reset
 619              time.sleep(0.1)
 620              self._setDTR(True)   # IO0=LOW
 621              self._setRTS(False)  # EN=HIGH, chip out of reset
 622              time.sleep(delay)
 623              self._setDTR(False)  # IO0=HIGH, done
 624  
 625      def _connect_attempt(self, mode='default_reset', usb_jtag_serial=False, extra_delay=False):
 626          """ A single connection attempt """
 627          last_error = None
 628          boot_log_detected = False
 629          download_mode = False
 630  
 631          # If we're doing no_sync, we're likely communicating as a pass through
 632          # with an intermediate device to the ESP32
 633          if mode == "no_reset_no_sync":
 634              return last_error
 635  
 636          if mode != 'no_reset':
 637              if not self.USES_RFC2217:  # Might block on rfc2217 ports
 638                  self._port.reset_input_buffer()  # Empty serial buffer to isolate boot log
 639              self.bootloader_reset(usb_jtag_serial, extra_delay)
 640  
 641              # Detect the ROM boot log and check actual boot mode (ESP32 and later only)
 642              waiting = self._port.inWaiting()
 643              read_bytes = self._port.read(waiting)
 644              data = re.search(b'boot:(0x[0-9a-fA-F]+)(.*waiting for download)?', read_bytes, re.DOTALL)
 645              if data is not None:
 646                  boot_log_detected = True
 647                  boot_mode = data.group(1)
 648                  download_mode = data.group(2) is not None
 649  
 650          for _ in range(5):
 651              try:
 652                  self.flush_input()
 653                  self._port.flushOutput()
 654                  self.sync()
 655                  return None
 656              except FatalError as e:
 657                  print('.', end='')
 658                  sys.stdout.flush()
 659                  time.sleep(0.05)
 660                  last_error = e
 661  
 662          if boot_log_detected:
 663              last_error = FatalError("Wrong boot mode detected ({})! The chip needs to be in download mode.".format(boot_mode.decode("utf-8")))
 664              if download_mode:
 665                  last_error = FatalError("Download mode successfully detected, but getting no sync reply: The serial TX path seems to be down.")
 666          return last_error
 667  
 668      def get_memory_region(self, name):
 669          """ Returns a tuple of (start, end) for the memory map entry with the given name, or None if it doesn't exist
 670          """
 671          try:
 672              return [(start, end) for (start, end, n) in self.MEMORY_MAP if n == name][0]
 673          except IndexError:
 674              return None
 675  
 676      def connect(self, mode='default_reset', attempts=DEFAULT_CONNECT_ATTEMPTS, detecting=False, warnings=True):
 677          """ Try connecting repeatedly until successful, or giving up """
 678          if warnings and mode in ['no_reset', 'no_reset_no_sync']:
 679              print('WARNING: Pre-connection option "{}" was selected.'.format(mode),
 680                    'Connection may fail if the chip is not in bootloader or flasher stub mode.')
 681          print('Connecting...', end='')
 682          sys.stdout.flush()
 683          last_error = None
 684  
 685          usb_jtag_serial = (mode == 'usb_reset') or (self._get_pid() == self.USB_JTAG_SERIAL_PID)
 686  
 687          try:
 688              for _, extra_delay in zip(range(attempts) if attempts > 0 else itertools.count(), itertools.cycle((False, True))):
 689                  last_error = self._connect_attempt(mode=mode, usb_jtag_serial=usb_jtag_serial, extra_delay=extra_delay)
 690                  if last_error is None:
 691                      break
 692          finally:
 693              print('')  # end 'Connecting...' line
 694  
 695          if last_error is not None:
 696              raise FatalError('Failed to connect to {}: {}'
 697                               '\nFor troubleshooting steps visit: '
 698                               'https://docs.espressif.com/projects/esptool/en/latest/troubleshooting.html'.format(self.CHIP_NAME, last_error))
 699  
 700          if not detecting:
 701              try:
 702                  # check the date code registers match what we expect to see
 703                  chip_magic_value = self.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR)
 704                  if chip_magic_value not in self.CHIP_DETECT_MAGIC_VALUE:
 705                      actually = None
 706                      for cls in [ESP8266ROM, ESP32ROM, ESP32S2ROM, ESP32S3BETA2ROM, ESP32S3ROM,
 707                                  ESP32C3ROM, ESP32H2BETA1ROM, ESP32H2BETA2ROM, ESP32C2ROM, ESP32C6BETAROM]:
 708                          if chip_magic_value in cls.CHIP_DETECT_MAGIC_VALUE:
 709                              actually = cls
 710                              break
 711                      if warnings and actually is None:
 712                          print(("WARNING: This chip doesn't appear to be a %s (chip magic value 0x%08x). "
 713                                 "Probably it is unsupported by this version of esptool.") % (self.CHIP_NAME, chip_magic_value))
 714                      else:
 715                          raise FatalError("This chip is %s not %s. Wrong --chip argument?" % (actually.CHIP_NAME, self.CHIP_NAME))
 716              except UnsupportedCommandError:
 717                  self.secure_download_mode = True
 718              self._post_connect()
 719              self.check_chip_id()
 720  
 721      def _post_connect(self):
 722          """
 723          Additional initialization hook, may be overridden by the chip-specific class.
 724          Gets called after connect, and after auto-detection.
 725          """
 726          pass
 727  
 728      def read_reg(self, addr, timeout=DEFAULT_TIMEOUT):
 729          """ Read memory address in target """
 730          # we don't call check_command here because read_reg() function is called
 731          # when detecting chip type, and the way we check for success (STATUS_BYTES_LENGTH) is different
 732          # for different chip types (!)
 733          val, data = self.command(self.ESP_READ_REG, struct.pack('<I', addr), timeout=timeout)
 734          if byte(data, 0) != 0:
 735              raise FatalError.WithResult("Failed to read register address %08x" % addr, data)
 736          return val
 737  
 738      """ Write to memory address in target """
 739      def write_reg(self, addr, value, mask=0xFFFFFFFF, delay_us=0, delay_after_us=0):
 740          command = struct.pack('<IIII', addr, value, mask, delay_us)
 741          if delay_after_us > 0:
 742              # add a dummy write to a date register as an excuse to have a delay
 743              command += struct.pack('<IIII', self.UART_DATE_REG_ADDR, 0, 0, delay_after_us)
 744  
 745          return self.check_command("write target memory", self.ESP_WRITE_REG, command)
 746  
 747      def update_reg(self, addr, mask, new_val):
 748          """ Update register at 'addr', replace the bits masked out by 'mask'
 749          with new_val. new_val is shifted left to match the LSB of 'mask'
 750  
 751          Returns just-written value of register.
 752          """
 753          shift = _mask_to_shift(mask)
 754          val = self.read_reg(addr)
 755          val &= ~mask
 756          val |= (new_val << shift) & mask
 757          self.write_reg(addr, val)
 758  
 759          return val
 760  
 761      """ Start downloading an application image to RAM """
 762      def mem_begin(self, size, blocks, blocksize, offset):
 763          if self.IS_STUB:  # check we're not going to overwrite a running stub with this data
 764              stub = self.STUB_CODE
 765              load_start = offset
 766              load_end = offset + size
 767              for (start, end) in [(stub["data_start"], stub["data_start"] + len(stub["data"])),
 768                                   (stub["text_start"], stub["text_start"] + len(stub["text"]))]:
 769                  if load_start < end and load_end > start:
 770                      raise FatalError(("Software loader is resident at 0x%08x-0x%08x. "
 771                                        "Can't load binary at overlapping address range 0x%08x-0x%08x. "
 772                                        "Either change binary loading address, or use the --no-stub "
 773                                        "option to disable the software loader.") % (start, end, load_start, load_end))
 774  
 775          return self.check_command("enter RAM download mode", self.ESP_MEM_BEGIN,
 776                                    struct.pack('<IIII', size, blocks, blocksize, offset))
 777  
 778      """ Send a block of an image to RAM """
 779      def mem_block(self, data, seq):
 780          return self.check_command("write to target RAM", self.ESP_MEM_DATA,
 781                                    struct.pack('<IIII', len(data), seq, 0, 0) + data,
 782                                    self.checksum(data))
 783  
 784      """ Leave download mode and run the application """
 785      def mem_finish(self, entrypoint=0):
 786          # Sending ESP_MEM_END usually sends a correct response back, however sometimes
 787          # (with ROM loader) the executed code may reset the UART or change the baud rate
 788          # before the transmit FIFO is empty. So in these cases we set a short timeout and
 789          # ignore errors.
 790          timeout = DEFAULT_TIMEOUT if self.IS_STUB else MEM_END_ROM_TIMEOUT
 791          data = struct.pack('<II', int(entrypoint == 0), entrypoint)
 792          try:
 793              return self.check_command("leave RAM download mode", self.ESP_MEM_END,
 794                                        data=data, timeout=timeout)
 795          except FatalError:
 796              if self.IS_STUB:
 797                  raise
 798              pass
 799  
 800      """ Start downloading to Flash (performs an erase)
 801  
 802      Returns number of blocks (of size self.FLASH_WRITE_SIZE) to write.
 803      """
 804      def flash_begin(self, size, offset, begin_rom_encrypted=False):
 805          num_blocks = (size + self.FLASH_WRITE_SIZE - 1) // self.FLASH_WRITE_SIZE
 806          erase_size = self.get_erase_size(offset, size)
 807  
 808          t = time.time()
 809          if self.IS_STUB:
 810              timeout = DEFAULT_TIMEOUT
 811          else:
 812              timeout = timeout_per_mb(ERASE_REGION_TIMEOUT_PER_MB, size)  # ROM performs the erase up front
 813  
 814          params = struct.pack('<IIII', erase_size, num_blocks, self.FLASH_WRITE_SIZE, offset)
 815          if isinstance(self, (ESP32S2ROM, ESP32S3BETA2ROM, ESP32S3ROM, ESP32C3ROM,
 816                               ESP32C6BETAROM, ESP32H2BETA1ROM, ESP32C2ROM, ESP32H2BETA2ROM)) and not self.IS_STUB:
 817              params += struct.pack('<I', 1 if begin_rom_encrypted else 0)
 818          self.check_command("enter Flash download mode", self.ESP_FLASH_BEGIN,
 819                             params, timeout=timeout)
 820          if size != 0 and not self.IS_STUB:
 821              print("Took %.2fs to erase flash block" % (time.time() - t))
 822          return num_blocks
 823  
 824      def flash_block(self, data, seq, timeout=DEFAULT_TIMEOUT):
 825          """Write block to flash, retry if fail"""
 826          for attempts_left in range(WRITE_BLOCK_ATTEMPTS - 1, -1, -1):
 827              try:
 828                  self.check_command(
 829                      "write to target Flash after seq %d" % seq,
 830                      self.ESP_FLASH_DATA,
 831                      struct.pack("<IIII", len(data), seq, 0, 0) + data,
 832                      self.checksum(data),
 833                      timeout=timeout,
 834                  )
 835                  break
 836              except FatalError:
 837                  if attempts_left:
 838                      self.trace(
 839                          "Block write failed, "
 840                          "retrying with {} attempts left".format(attempts_left)
 841                      )
 842                  else:
 843                      raise
 844  
 845      def flash_encrypt_block(self, data, seq, timeout=DEFAULT_TIMEOUT):
 846          """Encrypt, write block to flash, retry if fail"""
 847          if isinstance(self, (ESP32S2ROM, ESP32C3ROM, ESP32S3ROM, ESP32H2BETA1ROM, ESP32C2ROM, ESP32H2BETA2ROM)) and not self.IS_STUB:
 848              # ROM support performs the encrypted writes via the normal write command,
 849              # triggered by flash_begin(begin_rom_encrypted=True)
 850              return self.flash_block(data, seq, timeout)
 851  
 852          for attempts_left in range(WRITE_BLOCK_ATTEMPTS - 1, -1, -1):
 853              try:
 854                  self.check_command(
 855                      "Write encrypted to target Flash after seq %d" % seq,
 856                      self.ESP_FLASH_ENCRYPT_DATA,
 857                      struct.pack("<IIII", len(data), seq, 0, 0) + data,
 858                      self.checksum(data),
 859                      timeout=timeout,
 860                  )
 861                  break
 862              except FatalError:
 863                  if attempts_left:
 864                      self.trace(
 865                          "Encrypted block write failed, "
 866                          "retrying with {} attempts left".format(attempts_left)
 867                      )
 868                  else:
 869                      raise
 870  
 871      """ Leave flash mode and run/reboot """
 872      def flash_finish(self, reboot=False):
 873          pkt = struct.pack('<I', int(not reboot))
 874          # stub sends a reply to this command
 875          self.check_command("leave Flash mode", self.ESP_FLASH_END, pkt)
 876  
 877      """ Run application code in flash """
 878      def run(self, reboot=False):
 879          # Fake flash begin immediately followed by flash end
 880          self.flash_begin(0, 0)
 881          self.flash_finish(reboot)
 882  
 883      """ Read SPI flash manufacturer and device id """
 884      def flash_id(self):
 885          SPIFLASH_RDID = 0x9F
 886          return self.run_spiflash_command(SPIFLASH_RDID, b"", 24)
 887  
 888      def get_security_info(self):
 889          res = self.check_command('get security info', self.ESP_GET_SECURITY_INFO, b'')
 890          esp32s2 = True if len(res) == 12 else False
 891          res = struct.unpack("<IBBBBBBBB" if esp32s2 else "<IBBBBBBBBII", res)
 892          return {
 893              "flags": res[0],
 894              "flash_crypt_cnt": res[1],
 895              "key_purposes": res[2:9],
 896              "chip_id": None if esp32s2 else res[9],
 897              "api_version": None if esp32s2 else res[10],
 898          }
 899  
 900      @esp32s3_or_newer_function_only
 901      def get_chip_id(self):
 902          res = self.check_command('get security info', self.ESP_GET_SECURITY_INFO, b'')
 903          res = struct.unpack("<IBBBBBBBBI", res[:16])  # 4b flags, 1b flash_crypt_cnt, 7*1b key_purposes, 4b chip_id
 904          chip_id = res[9]  # 2/4 status bytes invariant
 905          return chip_id
 906  
 907      @classmethod
 908      def parse_flash_size_arg(cls, arg):
 909          try:
 910              return cls.FLASH_SIZES[arg]
 911          except KeyError:
 912              raise FatalError("Flash size '%s' is not supported by this chip type. Supported sizes: %s"
 913                               % (arg, ", ".join(cls.FLASH_SIZES.keys())))
 914  
 915      @classmethod
 916      def parse_flash_freq_arg(cls, arg):
 917          try:
 918              return cls.FLASH_FREQUENCY[arg]
 919          except KeyError:
 920              raise FatalError("Flash frequency '%s' is not supported by this chip type. Supported frequencies: %s"
 921                               % (arg, ", ".join(cls.FLASH_FREQUENCY.keys())))
 922  
 923      def run_stub(self, stub=None):
 924          if stub is None:
 925              stub = self.STUB_CODE
 926  
 927          if self.sync_stub_detected:
 928              print("Stub is already running. No upload is necessary.")
 929              return self.STUB_CLASS(self)
 930  
 931          # Upload
 932          print("Uploading stub...")
 933          for field in ['text', 'data']:
 934              if field in stub:
 935                  offs = stub[field + "_start"]
 936                  length = len(stub[field])
 937                  blocks = (length + self.ESP_RAM_BLOCK - 1) // self.ESP_RAM_BLOCK
 938                  self.mem_begin(length, blocks, self.ESP_RAM_BLOCK, offs)
 939                  for seq in range(blocks):
 940                      from_offs = seq * self.ESP_RAM_BLOCK
 941                      to_offs = from_offs + self.ESP_RAM_BLOCK
 942                      self.mem_block(stub[field][from_offs:to_offs], seq)
 943          print("Running stub...")
 944          self.mem_finish(stub['entry'])
 945  
 946          p = self.read()
 947          if p != b'OHAI':
 948              raise FatalError("Failed to start stub. Unexpected response: %s" % p)
 949          print("Stub running...")
 950          return self.STUB_CLASS(self)
 951  
 952      @stub_and_esp32_function_only
 953      def flash_defl_begin(self, size, compsize, offset):
 954          """ Start downloading compressed data to Flash (performs an erase)
 955  
 956          Returns number of blocks (size self.FLASH_WRITE_SIZE) to write.
 957          """
 958          num_blocks = (compsize + self.FLASH_WRITE_SIZE - 1) // self.FLASH_WRITE_SIZE
 959          erase_blocks = (size + self.FLASH_WRITE_SIZE - 1) // self.FLASH_WRITE_SIZE
 960  
 961          t = time.time()
 962          if self.IS_STUB:
 963              write_size = size  # stub expects number of bytes here, manages erasing internally
 964              timeout = DEFAULT_TIMEOUT
 965          else:
 966              write_size = erase_blocks * self.FLASH_WRITE_SIZE  # ROM expects rounded up to erase block size
 967              timeout = timeout_per_mb(ERASE_REGION_TIMEOUT_PER_MB, write_size)  # ROM performs the erase up front
 968          print("Compressed %d bytes to %d..." % (size, compsize))
 969          params = struct.pack('<IIII', write_size, num_blocks, self.FLASH_WRITE_SIZE, offset)
 970          if isinstance(self, (ESP32S2ROM, ESP32S3BETA2ROM, ESP32S3ROM, ESP32C3ROM,
 971                               ESP32C6BETAROM, ESP32H2BETA1ROM, ESP32C2ROM, ESP32H2BETA2ROM)) and not self.IS_STUB:
 972              params += struct.pack('<I', 0)  # extra param is to enter encrypted flash mode via ROM (not supported currently)
 973          self.check_command("enter compressed flash mode", self.ESP_FLASH_DEFL_BEGIN, params, timeout=timeout)
 974          if size != 0 and not self.IS_STUB:
 975              # (stub erases as it writes, but ROM loaders erase on begin)
 976              print("Took %.2fs to erase flash block" % (time.time() - t))
 977          return num_blocks
 978  
 979      @stub_and_esp32_function_only
 980      def flash_defl_block(self, data, seq, timeout=DEFAULT_TIMEOUT):
 981          """Write block to flash, send compressed, retry if fail"""
 982          for attempts_left in range(WRITE_BLOCK_ATTEMPTS - 1, -1, -1):
 983              try:
 984                  self.check_command(
 985                      "write compressed data to flash after seq %d" % seq,
 986                      self.ESP_FLASH_DEFL_DATA,
 987                      struct.pack("<IIII", len(data), seq, 0, 0) + data,
 988                      self.checksum(data),
 989                      timeout=timeout,
 990                  )
 991                  break
 992              except FatalError:
 993                  if attempts_left:
 994                      self.trace(
 995                          "Compressed block write failed, "
 996                          "retrying with {} attempts left".format(attempts_left)
 997                      )
 998                  else:
 999                      raise
1000  
1001      """ Leave compressed flash mode and run/reboot """
1002      @stub_and_esp32_function_only
1003      def flash_defl_finish(self, reboot=False):
1004          if not reboot and not self.IS_STUB:
1005              # skip sending flash_finish to ROM loader, as this
1006              # exits the bootloader. Stub doesn't do this.
1007              return
1008          pkt = struct.pack('<I', int(not reboot))
1009          self.check_command("leave compressed flash mode", self.ESP_FLASH_DEFL_END, pkt)
1010          self.in_bootloader = False
1011  
1012      @stub_and_esp32_function_only
1013      def flash_md5sum(self, addr, size):
1014          # the MD5 command returns additional bytes in the standard
1015          # command reply slot
1016          timeout = timeout_per_mb(MD5_TIMEOUT_PER_MB, size)
1017          res = self.check_command('calculate md5sum', self.ESP_SPI_FLASH_MD5, struct.pack('<IIII', addr, size, 0, 0),
1018                                   timeout=timeout)
1019  
1020          if len(res) == 32:
1021              return res.decode("utf-8")  # already hex formatted
1022          elif len(res) == 16:
1023              return hexify(res).lower()
1024          else:
1025              raise FatalError("MD5Sum command returned unexpected result: %r" % res)
1026  
1027      @stub_and_esp32_function_only
1028      def change_baud(self, baud):
1029          print("Changing baud rate to %d" % baud)
1030          # stub takes the new baud rate and the old one
1031          second_arg = self._port.baudrate if self.IS_STUB else 0
1032          self.command(self.ESP_CHANGE_BAUDRATE, struct.pack('<II', baud, second_arg))
1033          print("Changed.")
1034          self._set_port_baudrate(baud)
1035          time.sleep(0.05)  # get rid of crap sent during baud rate change
1036          self.flush_input()
1037  
1038      @stub_function_only
1039      def erase_flash(self):
1040          # depending on flash chip model the erase may take this long (maybe longer!)
1041          self.check_command("erase flash", self.ESP_ERASE_FLASH,
1042                             timeout=CHIP_ERASE_TIMEOUT)
1043  
1044      @stub_function_only
1045      def erase_region(self, offset, size):
1046          if offset % self.FLASH_SECTOR_SIZE != 0:
1047              raise FatalError("Offset to erase from must be a multiple of 4096")
1048          if size % self.FLASH_SECTOR_SIZE != 0:
1049              raise FatalError("Size of data to erase must be a multiple of 4096")
1050          timeout = timeout_per_mb(ERASE_REGION_TIMEOUT_PER_MB, size)
1051          self.check_command("erase region", self.ESP_ERASE_REGION, struct.pack('<II', offset, size), timeout=timeout)
1052  
1053      def read_flash_slow(self, offset, length, progress_fn):
1054          raise NotImplementedInROMError(self, self.read_flash_slow)
1055  
1056      def read_flash(self, offset, length, progress_fn=None):
1057          if not self.IS_STUB:
1058              return self.read_flash_slow(offset, length, progress_fn)  # ROM-only routine
1059  
1060          # issue a standard bootloader command to trigger the read
1061          self.check_command("read flash", self.ESP_READ_FLASH,
1062                             struct.pack('<IIII',
1063                                         offset,
1064                                         length,
1065                                         self.FLASH_SECTOR_SIZE,
1066                                         64))
1067          # now we expect (length // block_size) SLIP frames with the data
1068          data = b''
1069          while len(data) < length:
1070              p = self.read()
1071              data += p
1072              if len(data) < length and len(p) < self.FLASH_SECTOR_SIZE:
1073                  raise FatalError('Corrupt data, expected 0x%x bytes but received 0x%x bytes' % (self.FLASH_SECTOR_SIZE, len(p)))
1074              self.write(struct.pack('<I', len(data)))
1075              if progress_fn and (len(data) % 1024 == 0 or len(data) == length):
1076                  progress_fn(len(data), length)
1077          if progress_fn:
1078              progress_fn(len(data), length)
1079          if len(data) > length:
1080              raise FatalError('Read more than expected')
1081  
1082          digest_frame = self.read()
1083          if len(digest_frame) != 16:
1084              raise FatalError('Expected digest, got: %s' % hexify(digest_frame))
1085          expected_digest = hexify(digest_frame).upper()
1086          digest = hashlib.md5(data).hexdigest().upper()
1087          if digest != expected_digest:
1088              raise FatalError('Digest mismatch: expected %s, got %s' % (expected_digest, digest))
1089          return data
1090  
1091      def flash_spi_attach(self, hspi_arg):
1092          """Send SPI attach command to enable the SPI flash pins
1093  
1094          ESP8266 ROM does this when you send flash_begin, ESP32 ROM
1095          has it as a SPI command.
1096          """
1097          # last 3 bytes in ESP_SPI_ATTACH argument are reserved values
1098          arg = struct.pack('<I', hspi_arg)
1099          if not self.IS_STUB:
1100              # ESP32 ROM loader takes additional 'is legacy' arg, which is not
1101              # currently supported in the stub loader or esptool.py (as it's not usually needed.)
1102              is_legacy = 0
1103              arg += struct.pack('BBBB', is_legacy, 0, 0, 0)
1104          self.check_command("configure SPI flash pins", ESP32ROM.ESP_SPI_ATTACH, arg)
1105  
1106      def flash_set_parameters(self, size):
1107          """Tell the ESP bootloader the parameters of the chip
1108  
1109          Corresponds to the "flashchip" data structure that the ROM
1110          has in RAM.
1111  
1112          'size' is in bytes.
1113  
1114          All other flash parameters are currently hardcoded (on ESP8266
1115          these are mostly ignored by ROM code, on ESP32 I'm not sure.)
1116          """
1117          fl_id = 0
1118          total_size = size
1119          block_size = 64 * 1024
1120          sector_size = 4 * 1024
1121          page_size = 256
1122          status_mask = 0xffff
1123          self.check_command("set SPI params", ESP32ROM.ESP_SPI_SET_PARAMS,
1124                             struct.pack('<IIIIII', fl_id, total_size, block_size, sector_size, page_size, status_mask))
1125  
1126      def run_spiflash_command(self, spiflash_command, data=b"", read_bits=0, addr=None, addr_len=0, dummy_len=0):
1127          """Run an arbitrary SPI flash command.
1128  
1129          This function uses the "USR_COMMAND" functionality in the ESP
1130          SPI hardware, rather than the precanned commands supported by
1131          hardware. So the value of spiflash_command is an actual command
1132          byte, sent over the wire.
1133  
1134          After writing command byte, writes 'data' to MOSI and then
1135          reads back 'read_bits' of reply on MISO. Result is a number.
1136          """
1137  
1138          # SPI_USR register flags
1139          SPI_USR_COMMAND = (1 << 31)
1140          SPI_USR_ADDR    = (1 << 30)
1141          SPI_USR_DUMMY   = (1 << 29)
1142          SPI_USR_MISO    = (1 << 28)
1143          SPI_USR_MOSI    = (1 << 27)
1144  
1145          # SPI registers, base address differs ESP32* vs 8266
1146          base = self.SPI_REG_BASE
1147          SPI_CMD_REG       = base + 0x00
1148          SPI_ADDR_REG      = base + 0x04
1149          SPI_USR_REG       = base + self.SPI_USR_OFFS
1150          SPI_USR1_REG      = base + self.SPI_USR1_OFFS
1151          SPI_USR2_REG      = base + self.SPI_USR2_OFFS
1152          SPI_W0_REG        = base + self.SPI_W0_OFFS
1153  
1154          # following two registers are ESP32 and later chips only
1155          if self.SPI_MOSI_DLEN_OFFS is not None:
1156              # ESP32 and later chips have a more sophisticated way to set up "user" commands
1157              def set_data_lengths(mosi_bits, miso_bits):
1158                  SPI_MOSI_DLEN_REG = base + self.SPI_MOSI_DLEN_OFFS
1159                  SPI_MISO_DLEN_REG = base + self.SPI_MISO_DLEN_OFFS
1160                  if mosi_bits > 0:
1161                      self.write_reg(SPI_MOSI_DLEN_REG, mosi_bits - 1)
1162                  if miso_bits > 0:
1163                      self.write_reg(SPI_MISO_DLEN_REG, miso_bits - 1)
1164                  flags = 0
1165                  if dummy_len > 0:
1166                      flags |= (dummy_len - 1)
1167                  if addr_len > 0:
1168                      flags |= (addr_len - 1) << SPI_USR_ADDR_LEN_SHIFT
1169                  if flags:
1170                      self.write_reg(SPI_USR1_REG, flags)
1171          else:
1172              def set_data_lengths(mosi_bits, miso_bits):
1173                  SPI_DATA_LEN_REG = SPI_USR1_REG
1174                  SPI_MOSI_BITLEN_S = 17
1175                  SPI_MISO_BITLEN_S = 8
1176                  mosi_mask = 0 if (mosi_bits == 0) else (mosi_bits - 1)
1177                  miso_mask = 0 if (miso_bits == 0) else (miso_bits - 1)
1178                  flags = (miso_mask << SPI_MISO_BITLEN_S) | (mosi_mask << SPI_MOSI_BITLEN_S)
1179                  if dummy_len > 0:
1180                      flags |= (dummy_len - 1)
1181                  if addr_len > 0:
1182                      flags |= (addr_len - 1) << SPI_USR_ADDR_LEN_SHIFT
1183                  self.write_reg(SPI_DATA_LEN_REG, flags)
1184  
1185          # SPI peripheral "command" bitmasks for SPI_CMD_REG
1186          SPI_CMD_USR  = (1 << 18)
1187  
1188          # shift values
1189          SPI_USR2_COMMAND_LEN_SHIFT = 28
1190          SPI_USR_ADDR_LEN_SHIFT = 26
1191  
1192          if read_bits > 32:
1193              raise FatalError("Reading more than 32 bits back from a SPI flash operation is unsupported")
1194          if len(data) > 64:
1195              raise FatalError("Writing more than 64 bytes of data with one SPI command is unsupported")
1196  
1197          data_bits = len(data) * 8
1198          old_spi_usr = self.read_reg(SPI_USR_REG)
1199          old_spi_usr2 = self.read_reg(SPI_USR2_REG)
1200          flags = SPI_USR_COMMAND
1201          if read_bits > 0:
1202              flags |= SPI_USR_MISO
1203          if data_bits > 0:
1204              flags |= SPI_USR_MOSI
1205          if addr_len > 0:
1206              flags |= SPI_USR_ADDR
1207          if dummy_len > 0:
1208              flags |= SPI_USR_DUMMY
1209          set_data_lengths(data_bits, read_bits)
1210          self.write_reg(SPI_USR_REG, flags)
1211          self.write_reg(SPI_USR2_REG,
1212                         (7 << SPI_USR2_COMMAND_LEN_SHIFT) | spiflash_command)
1213          if addr and addr_len > 0:
1214              self.write_reg(SPI_ADDR_REG, addr)
1215          if data_bits == 0:
1216              self.write_reg(SPI_W0_REG, 0)  # clear data register before we read it
1217          else:
1218              data = pad_to(data, 4, b'\00')  # pad to 32-bit multiple
1219              words = struct.unpack("I" * (len(data) // 4), data)
1220              next_reg = SPI_W0_REG
1221              for word in words:
1222                  self.write_reg(next_reg, word)
1223                  next_reg += 4
1224          self.write_reg(SPI_CMD_REG, SPI_CMD_USR)
1225  
1226          def wait_done():
1227              for _ in range(10):
1228                  if (self.read_reg(SPI_CMD_REG) & SPI_CMD_USR) == 0:
1229                      return
1230              raise FatalError("SPI command did not complete in time")
1231          wait_done()
1232  
1233          status = self.read_reg(SPI_W0_REG)
1234          # restore some SPI controller registers
1235          self.write_reg(SPI_USR_REG, old_spi_usr)
1236          self.write_reg(SPI_USR2_REG, old_spi_usr2)
1237          return status
1238  
1239      def read_spiflash_sfdp(self, addr, read_bits):
1240          CMD_RDSFDP = 0x5A
1241          return self.run_spiflash_command(CMD_RDSFDP, read_bits=read_bits, addr=addr, addr_len=24, dummy_len=8)
1242  
1243      def read_status(self, num_bytes=2):
1244          """Read up to 24 bits (num_bytes) of SPI flash status register contents
1245          via RDSR, RDSR2, RDSR3 commands
1246  
1247          Not all SPI flash supports all three commands. The upper 1 or 2
1248          bytes may be 0xFF.
1249          """
1250          SPIFLASH_RDSR  = 0x05
1251          SPIFLASH_RDSR2 = 0x35
1252          SPIFLASH_RDSR3 = 0x15
1253  
1254          status = 0
1255          shift = 0
1256          for cmd in [SPIFLASH_RDSR, SPIFLASH_RDSR2, SPIFLASH_RDSR3][0:num_bytes]:
1257              status += self.run_spiflash_command(cmd, read_bits=8) << shift
1258              shift += 8
1259          return status
1260  
1261      def write_status(self, new_status, num_bytes=2, set_non_volatile=False):
1262          """Write up to 24 bits (num_bytes) of new status register
1263  
1264          num_bytes can be 1, 2 or 3.
1265  
1266          Not all flash supports the additional commands to write the
1267          second and third byte of the status register. When writing 2
1268          bytes, esptool also sends a 16-byte WRSR command (as some
1269          flash types use this instead of WRSR2.)
1270  
1271          If the set_non_volatile flag is set, non-volatile bits will
1272          be set as well as volatile ones (WREN used instead of WEVSR).
1273  
1274          """
1275          SPIFLASH_WRSR = 0x01
1276          SPIFLASH_WRSR2 = 0x31
1277          SPIFLASH_WRSR3 = 0x11
1278          SPIFLASH_WEVSR = 0x50
1279          SPIFLASH_WREN = 0x06
1280          SPIFLASH_WRDI = 0x04
1281  
1282          enable_cmd = SPIFLASH_WREN if set_non_volatile else SPIFLASH_WEVSR
1283  
1284          # try using a 16-bit WRSR (not supported by all chips)
1285          # this may be redundant, but shouldn't hurt
1286          if num_bytes == 2:
1287              self.run_spiflash_command(enable_cmd)
1288              self.run_spiflash_command(SPIFLASH_WRSR, struct.pack("<H", new_status))
1289  
1290          # also try using individual commands (also not supported by all chips for num_bytes 2 & 3)
1291          for cmd in [SPIFLASH_WRSR, SPIFLASH_WRSR2, SPIFLASH_WRSR3][0:num_bytes]:
1292              self.run_spiflash_command(enable_cmd)
1293              self.run_spiflash_command(cmd, struct.pack("B", new_status & 0xFF))
1294              new_status >>= 8
1295  
1296          self.run_spiflash_command(SPIFLASH_WRDI)
1297  
1298      def get_crystal_freq(self):
1299          # Figure out the crystal frequency from the UART clock divider
1300          # Returns a normalized value in integer MHz (40 or 26 are the only supported values)
1301          #
1302          # The logic here is:
1303          # - We know that our baud rate and the ESP UART baud rate are roughly the same, or we couldn't communicate
1304          # - We can read the UART clock divider register to know how the ESP derives this from the APB bus frequency
1305          # - Multiplying these two together gives us the bus frequency which is either the crystal frequency (ESP32)
1306          #   or double the crystal frequency (ESP8266). See the self.XTAL_CLK_DIVIDER parameter for this factor.
1307          uart_div = self.read_reg(self.UART_CLKDIV_REG) & self.UART_CLKDIV_MASK
1308          est_xtal = (self._port.baudrate * uart_div) / 1e6 / self.XTAL_CLK_DIVIDER
1309          norm_xtal = 40 if est_xtal > 33 else 26
1310          if abs(norm_xtal - est_xtal) > 1:
1311              print("WARNING: Detected crystal freq %.2fMHz is quite different to normalized freq %dMHz. Unsupported crystal in use?" % (est_xtal, norm_xtal))
1312          return norm_xtal
1313  
1314      def hard_reset(self):
1315          print('Hard resetting via RTS pin...')
1316          self._setRTS(True)  # EN->LOW
1317          time.sleep(0.1)
1318          self._setRTS(False)
1319  
1320      def soft_reset(self, stay_in_bootloader):
1321          if not self.IS_STUB:
1322              if stay_in_bootloader:
1323                  return  # ROM bootloader is already in bootloader!
1324              else:
1325                  # 'run user code' is as close to a soft reset as we can do
1326                  self.flash_begin(0, 0)
1327                  self.flash_finish(False)
1328          else:
1329              if stay_in_bootloader:
1330                  # soft resetting from the stub loader
1331                  # will re-load the ROM bootloader
1332                  self.flash_begin(0, 0)
1333                  self.flash_finish(True)
1334              elif self.CHIP_NAME != "ESP8266":
1335                  raise FatalError("Soft resetting is currently only supported on ESP8266")
1336              else:
1337                  # running user code from stub loader requires some hacks
1338                  # in the stub loader
1339                  self.command(self.ESP_RUN_USER_CODE, wait_response=False)
1340  
1341      def check_chip_id(self):
1342          try:
1343              chip_id = self.get_chip_id()
1344              if chip_id != self.IMAGE_CHIP_ID:
1345                  print("WARNING: Chip ID {} ({}) doesn't match expected Chip ID {}. esptool may not work correctly."
1346                        .format(chip_id, self.UNSUPPORTED_CHIPS.get(chip_id, 'Unknown'), self.IMAGE_CHIP_ID))
1347                  # Try to flash anyways by disabling stub
1348                  self.stub_is_disabled = True
1349          except NotImplementedInROMError:
1350              pass
1351  
1352  
1353  class ESP8266ROM(ESPLoader):
1354      """ Access class for ESP8266 ROM bootloader
1355      """
1356      CHIP_NAME = "ESP8266"
1357      IS_STUB = False
1358  
1359      CHIP_DETECT_MAGIC_VALUE = [0xfff0c101]
1360  
1361      # OTP ROM addresses
1362      ESP_OTP_MAC0    = 0x3ff00050
1363      ESP_OTP_MAC1    = 0x3ff00054
1364      ESP_OTP_MAC3    = 0x3ff0005c
1365  
1366      SPI_REG_BASE    = 0x60000200
1367      SPI_USR_OFFS    = 0x1c
1368      SPI_USR1_OFFS   = 0x20
1369      SPI_USR2_OFFS   = 0x24
1370      SPI_MOSI_DLEN_OFFS = None
1371      SPI_MISO_DLEN_OFFS = None
1372      SPI_W0_OFFS     = 0x40
1373  
1374      UART_CLKDIV_REG = 0x60000014
1375  
1376      XTAL_CLK_DIVIDER = 2
1377  
1378      FLASH_SIZES = {
1379          '512KB': 0x00,
1380          '256KB': 0x10,
1381          '1MB': 0x20,
1382          '2MB': 0x30,
1383          '4MB': 0x40,
1384          '2MB-c1': 0x50,
1385          '4MB-c1': 0x60,
1386          '8MB': 0x80,
1387          '16MB': 0x90,
1388      }
1389  
1390      FLASH_FREQUENCY = {
1391          '80m': 0xf,
1392          '40m': 0x0,
1393          '26m': 0x1,
1394          '20m': 0x2,
1395      }
1396  
1397      BOOTLOADER_FLASH_OFFSET = 0
1398  
1399      MEMORY_MAP = [[0x3FF00000, 0x3FF00010, "DPORT"],
1400                    [0x3FFE8000, 0x40000000, "DRAM"],
1401                    [0x40100000, 0x40108000, "IRAM"],
1402                    [0x40201010, 0x402E1010, "IROM"]]
1403  
1404      def get_efuses(self):
1405          # Return the 128 bits of ESP8266 efuse as a single Python integer
1406          result = self.read_reg(0x3ff0005c) << 96
1407          result |= self.read_reg(0x3ff00058) << 64
1408          result |= self.read_reg(0x3ff00054) << 32
1409          result |= self.read_reg(0x3ff00050)
1410          return result
1411  
1412      def _get_flash_size(self, efuses):
1413          # rX_Y = EFUSE_DATA_OUTX[Y]
1414          r0_4 = (efuses & (1 << 4)) != 0
1415          r3_25 = (efuses & (1 << 121)) != 0
1416          r3_26 = (efuses & (1 << 122)) != 0
1417          r3_27 = (efuses & (1 << 123)) != 0
1418  
1419          if r0_4 and not r3_25:
1420              if not r3_27 and not r3_26:
1421                  return 1
1422              elif not r3_27 and r3_26:
1423                  return 2
1424          if not r0_4 and r3_25:
1425              if not r3_27 and not r3_26:
1426                  return 2
1427              elif not r3_27 and r3_26:
1428                  return 4
1429          return -1
1430  
1431      def get_chip_description(self):
1432          efuses = self.get_efuses()
1433          is_8285 = (efuses & ((1 << 4) | 1 << 80)) != 0  # One or the other efuse bit is set for ESP8285
1434          if is_8285:
1435              flash_size = self._get_flash_size(efuses)
1436              max_temp = (efuses & (1 << 5)) != 0  # This efuse bit identifies the max flash temperature
1437              chip_name = {
1438                  1: "ESP8285H08" if max_temp else "ESP8285N08",
1439                  2: "ESP8285H16" if max_temp else "ESP8285N16"
1440              }.get(flash_size, "ESP8285")
1441              return chip_name
1442          return "ESP8266EX"
1443  
1444      def get_chip_features(self):
1445          features = ["WiFi"]
1446          if "ESP8285" in self.get_chip_description():
1447              features += ["Embedded Flash"]
1448          return features
1449  
1450      def flash_spi_attach(self, hspi_arg):
1451          if self.IS_STUB:
1452              super(ESP8266ROM, self).flash_spi_attach(hspi_arg)
1453          else:
1454              # ESP8266 ROM has no flash_spi_attach command in serial protocol,
1455              # but flash_begin will do it
1456              self.flash_begin(0, 0)
1457  
1458      def flash_set_parameters(self, size):
1459          # not implemented in ROM, but OK to silently skip for ROM
1460          if self.IS_STUB:
1461              super(ESP8266ROM, self).flash_set_parameters(size)
1462  
1463      def chip_id(self):
1464          """ Read Chip ID from efuse - the equivalent of the SDK system_get_chip_id() function """
1465          id0 = self.read_reg(self.ESP_OTP_MAC0)
1466          id1 = self.read_reg(self.ESP_OTP_MAC1)
1467          return (id0 >> 24) | ((id1 & MAX_UINT24) << 8)
1468  
1469      def read_mac(self):
1470          """ Read MAC from OTP ROM """
1471          mac0 = self.read_reg(self.ESP_OTP_MAC0)
1472          mac1 = self.read_reg(self.ESP_OTP_MAC1)
1473          mac3 = self.read_reg(self.ESP_OTP_MAC3)
1474          if (mac3 != 0):
1475              oui = ((mac3 >> 16) & 0xff, (mac3 >> 8) & 0xff, mac3 & 0xff)
1476          elif ((mac1 >> 16) & 0xff) == 0:
1477              oui = (0x18, 0xfe, 0x34)
1478          elif ((mac1 >> 16) & 0xff) == 1:
1479              oui = (0xac, 0xd0, 0x74)
1480          else:
1481              raise FatalError("Unknown OUI")
1482          return oui + ((mac1 >> 8) & 0xff, mac1 & 0xff, (mac0 >> 24) & 0xff)
1483  
1484      def get_erase_size(self, offset, size):
1485          """ Calculate an erase size given a specific size in bytes.
1486  
1487          Provides a workaround for the bootloader erase bug."""
1488  
1489          sectors_per_block = 16
1490          sector_size = self.FLASH_SECTOR_SIZE
1491          num_sectors = (size + sector_size - 1) // sector_size
1492          start_sector = offset // sector_size
1493  
1494          head_sectors = sectors_per_block - (start_sector % sectors_per_block)
1495          if num_sectors < head_sectors:
1496              head_sectors = num_sectors
1497  
1498          if num_sectors < 2 * head_sectors:
1499              return (num_sectors + 1) // 2 * sector_size
1500          else:
1501              return (num_sectors - head_sectors) * sector_size
1502  
1503      def override_vddsdio(self, new_voltage):
1504          raise NotImplementedInROMError("Overriding VDDSDIO setting only applies to ESP32")
1505  
1506  
1507  class ESP8266StubLoader(ESP8266ROM):
1508      """ Access class for ESP8266 stub loader, runs on top of ROM.
1509      """
1510      FLASH_WRITE_SIZE = 0x4000  # matches MAX_WRITE_BLOCK in stub_loader.c
1511      IS_STUB = True
1512  
1513      def __init__(self, rom_loader):
1514          self.secure_download_mode = rom_loader.secure_download_mode
1515          self._port = rom_loader._port
1516          self._trace_enabled = rom_loader._trace_enabled
1517          self.flush_input()  # resets _slip_reader
1518  
1519      def get_erase_size(self, offset, size):
1520          return size  # stub doesn't have same size bug as ROM loader
1521  
1522  
1523  ESP8266ROM.STUB_CLASS = ESP8266StubLoader
1524  
1525  
1526  class ESP32ROM(ESPLoader):
1527      """Access class for ESP32 ROM bootloader
1528  
1529      """
1530      CHIP_NAME = "ESP32"
1531      IMAGE_CHIP_ID = 0
1532      IS_STUB = False
1533  
1534      FPGA_SLOW_BOOT = True
1535  
1536      CHIP_DETECT_MAGIC_VALUE = [0x00f01d83]
1537  
1538      IROM_MAP_START = 0x400d0000
1539      IROM_MAP_END   = 0x40400000
1540  
1541      DROM_MAP_START = 0x3F400000
1542      DROM_MAP_END   = 0x3F800000
1543  
1544      # ESP32 uses a 4 byte status reply
1545      STATUS_BYTES_LENGTH = 4
1546  
1547      SPI_REG_BASE   = 0x3ff42000
1548      SPI_USR_OFFS    = 0x1c
1549      SPI_USR1_OFFS   = 0x20
1550      SPI_USR2_OFFS   = 0x24
1551      SPI_MOSI_DLEN_OFFS = 0x28
1552      SPI_MISO_DLEN_OFFS = 0x2c
1553      EFUSE_RD_REG_BASE = 0x3ff5a000
1554  
1555      EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE + 0x18
1556      EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = (1 << 7)  # EFUSE_RD_DISABLE_DL_ENCRYPT
1557  
1558      DR_REG_SYSCON_BASE = 0x3ff66000
1559      APB_CTL_DATE_ADDR = DR_REG_SYSCON_BASE + 0x7C
1560      APB_CTL_DATE_V = 0x1
1561      APB_CTL_DATE_S = 31
1562  
1563      SPI_W0_OFFS = 0x80
1564  
1565      UART_CLKDIV_REG = 0x3ff40014
1566  
1567      XTAL_CLK_DIVIDER = 1
1568  
1569      FLASH_SIZES = {
1570          '1MB': 0x00,
1571          '2MB': 0x10,
1572          '4MB': 0x20,
1573          '8MB': 0x30,
1574          '16MB': 0x40,
1575          '32MB': 0x50,
1576          '64MB': 0x60,
1577          '128MB': 0x70
1578      }
1579  
1580      FLASH_FREQUENCY = {
1581          '80m': 0xf,
1582          '40m': 0x0,
1583          '26m': 0x1,
1584          '20m': 0x2,
1585      }
1586  
1587      BOOTLOADER_FLASH_OFFSET = 0x1000
1588  
1589      OVERRIDE_VDDSDIO_CHOICES = ["1.8V", "1.9V", "OFF"]
1590  
1591      MEMORY_MAP = [[0x00000000, 0x00010000, "PADDING"],
1592                    [0x3F400000, 0x3F800000, "DROM"],
1593                    [0x3F800000, 0x3FC00000, "EXTRAM_DATA"],
1594                    [0x3FF80000, 0x3FF82000, "RTC_DRAM"],
1595                    [0x3FF90000, 0x40000000, "BYTE_ACCESSIBLE"],
1596                    [0x3FFAE000, 0x40000000, "DRAM"],
1597                    [0x3FFE0000, 0x3FFFFFFC, "DIRAM_DRAM"],
1598                    [0x40000000, 0x40070000, "IROM"],
1599                    [0x40070000, 0x40078000, "CACHE_PRO"],
1600                    [0x40078000, 0x40080000, "CACHE_APP"],
1601                    [0x40080000, 0x400A0000, "IRAM"],
1602                    [0x400A0000, 0x400BFFFC, "DIRAM_IRAM"],
1603                    [0x400C0000, 0x400C2000, "RTC_IRAM"],
1604                    [0x400D0000, 0x40400000, "IROM"],
1605                    [0x50000000, 0x50002000, "RTC_DATA"]]
1606  
1607      FLASH_ENCRYPTED_WRITE_ALIGN = 32
1608  
1609      """ Try to read the BLOCK1 (encryption key) and check if it is valid """
1610  
1611      def is_flash_encryption_key_valid(self):
1612  
1613          """ Bit 0 of efuse_rd_disable[3:0] is mapped to BLOCK1
1614          this bit is at position 16 in EFUSE_BLK0_RDATA0_REG """
1615          word0 = self.read_efuse(0)
1616          rd_disable = (word0 >> 16) & 0x1
1617  
1618          # reading of BLOCK1 is NOT ALLOWED so we assume valid key is programmed
1619          if rd_disable:
1620              return True
1621          else:
1622              # reading of BLOCK1 is ALLOWED so we will read and verify for non-zero.
1623              # When ESP32 has not generated AES/encryption key in BLOCK1, the contents will be readable and 0.
1624              # If the flash encryption is enabled it is expected to have a valid non-zero key. We break out on
1625              # first occurance of non-zero value
1626              key_word = [0] * 7
1627              for i in range(len(key_word)):
1628                  key_word[i] = self.read_efuse(14 + i)
1629                  # key is non-zero so break & return
1630                  if key_word[i] != 0:
1631                      return True
1632              return False
1633  
1634      def get_flash_crypt_config(self):
1635          """ For flash encryption related commands we need to make sure
1636          user has programmed all the relevant efuse correctly so before
1637          writing encrypted write_flash_encrypt esptool will verify the values
1638          of flash_crypt_config to be non zero if they are not read
1639          protected. If the values are zero a warning will be printed
1640  
1641          bit 3 in efuse_rd_disable[3:0] is mapped to flash_crypt_config
1642          this bit is at position 19 in EFUSE_BLK0_RDATA0_REG """
1643          word0 = self.read_efuse(0)
1644          rd_disable = (word0 >> 19) & 0x1
1645  
1646          if rd_disable == 0:
1647              """ we can read the flash_crypt_config efuse value
1648              so go & read it (EFUSE_BLK0_RDATA5_REG[31:28]) """
1649              word5 = self.read_efuse(5)
1650              word5 = (word5 >> 28) & 0xF
1651              return word5
1652          else:
1653              # if read of the efuse is disabled we assume it is set correctly
1654              return 0xF
1655  
1656      def get_encrypted_download_disabled(self):
1657          if self.read_reg(self.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG) & self.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT:
1658              return True
1659          else:
1660              return False
1661  
1662      def get_pkg_version(self):
1663          word3 = self.read_efuse(3)
1664          pkg_version = (word3 >> 9) & 0x07
1665          pkg_version += ((word3 >> 2) & 0x1) << 3
1666          return pkg_version
1667  
1668      # Returns new version format based on major and minor versions
1669      def get_chip_full_revision(self):
1670          return self.get_major_chip_version() * 100 + self.get_minor_chip_version()
1671  
1672      # Returns old version format (ECO number). Use the new format get_chip_full_revision().
1673      def get_chip_revision(self):
1674          return self.get_major_chip_version()
1675  
1676      def get_minor_chip_version(self):
1677          return (self.read_efuse(5) >> 24) & 0x3
1678  
1679      def get_major_chip_version(self):
1680          rev_bit0 = (self.read_efuse(3) >> 15) & 0x1
1681          rev_bit1 = (self.read_efuse(5) >> 20) & 0x1
1682          apb_ctl_date = self.read_reg(self.APB_CTL_DATE_ADDR)
1683          rev_bit2 = (apb_ctl_date >> self.APB_CTL_DATE_S) & self.APB_CTL_DATE_V
1684          combine_value = (rev_bit2 << 2) | (rev_bit1 << 1) | rev_bit0
1685  
1686          revision = {
1687              0: 0,
1688              1: 1,
1689              3: 2,
1690              7: 3,
1691          }.get(combine_value, 0)
1692          return revision
1693  
1694      def get_chip_description(self):
1695          pkg_version = self.get_pkg_version()
1696          major_rev = self.get_major_chip_version()
1697          minor_rev = self.get_minor_chip_version()
1698          rev3 = major_rev == 3
1699          single_core = self.read_efuse(3) & (1 << 0)  # CHIP_VER DIS_APP_CPU
1700  
1701          chip_name = {
1702              0: "ESP32-S0WDQ6" if single_core else "ESP32-D0WDQ6",
1703              1: "ESP32-S0WD" if single_core else "ESP32-D0WD",
1704              2: "ESP32-D2WD",
1705              4: "ESP32-U4WDH",
1706              5: "ESP32-PICO-V3" if rev3 else "ESP32-PICO-D4",
1707              6: "ESP32-PICO-V3-02",
1708              7: "ESP32-D0WDR2-V3",
1709          }.get(pkg_version, "unknown ESP32")
1710  
1711          # ESP32-D0WD-V3, ESP32-D0WDQ6-V3
1712          if chip_name.startswith("ESP32-D0WD") and rev3:
1713              chip_name += "-V3"
1714  
1715          return "%s (revision v%d.%d)" % (chip_name, major_rev, minor_rev)
1716  
1717      def get_chip_features(self):
1718          features = ["WiFi"]
1719          word3 = self.read_efuse(3)
1720  
1721          # names of variables in this section are lowercase
1722          #  versions of EFUSE names as documented in TRM and
1723          # ESP-IDF efuse_reg.h
1724  
1725          chip_ver_dis_bt = word3 & (1 << 1)
1726          if chip_ver_dis_bt == 0:
1727              features += ["BT"]
1728  
1729          chip_ver_dis_app_cpu = word3 & (1 << 0)
1730          if chip_ver_dis_app_cpu:
1731              features += ["Single Core"]
1732          else:
1733              features += ["Dual Core"]
1734  
1735          chip_cpu_freq_rated = word3 & (1 << 13)
1736          if chip_cpu_freq_rated:
1737              chip_cpu_freq_low = word3 & (1 << 12)
1738              if chip_cpu_freq_low:
1739                  features += ["160MHz"]
1740              else:
1741                  features += ["240MHz"]
1742  
1743          pkg_version = self.get_pkg_version()
1744          if pkg_version in [2, 4, 5, 6]:
1745              features += ["Embedded Flash"]
1746  
1747          if pkg_version == 6:
1748              features += ["Embedded PSRAM"]
1749  
1750          word4 = self.read_efuse(4)
1751          adc_vref = (word4 >> 8) & 0x1F
1752          if adc_vref:
1753              features += ["VRef calibration in efuse"]
1754  
1755          blk3_part_res = word3 >> 14 & 0x1
1756          if blk3_part_res:
1757              features += ["BLK3 partially reserved"]
1758  
1759          word6 = self.read_efuse(6)
1760          coding_scheme = word6 & 0x3
1761          features += ["Coding Scheme %s" % {
1762              0: "None",
1763              1: "3/4",
1764              2: "Repeat (UNSUPPORTED)",
1765              3: "Invalid"}[coding_scheme]]
1766  
1767          return features
1768  
1769      def read_efuse(self, n):
1770          """ Read the nth word of the ESP3x EFUSE region. """
1771          return self.read_reg(self.EFUSE_RD_REG_BASE + (4 * n))
1772  
1773      def chip_id(self):
1774          raise NotSupportedError(self, "chip_id")
1775  
1776      def read_mac(self):
1777          """ Read MAC from EFUSE region """
1778          words = [self.read_efuse(2), self.read_efuse(1)]
1779          bitstring = struct.pack(">II", *words)
1780          bitstring = bitstring[2:8]  # trim the 2 byte CRC
1781          try:
1782              return tuple(ord(b) for b in bitstring)
1783          except TypeError:  # Python 3, bitstring elements are already bytes
1784              return tuple(bitstring)
1785  
1786      def get_erase_size(self, offset, size):
1787          return size
1788  
1789      def override_vddsdio(self, new_voltage):
1790          new_voltage = new_voltage.upper()
1791          if new_voltage not in self.OVERRIDE_VDDSDIO_CHOICES:
1792              raise FatalError("The only accepted VDDSDIO overrides are '1.8V', '1.9V' and 'OFF'")
1793          RTC_CNTL_SDIO_CONF_REG = 0x3ff48074
1794          RTC_CNTL_XPD_SDIO_REG = (1 << 31)
1795          RTC_CNTL_DREFH_SDIO_M = (3 << 29)
1796          RTC_CNTL_DREFM_SDIO_M = (3 << 27)
1797          RTC_CNTL_DREFL_SDIO_M = (3 << 25)
1798          # RTC_CNTL_SDIO_TIEH = (1 << 23)  # not used here, setting TIEH=1 would set 3.3V output, not safe for esptool.py to do
1799          RTC_CNTL_SDIO_FORCE = (1 << 22)
1800          RTC_CNTL_SDIO_PD_EN = (1 << 21)
1801  
1802          reg_val = RTC_CNTL_SDIO_FORCE  # override efuse setting
1803          reg_val |= RTC_CNTL_SDIO_PD_EN
1804          if new_voltage != "OFF":
1805              reg_val |= RTC_CNTL_XPD_SDIO_REG  # enable internal LDO
1806          if new_voltage == "1.9V":
1807              reg_val |= (RTC_CNTL_DREFH_SDIO_M | RTC_CNTL_DREFM_SDIO_M | RTC_CNTL_DREFL_SDIO_M)  # boost voltage
1808          self.write_reg(RTC_CNTL_SDIO_CONF_REG, reg_val)
1809          print("VDDSDIO regulator set to %s" % new_voltage)
1810  
1811      def read_flash_slow(self, offset, length, progress_fn):
1812          BLOCK_LEN = 64  # ROM read limit per command (this limit is why it's so slow)
1813  
1814          data = b''
1815          while len(data) < length:
1816              block_len = min(BLOCK_LEN, length - len(data))
1817              r = self.check_command("read flash block", self.ESP_READ_FLASH_SLOW,
1818                                     struct.pack('<II', offset + len(data), block_len))
1819              if len(r) < block_len:
1820                  raise FatalError("Expected %d byte block, got %d bytes. Serial errors?" % (block_len, len(r)))
1821              data += r[:block_len]  # command always returns 64 byte buffer, regardless of how many bytes were actually read from flash
1822              if progress_fn and (len(data) % 1024 == 0 or len(data) == length):
1823                  progress_fn(len(data), length)
1824          return data
1825  
1826  
1827  class ESP32S2ROM(ESP32ROM):
1828      CHIP_NAME = "ESP32-S2"
1829      IMAGE_CHIP_ID = 2
1830  
1831      FPGA_SLOW_BOOT = False
1832  
1833      IROM_MAP_START = 0x40080000
1834      IROM_MAP_END   = 0x40b80000
1835      DROM_MAP_START = 0x3F000000
1836      DROM_MAP_END   = 0x3F3F0000
1837  
1838      CHIP_DETECT_MAGIC_VALUE = [0x000007c6]
1839  
1840      SPI_REG_BASE = 0x3f402000
1841      SPI_USR_OFFS    = 0x18
1842      SPI_USR1_OFFS   = 0x1c
1843      SPI_USR2_OFFS   = 0x20
1844      SPI_MOSI_DLEN_OFFS = 0x24
1845      SPI_MISO_DLEN_OFFS = 0x28
1846      SPI_W0_OFFS = 0x58
1847  
1848      MAC_EFUSE_REG = 0x3f41A044  # ESP32-S2 has special block for MAC efuses
1849  
1850      UART_CLKDIV_REG = 0x3f400014
1851  
1852      FLASH_ENCRYPTED_WRITE_ALIGN = 16
1853  
1854      # todo: use espefuse APIs to get this info
1855      EFUSE_BASE = 0x3f41A000
1856      EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030  # BLOCK0 read base address
1857      EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x044
1858      EFUSE_BLOCK2_ADDR = EFUSE_BASE + 0x05C
1859  
1860      EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34
1861      EFUSE_PURPOSE_KEY0_SHIFT = 24
1862      EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34
1863      EFUSE_PURPOSE_KEY1_SHIFT = 28
1864      EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38
1865      EFUSE_PURPOSE_KEY2_SHIFT = 0
1866      EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38
1867      EFUSE_PURPOSE_KEY3_SHIFT = 4
1868      EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38
1869      EFUSE_PURPOSE_KEY4_SHIFT = 8
1870      EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38
1871      EFUSE_PURPOSE_KEY5_SHIFT = 12
1872  
1873      EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE
1874      EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 19
1875  
1876      PURPOSE_VAL_XTS_AES256_KEY_1 = 2
1877      PURPOSE_VAL_XTS_AES256_KEY_2 = 3
1878      PURPOSE_VAL_XTS_AES128_KEY = 4
1879  
1880      UARTDEV_BUF_NO = 0x3ffffd14  # Variable in ROM .bss which indicates the port in use
1881      UARTDEV_BUF_NO_USB = 2  # Value of the above variable indicating that USB is in use
1882  
1883      USB_RAM_BLOCK = 0x800  # Max block size USB CDC is used
1884  
1885      GPIO_STRAP_REG = 0x3f404038
1886      GPIO_STRAP_SPI_BOOT_MASK = 0x8   # Not download mode
1887      RTC_CNTL_OPTION1_REG = 0x3f408128
1888      RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1  # Is download mode forced over USB?
1889  
1890      MEMORY_MAP = [[0x00000000, 0x00010000, "PADDING"],
1891                    [0x3F000000, 0x3FF80000, "DROM"],
1892                    [0x3F500000, 0x3FF80000, "EXTRAM_DATA"],
1893                    [0x3FF9E000, 0x3FFA0000, "RTC_DRAM"],
1894                    [0x3FF9E000, 0x40000000, "BYTE_ACCESSIBLE"],
1895                    [0x3FF9E000, 0x40072000, "MEM_INTERNAL"],
1896                    [0x3FFB0000, 0x40000000, "DRAM"],
1897                    [0x40000000, 0x4001A100, "IROM_MASK"],
1898                    [0x40020000, 0x40070000, "IRAM"],
1899                    [0x40070000, 0x40072000, "RTC_IRAM"],
1900                    [0x40080000, 0x40800000, "IROM"],
1901                    [0x50000000, 0x50002000, "RTC_DATA"]]
1902  
1903      # Returns old version format (ECO number). Use the new format get_chip_full_revision().
1904      def get_chip_revision(self):
1905          return self.get_major_chip_version()
1906  
1907      def get_pkg_version(self):
1908          num_word = 4
1909          return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x0F
1910  
1911      def get_minor_chip_version(self):
1912          hi_num_word = 3
1913          hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 20) & 0x01
1914          low_num_word = 4
1915          low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 4) & 0x07
1916          return (hi << 3) + low
1917  
1918      def get_major_chip_version(self):
1919          num_word = 3
1920          return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 18) & 0x03
1921  
1922      def get_flash_version(self):
1923          num_word = 3
1924          return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x0F
1925  
1926      def get_psram_version(self):
1927          num_word = 3
1928          return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 28) & 0x0F
1929  
1930      def get_block2_version(self):
1931          # BLK_VERSION_MINOR
1932          num_word = 4
1933          return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 4) & 0x07
1934  
1935      def get_chip_description(self):
1936          chip_name = {
1937              0: "ESP32-S2",
1938              1: "ESP32-S2FH2",
1939              2: "ESP32-S2FH4",
1940              102: "ESP32-S2FNR2",
1941              100: "ESP32-S2R2",
1942          }.get(self.get_flash_version() + self.get_psram_version() * 100, "unknown ESP32-S2")
1943  
1944          major_rev = self.get_major_chip_version()
1945          minor_rev = self.get_minor_chip_version()
1946          return "%s (revision v%d.%d)" % (chip_name, major_rev, minor_rev)
1947  
1948      def get_chip_features(self):
1949          features = ["WiFi"]
1950  
1951          if self.secure_download_mode:
1952              features += ["Secure Download Mode Enabled"]
1953  
1954          flash_version = {
1955              0: "No Embedded Flash",
1956              1: "Embedded Flash 2MB",
1957              2: "Embedded Flash 4MB",
1958          }.get(self.get_flash_version(), "Unknown Embedded Flash")
1959          features += [flash_version]
1960  
1961          psram_version = {
1962              0: "No Embedded PSRAM",
1963              1: "Embedded PSRAM 2MB",
1964              2: "Embedded PSRAM 4MB",
1965          }.get(self.get_psram_version(), "Unknown Embedded PSRAM")
1966          features += [psram_version]
1967  
1968          block2_version = {
1969              0: "No calibration in BLK2 of efuse",
1970              1: "ADC and temperature sensor calibration in BLK2 of efuse V1",
1971              2: "ADC and temperature sensor calibration in BLK2 of efuse V2",
1972          }.get(self.get_block2_version(), "Unknown Calibration in BLK2")
1973          features += [block2_version]
1974  
1975          return features
1976  
1977      def get_crystal_freq(self):
1978          # ESP32-S2 XTAL is fixed to 40MHz
1979          return 40
1980  
1981      def override_vddsdio(self, new_voltage):
1982          raise NotImplementedInROMError("VDD_SDIO overrides are not supported for ESP32-S2")
1983  
1984      def read_mac(self):
1985          mac0 = self.read_reg(self.MAC_EFUSE_REG)
1986          mac1 = self.read_reg(self.MAC_EFUSE_REG + 4)  # only bottom 16 bits are MAC
1987          bitstring = struct.pack(">II", mac1, mac0)[2:]
1988          try:
1989              return tuple(ord(b) for b in bitstring)
1990          except TypeError:  # Python 3, bitstring elements are already bytes
1991              return tuple(bitstring)
1992  
1993      def get_flash_crypt_config(self):
1994          return None  # doesn't exist on ESP32-S2
1995  
1996      def get_key_block_purpose(self, key_block):
1997          if key_block < 0 or key_block > 5:
1998              raise FatalError("Valid key block numbers must be in range 0-5")
1999  
2000          reg, shift = [(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT),
2001                        (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT),
2002                        (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT),
2003                        (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT),
2004                        (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT),
2005                        (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT)][key_block]
2006          return (self.read_reg(reg) >> shift) & 0xF
2007  
2008      def is_flash_encryption_key_valid(self):
2009          # Need to see either an AES-128 key or two AES-256 keys
2010          purposes = [self.get_key_block_purpose(b) for b in range(6)]
2011  
2012          if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes):
2013              return True
2014  
2015          return any(p == self.PURPOSE_VAL_XTS_AES256_KEY_1 for p in purposes) \
2016              and any(p == self.PURPOSE_VAL_XTS_AES256_KEY_2 for p in purposes)
2017  
2018      def uses_usb(self, _cache=[]):
2019          if self.secure_download_mode:
2020              return False  # can't detect native USB in secure download mode
2021          if not _cache:
2022              buf_no = self.read_reg(self.UARTDEV_BUF_NO) & 0xff
2023              _cache.append(buf_no == self.UARTDEV_BUF_NO_USB)
2024          return _cache[0]
2025  
2026      def _post_connect(self):
2027          if self.uses_usb():
2028              self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK
2029  
2030      def _check_if_can_reset(self):
2031          """
2032          Check the strapping register to see if we can reset out of download mode.
2033          """
2034          if os.getenv("ESPTOOL_TESTING") is not None:
2035              print("ESPTOOL_TESTING is set, ignoring strapping mode check")
2036              # Esptool tests over USB CDC run with GPIO0 strapped low, don't complain in this case.
2037              return
2038          strap_reg = self.read_reg(self.GPIO_STRAP_REG)
2039          force_dl_reg = self.read_reg(self.RTC_CNTL_OPTION1_REG)
2040          if strap_reg & self.GPIO_STRAP_SPI_BOOT_MASK == 0 and force_dl_reg & self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK == 0:
2041              print("WARNING: {} chip was placed into download mode using GPIO0.\n"
2042                    "esptool.py can not exit the download mode over USB. "
2043                    "To run the app, reset the chip manually.\n"
2044                    "To suppress this note, set --after option to 'no_reset'.".format(self.get_chip_description()))
2045              raise SystemExit(1)
2046  
2047      def hard_reset(self):
2048          if self.uses_usb():
2049              self._check_if_can_reset()
2050  
2051          print('Hard resetting via RTS pin...')
2052          self._setRTS(True)  # EN->LOW
2053          if self.uses_usb():
2054              # Give the chip some time to come out of reset, to be able to handle further DTR/RTS transitions
2055              time.sleep(0.2)
2056              self._setRTS(False)
2057              time.sleep(0.2)
2058          else:
2059              time.sleep(0.1)
2060              self._setRTS(False)
2061  
2062  
2063  class ESP32S3ROM(ESP32ROM):
2064      CHIP_NAME = "ESP32-S3"
2065  
2066      IMAGE_CHIP_ID = 9
2067  
2068      CHIP_DETECT_MAGIC_VALUE = [0x9]
2069  
2070      BOOTLOADER_FLASH_OFFSET = 0x0
2071  
2072      FPGA_SLOW_BOOT = False
2073  
2074      IROM_MAP_START = 0x42000000
2075      IROM_MAP_END   = 0x44000000
2076      DROM_MAP_START = 0x3c000000
2077      DROM_MAP_END   = 0x3e000000
2078  
2079      UART_DATE_REG_ADDR = 0x60000080
2080  
2081      SPI_REG_BASE = 0x60002000
2082      SPI_USR_OFFS    = 0x18
2083      SPI_USR1_OFFS   = 0x1c
2084      SPI_USR2_OFFS   = 0x20
2085      SPI_MOSI_DLEN_OFFS = 0x24
2086      SPI_MISO_DLEN_OFFS = 0x28
2087      SPI_W0_OFFS = 0x58
2088  
2089      FLASH_ENCRYPTED_WRITE_ALIGN = 16
2090  
2091      # todo: use espefuse APIs to get this info
2092      EFUSE_BASE = 0x60007000  # BLOCK0 read base address
2093      MAC_EFUSE_REG = EFUSE_BASE + 0x044
2094      EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x44
2095      EFUSE_BLOCK2_ADDR = EFUSE_BASE + 0x5C
2096      EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030  # BLOCK0 read base address
2097  
2098      EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34
2099      EFUSE_PURPOSE_KEY0_SHIFT = 24
2100      EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34
2101      EFUSE_PURPOSE_KEY1_SHIFT = 28
2102      EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38
2103      EFUSE_PURPOSE_KEY2_SHIFT = 0
2104      EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38
2105      EFUSE_PURPOSE_KEY3_SHIFT = 4
2106      EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38
2107      EFUSE_PURPOSE_KEY4_SHIFT = 8
2108      EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38
2109      EFUSE_PURPOSE_KEY5_SHIFT = 12
2110  
2111      EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE
2112      EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20
2113  
2114      PURPOSE_VAL_XTS_AES256_KEY_1 = 2
2115      PURPOSE_VAL_XTS_AES256_KEY_2 = 3
2116      PURPOSE_VAL_XTS_AES128_KEY = 4
2117  
2118      UARTDEV_BUF_NO = 0x3fcef14c  # Variable in ROM .bss which indicates the port in use
2119      UARTDEV_BUF_NO_USB = 3  # Value of the above variable indicating that USB is in use
2120  
2121      USB_RAM_BLOCK = 0x800  # Max block size USB CDC is used
2122  
2123      GPIO_STRAP_REG = 0x60004038
2124      GPIO_STRAP_SPI_BOOT_MASK = 0x8   # Not download mode
2125      RTC_CNTL_OPTION1_REG = 0x6000812C
2126      RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1  # Is download mode forced over USB?
2127  
2128      UART_CLKDIV_REG = 0x60000014
2129  
2130      MEMORY_MAP = [[0x00000000, 0x00010000, "PADDING"],
2131                    [0x3C000000, 0x3D000000, "DROM"],
2132                    [0x3D000000, 0x3E000000, "EXTRAM_DATA"],
2133                    [0x600FE000, 0x60100000, "RTC_DRAM"],
2134                    [0x3FC88000, 0x3FD00000, "BYTE_ACCESSIBLE"],
2135                    [0x3FC88000, 0x403E2000, "MEM_INTERNAL"],
2136                    [0x3FC88000, 0x3FD00000, "DRAM"],
2137                    [0x40000000, 0x4001A100, "IROM_MASK"],
2138                    [0x40370000, 0x403E0000, "IRAM"],
2139                    [0x600FE000, 0x60100000, "RTC_IRAM"],
2140                    [0x42000000, 0x42800000, "IROM"],
2141                    [0x50000000, 0x50002000, "RTC_DATA"]]
2142  
2143      # Returns old version format (ECO number). Use the new format get_chip_full_revision().
2144      def get_chip_revision(self):
2145          return self.get_minor_chip_version()
2146  
2147      def get_pkg_version(self):
2148          num_word = 3
2149          return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x07
2150  
2151      def is_eco0(self, minor_raw):
2152          # Workaround: The major version field was allocated to other purposes
2153          # when block version is v1.1.
2154          # Luckily only chip v0.0 have this kind of block version and efuse usage.
2155          return (
2156              (minor_raw & 0x7) == 0 and self.get_blk_version_major() == 1 and self.get_blk_version_minor() == 1
2157          )
2158  
2159      def get_minor_chip_version(self):
2160          minor_raw = self.get_raw_minor_chip_version()
2161          if self.is_eco0(minor_raw):
2162              return 0
2163          return minor_raw
2164  
2165      def get_raw_minor_chip_version(self):
2166          hi_num_word = 5
2167          hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 23) & 0x01
2168          low_num_word = 3
2169          low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 18) & 0x07
2170          return (hi << 3) + low
2171  
2172      def get_blk_version_major(self):
2173          num_word = 4
2174          return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 0) & 0x03
2175  
2176      def get_blk_version_minor(self):
2177          num_word = 3
2178          return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x07
2179  
2180      def get_major_chip_version(self):
2181          minor_raw = self.get_raw_minor_chip_version()
2182          if self.is_eco0(minor_raw):
2183              return 0
2184          return self.get_raw_major_chip_version()
2185  
2186      def get_raw_major_chip_version(self):
2187          num_word = 5
2188          return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x03
2189  
2190      def get_chip_description(self):
2191          major_rev = self.get_major_chip_version()
2192          minor_rev = self.get_minor_chip_version()
2193          return "%s (revision v%d.%d)" % (self.CHIP_NAME, major_rev, minor_rev)
2194  
2195      def get_chip_features(self):
2196          return ["WiFi", "BLE"]
2197  
2198      def get_crystal_freq(self):
2199          # ESP32S3 XTAL is fixed to 40MHz
2200          return 40
2201  
2202      def get_flash_crypt_config(self):
2203          return None  # doesn't exist on ESP32-S3
2204  
2205      def get_key_block_purpose(self, key_block):
2206          if key_block < 0 or key_block > 5:
2207              raise FatalError("Valid key block numbers must be in range 0-5")
2208  
2209          reg, shift = [(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT),
2210                        (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT),
2211                        (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT),
2212                        (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT),
2213                        (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT),
2214                        (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT)][key_block]
2215          return (self.read_reg(reg) >> shift) & 0xF
2216  
2217      def is_flash_encryption_key_valid(self):
2218          # Need to see either an AES-128 key or two AES-256 keys
2219          purposes = [self.get_key_block_purpose(b) for b in range(6)]
2220  
2221          if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes):
2222              return True
2223  
2224          return any(p == self.PURPOSE_VAL_XTS_AES256_KEY_1 for p in purposes) \
2225              and any(p == self.PURPOSE_VAL_XTS_AES256_KEY_2 for p in purposes)
2226  
2227      def override_vddsdio(self, new_voltage):
2228          raise NotImplementedInROMError("VDD_SDIO overrides are not supported for ESP32-S3")
2229  
2230      def read_mac(self):
2231          mac0 = self.read_reg(self.MAC_EFUSE_REG)
2232          mac1 = self.read_reg(self.MAC_EFUSE_REG + 4)  # only bottom 16 bits are MAC
2233          bitstring = struct.pack(">II", mac1, mac0)[2:]
2234          try:
2235              return tuple(ord(b) for b in bitstring)
2236          except TypeError:  # Python 3, bitstring elements are already bytes
2237              return tuple(bitstring)
2238  
2239      def uses_usb(self, _cache=[]):
2240          if self.secure_download_mode:
2241              return False  # can't detect native USB in secure download mode
2242          if not _cache:
2243              buf_no = self.read_reg(self.UARTDEV_BUF_NO) & 0xff
2244              _cache.append(buf_no == self.UARTDEV_BUF_NO_USB)
2245          return _cache[0]
2246  
2247      def _post_connect(self):
2248          if self.uses_usb():
2249              self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK
2250  
2251      def _check_if_can_reset(self):
2252          """
2253          Check the strapping register to see if we can reset out of download mode.
2254          """
2255          if os.getenv("ESPTOOL_TESTING") is not None:
2256              print("ESPTOOL_TESTING is set, ignoring strapping mode check")
2257              # Esptool tests over USB CDC run with GPIO0 strapped low, don't complain in this case.
2258              return
2259          strap_reg = self.read_reg(self.GPIO_STRAP_REG)
2260          force_dl_reg = self.read_reg(self.RTC_CNTL_OPTION1_REG)
2261          if strap_reg & self.GPIO_STRAP_SPI_BOOT_MASK == 0 and force_dl_reg & self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK == 0:
2262              print("WARNING: {} chip was placed into download mode using GPIO0.\n"
2263                    "esptool.py can not exit the download mode over USB. "
2264                    "To run the app, reset the chip manually.\n"
2265                    "To suppress this note, set --after option to 'no_reset'.".format(self.get_chip_description()))
2266              raise SystemExit(1)
2267  
2268      def hard_reset(self):
2269          if self.uses_usb():
2270              self._check_if_can_reset()
2271  
2272          print('Hard resetting via RTS pin...')
2273          self._setRTS(True)  # EN->LOW
2274          if self.uses_usb():
2275              # Give the chip some time to come out of reset, to be able to handle further DTR/RTS transitions
2276              time.sleep(0.2)
2277              self._setRTS(False)
2278              time.sleep(0.2)
2279          else:
2280              time.sleep(0.1)
2281              self._setRTS(False)
2282  
2283  
2284  class ESP32S3BETA2ROM(ESP32S3ROM):
2285      CHIP_NAME = "ESP32-S3(beta2)"
2286      IMAGE_CHIP_ID = 4
2287  
2288      CHIP_DETECT_MAGIC_VALUE = [0xeb004136]
2289  
2290      EFUSE_BASE = 0x6001A000  # BLOCK0 read base address
2291  
2292      def get_chip_description(self):
2293          major_rev = self.get_major_chip_version()
2294          minor_rev = self.get_minor_chip_version()
2295          return "%s (revision v%d.%d)" % (self.CHIP_NAME, major_rev, minor_rev)
2296  
2297  
2298  class ESP32C3ROM(ESP32ROM):
2299      CHIP_NAME = "ESP32-C3"
2300      IMAGE_CHIP_ID = 5
2301  
2302      FPGA_SLOW_BOOT = False
2303  
2304      IROM_MAP_START = 0x42000000
2305      IROM_MAP_END   = 0x42800000
2306      DROM_MAP_START = 0x3c000000
2307      DROM_MAP_END   = 0x3c800000
2308  
2309      SPI_REG_BASE = 0x60002000
2310      SPI_USR_OFFS    = 0x18
2311      SPI_USR1_OFFS   = 0x1C
2312      SPI_USR2_OFFS   = 0x20
2313      SPI_MOSI_DLEN_OFFS = 0x24
2314      SPI_MISO_DLEN_OFFS = 0x28
2315      SPI_W0_OFFS = 0x58
2316  
2317      BOOTLOADER_FLASH_OFFSET = 0x0
2318  
2319      # Magic value for ESP32C3 eco 1+2 and ESP32C3 eco3 respectivly
2320      CHIP_DETECT_MAGIC_VALUE = [0x6921506f, 0x1b31506f]
2321  
2322      UART_DATE_REG_ADDR = 0x60000000 + 0x7c
2323  
2324      EFUSE_BASE = 0x60008800
2325      EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x044
2326      MAC_EFUSE_REG  = EFUSE_BASE + 0x044
2327  
2328      EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030  # BLOCK0 read base address
2329  
2330      EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34
2331      EFUSE_PURPOSE_KEY0_SHIFT = 24
2332      EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34
2333      EFUSE_PURPOSE_KEY1_SHIFT = 28
2334      EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38
2335      EFUSE_PURPOSE_KEY2_SHIFT = 0
2336      EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38
2337      EFUSE_PURPOSE_KEY3_SHIFT = 4
2338      EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38
2339      EFUSE_PURPOSE_KEY4_SHIFT = 8
2340      EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38
2341      EFUSE_PURPOSE_KEY5_SHIFT = 12
2342  
2343      EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE
2344      EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20
2345  
2346      PURPOSE_VAL_XTS_AES128_KEY = 4
2347  
2348      GPIO_STRAP_REG = 0x3f404038
2349  
2350      FLASH_ENCRYPTED_WRITE_ALIGN = 16
2351  
2352      MEMORY_MAP = [[0x00000000, 0x00010000, "PADDING"],
2353                    [0x3C000000, 0x3C800000, "DROM"],
2354                    [0x3FC80000, 0x3FCE0000, "DRAM"],
2355                    [0x3FC88000, 0x3FD00000, "BYTE_ACCESSIBLE"],
2356                    [0x3FF00000, 0x3FF20000, "DROM_MASK"],
2357                    [0x40000000, 0x40060000, "IROM_MASK"],
2358                    [0x42000000, 0x42800000, "IROM"],
2359                    [0x4037C000, 0x403E0000, "IRAM"],
2360                    [0x50000000, 0x50002000, "RTC_IRAM"],
2361                    [0x50000000, 0x50002000, "RTC_DRAM"],
2362                    [0x600FE000, 0x60100000, "MEM_INTERNAL2"]]
2363  
2364      # Returns old version format (ECO number). Use the new format get_chip_full_revision().
2365      def get_chip_revision(self):
2366          return self.get_minor_chip_version()
2367  
2368      def get_pkg_version(self):
2369          num_word = 3
2370          return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x07
2371  
2372      def get_minor_chip_version(self):
2373          hi_num_word = 5
2374          hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 23) & 0x01
2375          low_num_word = 3
2376          low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 18) & 0x07
2377          return (hi << 3) + low
2378  
2379      def get_major_chip_version(self):
2380          num_word = 5
2381          return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x03
2382  
2383      def get_chip_description(self):
2384          chip_name = {
2385              0: "ESP32-C3",
2386          }.get(self.get_pkg_version(), "unknown ESP32-C3")
2387          major_rev = self.get_major_chip_version()
2388          minor_rev = self.get_minor_chip_version()
2389          return "%s (revision v%d.%d)" % (chip_name, major_rev, minor_rev)
2390  
2391      def get_chip_features(self):
2392          return ["Wi-Fi"]
2393  
2394      def get_crystal_freq(self):
2395          # ESP32C3 XTAL is fixed to 40MHz
2396          return 40
2397  
2398      def override_vddsdio(self, new_voltage):
2399          raise NotImplementedInROMError("VDD_SDIO overrides are not supported for ESP32-C3")
2400  
2401      def read_mac(self):
2402          mac0 = self.read_reg(self.MAC_EFUSE_REG)
2403          mac1 = self.read_reg(self.MAC_EFUSE_REG + 4)  # only bottom 16 bits are MAC
2404          bitstring = struct.pack(">II", mac1, mac0)[2:]
2405          try:
2406              return tuple(ord(b) for b in bitstring)
2407          except TypeError:  # Python 3, bitstring elements are already bytes
2408              return tuple(bitstring)
2409  
2410      def get_flash_crypt_config(self):
2411          return None  # doesn't exist on ESP32-C3
2412  
2413      def get_key_block_purpose(self, key_block):
2414          if key_block < 0 or key_block > 5:
2415              raise FatalError("Valid key block numbers must be in range 0-5")
2416  
2417          reg, shift = [(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT),
2418                        (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT),
2419                        (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT),
2420                        (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT),
2421                        (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT),
2422                        (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT)][key_block]
2423          return (self.read_reg(reg) >> shift) & 0xF
2424  
2425      def is_flash_encryption_key_valid(self):
2426          # Need to see an AES-128 key
2427          purposes = [self.get_key_block_purpose(b) for b in range(6)]
2428  
2429          return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes)
2430  
2431  
2432  class ESP32H2BETA1ROM(ESP32ROM):
2433      CHIP_NAME = "ESP32-H2(beta1)"
2434      IMAGE_CHIP_ID = 10
2435  
2436      IROM_MAP_START = 0x42000000
2437      IROM_MAP_END   = 0x42800000
2438      DROM_MAP_START = 0x3c000000
2439      DROM_MAP_END   = 0x3c800000
2440  
2441      SPI_REG_BASE = 0x60002000
2442      SPI_USR_OFFS    = 0x18
2443      SPI_USR1_OFFS   = 0x1C
2444      SPI_USR2_OFFS   = 0x20
2445      SPI_MOSI_DLEN_OFFS = 0x24
2446      SPI_MISO_DLEN_OFFS = 0x28
2447      SPI_W0_OFFS = 0x58
2448  
2449      BOOTLOADER_FLASH_OFFSET = 0x0
2450  
2451      CHIP_DETECT_MAGIC_VALUE = [0xca26cc22]
2452  
2453      UART_DATE_REG_ADDR = 0x60000000 + 0x7c
2454  
2455      EFUSE_BASE = 0x6001A000
2456      EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x044
2457      MAC_EFUSE_REG  = EFUSE_BASE + 0x044
2458  
2459      EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030  # BLOCK0 read base address
2460  
2461      EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34
2462      EFUSE_PURPOSE_KEY0_SHIFT = 24
2463      EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34
2464      EFUSE_PURPOSE_KEY1_SHIFT = 28
2465      EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38
2466      EFUSE_PURPOSE_KEY2_SHIFT = 0
2467      EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38
2468      EFUSE_PURPOSE_KEY3_SHIFT = 4
2469      EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38
2470      EFUSE_PURPOSE_KEY4_SHIFT = 8
2471      EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38
2472      EFUSE_PURPOSE_KEY5_SHIFT = 12
2473  
2474      EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE
2475      EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20
2476  
2477      PURPOSE_VAL_XTS_AES128_KEY = 4
2478  
2479      GPIO_STRAP_REG = 0x3f404038
2480  
2481      FLASH_ENCRYPTED_WRITE_ALIGN = 16
2482  
2483      MEMORY_MAP = []
2484  
2485      FLASH_FREQUENCY = {
2486          '48m': 0xf,
2487          '24m': 0x0,
2488          '16m': 0x1,
2489          '12m': 0x2,
2490      }
2491  
2492      # Returns old version format (ECO number). Use the new format get_chip_full_revision().
2493      def get_chip_revision(self):
2494          return 0
2495  
2496      def get_pkg_version(self):
2497          num_word = 4
2498          return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x07
2499  
2500      def get_minor_chip_version(self):
2501          num_word = 3
2502          return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 18) & 0x07
2503  
2504      def get_major_chip_version(self):
2505          num_word = 3
2506          return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x03
2507  
2508      def get_chip_description(self):
2509          chip_name = {
2510              0: "ESP32-H2",
2511          }.get(self.get_pkg_version(), "unknown ESP32-H2")
2512          major_rev = self.get_major_chip_version()
2513          minor_rev = self.get_minor_chip_version()
2514          return "%s (revision v%d.%d)" % (chip_name, major_rev, minor_rev)
2515  
2516      def get_chip_features(self):
2517          return ["BLE/802.15.4"]
2518  
2519      def get_crystal_freq(self):
2520          return 32
2521  
2522      def override_vddsdio(self, new_voltage):
2523          raise NotImplementedInROMError("VDD_SDIO overrides are not supported for ESP32-H2")
2524  
2525      def read_mac(self):
2526          mac0 = self.read_reg(self.MAC_EFUSE_REG)
2527          mac1 = self.read_reg(self.MAC_EFUSE_REG + 4)  # only bottom 16 bits are MAC
2528          bitstring = struct.pack(">II", mac1, mac0)[2:]
2529          try:
2530              return tuple(ord(b) for b in bitstring)
2531          except TypeError:  # Python 3, bitstring elements are already bytes
2532              return tuple(bitstring)
2533  
2534      def get_flash_crypt_config(self):
2535          return None  # doesn't exist on ESP32-H2
2536  
2537      def get_key_block_purpose(self, key_block):
2538          if key_block < 0 or key_block > 5:
2539              raise FatalError("Valid key block numbers must be in range 0-5")
2540  
2541          reg, shift = [(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT),
2542                        (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT),
2543                        (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT),
2544                        (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT),
2545                        (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT),
2546                        (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT)][key_block]
2547          return (self.read_reg(reg) >> shift) & 0xF
2548  
2549      def is_flash_encryption_key_valid(self):
2550          # Need to see an AES-128 key
2551          purposes = [self.get_key_block_purpose(b) for b in range(6)]
2552  
2553          return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes)
2554  
2555  
2556  class ESP32H2BETA2ROM(ESP32H2BETA1ROM):
2557      CHIP_NAME = "ESP32-H2(beta2)"
2558      IMAGE_CHIP_ID = 14
2559  
2560      def get_chip_description(self):
2561          major_rev = self.get_major_chip_version()
2562          minor_rev = self.get_minor_chip_version()
2563          return "%s (revision v%d.%d)" % (self.CHIP_NAME, major_rev, minor_rev)
2564  
2565  
2566  class ESP32C2ROM(ESP32C3ROM):
2567      CHIP_NAME = "ESP32-C2"
2568      IMAGE_CHIP_ID = 12
2569  
2570      IROM_MAP_START = 0x42000000
2571      IROM_MAP_END   = 0x42400000
2572      DROM_MAP_START = 0x3c000000
2573      DROM_MAP_END   = 0x3c400000
2574  
2575      # Magic value for ESP32C2 ECO0 and ECO1 respectively
2576      CHIP_DETECT_MAGIC_VALUE = [0x6F51306F, 0x7c41a06f]
2577  
2578      EFUSE_BASE = 0x60008800
2579      EFUSE_BLOCK2_ADDR = EFUSE_BASE + 0x040
2580      MAC_EFUSE_REG  = EFUSE_BASE + 0x040
2581  
2582      FLASH_FREQUENCY = {
2583          '60m': 0xf,
2584          '30m': 0x0,
2585          '20m': 0x1,
2586          '15m': 0x2,
2587      }
2588  
2589      # Returns old version format (ECO number). Use the new format get_chip_full_revision().
2590      def get_chip_revision(self):
2591          return self.get_major_chip_version()
2592  
2593      def get_pkg_version(self):
2594          num_word = 1
2595          return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 22) & 0x07
2596  
2597      def get_chip_description(self):
2598          chip_name = {
2599              0: "ESP32-C2",
2600              1: "ESP32-C2",
2601          }.get(self.get_pkg_version(), "unknown ESP32-C2")
2602          major_rev = self.get_major_chip_version()
2603          minor_rev = self.get_minor_chip_version()
2604          return "%s (revision v%d.%d)" % (chip_name, major_rev, minor_rev)
2605  
2606      def get_minor_chip_version(self):
2607          num_word = 1
2608          return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 16) & 0xF
2609  
2610      def get_major_chip_version(self):
2611          num_word = 1
2612          return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 20) & 0x3
2613  
2614      def _post_connect(self):
2615          # ESP32C2 ECO0 is no longer supported by the flasher stub
2616          if self.get_chip_revision() == 0:
2617              self.stub_is_disabled = True
2618              self.IS_STUB = False
2619  
2620  
2621  class ESP32C6BETAROM(ESP32C3ROM):
2622      CHIP_NAME = "ESP32-C6(beta)"
2623      IMAGE_CHIP_ID = 7
2624  
2625      CHIP_DETECT_MAGIC_VALUE = [0x0da1806f]
2626  
2627      UART_DATE_REG_ADDR = 0x00000500
2628  
2629      # Returns old version format (ECO number). Use the new format get_chip_full_revision().
2630      def get_chip_revision(self):
2631          return 0
2632  
2633      def get_pkg_version(self):
2634          num_word = 3
2635          return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 29) & 0x07
2636  
2637      def get_minor_chip_version(self):
2638          num_word = 3
2639          return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 18) & 0x0F
2640  
2641      def get_major_chip_version(self):
2642          num_word = 3
2643          return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 22) & 0x03
2644  
2645      def get_chip_description(self):
2646          chip_name = {
2647              0: "ESP32-C6",
2648          }.get(self.get_pkg_version(), "unknown ESP32-C6")
2649          major_rev = self.get_major_chip_version()
2650          minor_rev = self.get_minor_chip_version()
2651          return "%s (revision v%d.%d)" % (chip_name, major_rev, minor_rev)
2652  
2653  
2654  class ESP32StubLoader(ESP32ROM):
2655      """ Access class for ESP32 stub loader, runs on top of ROM.
2656      """
2657      FLASH_WRITE_SIZE = 0x4000  # matches MAX_WRITE_BLOCK in stub_loader.c
2658      STATUS_BYTES_LENGTH = 2  # same as ESP8266, different to ESP32 ROM
2659      IS_STUB = True
2660  
2661      def __init__(self, rom_loader):
2662          self.secure_download_mode = rom_loader.secure_download_mode
2663          self._port = rom_loader._port
2664          self._trace_enabled = rom_loader._trace_enabled
2665          self.flush_input()  # resets _slip_reader
2666  
2667  
2668  ESP32ROM.STUB_CLASS = ESP32StubLoader
2669  
2670  
2671  class ESP32S2StubLoader(ESP32S2ROM):
2672      """ Access class for ESP32-S2 stub loader, runs on top of ROM.
2673  
2674      (Basically the same as ESP32StubLoader, but different base class.
2675      Can possibly be made into a mixin.)
2676      """
2677      FLASH_WRITE_SIZE = 0x4000  # matches MAX_WRITE_BLOCK in stub_loader.c
2678      STATUS_BYTES_LENGTH = 2  # same as ESP8266, different to ESP32 ROM
2679      IS_STUB = True
2680  
2681      def __init__(self, rom_loader):
2682          self.secure_download_mode = rom_loader.secure_download_mode
2683          self._port = rom_loader._port
2684          self._trace_enabled = rom_loader._trace_enabled
2685          self.flush_input()  # resets _slip_reader
2686  
2687          if rom_loader.uses_usb():
2688              self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK
2689              self.FLASH_WRITE_SIZE = self.USB_RAM_BLOCK
2690  
2691  
2692  ESP32S2ROM.STUB_CLASS = ESP32S2StubLoader
2693  
2694  
2695  class ESP32S3BETA2StubLoader(ESP32S3BETA2ROM):
2696      """ Access class for ESP32S3 stub loader, runs on top of ROM.
2697  
2698      (Basically the same as ESP32StubLoader, but different base class.
2699      Can possibly be made into a mixin.)
2700      """
2701      FLASH_WRITE_SIZE = 0x4000  # matches MAX_WRITE_BLOCK in stub_loader.c
2702      STATUS_BYTES_LENGTH = 2  # same as ESP8266, different to ESP32 ROM
2703      IS_STUB = True
2704  
2705      def __init__(self, rom_loader):
2706          self.secure_download_mode = rom_loader.secure_download_mode
2707          self._port = rom_loader._port
2708          self._trace_enabled = rom_loader._trace_enabled
2709          self.flush_input()  # resets _slip_reader
2710  
2711  
2712  ESP32S3BETA2ROM.STUB_CLASS = ESP32S3BETA2StubLoader
2713  
2714  
2715  class ESP32S3StubLoader(ESP32S3ROM):
2716      """ Access class for ESP32S3 stub loader, runs on top of ROM.
2717  
2718      (Basically the same as ESP32StubLoader, but different base class.
2719      Can possibly be made into a mixin.)
2720      """
2721      FLASH_WRITE_SIZE = 0x4000  # matches MAX_WRITE_BLOCK in stub_loader.c
2722      STATUS_BYTES_LENGTH = 2  # same as ESP8266, different to ESP32 ROM
2723      IS_STUB = True
2724  
2725      def __init__(self, rom_loader):
2726          self.secure_download_mode = rom_loader.secure_download_mode
2727          self._port = rom_loader._port
2728          self._trace_enabled = rom_loader._trace_enabled
2729          self.flush_input()  # resets _slip_reader
2730  
2731          if rom_loader.uses_usb():
2732              self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK
2733              self.FLASH_WRITE_SIZE = self.USB_RAM_BLOCK
2734  
2735  
2736  ESP32S3ROM.STUB_CLASS = ESP32S3StubLoader
2737  
2738  
2739  class ESP32C3StubLoader(ESP32C3ROM):
2740      """ Access class for ESP32C3 stub loader, runs on top of ROM.
2741  
2742      (Basically the same as ESP32StubLoader, but different base class.
2743      Can possibly be made into a mixin.)
2744      """
2745      FLASH_WRITE_SIZE = 0x4000  # matches MAX_WRITE_BLOCK in stub_loader.c
2746      STATUS_BYTES_LENGTH = 2  # same as ESP8266, different to ESP32 ROM
2747      IS_STUB = True
2748  
2749      def __init__(self, rom_loader):
2750          self.secure_download_mode = rom_loader.secure_download_mode
2751          self._port = rom_loader._port
2752          self._trace_enabled = rom_loader._trace_enabled
2753          self.flush_input()  # resets _slip_reader
2754  
2755  
2756  ESP32C3ROM.STUB_CLASS = ESP32C3StubLoader
2757  
2758  
2759  class ESP32H2BETA1StubLoader(ESP32H2BETA1ROM):
2760      """ Access class for ESP32H2BETA1 stub loader, runs on top of ROM.
2761  
2762      (Basically the same as ESP32StubLoader, but different base class.
2763      Can possibly be made into a mixin.)
2764      """
2765      FLASH_WRITE_SIZE = 0x4000  # matches MAX_WRITE_BLOCK in stub_loader.c
2766      STATUS_BYTES_LENGTH = 2  # same as ESP8266, different to ESP32 ROM
2767      IS_STUB = True
2768  
2769      def __init__(self, rom_loader):
2770          self.secure_download_mode = rom_loader.secure_download_mode
2771          self._port = rom_loader._port
2772          self._trace_enabled = rom_loader._trace_enabled
2773          self.flush_input()  # resets _slip_reader
2774  
2775  
2776  ESP32H2BETA1ROM.STUB_CLASS = ESP32H2BETA1StubLoader
2777  
2778  
2779  class ESP32H2BETA2StubLoader(ESP32H2BETA2ROM):
2780      """ Access class for ESP32H2BETA2 stub loader, runs on top of ROM.
2781  
2782      (Basically the same as ESP32StubLoader, but different base class.
2783      Can possibly be made into a mixin.)
2784      """
2785      FLASH_WRITE_SIZE = 0x4000  # matches MAX_WRITE_BLOCK in stub_loader.c
2786      STATUS_BYTES_LENGTH = 2  # same as ESP8266, different to ESP32 ROM
2787      IS_STUB = True
2788  
2789      def __init__(self, rom_loader):
2790          self.secure_download_mode = rom_loader.secure_download_mode
2791          self._port = rom_loader._port
2792          self._trace_enabled = rom_loader._trace_enabled
2793          self.flush_input()  # resets _slip_reader
2794  
2795  
2796  ESP32H2BETA2ROM.STUB_CLASS = ESP32H2BETA2StubLoader
2797  
2798  
2799  class ESP32C2StubLoader(ESP32C2ROM):
2800      """ Access class for ESP32C2 stub loader, runs on top of ROM.
2801  
2802      (Basically the same as ESP32StubLoader, but different base class.
2803      Can possibly be made into a mixin.)
2804      """
2805      FLASH_WRITE_SIZE = 0x4000  # matches MAX_WRITE_BLOCK in stub_loader.c
2806      STATUS_BYTES_LENGTH = 2  # same as ESP8266, different to ESP32 ROM
2807      IS_STUB = True
2808  
2809      def __init__(self, rom_loader):
2810          self.secure_download_mode = rom_loader.secure_download_mode
2811          self._port = rom_loader._port
2812          self._trace_enabled = rom_loader._trace_enabled
2813          self.flush_input()  # resets _slip_reader
2814  
2815  
2816  ESP32C2ROM.STUB_CLASS = ESP32C2StubLoader
2817  
2818  
2819  class ESPBOOTLOADER(object):
2820      """ These are constants related to software ESP8266 bootloader, working with 'v2' image files """
2821  
2822      # First byte of the "v2" application image
2823      IMAGE_V2_MAGIC = 0xea
2824  
2825      # First 'segment' value in a "v2" application image, appears to be a constant version value?
2826      IMAGE_V2_SEGMENT = 4
2827  
2828  
2829  def LoadFirmwareImage(chip, filename):
2830      """ Load a firmware image. Can be for any supported SoC.
2831  
2832          ESP8266 images will be examined to determine if they are original ROM firmware images (ESP8266ROMFirmwareImage)
2833          or "v2" OTA bootloader images.
2834  
2835          Returns a BaseFirmwareImage subclass, either ESP8266ROMFirmwareImage (v1) or ESP8266V2FirmwareImage (v2).
2836      """
2837      chip = re.sub(r"[-()]", "", chip.lower())
2838      with open(filename, 'rb') as f:
2839          if chip == 'esp32':
2840              return ESP32FirmwareImage(f)
2841          elif chip == "esp32s2":
2842              return ESP32S2FirmwareImage(f)
2843          elif chip == "esp32s3beta2":
2844              return ESP32S3BETA2FirmwareImage(f)
2845          elif chip == "esp32s3":
2846              return ESP32S3FirmwareImage(f)
2847          elif chip == 'esp32c3':
2848              return ESP32C3FirmwareImage(f)
2849          elif chip == 'esp32c6beta':
2850              return ESP32C6BETAFirmwareImage(f)
2851          elif chip == 'esp32h2beta1':
2852              return ESP32H2BETA1FirmwareImage(f)
2853          elif chip == 'esp32h2beta2':
2854              return ESP32H2BETA2FirmwareImage(f)
2855          elif chip == 'esp32c2':
2856              return ESP32C2FirmwareImage(f)
2857          else:  # Otherwise, ESP8266 so look at magic to determine the image type
2858              magic = ord(f.read(1))
2859              f.seek(0)
2860              if magic == ESPLoader.ESP_IMAGE_MAGIC:
2861                  return ESP8266ROMFirmwareImage(f)
2862              elif magic == ESPBOOTLOADER.IMAGE_V2_MAGIC:
2863                  return ESP8266V2FirmwareImage(f)
2864              else:
2865                  raise FatalError("Invalid image magic number: %d" % magic)
2866  
2867  
2868  class ImageSegment(object):
2869      """ Wrapper class for a segment in an ESP image
2870      (very similar to a section in an ELFImage also) """
2871      def __init__(self, addr, data, file_offs=None):
2872          self.addr = addr
2873          self.data = data
2874          self.file_offs = file_offs
2875          self.include_in_checksum = True
2876          if self.addr != 0:
2877              self.pad_to_alignment(4)  # pad all "real" ImageSegments 4 byte aligned length
2878  
2879      def copy_with_new_addr(self, new_addr):
2880          """ Return a new ImageSegment with same data, but mapped at
2881          a new address. """
2882          return ImageSegment(new_addr, self.data, 0)
2883  
2884      def split_image(self, split_len):
2885          """ Return a new ImageSegment which splits "split_len" bytes
2886          from the beginning of the data. Remaining bytes are kept in
2887          this segment object (and the start address is adjusted to match.) """
2888          result = copy.copy(self)
2889          result.data = self.data[:split_len]
2890          self.data = self.data[split_len:]
2891          self.addr += split_len
2892          self.file_offs = None
2893          result.file_offs = None
2894          return result
2895  
2896      def __repr__(self):
2897          r = "len 0x%05x load 0x%08x" % (len(self.data), self.addr)
2898          if self.file_offs is not None:
2899              r += " file_offs 0x%08x" % (self.file_offs)
2900          return r
2901  
2902      def get_memory_type(self, image):
2903          """
2904          Return a list describing the memory type(s) that is covered by this
2905          segment's start address.
2906          """
2907          return [map_range[2] for map_range in image.ROM_LOADER.MEMORY_MAP if map_range[0] <= self.addr < map_range[1]]
2908  
2909      def pad_to_alignment(self, alignment):
2910          self.data = pad_to(self.data, alignment, b'\x00')
2911  
2912  
2913  class ELFSection(ImageSegment):
2914      """ Wrapper class for a section in an ELF image, has a section
2915      name as well as the common properties of an ImageSegment. """
2916      def __init__(self, name, addr, data):
2917          super(ELFSection, self).__init__(addr, data)
2918          self.name = name.decode("utf-8")
2919  
2920      def __repr__(self):
2921          return "%s %s" % (self.name, super(ELFSection, self).__repr__())
2922  
2923  
2924  class BaseFirmwareImage(object):
2925      SEG_HEADER_LEN = 8
2926      SHA256_DIGEST_LEN = 32
2927  
2928      """ Base class with common firmware image functions """
2929      def __init__(self):
2930          self.segments = []
2931          self.entrypoint = 0
2932          self.elf_sha256 = None
2933          self.elf_sha256_offset = 0
2934          self.pad_to_size = 0
2935  
2936      def load_common_header(self, load_file, expected_magic):
2937          (magic, segments, self.flash_mode, self.flash_size_freq, self.entrypoint) = struct.unpack('<BBBBI', load_file.read(8))
2938  
2939          if magic != expected_magic:
2940              raise FatalError('Invalid firmware image magic=0x%x' % (magic))
2941          return segments
2942  
2943      def verify(self):
2944          if len(self.segments) > 16:
2945              raise FatalError('Invalid segment count %d (max 16). Usually this indicates a linker script problem.' % len(self.segments))
2946  
2947      def load_segment(self, f, is_irom_segment=False):
2948          """ Load the next segment from the image file """
2949          file_offs = f.tell()
2950          (offset, size) = struct.unpack('<II', f.read(8))
2951          self.warn_if_unusual_segment(offset, size, is_irom_segment)
2952          segment_data = f.read(size)
2953          if len(segment_data) < size:
2954              raise FatalError('End of file reading segment 0x%x, length %d (actual length %d)' % (offset, size, len(segment_data)))
2955          segment = ImageSegment(offset, segment_data, file_offs)
2956          self.segments.append(segment)
2957          return segment
2958  
2959      def warn_if_unusual_segment(self, offset, size, is_irom_segment):
2960          if not is_irom_segment:
2961              if offset > 0x40200000 or offset < 0x3ffe0000 or size > 65536:
2962                  print('WARNING: Suspicious segment 0x%x, length %d' % (offset, size))
2963  
2964      def maybe_patch_segment_data(self, f, segment_data):
2965          """If SHA256 digest of the ELF file needs to be inserted into this segment, do so. Returns segment data."""
2966          segment_len = len(segment_data)
2967          file_pos = f.tell()  # file_pos is position in the .bin file
2968          if self.elf_sha256_offset >= file_pos and self.elf_sha256_offset < file_pos + segment_len:
2969              # SHA256 digest needs to be patched into this binary segment,
2970              # calculate offset of the digest inside the binary segment.
2971              patch_offset = self.elf_sha256_offset - file_pos
2972              # Sanity checks
2973              if patch_offset < self.SEG_HEADER_LEN or patch_offset + self.SHA256_DIGEST_LEN > segment_len:
2974                  raise FatalError('Cannot place SHA256 digest on segment boundary'
2975                                   '(elf_sha256_offset=%d, file_pos=%d, segment_size=%d)' %
2976                                   (self.elf_sha256_offset, file_pos, segment_len))
2977              # offset relative to the data part
2978              patch_offset -= self.SEG_HEADER_LEN
2979              if segment_data[patch_offset:patch_offset + self.SHA256_DIGEST_LEN] != b'\x00' * self.SHA256_DIGEST_LEN:
2980                  raise FatalError('Contents of segment at SHA256 digest offset 0x%x are not all zero. Refusing to overwrite.' %
2981                                   self.elf_sha256_offset)
2982              assert len(self.elf_sha256) == self.SHA256_DIGEST_LEN
2983              segment_data = segment_data[0:patch_offset] + self.elf_sha256 + \
2984                  segment_data[patch_offset + self.SHA256_DIGEST_LEN:]
2985          return segment_data
2986  
2987      def save_segment(self, f, segment, checksum=None):
2988          """ Save the next segment to the image file, return next checksum value if provided """
2989          segment_data = self.maybe_patch_segment_data(f, segment.data)
2990          f.write(struct.pack('<II', segment.addr, len(segment_data)))
2991          f.write(segment_data)
2992          if checksum is not None:
2993              return ESPLoader.checksum(segment_data, checksum)
2994  
2995      def read_checksum(self, f):
2996          """ Return ESPLoader checksum from end of just-read image """
2997          # Skip the padding. The checksum is stored in the last byte so that the
2998          # file is a multiple of 16 bytes.
2999          align_file_position(f, 16)
3000          return ord(f.read(1))
3001  
3002      def calculate_checksum(self):
3003          """ Calculate checksum of loaded image, based on segments in
3004          segment array.
3005          """
3006          checksum = ESPLoader.ESP_CHECKSUM_MAGIC
3007          for seg in self.segments:
3008              if seg.include_in_checksum:
3009                  checksum = ESPLoader.checksum(seg.data, checksum)
3010          return checksum
3011  
3012      def append_checksum(self, f, checksum):
3013          """ Append ESPLoader checksum to the just-written image """
3014          align_file_position(f, 16)
3015          f.write(struct.pack(b'B', checksum))
3016  
3017      def write_common_header(self, f, segments):
3018          f.write(struct.pack('<BBBBI', ESPLoader.ESP_IMAGE_MAGIC, len(segments),
3019                              self.flash_mode, self.flash_size_freq, self.entrypoint))
3020  
3021      def is_irom_addr(self, addr):
3022          """ Returns True if an address starts in the irom region.
3023          Valid for ESP8266 only.
3024          """
3025          return ESP8266ROM.IROM_MAP_START <= addr < ESP8266ROM.IROM_MAP_END
3026  
3027      def get_irom_segment(self):
3028          irom_segments = [s for s in self.segments if self.is_irom_addr(s.addr)]
3029          if len(irom_segments) > 0:
3030              if len(irom_segments) != 1:
3031                  raise FatalError('Found %d segments that could be irom0. Bad ELF file?' % len(irom_segments))
3032              return irom_segments[0]
3033          return None
3034  
3035      def get_non_irom_segments(self):
3036          irom_segment = self.get_irom_segment()
3037          return [s for s in self.segments if s != irom_segment]
3038  
3039      def merge_adjacent_segments(self):
3040          if not self.segments:
3041              return  # nothing to merge
3042  
3043          segments = []
3044          # The easiest way to merge the sections is the browse them backward.
3045          for i in range(len(self.segments) - 1, 0, -1):
3046              # elem is the previous section, the one `next_elem` may need to be
3047              # merged in
3048              elem = self.segments[i - 1]
3049              next_elem = self.segments[i]
3050              if all((elem.get_memory_type(self) == next_elem.get_memory_type(self),
3051                      elem.include_in_checksum == next_elem.include_in_checksum,
3052                      next_elem.addr == elem.addr + len(elem.data))):
3053                  # Merge any segment that ends where the next one starts, without spanning memory types
3054                  #
3055                  # (don't 'pad' any gaps here as they may be excluded from the image due to 'noinit'
3056                  # or other reasons.)
3057                  elem.data += next_elem.data
3058              else:
3059                  # The section next_elem cannot be merged into the previous one,
3060                  # which means it needs to be part of the final segments.
3061                  # As we are browsing the list backward, the elements need to be
3062                  # inserted at the beginning of the final list.
3063                  segments.insert(0, next_elem)
3064  
3065          # The first segment will always be here as it cannot be merged into any
3066          # "previous" section.
3067          segments.insert(0, self.segments[0])
3068  
3069          # note: we could sort segments here as well, but the ordering of segments is sometimes
3070          # important for other reasons (like embedded ELF SHA-256), so we assume that the linker
3071          # script will have produced any adjacent sections in linear order in the ELF, anyhow.
3072          self.segments = segments
3073  
3074      def set_mmu_page_size(self, size):
3075          """ If supported, this should be overridden by the chip-specific class. Gets called in elf2image. """
3076          print('WARNING: Changing MMU page size is not supported on {}! Defaulting to 64KB.'.format(self.ROM_LOADER.CHIP_NAME))
3077  
3078  
3079  class ESP8266ROMFirmwareImage(BaseFirmwareImage):
3080      """ 'Version 1' firmware image, segments loaded directly by the ROM bootloader. """
3081  
3082      ROM_LOADER = ESP8266ROM
3083  
3084      def __init__(self, load_file=None):
3085          super(ESP8266ROMFirmwareImage, self).__init__()
3086          self.flash_mode = 0
3087          self.flash_size_freq = 0
3088          self.version = 1
3089  
3090          if load_file is not None:
3091              segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC)
3092  
3093              for _ in range(segments):
3094                  self.load_segment(load_file)
3095              self.checksum = self.read_checksum(load_file)
3096  
3097              self.verify()
3098  
3099      def default_output_name(self, input_file):
3100          """ Derive a default output name from the ELF name. """
3101          return input_file + '-'
3102  
3103      def save(self, basename):
3104          """ Save a set of V1 images for flashing. Parameter is a base filename. """
3105          # IROM data goes in its own plain binary file
3106          irom_segment = self.get_irom_segment()
3107          if irom_segment is not None:
3108              with open("%s0x%05x.bin" % (basename, irom_segment.addr - ESP8266ROM.IROM_MAP_START), "wb") as f:
3109                  f.write(irom_segment.data)
3110  
3111          # everything but IROM goes at 0x00000 in an image file
3112          normal_segments = self.get_non_irom_segments()
3113          with open("%s0x00000.bin" % basename, 'wb') as f:
3114              self.write_common_header(f, normal_segments)
3115              checksum = ESPLoader.ESP_CHECKSUM_MAGIC
3116              for segment in normal_segments:
3117                  checksum = self.save_segment(f, segment, checksum)
3118              self.append_checksum(f, checksum)
3119  
3120  
3121  ESP8266ROM.BOOTLOADER_IMAGE = ESP8266ROMFirmwareImage
3122  
3123  
3124  class ESP8266V2FirmwareImage(BaseFirmwareImage):
3125      """ 'Version 2' firmware image, segments loaded by software bootloader stub
3126          (ie Espressif bootloader or rboot)
3127      """
3128  
3129      ROM_LOADER = ESP8266ROM
3130  
3131      def __init__(self, load_file=None):
3132          super(ESP8266V2FirmwareImage, self).__init__()
3133          self.version = 2
3134          if load_file is not None:
3135              segments = self.load_common_header(load_file, ESPBOOTLOADER.IMAGE_V2_MAGIC)
3136              if segments != ESPBOOTLOADER.IMAGE_V2_SEGMENT:
3137                  # segment count is not really segment count here, but we expect to see '4'
3138                  print('Warning: V2 header has unexpected "segment" count %d (usually 4)' % segments)
3139  
3140              # irom segment comes before the second header
3141              #
3142              # the file is saved in the image with a zero load address
3143              # in the header, so we need to calculate a load address
3144              irom_segment = self.load_segment(load_file, True)
3145              irom_segment.addr = 0  # for actual mapped addr, add ESP8266ROM.IROM_MAP_START + flashing_addr + 8
3146              irom_segment.include_in_checksum = False
3147  
3148              first_flash_mode = self.flash_mode
3149              first_flash_size_freq = self.flash_size_freq
3150              first_entrypoint = self.entrypoint
3151              # load the second header
3152  
3153              segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC)
3154  
3155              if first_flash_mode != self.flash_mode:
3156                  print('WARNING: Flash mode value in first header (0x%02x) disagrees with second (0x%02x). Using second value.'
3157                        % (first_flash_mode, self.flash_mode))
3158              if first_flash_size_freq != self.flash_size_freq:
3159                  print('WARNING: Flash size/freq value in first header (0x%02x) disagrees with second (0x%02x). Using second value.'
3160                        % (first_flash_size_freq, self.flash_size_freq))
3161              if first_entrypoint != self.entrypoint:
3162                  print('WARNING: Entrypoint address in first header (0x%08x) disagrees with second header (0x%08x). Using second value.'
3163                        % (first_entrypoint, self.entrypoint))
3164  
3165              # load all the usual segments
3166              for _ in range(segments):
3167                  self.load_segment(load_file)
3168              self.checksum = self.read_checksum(load_file)
3169  
3170              self.verify()
3171  
3172      def default_output_name(self, input_file):
3173          """ Derive a default output name from the ELF name. """
3174          irom_segment = self.get_irom_segment()
3175          if irom_segment is not None:
3176              irom_offs = irom_segment.addr - ESP8266ROM.IROM_MAP_START
3177          else:
3178              irom_offs = 0
3179          return "%s-0x%05x.bin" % (os.path.splitext(input_file)[0],
3180                                    irom_offs & ~(ESPLoader.FLASH_SECTOR_SIZE - 1))
3181  
3182      def save(self, filename):
3183          with open(filename, 'wb') as f:
3184              # Save first header for irom0 segment
3185              f.write(struct.pack(b'<BBBBI', ESPBOOTLOADER.IMAGE_V2_MAGIC, ESPBOOTLOADER.IMAGE_V2_SEGMENT,
3186                                  self.flash_mode, self.flash_size_freq, self.entrypoint))
3187  
3188              irom_segment = self.get_irom_segment()
3189              if irom_segment is not None:
3190                  # save irom0 segment, make sure it has load addr 0 in the file
3191                  irom_segment = irom_segment.copy_with_new_addr(0)
3192                  irom_segment.pad_to_alignment(16)  # irom_segment must end on a 16 byte boundary
3193                  self.save_segment(f, irom_segment)
3194  
3195              # second header, matches V1 header and contains loadable segments
3196              normal_segments = self.get_non_irom_segments()
3197              self.write_common_header(f, normal_segments)
3198              checksum = ESPLoader.ESP_CHECKSUM_MAGIC
3199              for segment in normal_segments:
3200                  checksum = self.save_segment(f, segment, checksum)
3201              self.append_checksum(f, checksum)
3202  
3203          # calculate a crc32 of entire file and append
3204          # (algorithm used by recent 8266 SDK bootloaders)
3205          with open(filename, 'rb') as f:
3206              crc = esp8266_crc32(f.read())
3207          with open(filename, 'ab') as f:
3208              f.write(struct.pack(b'<I', crc))
3209  
3210  
3211  def esp8266_crc32(data):
3212      """
3213      CRC32 algorithm used by 8266 SDK bootloader (and gen_appbin.py).
3214      """
3215      crc = binascii.crc32(data, 0) & 0xFFFFFFFF
3216      if crc & 0x80000000:
3217          return crc ^ 0xFFFFFFFF
3218      else:
3219          return crc + 1
3220  
3221  
3222  class ESP32FirmwareImage(BaseFirmwareImage):
3223      """ ESP32 firmware image is very similar to V1 ESP8266 image,
3224      except with an additional 16 byte reserved header at top of image,
3225      and because of new flash mapping capabilities the flash-mapped regions
3226      can be placed in the normal image (just @ 64kB padded offsets).
3227      """
3228  
3229      ROM_LOADER = ESP32ROM
3230  
3231      # ROM bootloader will read the wp_pin field if SPI flash
3232      # pins are remapped via flash. IDF actually enables QIO only
3233      # from software bootloader, so this can be ignored. But needs
3234      # to be set to this value so ROM bootloader will skip it.
3235      WP_PIN_DISABLED = 0xEE
3236  
3237      EXTENDED_HEADER_STRUCT_FMT = "<BBBBHBHH" + ("B" * 4) + "B"
3238  
3239      IROM_ALIGN = 65536
3240  
3241      def __init__(self, load_file=None):
3242          super(ESP32FirmwareImage, self).__init__()
3243          self.secure_pad = None
3244          self.flash_mode = 0
3245          self.flash_size_freq = 0
3246          self.version = 1
3247          self.wp_pin = self.WP_PIN_DISABLED
3248          # SPI pin drive levels
3249          self.clk_drv = 0
3250          self.q_drv = 0
3251          self.d_drv = 0
3252          self.cs_drv = 0
3253          self.hd_drv = 0
3254          self.wp_drv = 0
3255          self.min_rev = 0
3256          self.min_rev_full = 0
3257          self.max_rev_full = 0
3258  
3259          self.append_digest = True
3260  
3261          if load_file is not None:
3262              start = load_file.tell()
3263  
3264              segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC)
3265              self.load_extended_header(load_file)
3266  
3267              for _ in range(segments):
3268                  self.load_segment(load_file)
3269              self.checksum = self.read_checksum(load_file)
3270  
3271              if self.append_digest:
3272                  end = load_file.tell()
3273                  self.stored_digest = load_file.read(32)
3274                  load_file.seek(start)
3275                  calc_digest = hashlib.sha256()
3276                  calc_digest.update(load_file.read(end - start))
3277                  self.calc_digest = calc_digest.digest()  # TODO: decide what to do here?
3278  
3279              self.verify()
3280  
3281      def is_flash_addr(self, addr):
3282          return (self.ROM_LOADER.IROM_MAP_START <= addr < self.ROM_LOADER.IROM_MAP_END) \
3283              or (self.ROM_LOADER.DROM_MAP_START <= addr < self.ROM_LOADER.DROM_MAP_END)
3284  
3285      def default_output_name(self, input_file):
3286          """ Derive a default output name from the ELF name. """
3287          return "%s.bin" % (os.path.splitext(input_file)[0])
3288  
3289      def warn_if_unusual_segment(self, offset, size, is_irom_segment):
3290          pass  # TODO: add warnings for ESP32 segment offset/size combinations that are wrong
3291  
3292      def save(self, filename):
3293          total_segments = 0
3294          with io.BytesIO() as f:  # write file to memory first
3295              self.write_common_header(f, self.segments)
3296  
3297              # first 4 bytes of header are read by ROM bootloader for SPI
3298              # config, but currently unused
3299              self.save_extended_header(f)
3300  
3301              checksum = ESPLoader.ESP_CHECKSUM_MAGIC
3302  
3303              # split segments into flash-mapped vs ram-loaded, and take copies so we can mutate them
3304              flash_segments = [copy.deepcopy(s) for s in sorted(self.segments, key=lambda s:s.addr) if self.is_flash_addr(s.addr)]
3305              ram_segments = [copy.deepcopy(s) for s in sorted(self.segments, key=lambda s:s.addr) if not self.is_flash_addr(s.addr)]
3306  
3307              # check for multiple ELF sections that are mapped in the same flash mapping region.
3308              # this is usually a sign of a broken linker script, but if you have a legitimate
3309              # use case then let us know
3310              if len(flash_segments) > 0:
3311                  last_addr = flash_segments[0].addr
3312                  for segment in flash_segments[1:]:
3313                      if segment.addr // self.IROM_ALIGN == last_addr // self.IROM_ALIGN:
3314                          raise FatalError(("Segment loaded at 0x%08x lands in same 64KB flash mapping as segment loaded at 0x%08x. "
3315                                            "Can't generate binary. Suggest changing linker script or ELF to merge sections.") %
3316                                           (segment.addr, last_addr))
3317                      last_addr = segment.addr
3318  
3319              def get_alignment_data_needed(segment):
3320                  # Actual alignment (in data bytes) required for a segment header: positioned so that
3321                  # after we write the next 8 byte header, file_offs % IROM_ALIGN == segment.addr % IROM_ALIGN
3322                  #
3323                  # (this is because the segment's vaddr may not be IROM_ALIGNed, more likely is aligned
3324                  # IROM_ALIGN+0x18 to account for the binary file header
3325                  align_past = (segment.addr % self.IROM_ALIGN) - self.SEG_HEADER_LEN
3326                  pad_len = (self.IROM_ALIGN - (f.tell() % self.IROM_ALIGN)) + align_past
3327                  if pad_len == 0 or pad_len == self.IROM_ALIGN:
3328                      return 0  # already aligned
3329  
3330                  # subtract SEG_HEADER_LEN a second time, as the padding block has a header as well
3331                  pad_len -= self.SEG_HEADER_LEN
3332                  if pad_len < 0:
3333                      pad_len += self.IROM_ALIGN
3334                  return pad_len
3335  
3336              # try to fit each flash segment on a 64kB aligned boundary
3337              # by padding with parts of the non-flash segments...
3338              while len(flash_segments) > 0:
3339                  segment = flash_segments[0]
3340                  pad_len = get_alignment_data_needed(segment)
3341                  if pad_len > 0:  # need to pad
3342                      if len(ram_segments) > 0 and pad_len > self.SEG_HEADER_LEN:
3343                          pad_segment = ram_segments[0].split_image(pad_len)
3344                          if len(ram_segments[0].data) == 0:
3345                              ram_segments.pop(0)
3346                      else:
3347                          pad_segment = ImageSegment(0, b'\x00' * pad_len, f.tell())
3348                      checksum = self.save_segment(f, pad_segment, checksum)
3349                      total_segments += 1
3350                  else:
3351                      # write the flash segment
3352                      assert (f.tell() + 8) % self.IROM_ALIGN == segment.addr % self.IROM_ALIGN
3353                      checksum = self.save_flash_segment(f, segment, checksum)
3354                      flash_segments.pop(0)
3355                      total_segments += 1
3356  
3357              # flash segments all written, so write any remaining RAM segments
3358              for segment in ram_segments:
3359                  checksum = self.save_segment(f, segment, checksum)
3360                  total_segments += 1
3361  
3362              if self.secure_pad:
3363                  # pad the image so that after signing it will end on a a 64KB boundary.
3364                  # This ensures all mapped flash content will be verified.
3365                  if not self.append_digest:
3366                      raise FatalError("secure_pad only applies if a SHA-256 digest is also appended to the image")
3367                  align_past = (f.tell() + self.SEG_HEADER_LEN) % self.IROM_ALIGN
3368                  # 16 byte aligned checksum (force the alignment to simplify calculations)
3369                  checksum_space = 16
3370                  if self.secure_pad == '1':
3371                      # after checksum: SHA-256 digest + (to be added by signing process) version, signature + 12 trailing bytes due to alignment
3372                      space_after_checksum = 32 + 4 + 64 + 12
3373                  elif self.secure_pad == '2':  # Secure Boot V2
3374                      # after checksum: SHA-256 digest + signature sector, but we place signature sector after the 64KB boundary
3375                      space_after_checksum = 32
3376                  pad_len = (self.IROM_ALIGN - align_past - checksum_space - space_after_checksum) % self.IROM_ALIGN
3377                  pad_segment = ImageSegment(0, b'\x00' * pad_len, f.tell())
3378  
3379                  checksum = self.save_segment(f, pad_segment, checksum)
3380                  total_segments += 1
3381  
3382              # done writing segments
3383              self.append_checksum(f, checksum)
3384              image_length = f.tell()
3385  
3386              if self.secure_pad:
3387                  assert ((image_length + space_after_checksum) % self.IROM_ALIGN) == 0
3388  
3389              # kinda hacky: go back to the initial header and write the new segment count
3390              # that includes padding segments. This header is not checksummed
3391              f.seek(1)
3392              try:
3393                  f.write(chr(total_segments))
3394              except TypeError:  # Python 3
3395                  f.write(bytes([total_segments]))
3396  
3397              if self.append_digest:
3398                  # calculate the SHA256 of the whole file and append it
3399                  f.seek(0)
3400                  digest = hashlib.sha256()
3401                  digest.update(f.read(image_length))
3402                  f.write(digest.digest())
3403  
3404              if self.pad_to_size:
3405                  image_length = f.tell()
3406                  if image_length % self.pad_to_size != 0:
3407                      pad_by = self.pad_to_size - (image_length % self.pad_to_size)
3408                      f.write(b"\xff" * pad_by)
3409  
3410              with open(filename, 'wb') as real_file:
3411                  real_file.write(f.getvalue())
3412  
3413      def save_flash_segment(self, f, segment, checksum=None):
3414          """ Save the next segment to the image file, return next checksum value if provided """
3415          segment_end_pos = f.tell() + len(segment.data) + self.SEG_HEADER_LEN
3416          segment_len_remainder = segment_end_pos % self.IROM_ALIGN
3417          if segment_len_remainder < 0x24:
3418              # Work around a bug in ESP-IDF 2nd stage bootloader, that it didn't map the
3419              # last MMU page, if an IROM/DROM segment was < 0x24 bytes over the page boundary.
3420              segment.data += b'\x00' * (0x24 - segment_len_remainder)
3421          return self.save_segment(f, segment, checksum)
3422  
3423      def load_extended_header(self, load_file):
3424          def split_byte(n):
3425              return (n & 0x0F, (n >> 4) & 0x0F)
3426  
3427          fields = list(struct.unpack(self.EXTENDED_HEADER_STRUCT_FMT, load_file.read(16)))
3428  
3429          self.wp_pin = fields[0]
3430  
3431          # SPI pin drive stengths are two per byte
3432          self.clk_drv, self.q_drv = split_byte(fields[1])
3433          self.d_drv, self.cs_drv = split_byte(fields[2])
3434          self.hd_drv, self.wp_drv = split_byte(fields[3])
3435  
3436          chip_id = fields[4]
3437          if chip_id != self.ROM_LOADER.IMAGE_CHIP_ID:
3438              print(("Unexpected chip id in image. Expected %d but value was %d. "
3439                     "Is this image for a different chip model?") % (self.ROM_LOADER.IMAGE_CHIP_ID, chip_id))
3440  
3441          self.min_rev = fields[5]
3442          self.min_rev_full = fields[6]
3443          self.max_rev_full = fields[7]
3444  
3445          # reserved fields in the middle should all be zero
3446          if any(f for f in fields[8:-1] if f != 0):
3447              print("Warning: some reserved header fields have non-zero values. This image may be from a newer esptool.py?")
3448  
3449          append_digest = fields[-1]  # last byte is append_digest
3450          if append_digest in [0, 1]:
3451              self.append_digest = (append_digest == 1)
3452          else:
3453              raise RuntimeError("Invalid value for append_digest field (0x%02x). Should be 0 or 1.", append_digest)
3454  
3455      def save_extended_header(self, save_file):
3456          def join_byte(ln, hn):
3457              return (ln & 0x0F) + ((hn & 0x0F) << 4)
3458  
3459          append_digest = 1 if self.append_digest else 0
3460  
3461          fields = [self.wp_pin,
3462                    join_byte(self.clk_drv, self.q_drv),
3463                    join_byte(self.d_drv, self.cs_drv),
3464                    join_byte(self.hd_drv, self.wp_drv),
3465                    self.ROM_LOADER.IMAGE_CHIP_ID,
3466                    self.min_rev,
3467                    self.min_rev_full,
3468                    self.max_rev_full]
3469          fields += [0] * 4  # padding
3470          fields += [append_digest]
3471  
3472          packed = struct.pack(self.EXTENDED_HEADER_STRUCT_FMT, *fields)
3473          save_file.write(packed)
3474  
3475  
3476  class ESP8266V3FirmwareImage(ESP32FirmwareImage):
3477      """ ESP8266 V3 firmware image is very similar to ESP32 image
3478      """
3479  
3480      EXTENDED_HEADER_STRUCT_FMT = "B" * 16
3481  
3482      def is_flash_addr(self, addr):
3483          return (addr > ESP8266ROM.IROM_MAP_START)
3484  
3485      def save(self, filename):
3486          total_segments = 0
3487          with io.BytesIO() as f:  # write file to memory first
3488              self.write_common_header(f, self.segments)
3489  
3490              checksum = ESPLoader.ESP_CHECKSUM_MAGIC
3491  
3492              # split segments into flash-mapped vs ram-loaded, and take copies so we can mutate them
3493              flash_segments = [copy.deepcopy(s) for s in sorted(self.segments, key=lambda s:s.addr) if self.is_flash_addr(s.addr) and len(s.data)]
3494              ram_segments = [copy.deepcopy(s) for s in sorted(self.segments, key=lambda s:s.addr) if not self.is_flash_addr(s.addr) and len(s.data)]
3495  
3496              # check for multiple ELF sections that are mapped in the same flash mapping region.
3497              # this is usually a sign of a broken linker script, but if you have a legitimate
3498              # use case then let us know
3499              if len(flash_segments) > 0:
3500                  last_addr = flash_segments[0].addr
3501                  for segment in flash_segments[1:]:
3502                      if segment.addr // self.IROM_ALIGN == last_addr // self.IROM_ALIGN:
3503                          raise FatalError(("Segment loaded at 0x%08x lands in same 64KB flash mapping as segment loaded at 0x%08x. "
3504                                            "Can't generate binary. Suggest changing linker script or ELF to merge sections.") %
3505                                           (segment.addr, last_addr))
3506                      last_addr = segment.addr
3507  
3508              # try to fit each flash segment on a 64kB aligned boundary
3509              # by padding with parts of the non-flash segments...
3510              while len(flash_segments) > 0:
3511                  segment = flash_segments[0]
3512                  # remove 8 bytes empty data for insert segment header
3513                  if segment.name == '.flash.rodata':
3514                      segment.data = segment.data[8:]
3515                  # write the flash segment
3516                  checksum = self.save_segment(f, segment, checksum)
3517                  flash_segments.pop(0)
3518                  total_segments += 1
3519  
3520              # flash segments all written, so write any remaining RAM segments
3521              for segment in ram_segments:
3522                  checksum = self.save_segment(f, segment, checksum)
3523                  total_segments += 1
3524  
3525              # done writing segments
3526              self.append_checksum(f, checksum)
3527              image_length = f.tell()
3528  
3529              # kinda hacky: go back to the initial header and write the new segment count
3530              # that includes padding segments. This header is not checksummed
3531              f.seek(1)
3532              try:
3533                  f.write(chr(total_segments))
3534              except TypeError:  # Python 3
3535                  f.write(bytes([total_segments]))
3536  
3537              if self.append_digest:
3538                  # calculate the SHA256 of the whole file and append it
3539                  f.seek(0)
3540                  digest = hashlib.sha256()
3541                  digest.update(f.read(image_length))
3542                  f.write(digest.digest())
3543  
3544              with open(filename, 'wb') as real_file:
3545                  real_file.write(f.getvalue())
3546  
3547      def load_extended_header(self, load_file):
3548          def split_byte(n):
3549              return (n & 0x0F, (n >> 4) & 0x0F)
3550  
3551          fields = list(struct.unpack(self.EXTENDED_HEADER_STRUCT_FMT, load_file.read(16)))
3552  
3553          self.wp_pin = fields[0]
3554  
3555          # SPI pin drive stengths are two per byte
3556          self.clk_drv, self.q_drv = split_byte(fields[1])
3557          self.d_drv, self.cs_drv = split_byte(fields[2])
3558          self.hd_drv, self.wp_drv = split_byte(fields[3])
3559  
3560          if fields[15] in [0, 1]:
3561              self.append_digest = (fields[15] == 1)
3562          else:
3563              raise RuntimeError("Invalid value for append_digest field (0x%02x). Should be 0 or 1.", fields[15])
3564  
3565          # remaining fields in the middle should all be zero
3566          if any(f for f in fields[4:15] if f != 0):
3567              print("Warning: some reserved header fields have non-zero values. This image may be from a newer esptool.py?")
3568  
3569  
3570  ESP32ROM.BOOTLOADER_IMAGE = ESP32FirmwareImage
3571  
3572  
3573  class ESP32S2FirmwareImage(ESP32FirmwareImage):
3574      """ ESP32S2 Firmware Image almost exactly the same as ESP32FirmwareImage """
3575      ROM_LOADER = ESP32S2ROM
3576  
3577  
3578  ESP32S2ROM.BOOTLOADER_IMAGE = ESP32S2FirmwareImage
3579  
3580  
3581  class ESP32S3BETA2FirmwareImage(ESP32FirmwareImage):
3582      """ ESP32S3 Firmware Image almost exactly the same as ESP32FirmwareImage """
3583      ROM_LOADER = ESP32S3BETA2ROM
3584  
3585  
3586  ESP32S3BETA2ROM.BOOTLOADER_IMAGE = ESP32S3BETA2FirmwareImage
3587  
3588  
3589  class ESP32S3FirmwareImage(ESP32FirmwareImage):
3590      """ ESP32S3 Firmware Image almost exactly the same as ESP32FirmwareImage """
3591      ROM_LOADER = ESP32S3ROM
3592  
3593  
3594  ESP32S3ROM.BOOTLOADER_IMAGE = ESP32S3FirmwareImage
3595  
3596  
3597  class ESP32C3FirmwareImage(ESP32FirmwareImage):
3598      """ ESP32C3 Firmware Image almost exactly the same as ESP32FirmwareImage """
3599      ROM_LOADER = ESP32C3ROM
3600  
3601  
3602  ESP32C3ROM.BOOTLOADER_IMAGE = ESP32C3FirmwareImage
3603  
3604  
3605  class ESP32C6BETAFirmwareImage(ESP32FirmwareImage):
3606      """ ESP32C6 Firmware Image almost exactly the same as ESP32FirmwareImage """
3607      ROM_LOADER = ESP32C6BETAROM
3608  
3609  
3610  ESP32C6BETAROM.BOOTLOADER_IMAGE = ESP32C6BETAFirmwareImage
3611  
3612  
3613  class ESP32H2BETA1FirmwareImage(ESP32FirmwareImage):
3614      """ ESP32H2 Firmware Image almost exactly the same as ESP32FirmwareImage """
3615      ROM_LOADER = ESP32H2BETA1ROM
3616  
3617  
3618  ESP32H2BETA1ROM.BOOTLOADER_IMAGE = ESP32H2BETA1FirmwareImage
3619  
3620  
3621  class ESP32H2BETA2FirmwareImage(ESP32FirmwareImage):
3622      """ ESP32H2 Firmware Image almost exactly the same as ESP32FirmwareImage """
3623      ROM_LOADER = ESP32H2BETA2ROM
3624  
3625  
3626  ESP32H2BETA2ROM.BOOTLOADER_IMAGE = ESP32H2BETA2FirmwareImage
3627  
3628  
3629  class ESP32C2FirmwareImage(ESP32FirmwareImage):
3630      """ ESP32C2 Firmware Image almost exactly the same as ESP32FirmwareImage """
3631      ROM_LOADER = ESP32C2ROM
3632  
3633      def set_mmu_page_size(self, size):
3634          if size not in [16384, 32768, 65536]:
3635              raise FatalError("{} is not a valid page size.".format(size))
3636          self.IROM_ALIGN = size
3637  
3638  
3639  ESP32C2ROM.BOOTLOADER_IMAGE = ESP32C2FirmwareImage
3640  
3641  
3642  class ELFFile(object):
3643      SEC_TYPE_PROGBITS = 0x01
3644      SEC_TYPE_STRTAB = 0x03
3645      SEC_TYPE_INITARRAY = 0x0e
3646      SEC_TYPE_FINIARRAY = 0x0f
3647  
3648      PROG_SEC_TYPES = (SEC_TYPE_PROGBITS, SEC_TYPE_INITARRAY, SEC_TYPE_FINIARRAY)
3649  
3650      LEN_SEC_HEADER = 0x28
3651  
3652      SEG_TYPE_LOAD = 0x01
3653      LEN_SEG_HEADER = 0x20
3654  
3655      def __init__(self, name):
3656          # Load sections from the ELF file
3657          self.name = name
3658          with open(self.name, 'rb') as f:
3659              self._read_elf_file(f)
3660  
3661      def get_section(self, section_name):
3662          for s in self.sections:
3663              if s.name == section_name:
3664                  return s
3665          raise ValueError("No section %s in ELF file" % section_name)
3666  
3667      def _read_elf_file(self, f):
3668          # read the ELF file header
3669          LEN_FILE_HEADER = 0x34
3670          try:
3671              (ident, _type, machine, _version,
3672               self.entrypoint, _phoff, shoff, _flags,
3673               _ehsize, _phentsize, _phnum, shentsize,
3674               shnum, shstrndx) = struct.unpack("<16sHHLLLLLHHHHHH", f.read(LEN_FILE_HEADER))
3675          except struct.error as e:
3676              raise FatalError("Failed to read a valid ELF header from %s: %s" % (self.name, e))
3677  
3678          if byte(ident, 0) != 0x7f or ident[1:4] != b'ELF':
3679              raise FatalError("%s has invalid ELF magic header" % self.name)
3680          if machine not in [0x5e, 0xf3]:
3681              raise FatalError("%s does not appear to be an Xtensa or an RISCV ELF file. e_machine=%04x" % (self.name, machine))
3682          if shentsize != self.LEN_SEC_HEADER:
3683              raise FatalError("%s has unexpected section header entry size 0x%x (not 0x%x)" % (self.name, shentsize, self.LEN_SEC_HEADER))
3684          if shnum == 0:
3685              raise FatalError("%s has 0 section headers" % (self.name))
3686          self._read_sections(f, shoff, shnum, shstrndx)
3687          self._read_segments(f, _phoff, _phnum, shstrndx)
3688  
3689      def _read_sections(self, f, section_header_offs, section_header_count, shstrndx):
3690          f.seek(section_header_offs)
3691          len_bytes = section_header_count * self.LEN_SEC_HEADER
3692          section_header = f.read(len_bytes)
3693          if len(section_header) == 0:
3694              raise FatalError("No section header found at offset %04x in ELF file." % section_header_offs)
3695          if len(section_header) != (len_bytes):
3696              raise FatalError("Only read 0x%x bytes from section header (expected 0x%x.) Truncated ELF file?" % (len(section_header), len_bytes))
3697  
3698          # walk through the section header and extract all sections
3699          section_header_offsets = range(0, len(section_header), self.LEN_SEC_HEADER)
3700  
3701          def read_section_header(offs):
3702              name_offs, sec_type, _flags, lma, sec_offs, size = struct.unpack_from("<LLLLLL", section_header[offs:])
3703              return (name_offs, sec_type, lma, size, sec_offs)
3704          all_sections = [read_section_header(offs) for offs in section_header_offsets]
3705          prog_sections = [s for s in all_sections if s[1] in ELFFile.PROG_SEC_TYPES]
3706  
3707          # search for the string table section
3708          if not (shstrndx * self.LEN_SEC_HEADER) in section_header_offsets:
3709              raise FatalError("ELF file has no STRTAB section at shstrndx %d" % shstrndx)
3710          _, sec_type, _, sec_size, sec_offs = read_section_header(shstrndx * self.LEN_SEC_HEADER)
3711          if sec_type != ELFFile.SEC_TYPE_STRTAB:
3712              print('WARNING: ELF file has incorrect STRTAB section type 0x%02x' % sec_type)
3713          f.seek(sec_offs)
3714          string_table = f.read(sec_size)
3715  
3716          # build the real list of ELFSections by reading the actual section names from the
3717          # string table section, and actual data for each section from the ELF file itself
3718          def lookup_string(offs):
3719              raw = string_table[offs:]
3720              return raw[:raw.index(b'\x00')]
3721  
3722          def read_data(offs, size):
3723              f.seek(offs)
3724              return f.read(size)
3725  
3726          prog_sections = [ELFSection(lookup_string(n_offs), lma, read_data(offs, size)) for (n_offs, _type, lma, size, offs) in prog_sections
3727                           if lma != 0 and size > 0]
3728          self.sections = prog_sections
3729  
3730      def _read_segments(self, f, segment_header_offs, segment_header_count, shstrndx):
3731          f.seek(segment_header_offs)
3732          len_bytes = segment_header_count * self.LEN_SEG_HEADER
3733          segment_header = f.read(len_bytes)
3734          if len(segment_header) == 0:
3735              raise FatalError("No segment header found at offset %04x in ELF file." % segment_header_offs)
3736          if len(segment_header) != (len_bytes):
3737              raise FatalError("Only read 0x%x bytes from segment header (expected 0x%x.) Truncated ELF file?" % (len(segment_header), len_bytes))
3738  
3739          # walk through the segment header and extract all segments
3740          segment_header_offsets = range(0, len(segment_header), self.LEN_SEG_HEADER)
3741  
3742          def read_segment_header(offs):
3743              seg_type, seg_offs, _vaddr, lma, size, _memsize, _flags, _align = struct.unpack_from("<LLLLLLLL", segment_header[offs:])
3744              return (seg_type, lma, size, seg_offs)
3745          all_segments = [read_segment_header(offs) for offs in segment_header_offsets]
3746          prog_segments = [s for s in all_segments if s[0] == ELFFile.SEG_TYPE_LOAD]
3747  
3748          def read_data(offs, size):
3749              f.seek(offs)
3750              return f.read(size)
3751  
3752          prog_segments = [ELFSection(b'PHDR', lma, read_data(offs, size)) for (_type, lma, size, offs) in prog_segments
3753                           if lma != 0 and size > 0]
3754          self.segments = prog_segments
3755  
3756      def sha256(self):
3757          # return SHA256 hash of the input ELF file
3758          sha256 = hashlib.sha256()
3759          with open(self.name, 'rb') as f:
3760              sha256.update(f.read())
3761          return sha256.digest()
3762  
3763  
3764  def slip_reader(port, trace_function):
3765      """Generator to read SLIP packets from a serial port.
3766      Yields one full SLIP packet at a time, raises exception on timeout or invalid data.
3767  
3768      Designed to avoid too many calls to serial.read(1), which can bog
3769      down on slow systems.
3770      """
3771      partial_packet = None
3772      in_escape = False
3773      successful_slip = False
3774      while True:
3775          waiting = port.inWaiting()
3776          read_bytes = port.read(1 if waiting == 0 else waiting)
3777          if read_bytes == b'':
3778              if partial_packet is None:  # fail due to no data
3779                  msg = "Serial data stream stopped: Possible serial noise or corruption." if successful_slip else "No serial data received."
3780              else:  # fail during packet transfer
3781                  msg = "Packet content transfer stopped (received {} bytes)".format(len(partial_packet))
3782              trace_function(msg)
3783              raise FatalError(msg)
3784          trace_function("Read %d bytes: %s", len(read_bytes), HexFormatter(read_bytes))
3785          for b in read_bytes:
3786              if type(b) is int:
3787                  b = bytes([b])  # python 2/3 compat
3788  
3789              if partial_packet is None:  # waiting for packet header
3790                  if b == b'\xc0':
3791                      partial_packet = b""
3792                  else:
3793                      trace_function("Read invalid data: %s", HexFormatter(read_bytes))
3794                      trace_function("Remaining data in serial buffer: %s", HexFormatter(port.read(port.inWaiting())))
3795                      raise FatalError('Invalid head of packet (0x%s): Possible serial noise or corruption.' % hexify(b))
3796              elif in_escape:  # part-way through escape sequence
3797                  in_escape = False
3798                  if b == b'\xdc':
3799                      partial_packet += b'\xc0'
3800                  elif b == b'\xdd':
3801                      partial_packet += b'\xdb'
3802                  else:
3803                      trace_function("Read invalid data: %s", HexFormatter(read_bytes))
3804                      trace_function("Remaining data in serial buffer: %s", HexFormatter(port.read(port.inWaiting())))
3805                      raise FatalError('Invalid SLIP escape (0xdb, 0x%s)' % (hexify(b)))
3806              elif b == b'\xdb':  # start of escape sequence
3807                  in_escape = True
3808              elif b == b'\xc0':  # end of packet
3809                  trace_function("Received full packet: %s", HexFormatter(partial_packet))
3810                  yield partial_packet
3811                  partial_packet = None
3812                  successful_slip = True
3813              else:  # normal byte in packet
3814                  partial_packet += b
3815  
3816  
3817  def arg_auto_int(x):
3818      return int(x, 0)
3819  
3820  
3821  def format_chip_name(c):
3822      """ Normalize chip name from user input """
3823      c = c.lower().replace('-', '')
3824      if c == 'esp8684':  # TODO: Delete alias, ESPTOOL-389
3825          print('WARNING: Chip name ESP8684 is deprecated in favor of ESP32-C2 and will be removed in a future release. Using ESP32-C2 instead.')
3826          return 'esp32c2'
3827      return c
3828  
3829  
3830  def div_roundup(a, b):
3831      """ Return a/b rounded up to nearest integer,
3832      equivalent result to int(math.ceil(float(int(a)) / float(int(b))), only
3833      without possible floating point accuracy errors.
3834      """
3835      return (int(a) + int(b) - 1) // int(b)
3836  
3837  
3838  def align_file_position(f, size):
3839      """ Align the position in the file to the next block of specified size """
3840      align = (size - 1) - (f.tell() % size)
3841      f.seek(align, 1)
3842  
3843  
3844  def flash_size_bytes(size):
3845      """ Given a flash size of the type passed in args.flash_size
3846      (ie 512KB or 1MB) then return the size in bytes.
3847      """
3848      if "MB" in size:
3849          return int(size[:size.index("MB")]) * 1024 * 1024
3850      elif "KB" in size:
3851          return int(size[:size.index("KB")]) * 1024
3852      else:
3853          raise FatalError("Unknown size %s" % size)
3854  
3855  
3856  def hexify(s, uppercase=True):
3857      format_str = '%02X' if uppercase else '%02x'
3858      if not PYTHON2:
3859          return ''.join(format_str % c for c in s)
3860      else:
3861          return ''.join(format_str % ord(c) for c in s)
3862  
3863  
3864  class HexFormatter(object):
3865      """
3866      Wrapper class which takes binary data in its constructor
3867      and returns a hex string as it's __str__ method.
3868  
3869      This is intended for "lazy formatting" of trace() output
3870      in hex format. Avoids overhead (significant on slow computers)
3871      of generating long hex strings even if tracing is disabled.
3872  
3873      Note that this doesn't save any overhead if passed as an
3874      argument to "%", only when passed to trace()
3875  
3876      If auto_split is set (default), any long line (> 16 bytes) will be
3877      printed as separately indented lines, with ASCII decoding at the end
3878      of each line.
3879      """
3880      def __init__(self, binary_string, auto_split=True):
3881          self._s = binary_string
3882          self._auto_split = auto_split
3883  
3884      def __str__(self):
3885          if self._auto_split and len(self._s) > 16:
3886              result = ""
3887              s = self._s
3888              while len(s) > 0:
3889                  line = s[:16]
3890                  ascii_line = "".join(c if (c == ' ' or (c in string.printable and c not in string.whitespace))
3891                                       else '.' for c in line.decode('ascii', 'replace'))
3892                  s = s[16:]
3893                  result += "\n    %-16s %-16s | %s" % (hexify(line[:8], False), hexify(line[8:], False), ascii_line)
3894              return result
3895          else:
3896              return hexify(self._s, False)
3897  
3898  
3899  def pad_to(data, alignment, pad_character=b'\xFF'):
3900      """ Pad to the next alignment boundary """
3901      pad_mod = len(data) % alignment
3902      if pad_mod != 0:
3903          data += pad_character * (alignment - pad_mod)
3904      return data
3905  
3906  
3907  class FatalError(RuntimeError):
3908      """
3909      Wrapper class for runtime errors that aren't caused by internal bugs, but by
3910      ESP ROM responses or input content.
3911      """
3912      def __init__(self, message):
3913          RuntimeError.__init__(self, message)
3914  
3915      @staticmethod
3916      def WithResult(message, result):
3917          """
3918          Return a fatal error object that appends the hex values of
3919          'result' and its meaning as a string formatted argument.
3920          """
3921  
3922          err_defs = {
3923              0x101: 'Out of memory',
3924              0x102: 'Invalid argument',
3925              0x103: 'Invalid state',
3926              0x104: 'Invalid size',
3927              0x105: 'Requested resource not found',
3928              0x106: 'Operation or feature not supported',
3929              0x107: 'Operation timed out',
3930              0x108: 'Received response was invalid',
3931              0x109: 'CRC or checksum was invalid',
3932              0x10A: 'Version was invalid',
3933              0x10B: 'MAC address was invalid',
3934              # Flasher stub error codes
3935              0xC000: 'Bad data length',
3936              0xC100: 'Bad data checksum',
3937              0xC200: 'Bad blocksize',
3938              0xC300: 'Invalid command',
3939              0xC400: 'Failed SPI operation',
3940              0xC500: 'Failed SPI unlock',
3941              0xC600: 'Not in flash mode',
3942              0xC700: 'Inflate error',
3943              0xC800: 'Not enough data',
3944              0xC900: 'Too much data',
3945              0xFF00: 'Command not implemented',
3946          }
3947  
3948          err_code = struct.unpack(">H", result[:2])
3949          message += " (result was {}: {})".format(hexify(result), err_defs.get(err_code[0], 'Unknown result'))
3950          return FatalError(message)
3951  
3952  
3953  class NotImplementedInROMError(FatalError):
3954      """
3955      Wrapper class for the error thrown when a particular ESP bootloader function
3956      is not implemented in the ROM bootloader.
3957      """
3958      def __init__(self, bootloader, func):
3959          FatalError.__init__(self, "%s ROM does not support function %s." % (bootloader.CHIP_NAME, func.__name__))
3960  
3961  
3962  class NotSupportedError(FatalError):
3963      def __init__(self, esp, function_name):
3964          FatalError.__init__(self, "Function %s is not supported for %s." % (function_name, esp.CHIP_NAME))
3965  
3966  # "Operation" commands, executable at command line. One function each
3967  #
3968  # Each function takes either two args (<ESPLoader instance>, <args>) or a single <args>
3969  # argument.
3970  
3971  
3972  class UnsupportedCommandError(RuntimeError):
3973      """
3974      Wrapper class for when ROM loader returns an invalid command response.
3975  
3976      Usually this indicates the loader is running in Secure Download Mode.
3977      """
3978      def __init__(self, esp, op):
3979          if esp.secure_download_mode:
3980              msg = "This command (0x%x) is not supported in Secure Download Mode" % op
3981          else:
3982              msg = "Invalid (unsupported) command 0x%x" % op
3983          RuntimeError.__init__(self, msg)
3984  
3985  
3986  def load_ram(esp, args):
3987      image = LoadFirmwareImage(esp.CHIP_NAME, args.filename)
3988  
3989      print('RAM boot...')
3990      for seg in image.segments:
3991          size = len(seg.data)
3992          print('Downloading %d bytes at %08x...' % (size, seg.addr), end=' ')
3993          sys.stdout.flush()
3994          esp.mem_begin(size, div_roundup(size, esp.ESP_RAM_BLOCK), esp.ESP_RAM_BLOCK, seg.addr)
3995  
3996          seq = 0
3997          while len(seg.data) > 0:
3998              esp.mem_block(seg.data[0:esp.ESP_RAM_BLOCK], seq)
3999              seg.data = seg.data[esp.ESP_RAM_BLOCK:]
4000              seq += 1
4001          print('done!')
4002  
4003      print('All segments done, executing at %08x' % image.entrypoint)
4004      esp.mem_finish(image.entrypoint)
4005  
4006  
4007  def read_mem(esp, args):
4008      print('0x%08x = 0x%08x' % (args.address, esp.read_reg(args.address)))
4009  
4010  
4011  def write_mem(esp, args):
4012      esp.write_reg(args.address, args.value, args.mask, 0)
4013      print('Wrote %08x, mask %08x to %08x' % (args.value, args.mask, args.address))
4014  
4015  
4016  def dump_mem(esp, args):
4017      with open(args.filename, 'wb') as f:
4018          for i in range(args.size // 4):
4019              d = esp.read_reg(args.address + (i * 4))
4020              f.write(struct.pack(b'<I', d))
4021              if f.tell() % 1024 == 0:
4022                  print_overwrite('%d bytes read... (%d %%)' % (f.tell(),
4023                                                                f.tell() * 100 // args.size))
4024              sys.stdout.flush()
4025          print_overwrite("Read %d bytes" % f.tell(), last_line=True)
4026      print('Done!')
4027  
4028  
4029  def detect_flash_size(esp, args):
4030      if args.flash_size == 'detect':
4031          if esp.secure_download_mode:
4032              raise FatalError("Detecting flash size is not supported in secure download mode. Need to manually specify flash size.")
4033          flash_id = esp.flash_id()
4034          size_id = flash_id >> 16
4035          args.flash_size = DETECTED_FLASH_SIZES.get(size_id)
4036          if args.flash_size is None:
4037              print('Warning: Could not auto-detect Flash size (FlashID=0x%x, SizeID=0x%x), defaulting to 4MB' % (flash_id, size_id))
4038              args.flash_size = '4MB'
4039          else:
4040              print('Auto-detected Flash size:', args.flash_size)
4041  
4042  
4043  def _update_image_flash_params(esp, address, args, image):
4044      """ Modify the flash mode & size bytes if this looks like an executable bootloader image  """
4045      if len(image) < 8:
4046          return image  # not long enough to be a bootloader image
4047  
4048      # unpack the (potential) image header
4049      magic, _, flash_mode, flash_size_freq = struct.unpack("BBBB", image[:4])
4050      if address != esp.BOOTLOADER_FLASH_OFFSET:
4051          return image  # not flashing bootloader offset, so don't modify this
4052  
4053      if (args.flash_mode, args.flash_freq, args.flash_size) == ('keep',) * 3:
4054          return image  # all settings are 'keep', not modifying anything
4055  
4056      # easy check if this is an image: does it start with a magic byte?
4057      if magic != esp.ESP_IMAGE_MAGIC:
4058          print("Warning: Image file at 0x%x doesn't look like an image file, so not changing any flash settings." % address)
4059          return image
4060  
4061      # make sure this really is an image, and not just data that
4062      # starts with esp.ESP_IMAGE_MAGIC (mostly a problem for encrypted
4063      # images that happen to start with a magic byte
4064      try:
4065          test_image = esp.BOOTLOADER_IMAGE(io.BytesIO(image))
4066          test_image.verify()
4067      except Exception:
4068          print("Warning: Image file at 0x%x is not a valid %s image, so not changing any flash settings." % (address, esp.CHIP_NAME))
4069          return image
4070  
4071      if args.flash_mode != 'keep':
4072          flash_mode = {'qio': 0, 'qout': 1, 'dio': 2, 'dout': 3}[args.flash_mode]
4073  
4074      flash_freq = flash_size_freq & 0x0F
4075      if args.flash_freq != 'keep':
4076          flash_freq = esp.parse_flash_freq_arg(args.flash_freq)
4077  
4078      flash_size = flash_size_freq & 0xF0
4079      if args.flash_size != 'keep':
4080          flash_size = esp.parse_flash_size_arg(args.flash_size)
4081  
4082      flash_params = struct.pack(b'BB', flash_mode, flash_size + flash_freq)
4083      if flash_params != image[2:4]:
4084          print('Flash params set to 0x%04x' % struct.unpack(">H", flash_params))
4085          image = image[0:2] + flash_params + image[4:]
4086      return image
4087  
4088  
4089  def write_flash(esp, args):
4090      # set args.compress based on default behaviour:
4091      # -> if either --compress or --no-compress is set, honour that
4092      # -> otherwise, set --compress unless --no-stub is set
4093      if args.compress is None and not args.no_compress:
4094          args.compress = not args.no_stub
4095  
4096      # In case we have encrypted files to write, we first do few sanity checks before actual flash
4097      if args.encrypt or args.encrypt_files is not None:
4098          do_write = True
4099  
4100          if not esp.secure_download_mode:
4101              if esp.get_encrypted_download_disabled():
4102                  raise FatalError("This chip has encrypt functionality in UART download mode disabled. "
4103                                   "This is the Flash Encryption configuration for Production mode instead of Development mode.")
4104  
4105              crypt_cfg_efuse = esp.get_flash_crypt_config()
4106  
4107              if crypt_cfg_efuse is not None and crypt_cfg_efuse != 0xF:
4108                  print('Unexpected FLASH_CRYPT_CONFIG value: 0x%x' % (crypt_cfg_efuse))
4109                  do_write = False
4110  
4111              enc_key_valid = esp.is_flash_encryption_key_valid()
4112  
4113              if not enc_key_valid:
4114                  print('Flash encryption key is not programmed')
4115                  do_write = False
4116  
4117          # Determine which files list contain the ones to encrypt
4118          files_to_encrypt = args.addr_filename if args.encrypt else args.encrypt_files
4119  
4120          for address, argfile in files_to_encrypt:
4121              if address % esp.FLASH_ENCRYPTED_WRITE_ALIGN:
4122                  print("File %s address 0x%x is not %d byte aligned, can't flash encrypted" %
4123                        (argfile.name, address, esp.FLASH_ENCRYPTED_WRITE_ALIGN))
4124                  do_write = False
4125  
4126          if not do_write and not args.ignore_flash_encryption_efuse_setting:
4127              raise FatalError("Can't perform encrypted flash write, consult Flash Encryption documentation for more information")
4128  
4129      # verify file sizes fit in flash
4130      if args.flash_size != 'keep':  # TODO: check this even with 'keep'
4131          flash_end = flash_size_bytes(args.flash_size)
4132          for address, argfile in args.addr_filename:
4133              argfile.seek(0, os.SEEK_END)
4134              if address + argfile.tell() > flash_end:
4135                  raise FatalError(("File %s (length %d) at offset %d will not fit in %d bytes of flash. "
4136                                    "Use --flash_size argument, or change flashing address.")
4137                                   % (argfile.name, argfile.tell(), address, flash_end))
4138              argfile.seek(0)
4139  
4140      if args.erase_all:
4141          erase_flash(esp, args)
4142      else:
4143          for address, argfile in args.addr_filename:
4144              argfile.seek(0, os.SEEK_END)
4145              write_end = address + argfile.tell()
4146              argfile.seek(0)
4147              bytes_over = address % esp.FLASH_SECTOR_SIZE
4148              if bytes_over != 0:
4149                  print("WARNING: Flash address {:#010x} is not aligned to a {:#x} byte flash sector. "
4150                        "{:#x} bytes before this address will be erased."
4151                        .format(address, esp.FLASH_SECTOR_SIZE, bytes_over))
4152              # Print the address range of to-be-erased flash memory region
4153              print("Flash will be erased from {:#010x} to {:#010x}..."
4154                    .format(address - bytes_over, div_roundup(write_end, esp.FLASH_SECTOR_SIZE) * esp.FLASH_SECTOR_SIZE - 1))
4155  
4156      """ Create a list describing all the files we have to flash. Each entry holds an "encrypt" flag
4157      marking whether the file needs encryption or not. This list needs to be sorted.
4158  
4159      First, append to each entry of our addr_filename list the flag args.encrypt
4160      For example, if addr_filename is [(0x1000, "partition.bin"), (0x8000, "bootloader")],
4161      all_files will be [(0x1000, "partition.bin", args.encrypt), (0x8000, "bootloader", args.encrypt)],
4162      where, of course, args.encrypt is either True or False
4163      """
4164      all_files = [(offs, filename, args.encrypt) for (offs, filename) in args.addr_filename]
4165  
4166      """Now do the same with encrypt_files list, if defined.
4167      In this case, the flag is True
4168      """
4169      if args.encrypt_files is not None:
4170          encrypted_files_flag = [(offs, filename, True) for (offs, filename) in args.encrypt_files]
4171  
4172          # Concatenate both lists and sort them.
4173          # As both list are already sorted, we could simply do a merge instead,
4174          # but for the sake of simplicity and because the lists are very small,
4175          # let's use sorted.
4176          all_files = sorted(all_files + encrypted_files_flag, key=lambda x: x[0])
4177  
4178      for address, argfile, encrypted in all_files:
4179          compress = args.compress
4180  
4181          # Check whether we can compress the current file before flashing
4182          if compress and encrypted:
4183              print('\nWARNING: - compress and encrypt options are mutually exclusive ')
4184              print('Will flash %s uncompressed' % argfile.name)
4185              compress = False
4186  
4187          if args.no_stub:
4188              print('Erasing flash...')
4189          image = pad_to(argfile.read(), esp.FLASH_ENCRYPTED_WRITE_ALIGN if encrypted else 4)
4190          if len(image) == 0:
4191              print('WARNING: File %s is empty' % argfile.name)
4192              continue
4193          image = _update_image_flash_params(esp, address, args, image)
4194          calcmd5 = hashlib.md5(image).hexdigest()
4195          uncsize = len(image)
4196          if compress:
4197              uncimage = image
4198              image = zlib.compress(uncimage, 9)
4199              # Decompress the compressed binary a block at a time, to dynamically calculate the
4200              # timeout based on the real write size
4201              decompress = zlib.decompressobj()
4202              blocks = esp.flash_defl_begin(uncsize, len(image), address)
4203          else:
4204              blocks = esp.flash_begin(uncsize, address, begin_rom_encrypted=encrypted)
4205          argfile.seek(0)  # in case we need it again
4206          seq = 0
4207          bytes_sent = 0  # bytes sent on wire
4208          bytes_written = 0  # bytes written to flash
4209          t = time.time()
4210  
4211          timeout = DEFAULT_TIMEOUT
4212  
4213          while len(image) > 0:
4214              print_overwrite('Writing at 0x%08x... (%d %%)' % (address + bytes_written, 100 * (seq + 1) // blocks))
4215              sys.stdout.flush()
4216              block = image[0:esp.FLASH_WRITE_SIZE]
4217              if compress:
4218                  # feeding each compressed block into the decompressor lets us see block-by-block how much will be written
4219                  block_uncompressed = len(decompress.decompress(block))
4220                  bytes_written += block_uncompressed
4221                  block_timeout = max(DEFAULT_TIMEOUT, timeout_per_mb(ERASE_WRITE_TIMEOUT_PER_MB, block_uncompressed))
4222                  if not esp.IS_STUB:
4223                      timeout = block_timeout  # ROM code writes block to flash before ACKing
4224                  esp.flash_defl_block(block, seq, timeout=timeout)
4225                  if esp.IS_STUB:
4226                      timeout = block_timeout  # Stub ACKs when block is received, then writes to flash while receiving the block after it
4227              else:
4228                  # Pad the last block
4229                  block = block + b'\xff' * (esp.FLASH_WRITE_SIZE - len(block))
4230                  if encrypted:
4231                      esp.flash_encrypt_block(block, seq)
4232                  else:
4233                      esp.flash_block(block, seq)
4234                  bytes_written += len(block)
4235              bytes_sent += len(block)
4236              image = image[esp.FLASH_WRITE_SIZE:]
4237              seq += 1
4238  
4239          if esp.IS_STUB:
4240              # Stub only writes each block to flash after 'ack'ing the receive, so do a final dummy operation which will
4241              # not be 'ack'ed until the last block has actually been written out to flash
4242              esp.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR, timeout=timeout)
4243  
4244          t = time.time() - t
4245          speed_msg = ""
4246          if compress:
4247              if t > 0.0:
4248                  speed_msg = " (effective %.1f kbit/s)" % (uncsize / t * 8 / 1000)
4249              print_overwrite('Wrote %d bytes (%d compressed) at 0x%08x in %.1f seconds%s...' % (uncsize,
4250                                                                                                 bytes_sent,
4251                                                                                                 address, t, speed_msg), last_line=True)
4252          else:
4253              if t > 0.0:
4254                  speed_msg = " (%.1f kbit/s)" % (bytes_written / t * 8 / 1000)
4255              print_overwrite('Wrote %d bytes at 0x%08x in %.1f seconds%s...' % (bytes_written, address, t, speed_msg), last_line=True)
4256  
4257          if not encrypted and not esp.secure_download_mode:
4258              try:
4259                  res = esp.flash_md5sum(address, uncsize)
4260                  if res != calcmd5:
4261                      print('File  md5: %s' % calcmd5)
4262                      print('Flash md5: %s' % res)
4263                      print('MD5 of 0xFF is %s' % (hashlib.md5(b'\xFF' * uncsize).hexdigest()))
4264                      raise FatalError("MD5 of file does not match data in flash!")
4265                  else:
4266                      print('Hash of data verified.')
4267              except NotImplementedInROMError:
4268                  pass
4269  
4270      print('\nLeaving...')
4271  
4272      if esp.IS_STUB:
4273          # skip sending flash_finish to ROM loader here,
4274          # as it causes the loader to exit and run user code
4275          esp.flash_begin(0, 0)
4276  
4277          # Get the "encrypted" flag for the last file flashed
4278          # Note: all_files list contains triplets like:
4279          # (address: Integer, filename: String, encrypted: Boolean)
4280          last_file_encrypted = all_files[-1][2]
4281  
4282          # Check whether the last file flashed was compressed or not
4283          if args.compress and not last_file_encrypted:
4284              esp.flash_defl_finish(False)
4285          else:
4286              esp.flash_finish(False)
4287  
4288      if args.verify:
4289          print('Verifying just-written flash...')
4290          print('(This option is deprecated, flash contents are now always read back after flashing.)')
4291          # If some encrypted files have been flashed print a warning saying that we won't check them
4292          if args.encrypt or args.encrypt_files is not None:
4293              print('WARNING: - cannot verify encrypted files, they will be ignored')
4294          # Call verify_flash function only if there at least one non-encrypted file flashed
4295          if not args.encrypt:
4296              verify_flash(esp, args)
4297  
4298  
4299  def image_info(args):
4300      if args.chip == "auto":
4301          print("WARNING: --chip not specified, defaulting to ESP8266.")
4302      image = LoadFirmwareImage(args.chip, args.filename)
4303      print('Image version: %d' % image.version)
4304      if args.chip != 'auto' and args.chip != 'esp8266':
4305          print(
4306              "Minimal chip revision:",
4307              "v{}.{},".format(image.min_rev_full // 100, image.min_rev_full % 100),
4308              "(legacy min_rev = {})".format(image.min_rev)
4309          )
4310          print(
4311              "Maximal chip revision:",
4312              "v{}.{}".format(image.max_rev_full // 100, image.max_rev_full % 100),
4313          )
4314      print('Entry point: %08x' % image.entrypoint if image.entrypoint != 0 else 'Entry point not set')
4315      print('%d segments' % len(image.segments))
4316      print()
4317      idx = 0
4318      for seg in image.segments:
4319          idx += 1
4320          segs = seg.get_memory_type(image)
4321          seg_name = ",".join(segs)
4322          print('Segment %d: %r [%s]' % (idx, seg, seg_name))
4323      calc_checksum = image.calculate_checksum()
4324      print('Checksum: %02x (%s)' % (image.checksum,
4325                                     'valid' if image.checksum == calc_checksum else 'invalid - calculated %02x' % calc_checksum))
4326      try:
4327          digest_msg = 'Not appended'
4328          if image.append_digest:
4329              is_valid = image.stored_digest == image.calc_digest
4330              digest_msg = "%s (%s)" % (hexify(image.calc_digest).lower(),
4331                                        "valid" if is_valid else "invalid")
4332              print('Validation Hash: %s' % digest_msg)
4333      except AttributeError:
4334          pass  # ESP8266 image has no append_digest field
4335  
4336  
4337  def make_image(args):
4338      image = ESP8266ROMFirmwareImage()
4339      if len(args.segfile) == 0:
4340          raise FatalError('No segments specified')
4341      if len(args.segfile) != len(args.segaddr):
4342          raise FatalError('Number of specified files does not match number of specified addresses')
4343      for (seg, addr) in zip(args.segfile, args.segaddr):
4344          with open(seg, 'rb') as f:
4345              data = f.read()
4346              image.segments.append(ImageSegment(addr, data))
4347      image.entrypoint = args.entrypoint
4348      image.save(args.output)
4349  
4350  
4351  def elf2image(args):
4352      e = ELFFile(args.input)
4353      if args.chip == 'auto':  # Default to ESP8266 for backwards compatibility
4354          args.chip = 'esp8266'
4355  
4356      print("Creating {} image...".format(args.chip))
4357  
4358      if args.chip == 'esp32':
4359          image = ESP32FirmwareImage()
4360          if args.secure_pad:
4361              image.secure_pad = '1'
4362          elif args.secure_pad_v2:
4363              image.secure_pad = '2'
4364      elif args.chip == 'esp32s2':
4365          image = ESP32S2FirmwareImage()
4366          if args.secure_pad_v2:
4367              image.secure_pad = '2'
4368      elif args.chip == 'esp32s3beta2':
4369          image = ESP32S3BETA2FirmwareImage()
4370          if args.secure_pad_v2:
4371              image.secure_pad = '2'
4372      elif args.chip == 'esp32s3':
4373          image = ESP32S3FirmwareImage()
4374          if args.secure_pad_v2:
4375              image.secure_pad = '2'
4376      elif args.chip == 'esp32c3':
4377          image = ESP32C3FirmwareImage()
4378          if args.secure_pad_v2:
4379              image.secure_pad = '2'
4380      elif args.chip == 'esp32c6beta':
4381          image = ESP32C6BETAFirmwareImage()
4382          if args.secure_pad_v2:
4383              image.secure_pad = '2'
4384      elif args.chip == 'esp32h2beta1':
4385          image = ESP32H2BETA1FirmwareImage()
4386          if args.secure_pad_v2:
4387              image.secure_pad = '2'
4388      elif args.chip == 'esp32h2beta2':
4389          image = ESP32H2BETA2FirmwareImage()
4390          if args.secure_pad_v2:
4391              image.secure_pad = '2'
4392      elif args.chip == 'esp32c2':
4393          image = ESP32C2FirmwareImage()
4394          if args.secure_pad_v2:
4395              image.secure_pad = '2'
4396      elif args.version == '1':  # ESP8266
4397          image = ESP8266ROMFirmwareImage()
4398      elif args.version == '2':
4399          image = ESP8266V2FirmwareImage()
4400      else:
4401          image = ESP8266V3FirmwareImage()
4402      image.entrypoint = e.entrypoint
4403      image.flash_mode = {'qio': 0, 'qout': 1, 'dio': 2, 'dout': 3}[args.flash_mode]
4404  
4405      if args.chip != 'esp8266':
4406          image.min_rev = args.min_rev
4407          image.min_rev_full = args.min_rev_full
4408          image.max_rev_full = args.max_rev_full
4409  
4410      if args.flash_mmu_page_size:
4411          image.set_mmu_page_size(flash_size_bytes(args.flash_mmu_page_size))
4412  
4413      # ELFSection is a subclass of ImageSegment, so can use interchangeably
4414      image.segments = e.segments if args.use_segments else e.sections
4415  
4416      if args.pad_to_size:
4417          image.pad_to_size = flash_size_bytes(args.pad_to_size)
4418  
4419      image.flash_size_freq = image.ROM_LOADER.parse_flash_size_arg(args.flash_size)
4420      image.flash_size_freq += image.ROM_LOADER.parse_flash_freq_arg(args.flash_freq)
4421  
4422      if args.elf_sha256_offset:
4423          image.elf_sha256 = e.sha256()
4424          image.elf_sha256_offset = args.elf_sha256_offset
4425  
4426      before = len(image.segments)
4427      image.merge_adjacent_segments()
4428      if len(image.segments) != before:
4429          delta = before - len(image.segments)
4430          print("Merged %d ELF section%s" % (delta, "s" if delta > 1 else ""))
4431  
4432      image.verify()
4433  
4434      if args.output is None:
4435          args.output = image.default_output_name(args.input)
4436      image.save(args.output)
4437  
4438      print("Successfully created {} image.".format(args.chip))
4439  
4440  
4441  def read_mac(esp, args):
4442      mac = esp.read_mac()
4443  
4444      def print_mac(label, mac):
4445          print('%s: %s' % (label, ':'.join(map(lambda x: '%02x' % x, mac))))
4446      print_mac("MAC", mac)
4447  
4448  
4449  def chip_id(esp, args):
4450      try:
4451          chipid = esp.chip_id()
4452          print('Chip ID: 0x%08x' % chipid)
4453      except NotSupportedError:
4454          print('Warning: %s has no Chip ID. Reading MAC instead.' % esp.CHIP_NAME)
4455          read_mac(esp, args)
4456  
4457  
4458  def erase_flash(esp, args):
4459      print('Erasing flash (this may take a while)...')
4460      t = time.time()
4461      esp.erase_flash()
4462      print('Chip erase completed successfully in %.1fs' % (time.time() - t))
4463  
4464  
4465  def erase_region(esp, args):
4466      print('Erasing region (may be slow depending on size)...')
4467      t = time.time()
4468      esp.erase_region(args.address, args.size)
4469      print('Erase completed successfully in %.1f seconds.' % (time.time() - t))
4470  
4471  
4472  def run(esp, args):
4473      esp.run()
4474  
4475  
4476  def flash_id(esp, args):
4477      flash_id = esp.flash_id()
4478      print('Manufacturer: %02x' % (flash_id & 0xff))
4479      flid_lowbyte = (flash_id >> 16) & 0xFF
4480      print('Device: %02x%02x' % ((flash_id >> 8) & 0xff, flid_lowbyte))
4481      print('Detected flash size: %s' % (DETECTED_FLASH_SIZES.get(flid_lowbyte, "Unknown")))
4482  
4483  
4484  def read_flash(esp, args):
4485      if args.no_progress:
4486          flash_progress = None
4487      else:
4488          def flash_progress(progress, length):
4489              msg = '%d (%d %%)' % (progress, progress * 100.0 / length)
4490              padding = '\b' * len(msg)
4491              if progress == length:
4492                  padding = '\n'
4493              sys.stdout.write(msg + padding)
4494              sys.stdout.flush()
4495      t = time.time()
4496      data = esp.read_flash(args.address, args.size, flash_progress)
4497      t = time.time() - t
4498      print_overwrite('Read %d bytes at 0x%x in %.1f seconds (%.1f kbit/s)...'
4499                      % (len(data), args.address, t, len(data) / t * 8 / 1000), last_line=True)
4500      with open(args.filename, 'wb') as f:
4501          f.write(data)
4502  
4503  
4504  def verify_flash(esp, args):
4505      differences = False
4506  
4507      for address, argfile in args.addr_filename:
4508          image = pad_to(argfile.read(), 4)
4509          argfile.seek(0)  # rewind in case we need it again
4510  
4511          image = _update_image_flash_params(esp, address, args, image)
4512  
4513          image_size = len(image)
4514          print('Verifying 0x%x (%d) bytes @ 0x%08x in flash against %s...' % (image_size, image_size, address, argfile.name))
4515          # Try digest first, only read if there are differences.
4516          digest = esp.flash_md5sum(address, image_size)
4517          expected_digest = hashlib.md5(image).hexdigest()
4518          if digest == expected_digest:
4519              print('-- verify OK (digest matched)')
4520              continue
4521          else:
4522              differences = True
4523              if getattr(args, 'diff', 'no') != 'yes':
4524                  print('-- verify FAILED (digest mismatch)')
4525                  continue
4526  
4527          flash = esp.read_flash(address, image_size)
4528          assert flash != image
4529          diff = [i for i in range(image_size) if flash[i] != image[i]]
4530          print('-- verify FAILED: %d differences, first @ 0x%08x' % (len(diff), address + diff[0]))
4531          for d in diff:
4532              flash_byte = flash[d]
4533              image_byte = image[d]
4534              if PYTHON2:
4535                  flash_byte = ord(flash_byte)
4536                  image_byte = ord(image_byte)
4537              print('   %08x %02x %02x' % (address + d, flash_byte, image_byte))
4538      if differences:
4539          raise FatalError("Verify failed.")
4540  
4541  
4542  def read_flash_status(esp, args):
4543      print('Status value: 0x%04x' % esp.read_status(args.bytes))
4544  
4545  
4546  def write_flash_status(esp, args):
4547      fmt = "0x%%0%dx" % (args.bytes * 2)
4548      args.value = args.value & ((1 << (args.bytes * 8)) - 1)
4549      print(('Initial flash status: ' + fmt) % esp.read_status(args.bytes))
4550      print(('Setting flash status: ' + fmt) % args.value)
4551      esp.write_status(args.value, args.bytes, args.non_volatile)
4552      print(('After flash status:   ' + fmt) % esp.read_status(args.bytes))
4553  
4554  
4555  def get_security_info(esp, args):
4556      si = esp.get_security_info()
4557      # TODO: better display and tests
4558      print('Flags: {:#010x} ({})'.format(si["flags"], bin(si["flags"])))
4559      print('Flash_Crypt_Cnt: {:#x}'.format(si["flash_crypt_cnt"]))
4560      print('Key_Purposes: {}'.format(si["key_purposes"]))
4561      if si["chip_id"] is not None and si["api_version"] is not None:
4562          print('Chip_ID: {}'.format(si["chip_id"]))
4563          print('Api_Version: {}'.format(si["api_version"]))
4564  
4565  
4566  def merge_bin(args):
4567      try:
4568          chip_class = _chip_to_rom_loader(args.chip)
4569      except KeyError:
4570          msg = "Please specify the chip argument" if args.chip == "auto" else "Invalid chip choice: '{}'".format(args.chip)
4571          msg = msg + " (choose from {})".format(', '.join(SUPPORTED_CHIPS))
4572          raise FatalError(msg)
4573  
4574      # sort the files by offset. The AddrFilenamePairAction has already checked for overlap
4575      input_files = sorted(args.addr_filename, key=lambda x: x[0])
4576      if not input_files:
4577          raise FatalError("No input files specified")
4578      first_addr = input_files[0][0]
4579      if first_addr < args.target_offset:
4580          raise FatalError("Output file target offset is 0x%x. Input file offset 0x%x is before this." % (args.target_offset, first_addr))
4581  
4582      if args.format != 'raw':
4583          raise FatalError("This version of esptool only supports the 'raw' output format")
4584  
4585      with open(args.output, 'wb') as of:
4586          def pad_to(flash_offs):
4587              # account for output file offset if there is any
4588              of.write(b'\xFF' * (flash_offs - args.target_offset - of.tell()))
4589          for addr, argfile in input_files:
4590              pad_to(addr)
4591              image = argfile.read()
4592              image = _update_image_flash_params(chip_class, addr, args, image)
4593              of.write(image)
4594          if args.fill_flash_size:
4595              pad_to(flash_size_bytes(args.fill_flash_size))
4596          print("Wrote 0x%x bytes to file %s, ready to flash to offset 0x%x" % (of.tell(), args.output, args.target_offset))
4597  
4598  
4599  def version(args):
4600      print(__version__)
4601  
4602  #
4603  # End of operations functions
4604  #
4605  
4606  
4607  def main(argv=None, esp=None):
4608      """
4609      Main function for esptool
4610  
4611      argv - Optional override for default arguments parsing (that uses sys.argv), can be a list of custom arguments
4612      as strings. Arguments and their values need to be added as individual items to the list e.g. "-b 115200" thus
4613      becomes ['-b', '115200'].
4614  
4615      esp - Optional override of the connected device previously returned by get_default_connected_device()
4616      """
4617  
4618      external_esp = esp is not None
4619  
4620      parser = argparse.ArgumentParser(description='esptool.py v%s - Espressif chips ROM Bootloader Utility' % __version__, prog='esptool')
4621  
4622      parser.add_argument('--chip', '-c',
4623                          help='Target chip type',
4624                          type=format_chip_name,  # support ESP32-S2, etc.
4625                          choices=['auto'] + SUPPORTED_CHIPS,
4626                          default=os.environ.get('ESPTOOL_CHIP', 'auto'))
4627  
4628      parser.add_argument(
4629          '--port', '-p',
4630          help='Serial port device',
4631          default=os.environ.get('ESPTOOL_PORT', None))
4632  
4633      parser.add_argument(
4634          '--baud', '-b',
4635          help='Serial port baud rate used when flashing/reading',
4636          type=arg_auto_int,
4637          default=os.environ.get('ESPTOOL_BAUD', ESPLoader.ESP_ROM_BAUD))
4638  
4639      parser.add_argument(
4640          '--before',
4641          help='What to do before connecting to the chip',
4642          choices=['default_reset', 'usb_reset', 'no_reset', 'no_reset_no_sync'],
4643          default=os.environ.get('ESPTOOL_BEFORE', 'default_reset'))
4644  
4645      parser.add_argument(
4646          '--after', '-a',
4647          help='What to do after esptool.py is finished',
4648          choices=['hard_reset', 'soft_reset', 'no_reset', 'no_reset_stub'],
4649          default=os.environ.get('ESPTOOL_AFTER', 'hard_reset'))
4650  
4651      parser.add_argument(
4652          '--no-stub',
4653          help="Disable launching the flasher stub, only talk to ROM bootloader. Some features will not be available.",
4654          action='store_true')
4655  
4656      parser.add_argument(
4657          '--trace', '-t',
4658          help="Enable trace-level output of esptool.py interactions.",
4659          action='store_true')
4660  
4661      parser.add_argument(
4662          '--override-vddsdio',
4663          help="Override ESP32 VDDSDIO internal voltage regulator (use with care)",
4664          choices=ESP32ROM.OVERRIDE_VDDSDIO_CHOICES,
4665          nargs='?')
4666  
4667      parser.add_argument(
4668          '--connect-attempts',
4669          help=('Number of attempts to connect, negative or 0 for infinite. '
4670                'Default: %d.' % DEFAULT_CONNECT_ATTEMPTS),
4671          type=int,
4672          default=os.environ.get('ESPTOOL_CONNECT_ATTEMPTS', DEFAULT_CONNECT_ATTEMPTS))
4673  
4674      subparsers = parser.add_subparsers(
4675          dest='operation',
4676          help='Run esptool {command} -h for additional help')
4677  
4678      def add_spi_connection_arg(parent):
4679          parent.add_argument('--spi-connection', '-sc', help='ESP32-only argument. Override default SPI Flash connection. '
4680                              '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).',
4681                              action=SpiConnectionAction)
4682  
4683      parser_load_ram = subparsers.add_parser(
4684          'load_ram',
4685          help='Download an image to RAM and execute')
4686      parser_load_ram.add_argument('filename', help='Firmware image')
4687  
4688      parser_dump_mem = subparsers.add_parser(
4689          'dump_mem',
4690          help='Dump arbitrary memory to disk')
4691      parser_dump_mem.add_argument('address', help='Base address', type=arg_auto_int)
4692      parser_dump_mem.add_argument('size', help='Size of region to dump', type=arg_auto_int)
4693      parser_dump_mem.add_argument('filename', help='Name of binary dump')
4694  
4695      parser_read_mem = subparsers.add_parser(
4696          'read_mem',
4697          help='Read arbitrary memory location')
4698      parser_read_mem.add_argument('address', help='Address to read', type=arg_auto_int)
4699  
4700      parser_write_mem = subparsers.add_parser(
4701          'write_mem',
4702          help='Read-modify-write to arbitrary memory location')
4703      parser_write_mem.add_argument('address', help='Address to write', type=arg_auto_int)
4704      parser_write_mem.add_argument('value', help='Value', type=arg_auto_int)
4705      parser_write_mem.add_argument('mask', help='Mask of bits to write', type=arg_auto_int, nargs='?', default='0xFFFFFFFF')
4706  
4707      def add_spi_flash_subparsers(parent, allow_keep, auto_detect):
4708          """ Add common parser arguments for SPI flash properties """
4709          extra_keep_args = ['keep'] if allow_keep else []
4710  
4711          if auto_detect and allow_keep:
4712              extra_fs_message = ", detect, or keep"
4713          elif auto_detect:
4714              extra_fs_message = ", or detect"
4715          elif allow_keep:
4716              extra_fs_message = ", or keep"
4717          else:
4718              extra_fs_message = ""
4719  
4720          parent.add_argument('--flash_freq', '-ff', help='SPI Flash frequency',
4721                              choices=extra_keep_args + ['80m', '60m', '48m', '40m', '30m', '26m', '24m', '20m', '16m', '15m', '12m'],
4722                              default=os.environ.get('ESPTOOL_FF', 'keep' if allow_keep else '40m'))
4723          parent.add_argument('--flash_mode', '-fm', help='SPI Flash mode',
4724                              choices=extra_keep_args + ['qio', 'qout', 'dio', 'dout'],
4725                              default=os.environ.get('ESPTOOL_FM', 'keep' if allow_keep else 'qio'))
4726          parent.add_argument('--flash_size', '-fs', help='SPI Flash size in MegaBytes (1MB, 2MB, 4MB, 8MB, 16MB, 32MB, 64MB, 128MB)'
4727                              ' plus ESP8266-only (256KB, 512KB, 2MB-c1, 4MB-c1)' + extra_fs_message,
4728                              action=FlashSizeAction, auto_detect=auto_detect,
4729                              default=os.environ.get('ESPTOOL_FS', 'keep' if allow_keep else '1MB'))
4730          add_spi_connection_arg(parent)
4731  
4732      parser_write_flash = subparsers.add_parser(
4733          'write_flash',
4734          help='Write a binary blob to flash')
4735  
4736      parser_write_flash.add_argument('addr_filename', metavar='<address> <filename>', help='Address followed by binary filename, separated by space',
4737                                      action=AddrFilenamePairAction)
4738      parser_write_flash.add_argument('--erase-all', '-e',
4739                                      help='Erase all regions of flash (not just write areas) before programming',
4740                                      action="store_true")
4741  
4742      add_spi_flash_subparsers(parser_write_flash, allow_keep=True, auto_detect=True)
4743      parser_write_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true")
4744      parser_write_flash.add_argument('--verify', help='Verify just-written data on flash '
4745                                      '(mostly superfluous, data is read back during flashing)', action='store_true')
4746      parser_write_flash.add_argument('--encrypt', help='Apply flash encryption when writing data (required correct efuse settings)',
4747                                      action='store_true')
4748      # In order to not break backward compatibility, our list of encrypted files to flash is a new parameter
4749      parser_write_flash.add_argument('--encrypt-files', metavar='<address> <filename>',
4750                                      help='Files to be encrypted on the flash. Address followed by binary filename, separated by space.',
4751                                      action=AddrFilenamePairAction)
4752      parser_write_flash.add_argument('--ignore-flash-encryption-efuse-setting', help='Ignore flash encryption efuse settings ',
4753                                      action='store_true')
4754  
4755      compress_args = parser_write_flash.add_mutually_exclusive_group(required=False)
4756      compress_args.add_argument('--compress', '-z', help='Compress data in transfer (default unless --no-stub is specified)',
4757                                 action="store_true", default=None)
4758      compress_args.add_argument('--no-compress', '-u', help='Disable data compression during transfer (default if --no-stub is specified)',
4759                                 action="store_true")
4760  
4761      subparsers.add_parser(
4762          'run',
4763          help='Run application code in flash')
4764  
4765      parser_image_info = subparsers.add_parser(
4766          'image_info',
4767          help='Dump headers from an application image')
4768      parser_image_info.add_argument('filename', help='Image file to parse')
4769  
4770      parser_make_image = subparsers.add_parser(
4771          'make_image',
4772          help='Create an application image from binary files')
4773      parser_make_image.add_argument('output', help='Output image file')
4774      parser_make_image.add_argument('--segfile', '-f', action='append', help='Segment input file')
4775      parser_make_image.add_argument('--segaddr', '-a', action='append', help='Segment base address', type=arg_auto_int)
4776      parser_make_image.add_argument('--entrypoint', '-e', help='Address of entry point', type=arg_auto_int, default=0)
4777  
4778      parser_elf2image = subparsers.add_parser(
4779          'elf2image',
4780          help='Create an application image from ELF file')
4781      parser_elf2image.add_argument('input', help='Input ELF file')
4782      parser_elf2image.add_argument('--output', '-o', help='Output filename prefix (for version 1 image), or filename (for version 2 single image)', type=str)
4783      parser_elf2image.add_argument('--version', '-e', help='Output image version', choices=['1', '2', '3'], default='1')
4784      parser_elf2image.add_argument(
4785          # kept for compatibility
4786          # Minimum chip revision (deprecated, consider using --min-rev-full)
4787          "--min-rev",
4788          "-r",
4789          # In v3 we do not do help=argparse.SUPPRESS because
4790          # it should remain visible.
4791          help="Minimal chip revision (ECO version format)",
4792          type=int,
4793          choices=range(256),
4794          metavar="{0, ... 255}",
4795          default=0,
4796      )
4797      parser_elf2image.add_argument(
4798          "--min-rev-full",
4799          help="Minimal chip revision (in format: major * 100 + minor)",
4800          type=int,
4801          choices=range(65536),
4802          metavar="{0, ... 65535}",
4803          default=0,
4804      )
4805      parser_elf2image.add_argument(
4806          "--max-rev-full",
4807          help="Maximal chip revision (in format: major * 100 + minor)",
4808          type=int,
4809          choices=range(65536),
4810          metavar="{0, ... 65535}",
4811          default=65535,
4812      )
4813      parser_elf2image.add_argument('--secure-pad', action='store_true',
4814                                    help='Pad image so once signed it will end on a 64KB boundary. For Secure Boot v1 images only.')
4815      parser_elf2image.add_argument('--secure-pad-v2', action='store_true',
4816                                    help='Pad image to 64KB, so once signed its signature sector will start at the next 64K block. '
4817                                    'For Secure Boot v2 images only.')
4818      parser_elf2image.add_argument('--elf-sha256-offset', help='If set, insert SHA256 hash (32 bytes) of the input ELF file at specified offset in the binary.',
4819                                    type=arg_auto_int, default=None)
4820      parser_elf2image.add_argument('--use_segments', help='If set, ELF segments will be used instead of ELF sections to genereate the image.',
4821                                    action='store_true')
4822      parser_elf2image.add_argument('--flash-mmu-page-size', help="Change flash MMU page size.", choices=['64KB', '32KB', '16KB'])
4823      parser_elf2image.add_argument(
4824          "--pad-to-size",
4825          help="The block size with which the final binary image after padding must be aligned to. Value 0xFF is used for padding, similar to erase_flash",
4826          default=None,
4827      )
4828      add_spi_flash_subparsers(parser_elf2image, allow_keep=False, auto_detect=False)
4829  
4830      subparsers.add_parser(
4831          'read_mac',
4832          help='Read MAC address from OTP ROM')
4833  
4834      subparsers.add_parser(
4835          'chip_id',
4836          help='Read Chip ID from OTP ROM')
4837  
4838      parser_flash_id = subparsers.add_parser(
4839          'flash_id',
4840          help='Read SPI flash manufacturer and device ID')
4841      add_spi_connection_arg(parser_flash_id)
4842  
4843      parser_read_status = subparsers.add_parser(
4844          'read_flash_status',
4845          help='Read SPI flash status register')
4846  
4847      add_spi_connection_arg(parser_read_status)
4848      parser_read_status.add_argument('--bytes', help='Number of bytes to read (1-3)', type=int, choices=[1, 2, 3], default=2)
4849  
4850      parser_write_status = subparsers.add_parser(
4851          'write_flash_status',
4852          help='Write SPI flash status register')
4853  
4854      add_spi_connection_arg(parser_write_status)
4855      parser_write_status.add_argument('--non-volatile', help='Write non-volatile bits (use with caution)', action='store_true')
4856      parser_write_status.add_argument('--bytes', help='Number of status bytes to write (1-3)', type=int, choices=[1, 2, 3], default=2)
4857      parser_write_status.add_argument('value', help='New value', type=arg_auto_int)
4858  
4859      parser_read_flash = subparsers.add_parser(
4860          'read_flash',
4861          help='Read SPI flash content')
4862      add_spi_connection_arg(parser_read_flash)
4863      parser_read_flash.add_argument('address', help='Start address', type=arg_auto_int)
4864      parser_read_flash.add_argument('size', help='Size of region to dump', type=arg_auto_int)
4865      parser_read_flash.add_argument('filename', help='Name of binary dump')
4866      parser_read_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true")
4867  
4868      parser_verify_flash = subparsers.add_parser(
4869          'verify_flash',
4870          help='Verify a binary blob against flash')
4871      parser_verify_flash.add_argument('addr_filename', help='Address and binary file to verify there, separated by space',
4872                                       action=AddrFilenamePairAction)
4873      parser_verify_flash.add_argument('--diff', '-d', help='Show differences',
4874                                       choices=['no', 'yes'], default='no')
4875      add_spi_flash_subparsers(parser_verify_flash, allow_keep=True, auto_detect=True)
4876  
4877      parser_erase_flash = subparsers.add_parser(
4878          'erase_flash',
4879          help='Perform Chip Erase on SPI flash')
4880      add_spi_connection_arg(parser_erase_flash)
4881  
4882      parser_erase_region = subparsers.add_parser(
4883          'erase_region',
4884          help='Erase a region of the flash')
4885      add_spi_connection_arg(parser_erase_region)
4886      parser_erase_region.add_argument('address', help='Start address (must be multiple of 4096)', type=arg_auto_int)
4887      parser_erase_region.add_argument('size', help='Size of region to erase (must be multiple of 4096)', type=arg_auto_int)
4888  
4889      parser_merge_bin = subparsers.add_parser(
4890          'merge_bin',
4891          help='Merge multiple raw binary files into a single file for later flashing')
4892  
4893      parser_merge_bin.add_argument('--output', '-o', help='Output filename', type=str, required=True)
4894      parser_merge_bin.add_argument('--format', '-f', help='Format of the output file', choices='raw', default='raw')  # for future expansion
4895      add_spi_flash_subparsers(parser_merge_bin, allow_keep=True, auto_detect=False)
4896  
4897      parser_merge_bin.add_argument('--target-offset', '-t', help='Target offset where the output file will be flashed',
4898                                    type=arg_auto_int, default=0)
4899      parser_merge_bin.add_argument('--fill-flash-size', help='If set, the final binary file will be padded with FF '
4900                                    'bytes up to this flash size.', action=FlashSizeAction)
4901      parser_merge_bin.add_argument('addr_filename', metavar='<address> <filename>',
4902                                    help='Address followed by binary filename, separated by space',
4903                                    action=AddrFilenamePairAction)
4904  
4905      subparsers.add_parser('get_security_info', help='Get some security-related data')
4906  
4907      subparsers.add_parser('version', help='Print esptool version')
4908  
4909      # internal sanity check - every operation matches a module function of the same name
4910      for operation in subparsers.choices.keys():
4911          assert operation in globals(), "%s should be a module function" % operation
4912  
4913      argv = expand_file_arguments(argv or sys.argv[1:])
4914  
4915      args = parser.parse_args(argv)
4916      print('esptool.py v%s' % __version__)
4917  
4918      # operation function can take 1 arg (args), 2 args (esp, arg)
4919      # or be a member function of the ESPLoader class.
4920  
4921      if args.operation is None:
4922          parser.print_help()
4923          sys.exit(1)
4924  
4925      # Forbid the usage of both --encrypt, which means encrypt all the given files,
4926      # and --encrypt-files, which represents the list of files to encrypt.
4927      # The reason is that allowing both at the same time increases the chances of
4928      # having contradictory lists (e.g. one file not available in one of list).
4929      if args.operation == "write_flash" and args.encrypt and args.encrypt_files is not None:
4930          raise FatalError("Options --encrypt and --encrypt-files must not be specified at the same time.")
4931  
4932      operation_func = globals()[args.operation]
4933  
4934      if PYTHON2:
4935          # This function is depreciated in Python3
4936          operation_args = inspect.getargspec(operation_func).args
4937      else:
4938          operation_args = inspect.getfullargspec(operation_func).args
4939  
4940      if operation_args[0] == 'esp':  # operation function takes an ESPLoader connection object
4941          if args.before != "no_reset_no_sync":
4942              initial_baud = min(ESPLoader.ESP_ROM_BAUD, args.baud)  # don't sync faster than the default baud rate
4943          else:
4944              initial_baud = args.baud
4945  
4946          if args.port is None:
4947              ser_list = get_port_list()
4948              print("Found %d serial ports" % len(ser_list))
4949          else:
4950              ser_list = [args.port]
4951          esp = esp or get_default_connected_device(ser_list, port=args.port, connect_attempts=args.connect_attempts,
4952                                                    initial_baud=initial_baud, chip=args.chip, trace=args.trace,
4953                                                    before=args.before)
4954  
4955          if esp is None:
4956              raise FatalError("Could not connect to an Espressif device on any of the %d available serial ports." % len(ser_list))
4957  
4958          if esp.secure_download_mode:
4959              print("Chip is %s in Secure Download Mode" % esp.CHIP_NAME)
4960          else:
4961              print("Chip is %s" % (esp.get_chip_description()))
4962              print("Features: %s" % ", ".join(esp.get_chip_features()))
4963              print("Crystal is %dMHz" % esp.get_crystal_freq())
4964              read_mac(esp, args)
4965  
4966          if not args.no_stub:
4967              if esp.secure_download_mode:
4968                  print("WARNING: Stub loader is not supported in Secure Download Mode, setting --no-stub")
4969                  args.no_stub = True
4970              elif not esp.IS_STUB and esp.stub_is_disabled:
4971                  print("WARNING: Stub loader has been disabled for compatibility, setting --no-stub")
4972                  args.no_stub = True
4973              else:
4974                  esp = esp.run_stub()
4975  
4976          if args.override_vddsdio:
4977              esp.override_vddsdio(args.override_vddsdio)
4978  
4979          if args.baud > initial_baud:
4980              try:
4981                  esp.change_baud(args.baud)
4982              except NotImplementedInROMError:
4983                  print("WARNING: ROM doesn't support changing baud rate. Keeping initial baud rate %d" % initial_baud)
4984  
4985          # override common SPI flash parameter stuff if configured to do so
4986          if hasattr(args, "spi_connection") and args.spi_connection is not None:
4987              if esp.CHIP_NAME != "ESP32":
4988                  raise FatalError("Chip %s does not support --spi-connection option." % esp.CHIP_NAME)
4989              print("Configuring SPI flash mode...")
4990              esp.flash_spi_attach(args.spi_connection)
4991          elif args.no_stub:
4992              print("Enabling default SPI flash mode...")
4993              # ROM loader doesn't enable flash unless we explicitly do it
4994              esp.flash_spi_attach(0)
4995  
4996          # XMC chip startup sequence
4997          XMC_VENDOR_ID = 0x20
4998  
4999          def is_xmc_chip_strict():
5000              id = esp.flash_id()
5001              rdid = ((id & 0xff) << 16) | ((id >> 16) & 0xff) | (id & 0xff00)
5002  
5003              vendor_id = ((rdid >> 16) & 0xFF)
5004              mfid = ((rdid >> 8) & 0xFF)
5005              cpid = (rdid & 0xFF)
5006  
5007              if vendor_id != XMC_VENDOR_ID:
5008                  return False
5009  
5010              matched = False
5011              if mfid == 0x40:
5012                  if cpid >= 0x13 and cpid <= 0x20:
5013                      matched = True
5014              elif mfid == 0x41:
5015                  if cpid >= 0x17 and cpid <= 0x20:
5016                      matched = True
5017              elif mfid == 0x50:
5018                  if cpid >= 0x15 and cpid <= 0x16:
5019                      matched = True
5020              return matched
5021  
5022          def flash_xmc_startup():
5023              # If the RDID value is a valid XMC one, may skip the flow
5024              fast_check = True
5025              if fast_check and is_xmc_chip_strict():
5026                  return  # Successful XMC flash chip boot-up detected by RDID, skipping.
5027  
5028              sfdp_mfid_addr = 0x10
5029              mf_id = esp.read_spiflash_sfdp(sfdp_mfid_addr, 8)
5030              if mf_id != XMC_VENDOR_ID:  # Non-XMC chip detected by SFDP Read, skipping.
5031                  return
5032  
5033              print("WARNING: XMC flash chip boot-up failure detected! Running XMC25QHxxC startup flow")
5034              esp.run_spiflash_command(0xB9)  # Enter DPD
5035              esp.run_spiflash_command(0x79)  # Enter UDPD
5036              esp.run_spiflash_command(0xFF)  # Exit UDPD
5037              time.sleep(0.002)               # Delay tXUDPD
5038              esp.run_spiflash_command(0xAB)  # Release Power-Down
5039              time.sleep(0.00002)
5040              # Check for success
5041              if not is_xmc_chip_strict():
5042                  print("WARNING: XMC flash boot-up fix failed.")
5043              print("XMC flash chip boot-up fix successful!")
5044  
5045          # Check flash chip connection
5046          if not esp.secure_download_mode:
5047              try:
5048                  flash_id = esp.flash_id()
5049                  if flash_id in (0xffffff, 0x000000):
5050                      print('WARNING: Failed to communicate with the flash chip, read/write operations will fail. '
5051                            'Try checking the chip connections or removing any other hardware connected to IOs.')
5052              except Exception as e:
5053                  esp.trace('Unable to verify flash chip connection ({}).'.format(e))
5054  
5055          # Check if XMC SPI flash chip booted-up successfully, fix if not
5056          if not esp.secure_download_mode:
5057              try:
5058                  flash_xmc_startup()
5059              except Exception as e:
5060                  esp.trace('Unable to perform XMC flash chip startup sequence ({}).'.format(e))
5061  
5062          if hasattr(args, "flash_size"):
5063              print("Configuring flash size...")
5064              detect_flash_size(esp, args)
5065              if args.flash_size != 'keep':  # TODO: should set this even with 'keep'
5066                  esp.flash_set_parameters(flash_size_bytes(args.flash_size))
5067                  # Check if stub supports chosen flash size
5068                  if esp.IS_STUB and args.flash_size in ('32MB', '64MB', '128MB'):
5069                      print("WARNING: Flasher stub doesn't fully support flash size larger than 16MB, in case of failure use --no-stub.")
5070  
5071          if esp.IS_STUB and hasattr(args, "address") and hasattr(args, "size"):
5072              if args.address + args.size > 0x1000000:
5073                  print("WARNING: Flasher stub doesn't fully support flash size larger than 16MB, in case of failure use --no-stub.")
5074  
5075          try:
5076              operation_func(esp, args)
5077          finally:
5078              try:  # Clean up AddrFilenamePairAction files
5079                  for address, argfile in args.addr_filename:
5080                      argfile.close()
5081              except AttributeError:
5082                  pass
5083  
5084          # Handle post-operation behaviour (reset or other)
5085          if operation_func == load_ram:
5086              # the ESP is now running the loaded image, so let it run
5087              print('Exiting immediately.')
5088          elif args.after == 'hard_reset':
5089              esp.hard_reset()
5090          elif args.after == 'soft_reset':
5091              print('Soft resetting...')
5092              # flash_finish will trigger a soft reset
5093              esp.soft_reset(False)
5094          elif args.after == 'no_reset_stub':
5095              print('Staying in flasher stub.')
5096          else:  # args.after == 'no_reset'
5097              print('Staying in bootloader.')
5098              if esp.IS_STUB:
5099                  esp.soft_reset(True)  # exit stub back to ROM loader
5100  
5101          if not external_esp:
5102              esp._port.close()
5103  
5104      else:
5105          operation_func(args)
5106  
5107  
5108  def get_port_list():
5109      if list_ports is None:
5110          raise FatalError("Listing all serial ports is currently not available. Please try to specify the port when "
5111                           "running esptool.py or update the pyserial package to the latest version")
5112      return sorted(ports.device for ports in list_ports.comports())
5113  
5114  
5115  def expand_file_arguments(argv):
5116      """ Any argument starting with "@" gets replaced with all values read from a text file.
5117      Text file arguments can be split by newline or by space.
5118      Values are added "as-is", as if they were specified in this order on the command line.
5119      """
5120      new_args = []
5121      expanded = False
5122      for arg in argv:
5123          if arg.startswith("@"):
5124              expanded = True
5125              with open(arg[1:], "r") as f:
5126                  for line in f.readlines():
5127                      new_args += shlex.split(line)
5128          else:
5129              new_args.append(arg)
5130      if expanded:
5131          print("esptool.py %s" % (" ".join(new_args[1:])))
5132          return new_args
5133      return argv
5134  
5135  
5136  class FlashSizeAction(argparse.Action):
5137      """ Custom flash size parser class to support backwards compatibility with megabit size arguments.
5138  
5139      (At next major relase, remove deprecated sizes and this can become a 'normal' choices= argument again.)
5140      """
5141      def __init__(self, option_strings, dest, nargs=1, auto_detect=False, **kwargs):
5142          super(FlashSizeAction, self).__init__(option_strings, dest, nargs, **kwargs)
5143          self._auto_detect = auto_detect
5144  
5145      def __call__(self, parser, namespace, values, option_string=None):
5146          try:
5147              value = {
5148                  '2m': '256KB',
5149                  '4m': '512KB',
5150                  '8m': '1MB',
5151                  '16m': '2MB',
5152                  '32m': '4MB',
5153                  '16m-c1': '2MB-c1',
5154                  '32m-c1': '4MB-c1',
5155              }[values[0]]
5156              print("WARNING: Flash size arguments in megabits like '%s' are deprecated." % (values[0]))
5157              print("Please use the equivalent size '%s'." % (value))
5158              print("Megabit arguments may be removed in a future release.")
5159          except KeyError:
5160              value = values[0]
5161  
5162          known_sizes = dict(ESP8266ROM.FLASH_SIZES)
5163          known_sizes.update(ESP32ROM.FLASH_SIZES)
5164          if self._auto_detect:
5165              known_sizes['detect'] = 'detect'
5166              known_sizes['keep'] = 'keep'
5167          if value not in known_sizes:
5168              raise argparse.ArgumentError(self, '%s is not a known flash size. Known sizes: %s' % (value, ", ".join(known_sizes.keys())))
5169          setattr(namespace, self.dest, value)
5170  
5171  
5172  class SpiConnectionAction(argparse.Action):
5173      """ Custom action to parse 'spi connection' override. Values are SPI, HSPI, or a sequence of 5 pin numbers separated by commas.
5174      """
5175      def __call__(self, parser, namespace, value, option_string=None):
5176          if value.upper() == "SPI":
5177              value = 0
5178          elif value.upper() == "HSPI":
5179              value = 1
5180          elif "," in value:
5181              values = value.split(",")
5182              if len(values) != 5:
5183                  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)
5184              try:
5185                  values = tuple(int(v, 0) for v in values)
5186              except ValueError:
5187                  raise argparse.ArgumentError(self, '%s is not a valid argument. All pins must be numeric values' % values)
5188              if any([v for v in values if v > 33 or v < 0]):
5189                  raise argparse.ArgumentError(self, 'Pin numbers must be in the range 0-33.')
5190              # encode the pin numbers as a 32-bit integer with packed 6-bit values, the same way ESP32 ROM takes them
5191              # TODO: make this less ESP32 ROM specific somehow...
5192              clk, q, d, hd, cs = values
5193              value = (hd << 24) | (cs << 18) | (d << 12) | (q << 6) | clk
5194          else:
5195              raise argparse.ArgumentError(self, '%s is not a valid spi-connection value. '
5196                                           'Values are SPI, HSPI, or a sequence of 5 pin numbers CLK,Q,D,HD,CS).' % value)
5197          setattr(namespace, self.dest, value)
5198  
5199  
5200  class AddrFilenamePairAction(argparse.Action):
5201      """ Custom parser class for the address/filename pairs passed as arguments """
5202      def __init__(self, option_strings, dest, nargs='+', **kwargs):
5203          super(AddrFilenamePairAction, self).__init__(option_strings, dest, nargs, **kwargs)
5204  
5205      def __call__(self, parser, namespace, values, option_string=None):
5206          # validate pair arguments
5207          pairs = []
5208          for i in range(0, len(values), 2):
5209              try:
5210                  address = int(values[i], 0)
5211              except ValueError:
5212                  raise argparse.ArgumentError(self, 'Address "%s" must be a number' % values[i])
5213              try:
5214                  argfile = open(values[i + 1], 'rb')
5215              except IOError as e:
5216                  raise argparse.ArgumentError(self, e)
5217              except IndexError:
5218                  raise argparse.ArgumentError(self, 'Must be pairs of an address and the binary filename to write there')
5219              pairs.append((address, argfile))
5220  
5221          # Sort the addresses and check for overlapping
5222          end = 0
5223          for address, argfile in sorted(pairs, key=lambda x: x[0]):
5224              argfile.seek(0, 2)  # seek to end
5225              size = argfile.tell()
5226              argfile.seek(0)
5227              sector_start = address & ~(ESPLoader.FLASH_SECTOR_SIZE - 1)
5228              sector_end = ((address + size + ESPLoader.FLASH_SECTOR_SIZE - 1) & ~(ESPLoader.FLASH_SECTOR_SIZE - 1)) - 1
5229              if sector_start < end:
5230                  message = 'Detected overlap at address: 0x%x for file: %s' % (address, argfile.name)
5231                  raise argparse.ArgumentError(self, message)
5232              end = sector_end
5233          setattr(namespace, self.dest, pairs)
5234  
5235  
5236  # Binary stub code (see flasher_stub dir for source & details)
5237  ESP8266ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b"""
5238  eNq9Pftj1DbS/4rthCQbkiLZXq/Mo2w2yQItXCEcKddL28gvelxpwzZXcj34/vbP85Jl7yaB67U/LFl5ZWk0M5q3xH8265/OF//evB1oNUlNmmTjeCfYrOy5bZ8VmycXypxcGH1y0dT328aYP2n7Ue0nbj9J+5lw\
5239  O+FPQe0iP7mo2t+0mp5c1I3X0FXbMNworGv80PZzfer2cY6Nc/ft5KJUruH3Ni0sleVG03gNfKEYvNB9e9n+Wg6etf9WDb8OC6kVNu64b6sGouUtdWjX1w5Va2y0S6pjfmxbTNUJNtr56xS/tf/W40unWPWtXVmd\
5240  DZ597c2ew/IrwVLj4d1mbrK2Ufj4K1fiuC7dXPojofv0b5GD43sPoqL2YFWa+U15fOi3k0E7HbTHg/ak1z7vtRb9vnowt879dug3ej33/Ibtj2EGY5bD9en+ms2gjd/jQTsZtNNBOxu0zaBd9tt6AI/u9Q/8Rq/n\
5241  1G+cDtb1R370Ne34E3noOp66jseG7eya9uSatrmyfX5F66crWk52X9our2wvrto7134+dd9mn4Sj809Y9xDy5hopMIBcDyDRAyzq3nhrfuOm3+gNe8dv7PuN536jR5BfBpJmAKcdtMtBu05W7BL9J+7iP1oK/F4p\
5242  8XulyO+VMr9XCl3X/sSP5r2hY28HTnDnZbjjxrzTUpYcCe406F0z9pvVOm+JMr2VbrZW63l9cc5WqzWisNhaAIOwaeZ9CYCmERq3zX0GVRpG0cftm9RvT51Se7jHL+SpGwr+zWlKQAMo80YFAcwdWyJxOSZ7WEEH\
5243  C7C1q88zQEXyrG2l8DoMncEXLU/aQQCBRn3xAsxaVLo/wDuzdiqk3FRRX13sA5DwlfvNXsC/tzP3IEIJEsmbYNAVNAm818qvPL4fkr2HINCXFqgaF3c77oPwTHqcbMJSaO0mW80m/OKIyMitv8Ew824PeY/T3iuJ\
5244  JoO+BSJybCQd4iPhPN3hX6NAb0To9cR7bnwmUM7d+erh3iPiJFvyrzZ1ja0WBLL0H7cLFyu1k72nwnPL++NaGcXPTNnnQeeN+J9ukumyTUlOW+o12vk3vRHTVeAyyL2V99zAovdLb6eY0WB3Nf4ACTe08howkhst\
5245  L3k/NPdhhEq2o3GPiWBDfdpp+tNOzT9lqSINJ5TUXQ/MQnnzF6nXqKBhsXHHe6HpSY3ShwyGqj0R4hsV2v8Re8rqrlOoAKH2BHOj+4yV+/TAhpXllELPKSHRNWzXeIlUnB7M8c/OY/xz8dDx1Bf8rUgf8bey/Iy/\
5246  VQbdn+lDcpiVOJw1Lmn6eEPm5ndDggmgz0H0sWORs9ooVWTXItyhrE5tK6XK2LYCrootCJ/YglyLLeOtZklb+i5YEbPMKhLGVMkKI/OxDSDFX0YT6G1IKBeAZs0QwAZU5f52SHrLsoLAfjCYDt/z5Po3ntCiWNre\
5247  ceKo/QIYikN6vwMGn2r/6RENXy2tSJOr3jQRYQyBoOGBSkmwHvTlI8If8HDJcDh+Hn/s87eyEVsRn9esBOiLli8FQyYOAZuJZbWCOjkygCZMrTnIDb2ms1/kHUZgxb8MBH3ePdVxtAc8FqFcR+d1HZ+MZ8/2Yxtt\
5248  ILe1ckGXCQQZ4oBVU88/oLeTWCwSg8pRqyhoQL/qrV039xb0iGzU5yhdRtGzfWIQYhZhJLZ2QOZpGx224BT08+oZ46BF0wSlyoS4WSc8VMlGWp5549c1rLV9OGZgxsQxSs8puNVJCw9FwMUPaB8iItsXcgpmbKb7\
5249  QFF4WoLBwFKsyRZBw71N9lYezgCRwJvU/xiet/OWGOQA1MnoBp2i5qgb2fLIQIwSbYUNiOT9mywf4+bqegLYuQ82/GhweU1/Lc61yTr+0+W6e3X+nteKGhGQ1B/2c/mZpDhExJbm5SkA9MJ5fA2RrJ3hS5kVVG03\
5250  8UvGUIGvz4iGlX4AnPkDzogmdOm9YvyeCnt+xSh0Jof8HPegvIVGEf+UTNyI7ReAeNx+sQj6zoy3WI0WXCKYfI29brKxKJIZ2M34PK7UoTyhHZDzd+O+u93A4MD207iJCtAp9JoBttHHzLeVgwlliarnvHfEkCzJ\
5251  9zbZ97wY3APIRknHZ7njhVP2QfQ/lsVfY2nAvIzoNdOz3oIdiOHpZvjiMVBjDZQSKcEGbYQd6lKWtwC6f4hrFaCBfkxQg9Vf65s0IAokhBMtcATyuxVzVSC9atX94uZqJp+TLeoNRTB/vTTOEcUgVsB8LB7J/BsA\
5252  mjFpmM37tkrDTAzsVFWeegN6xZ3EMtnF6tcOGC+ZZ5DLi0pvSD/ocYYcuAu6U1OABWar444n3YwxcHDh6I+RHvMtyG8z/hXF4t3O1V05ncmQ9DFOF9KCLp9u4nY8mptb91gtlQFFUExMs5djCMfA5ga5DtQnW+AR\
5253  DFUlN8GcWQNdnjL7Zt94erNJvHfqmecoCSAZBdHBF6WVv+QO5mhrN6boToPbuBRpgQoN5stBw8Ae0+YJvF6waml8uh91frcuDp62bu1fYYjJDTBKSY2Nj/AP62LYRMCJFC9D7fZ0P5jGAf086dgFdZ8CbRtswJiy\
5254  JggtA9w57vL9QbAdjSWHk/gKnDhuQLkOqsV1MZx7cKx2l4Nf8czhDx7f7sS5iTvIidTAct0IE6d7wBsZN/ud0kCrezzHFaggXb1hEB4nw6kvCA4waiDhU7E8jN8TXyCJUBNsfR6LjaiWTLmHn21BXq1A8z1YB+M7\
5255  DoHvkaq1s2+OyG7fV08PKCfTNzAzcsBAVgLOy5qs+GZyl7CJshOdFFr4nOQHSgdQWQhX429uCpmgnw+DZ4NkCUo3R+aHbHYB3seOrWGr1tMbsHPUd/Dv04jAWmKF+Mu3lJtE+VsxlilS1fXOJ+Zz9BrTPmkAeton\
5256  5CFU4w4cpb7ZRtvwyxcY2/jyQAzUp6QE270ypjl1hgm3R57hBbNnrxg3JulYph53ETNdehyTLTuPqyQUm9PIit+z34TNcvwdNA9/Rp37GB0w30BsyFzWJ5uwh8dizKHdAeRIxUwEKJGa6Ubc9Wlkd2b9PJku1yad\
5257  jYKjAfbFLqhinPWQLafE7YW5CMG5jBeStENCmnvw6q7nfTDX9L2PWB52Xgc8xuBGsEYsU4Mwwoyyuhcc0p5TGsQ6jl/S+MJCpedCUFaXTZ8iG3ZgLsRFvPz24RNzL2KAy/HrLjFXC78rEqpJ1LkIKE2DJHCM7+T2\
5258  4SNed+YJRjf1kXQ7suuXuzoRcU07qt1mhkPb5BwV4TsY9C15HzB6EUe4QxmnRnivhXeTRs7jcBt+sKiSef0tPjZzj6Ut7CrqEYPfYsY5IzAOs7dvYPCjcL0It8++ZKvKHrx+QXajSY/sDZxhhz0Mp/1Aj6UtyAUy\
5259  6fwx/QbbEDagBVJqdQT/jrfeAKh2CwHZOLKju89BlH2A/bRLMgBCBK09uumZPOB3QTTyBCAk63iLJrcuF6BnvugScUCBC1BK8NckI9D/I+aeFgI2fbFcAKByxi+lvBahOC06OQODOX0PWvx0H8Z8g+GF4lvAzyJc\
5260  7zhKkgsmhqctZW6EbFKAKwBRvQr3pBENf5ETTVFtsUy2Mc+9pl4D/t6T2Y6wpKewV3oAzCmMppIWkqN2wi2Y8Gtyl6riqMvZAXpN/DfaoEbdEicItvw30czqI4i+En+A8oFaCzP+dyerdDIL9VGYEtccs8wgpyMI\
5261  XgCsx+SNUBAvSIXXTTDmeKSvY8WW1bH9rNtLupnyZrL668xLVzQYb1ckSZDYsEeQQxPVBOBVzLsNpVZI7qolS8hyt97ZZwSyfkCfhx8UYKBAdQzSpRaGAEowNwjkp2tCD7RXEnqFYok8GMw693ygiq22ljtoPxUV\
5262  l88UZOOrYjfqJ7p0ORXhpwl3JVq+dcCw1imA0Lx1mwjUz/oIuCAI99q/JRfO1Mke0aRE5++Yq2VIfa2fbN7dmne51zyWRUMA8ap1Pw+pdqWp8uIDyDz0CdNhuu1qjFjEiBWM6P8WI7wSipFig0OdU8YB8lDEXpgJ\
5263  2MlV5KyJagcTKWcuM9kwDf9x7MCv1+xKwboZBX3q1+pj1yprNGwdggI0WNwUCk09+gIvgDghfVhBLNCZ3IgJs7dqcUx52fWO+CQCU1kikLb8hbVhurREIGdItVkAlKNr1c5ZwdatYskSNTb6IhLABBGaRUSTfdWZ\
5264  QBRrC0nBNNV7zuDFJIw0xBXwSxU06hRyLmlr7uWN2H5UFyeyAAHdwRVwnoXQGbD4aD6O0ncT6l/04oCftPvz/373C/3fEMDI3OpclK2jco8nEAiXCLy7z7hlve5QE/7TKWROp7Y0Bf1b/AxfD4VeL2i3ICNNjhkR\
5265  hkVh6YUVEy/cGV+FoND2Zra+hryeIDslC6C8n8a5dP9VoKEhJAazV5BuLdVTkJrqFQceNA78itiZWs0Tgggs2Kc3glZ7FnYsChRtK9mObmMV4djpz9dA7tcvTn/GkBDweT6nlCd6lYiGDTYW0A1JVy4E02NJ+LMn\
5266  e9EZmAxkL+apNpydH3QSJXoFEn+H4zAGJ5/Bs1ik4ZjWtgDbqVseZkx6y1uECa2N1gnW/IRDkrmEQ5SZ7UPQpICdWiQQJqO0izoTJoHwKhr41Ocd9SFWytMFAP0S6PVCwpcXc3/D/Na+lVt0L1Ci7BImdIFhWxCE\
5267  OnwNr+rXXfzT9HJXMyeKInajmskcw6bhK3qnlVjdLgVdo6v5chGT9RSovVKBss2b/XUgRkM0fWnCIrzxapW1cC/u05rlBcyJrlYdeGEbEh8T1I4hzFbt/dXN4PUTK5pqrzoBNP2K6QjEQZNcS5jYMULSU7QPMaC4\
5268  iwpoXUE+WkUlG59OxN/3RDxGAsMHyIes8ygz5RmoSv8w5D7ifeRzt8nm/bwu2+aOJuYamtQi2VEyLtymy1F6oE0vBcnFO/xp85N0N1pu6CKAUtTxCt5qR1fr2wehGqj2Fpc9TQ52HaEzJBhb9Rqhb3+fUpS6TCDk\
5269  COoc2WbCCkOJUkTOf+xFCSiOqQ98yYYyQOn9lVsfI1KwgBLXe8pVPuBHxHtkULdwb+EKhabsImCsJ7Yoau8MB0eigRkDIQliCqwWunwoBVQfDgIhkLKhwcBz2EZWjIgVjSVWbL3T3ch+9oTZqJVkTqqxHE4YM6p5\
5270  2XGwJuc0joSxCdk6Dj+b3sKoVEzlO5S8HcFvI5ajlZdnBe8UKg9hkBxraw6+oDAPZniaL/a2RxyS4HlG5Dzn4OJbDvnVYyknKqGAx/BebrLtmyEbX+WMJHilpg/YB4XpzEsIJhiW2kW5vY50uYngnWMsdGbziKJ9\
5271  +x9j6+wknTL25OJNnPAnIS9Yh2hDGHK4NPj5Vl1wyrrggH7uaUfTZcIZH5E1s9CcBRgdt7d4HzTTKDh7u3f8fRcmgNnMZHLn7IIxrd6hQnwHzbMzPQvVAt/H2MlbjjOxJaMNFwJBEYjVgK/0jCDPuXgF8lxaLyja\
5272  4FJPTizMwlvwdjR72aXF2tc3SZxgAjgLaFflAW0irH6ztJks788CSt5tTpBZHGMqcCyYYUAsl69imKj8QBEpCmrVwWiPDEqEsWTnJ3UxJCQNz67SbiuXLfj5e6IHPVMMWsqVO4Z+sGolMO8IGPWLi69hXCucsGFG\
5273  gmAyCzl1UZjWg6WpAQmlCZC+wTsA5T0KgDA/+039togE/u13YMiAtCmhW44Ozdcw3Az4DrgpR4v7YGZvLsLPSJSjycTR0oJTbS1cuxtbXpoz58yy8dQkRuhdhgo6dl7UDdhhI45rZ5FouRscKxXc19g93fsAze01\
5274  OKKRK/ZlyDeLMXwYaA4N4vRqBCIgizHVBkZIa9itd/l+HfftP5gM4UwOKIgCRGnK92Sse5r7FH4G76lF85ba3kB7ysGUi6wqfiFZgrjIbpZo770iuxdUzFJQ6yMsIS0lKNmv/fCqs4g8K6iOxIHUxb3r9a3QCtUf\
5275  4sKLj1e2S59QfI7j9KxwXcyObCEsrXLTV7Fki1ZAsRa4wEQsoYbwXz2626+I7qeI49ine9KjuyW65xrGNOl8UHHfauAfWVABvVPeproQ3yFF+kIJmcYS2zQ42VQo42102ifwE0JSDkzrCNG37JzdF4GrSemMk02I\
5276  8I/D2wDHAqORLe2/I6ikJGkxk+xd2tPxWw1FSSFeDZ7+hKx+5836QT3PB6IkBghV4uYMg3kZlPfW+jmhtgu7C5kiu8GWOw9cfyqrQmlVNv4IPl3t3S/Cjesc/HccxWpxs6H52FhOPnjZZfFYIzX0fo4mWtG49AWQ\
5277  Czw/dBd9n9D0qLndhKxLjCPs9i0xrhrKxqhSElfwsftoZ5W3U+eCoFM89IUfUf+hI/yathzZVkuVzCVHNLFkvDVXIP3bohH+xK8rccVjrn1AN2LCZVuTvsbtscrzOVUMSmCg9QZh/BDYHEVYs8LeHQ8X0BqFYmdT\
5278  MYTWBGF8+z6f0Oq/4DvGmkIaTbHF50GKaMTE0uT6NsUF2apVxokDKpfSBWgUJ8ik8sqiKVbvsaFSPE3l+RoqhSeSGROB52krk33GfM+db/BhOgy1FRhJBhnV00nFCp1E2QDWSUeeToKJ17tSseFJnhXqiVVS7lkk\
5279  f6R6+phgru5FOquPCir9j9QTOsXFUD0t1Rua4u6lKuomD/sxummPA+NXkh7tbaiiUkx9quQKRlpKncggLH14kciok4qOsLpY61GXNAsbnTjUhE3kJkGBwaePc47XY3AXiQsNqMZz4rgf8A+ctupy9g2b08Sk0852\
5280  6vvwUixEnnSP6uE/EFVejLShwCi717p4MaeDH31zEVgqpwB4Z0dI7nktuIxdmJAdtcR17aiFroVatiCJZEojjarMK6YpiRq6WL/MTFC5ug1puwkzhrHJNGFbBQt/0FyI2ThNOAxQefrmElq8A9S+51IorLiYAGR2\
5281  toSZAwR/Pbh8I/lWmMfOZ8vYIYae+djB4aeBnj4S7MQedjAlKq5ttiSFRq0UwpMHxSpL6g2nxdlubyqJnHu4FsRMPcQojv5igB2RGc+HB+Cg1Fnpm5Cql7LuBkAzaUOpkV7tCOILw2vbECbCeA2iBhhVv4646npD\
5282  iqOL1kR5XbB/wGVDWAEwBsNEU5h98WlxNV1KqdnfO0HqCVEvtlbxJQErMyOtmbZxfX7EUqSzwL5+SDPz3/GDmaeDYGbWN5K6tHW9pEjZMkCfDyRCz+kryenbS1aLVmBe1Vzh8ilfvc566tWIei2HXt9qzw8SHcWf\
5283  4vZ9ClMgkNnNS7XrgDH+V9pV/bna1XLdQscCp30WuML7M33vr69dDZY5F3+264fWcyG7R28nBqNMdkNz/TNlgje211i4bKEY+YlT/hMstJh/SpWF5mQJ1r9Nurjc9QZZdZksyT9KluQT9sZY9PjixHavFZQi7STK\
5284  NnkaP/UQyOWOiMPCbr3lg1aW/OCzf5H0aAHbkVCuRceMxQrpdnU7WtjRPRIGcrYKTaE9PlIEpoFFzOFBF8wSOyERklRo7M5z8JjL7A2mg6GYAwqWG/vhZIGudAnLzX68LtQrxX/WmUOErFGLkkW4c7qNDENJzsae\
5285  EXLWPDoAdSwe3yEJb0es/VTx8zHzU+UXg/JOpPcL5gnWr/Ic6xyVU+fxnQgfxHdAq2ZcLKhFaLl+ikaBnKkaC4ya9O3rF37VikRj31OA2jivZeGd2ZKgYi1RP9gIqDomJJjcCaGJuI5YxRJ4acWPk6Y7bHXx0ZVu\
5286  e1xdkTAQr/kfW33zHlbjKm9aPpsPyrUgn+bXLLRstP6ObjLCiD/XPaD3jlFzgIS+PMGzfjb18tXu7JZkLsEZb3341hkPqMpQu5ADMMPOZXU/n0CEPOmXkfyvk4dS+IFO03kfW67sgy8F+Oiw1g5WE8A+s3kxHwLP\
5287  MS2sLIy7ysIW/r1TznqgJt2SDJ0UvY7oAZXymLkkef2xw29QEq3lRVfdZ1y8T3pheG+D9qsefwkHGcqFfx7nEvYxzD5myD6gOCH6asXmBT6iLxAnuhBmkvoHSlJyYGssR+4kqUixOdYPwGeQMKgxlBGOmCwxn1SO\
5288  f4AvmF0E5nY56yIcc2E3EBJ7gfeAX8Zcvz0hLYCpmjHXYcs+xvOBNQaCLQ1SGaypPqSFyQlrLIFOn0gGjT/jD4z+8gIBwSz6jWF1mG+Gi1yloBZW3cZdiTg8AyRAfhL2GcJUeyIvod/kg669pVOvrk96xW/jK37L\
5289  rvht0v8NYKu5bYroNqziQQ6ona5BXA1YuWCU5+q054jFvgaDV7vBtnOSpyp+AHnZRv8FUIAnKWatqbCCqegAlYKyCcAVnZ7YlVMtv1L+ULuk9PQdHwBr+W8PIt6W+EcOXUDsYrLLZa1YSyQl8tny5QmYyQaJqgzP\
5290  bimmAVSt4o7JqvJYxDIzU83puJpr0DA9mMxXXNgigpbdBSWpR1TuWAduj8LtrbUCaqgrOksFX17wF+hY8bH2xo7WzMniLaGnfRFq6kv7/G8nizPeF+60dEn7p1F4gqWw60FwfixG9YxCO0bR+RYu/4y928LGQjgY\
5291  b7ztjp4vEPfbBBPWwOYsWwwcU4Nxilh23S0t5wlgJegaMjkwe1qwZ9V4Z0HLyehb72yUUscgQ2sYG5VnPH/GVRBy1KqLpMvDHIbAcv3W7NjtCgJU9phFH25NPhOAMtwuH1zX9fJzkz3gh0YAwqjAocDkHdZA7A/e\
5292  L/0LrsCtbaycsqHCjvdzPoBiLx+kd2onWXbjSpB/DQDYqJ0AnCfwikpGQsnV342dQxl6gZstkVMjZpx453u72fjQnEFHZIQR+w8/vKETJ1u7HYsrjrE0eR/m2q/6iGnPlLGERm6wK+dhvxkvD+ImyTpni8x62L/5\
5293  w2/N7iM+fkOnhXLvOJjliwbQ1M9loxg+JOaO7jFceIJCQQwQlU1+r5tdl/iT69iF+QGeXpZVQoOlAFDRHolIBpRcuYjEjsP9/h0WZRziLRUh3lIR4i0V4X0S31r7988MLyzpqlBV74K8U/++mNOQs1q9a4VI9nkX\
5294  QjA/xyfn8CznI5IoNRszuFihZHMDZS4W5ZYkIyrN2gdqPvsXLbm7iHp3IbRu9QJvOCr9O6zQFpRwFBfJ0sshx6IbvqenA55ZoDQhX1SpvR268jYfpx3kmgiHt9jHaLyEXpjbQ6p/IEyp9eN9BpTuctoTluzfPyFH\
5295  ebvHj3qXVOD9HMfnSwhzBVVULqKipSWZpYtgpt0dPU4zpv6aPa5Aypf6DME/11uPhQ9I5KOZYQ67NWmJ3xjoyrly+LsAZxmzjOXWjoo6T6QFnZVSQi5zBRxE93/BkTnhNjzniIis5MqQul5GZB//x79w12qyzGtB\
5296  sH48Qzt+R8vphSlVnPHhLJ11pRoiwSq4ZS9PzOMAqZM/Njvba6Md5MpzySliXQ2eugeLAg07qFys+gBHlcixvPc8LnoXV3krYd5XHuWQ7X0uX8Xdj+XuHfkR5TBszVIfMGviyUUDHm2+d3L+Fgj9rBPgGFdJWEZz\
5297  cFGzPIb14ewlFZhANBspWe6tOqF0yAmCBljESpgm3dpBFqm47IePtwpT5TF7XBrjds297qa3jg3cDQsgQkwZi2GQjk5O8NWHd1lhN5AJKaFkSZePIORp3gIa8QLhZ+xUNt3NX8FKQdH4pdDPRoRHb/ecy4mydIZB\
5298  FvV479ENJwCg73g0xpx5+kW0vRbs7I0ORUFFtVww8fcVCtFoUVzjYI206VVCjW58nA6Ac2eRufhby8UaVeEp9GZ4LhUJO19xRaC7HaSQQ7lyE1522ThckNtpgt4ar5PUS4KKRdO5lgzZbVCkPes86gwxcZzlohGR\
5299  YSArcpFcYFSgHMYv8dXbDD4vfWV71r+pDWp2sUQQaVHitS7T1HuGbmcu9195JFjat7BJcc+6ndrdfSW7Nuo8iFKSlGJiDa8s0Z0zblwwRRwXTKo0fK9TRYdCFrAD9W63DRu1chs25Uw6e/Ao8SIGcFRyCxF/p8jx\
5300  MR/grEI6w97gdSM8FNYkGxlzsuKiqFp3JytUInfFjAUTu5112RdUyKUlEUJuJWEr7JMERsen3eVL53y3QmH/C9b/0WerH/zGud+48Bvv+6xoBpcI5sO2f8mbKe+s0B/In0xD0h6Vb4U3zKnAnOdvmWN9Jm3J4Gwf\
5301  lO47nPmwCRo/ex1PVT697YF32Yo6qmQ3v6GKutW8aCVUIWf1sP6ugSsns1d+UesjOkyz8E7eL10Ll4gBgvyzvdfBPP98cOME3emn+XacppsU32GbFC2Jje7GDoIGYqFxYIX37rHxahu4yRIPpdfobPMFbdasuO+t\
5302  MnzfJ073kuYCv7iQ5Fs6e8O2XOVdnLEUzdgjAuDdNWLr2QO+UxP3khYg693uwiY8kdkcCkUQaF5IpXYZ8tyuoJbYRDVfptSu+6VkXxIBgFL5aK7BmoAzsfyxebojPEaZuu5mHUHnXBYiJ7bU7MmPIV/G0AzESrHq\
5303  sia6FsXKK9HJAvclGGmo+SA64E6lc/oD7+zJ/iJlbNphEqBJo/0uQVTzvRyEV3e960QOTGz8ZRT+h3eHuhf0AP6/FTJlzCV3TfjrkjQ8lMLgQ5GgO11o03J4tR8PkXuP4NhA0+DlB7a/oyAwZb6+66WfxcrNPK/N\
5304  yxJ+uniFiFHsLiBCo/4BZ0AAe6UXeFCZd623HE5RUKZVxzSYnBqcbER8WctSWkRuRiuAw8c4FR4ZiUa8sXoRGWcs4T1oY/QOEBrsjTdC9UW7DI/nfWNZSdF7rTPR3KvYe6gkimwNa7PgwpgmwzsFEjgCU4xHTw+8\
5305  i+zS7hCdIGaCxzAVM5AuInfyRGkw6IuDYw6n1JNMYmmRD4Ch4hd8Msm8EEq8yoyjz8Gj4y6Myb3aRWwB/LEPP8ROLlsCBWEo8tgCO2M4m2Ous2v8DjJLWT6/DpCl66mxzCDD4c+DRP7nBcoULr0T+9jtfpa7pr//\
5306  5dwuzr3/OyU1/H+n+L8kk1ilxnz4f3giyVw=\
5307  """)))
5308  ESP32ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b"""
5309  eNqVWm1z2zYS/iuyEtmRL+kAFEUCvutEdh3ZTtKp3TaKk1PvSoJkk7uMx3Z0Y8VN/vth3wiQUtO7D7JJEFzsLnaffQF/31vV69XewaDcW66V8T+1XDfp0+Vau+gGLtqbIl2u69LfVDAtPMkO4XLHXxf+1yzXTg1g\
5310  BKgm/lljO8OP/J90MFgt19YvVSf+NvO/aVhNKXhrSm8Z7f9nHQqeFaDt2TGGuC9gTHmStQriqHLYAAt+NPdTgUYKdIBT3SFoaZqu/KiKpDYDFr0xsaiec3i/6jHlmfEcwEyjHi5O6SnOLP6Xmf3V4afVoN2JQW9P\
5311  8GeEoxrU5US8kkgqR9oIC7OkyFUZKdj2OLTJO7oII6jqxadNUTzFz340AWmGajCgrdkmjlIz4rcWZv08vy+2CKzUVaQ412fL9gTqcrV9TdJ0f0xpetsptmggID+ckA565o3cmCPg+QFZbQFaN0EQV5AlG5gF2gfF\
5312  4i5M/KC3woLvjRn65Vl/6Et+UFtgFf5MlFrVwXBwmQnrCN/UpPCmmTEJDfT9q5pVJ2p0QHfCY6zOAq6b/kba5RXtQKX/T607sGYUwLCN2cm39IpXBAvp8NERCnBg7DDsllXRLigz4yvT2ziTxvezmVyd0jC+Y9OW\
5313  lDhGqWWLBowRoM0yZ0n8ptWyaRHS2Oi6BRfLopvYL8pkHGECb5LsbGemBYhhbLL8A0TS3hGdJcYdj7UvueSS3/Bc2jLGwOSs76LRAuhKReBGFhOt4nUOezvnyWmQvJ5GhsLmjevHRuAQxMroVaSHdjwnmFHqMxGA\
5314  J9oTqPUcbSXa1p4BBr3a5aol0x2/6r61IqariFFEJTTLSDmsyC5InD8BT2ZY8n9K0E1zmZ5PHLk0bKma+Hd1+van8+XykEIJvV1zRES/PPYKy3gHMDY9ZJ+fkrOCVuvJJupBMNOw5xXhRFmR2KZ18m5ca+3RuIMh\
5315  3bp0/PMjoHIwHMO/RykQcMrGQGy6EQQd6JoicFM8PX2IioC5Q1JJESJLJchREXCaCJwDa9/CziAOJAQ2tWyFJuMskmD9AnwYkjRpqdaR9yXBBsWtegG0wKtBPJ68F6/cYWVFWIdcq20KfQCYuyUBgFAg4UAJogAZ\
5316  I96SEBsk4SlMAPncoYTQJE4BcESPDXKYggUM9yfqb4ccI5LxpT2Ng8oThGRQaCEbPe1z+ZiYaEMrKIHnKt6whF0TptW0C/C8Klkl5RaVyBzH5j7p0sZ3haZhOvlX6FQ8J92csxm1SZIDSQ2T8AwtiO91OWSoQgGY\
5317  myb9o1RKri/jGw9QFeL/DBDkG/YBQLF2GDJikNffZDs7xAPESM22IFE6lsnr7Cpe/yIktOje+vw7xx6fRrukw7StHt+4/fBWyc64wc8GZjwfziZgD4sJpa3oFLzrZfQ2QHRRdPOwDh9RPMA6oQzv4EbkZLVkLhmx\
5318  F5SANuz+eMODuTgWxZV/Zi4f4s18F99cxzer+GYd34BSf2M8rFTrTLDeO3arnSJkyHG2rIvmjOTUCHRl0CT6c/p4efUGCB01PCXKKoJIF6EeQZklMS5eQcSavvZ7ZNjqM9mIitbF+dvMr93AG3/RsgtM3l8gS/Pd\
5319  aCZu6ewj6V0zaEttRWZ2vRZ7zbv26vRXIxTw53momqgimf6IEfD+BoW4jbx7KvZ/DSnaIAQcjJ1oDKPgIsKOxjRv8CvE3wFxVW5o+X54f5wT7LqaE12kc77aLrfJQdIJ4V8dFwYOfLtI5h+YjIr1C08eBd1WG/Fm\
5320  wYlZTclTIVkm7u8HIlOWNUUktM+cc1QwjOzZcvWW8tcieSFA+Irr20nw6aLhNXLKNJxpngELP+7CEqAJqLiT16QSyEHAuy3q+FdQ4IAUi3jAwtgNTKgDUODr2TZwqEPeg1EeDfyxlBul+7qPY0rsnv5wenhGfLY9\
5321  CFA2pAyqnFEGhSTgRoUmBtYT6dNebdcrBHG3XLfe0GrWqVz7aStyRUjc3niF7UUU0qh7IomHsNCRRJmISBnqoc/veOmaMepSss/Zh32MVSbhkKU92tCVc3T1kv5BKjplMoAtlsRZU6xTZNs+tl222PeS4jxgH/k3\
5322  lr9V3brw1bBAWOPcR2Ck7m8kvIXgoymc4uttWDiCmKpeDPOEbXxK79IiL8glnB54j69ydn16LjVHvSMXPn5UGPb2H/S7OmVb0u6wl9RsIPh7exawTFAqSHBIKNM0P5KBA+dxH8tb8fkwZKBjTv31l+2YLDVKR0Uu\
5323  2+iPJbRVZUGu5NIZ1MsQQF1CySHGUehQQFcOkYJdLq7g4+VBaSXLV2nuObloEMHIkHqgJih0DHdxZOpwT+57yOBd708gCqZ/kZS2FeuUsgKdrTrSXnKy4LgGCnqcHB2CzEfcG9Q4aQ8H9e3FcjW+2KWCH0OAy++I\
5324  gq7ZxpBBeXtyyxfYpToGGtfHgwYvTkcdJtOzi3k3c9Hu4dHFkjsHVRIiGUR8MlLCSrBgh1v89XVvyHF8mLsGdDghlNZJNwFCapMQHWAcfKyIxnEObuA8CrLqtqM0dEKb0w85ySg+KHVnF81vklxQJoDGkt0NGp7s\
5325  OM+g8RPQbIMol8x/Yx1lGPCaExz0yyDcNK8DXYdIOH/YWhT2lGKWYJUiJ5ZAmc1dGHdEjyoO4l96EBnFioDMjZCchyYPrFd21psLDHL6ZmWje1yp7JxY2m+YHLL0i0xfru5sABPIC2tOjbrqPo7YbY2K1u0uV7cW\
5326  dxIP/95WFejj0uzByirpK3IWtBhNUzRF53+PBrUMvifmVNqNcu3ESV+i11FVDxPS/oQFdw9QH7di8HMybV+2nvWeOWEwDWaDqacaX0igwYwwGXALL5WmXgIhgm5rDp3hgrMhhqzYW1V5XEumuODStJK8uP3BNuUL\
5327  CK5GbyuJMM2dsAFX/fRil1oFqFekxH1j3MSjEcoPfyfXgGWD0Ksr0wUZbVOPyDM8WhxThoI9toIoSfe5Tbuh9WD05UbWeQDS3XIoheKo4MTFo/peqCbaVLztEy6opdXU82AfBf8abJtebCy2T0W50X/lukbd8Q7r\
5328  A07vaXkwU4jgLuox9EQ53SqK3RRlwXqCrc+uGQbJtGQbGPuMi42THThnqCt439CgAF4zF9FSmh+1tDLpOI1IhAIvkprMgVOetum5jUysewsPfPwY8QFEnQxEiuSELIJe+cLLmlcQtkfBviAkFEpa17dinevIni14\
5329  Rfr49BlFeS1xveOPuFwhy6EA62lY8HpFWSIAeQ1B19acCODBWEXd4lqPqyNMCD49JLqAGGbyBTWS/TtqnRt6S/bEe+oq3sMMm6ZXYPnrN0B8dERxpYHE1nL/PI6fVt3DSYRkMAJVkO6aflupoHoIoALrFkNteRzH\
5330  ExB1sk+dFazTICBNJcZzVx3maj2Di4E8yiIbo+jPpp22U3JODLCTo44HrTAMeoBWFk8fsBT9ibNSTIQjdWHqnm4qbEWIak0q42ZBTVc8FUowMq1/Bl5fxhkPzYkdROddgHbZKXuRgZ03/4H09hUs8AysZMpmaWRv\
5331  6y5wetJXrYHOj2K03T2WFZ5KrTTqtcXVJkEM5E1UrhWgj16MWbCD1zPwgENK4ez0++XVZ+poo2XUkWXgcZalQhO8D/YD0oSK2+BYw/VYsVyYQt/G6fWYe/wWCwBuXBfcuHDJzvJ2lwxB1VEvu06eYBvrM1wDfmqE\
5332  uiLudxcW2ie6PCYDcBz0yzylSkG1xwzNcYAsa7b5u5o/6GxCwCdH6RbUQoyv1rxaj/hgELSIJUz9Ce6+BEyTTwmgssC6pQ6WBGmyKlsXpt6CSmacRmC78hYurmkZ3c8lXLZ4E87tsAqp5REp2m/HybfPZzSm09gG\
5333  MHp6VuW8omlPmu4pfbXm/Ys7XPmKWYOYBQw4mFK4k5xudTamxBtPdkvqtmE5n92HQkHnDUUgnR9yXKjXDB34uHXOcZQsFIdEtqmvCXfMZNzyTcq4lu8eEmwQ1VTr4+biCYgjw6Q5Uzm3GDHkQtoDdlfatsqS+NRg\
5334  OTU7QYcn45l/wgS7Col5ju62G0VDPCiq6Vqc0ZvB3h2XajjtI4wDZYRalD96rrIDLBY8MSnWBdOqBLn6QW5TvP0456qs7iWviEHgptgKuxwFRzbS6qp7xb4R/mFSxclEVBuKb89WkBkIpFNlp2YnmMAd8zEJKuqc\
5335  W44MXDbZRAuMWRM+KG3Z3QQ37LRGMlaYaAAfeJHGOYgZjQjpMOUuXXizlPg1OYpLU+g5cvwqywFNw+47M4gHT6b+V2jiYOJc/0mhC3l1FXs5HhqpYz5chdQPcmFQquZGJdiBlUO0+PzHoo1jAfyznL7ttEdae3yU\
5336  kol3I+juEdCCFwFQF9OXW85f6WUQ3aUi+pSSAAMO0d8MnT3vnbSBH9vXfKiZP+dGsiWhjMlnp2AtZ5xiWjGyZKvy5IuOazzdPIqYSbi9ICCNKcYgsnj9GijfvAGtX+I3RvbmHFPvT8V6V2Dljk8UsDtwQIxiKcje\
5337  UWCYq78Px9NO35wTz4Uc1+ofWDEV8V7yqVQjFZahUz/Mq2XMSW3S0egvgGel2YW1bkj6SocoWrBxOjz935VzXx6ggFrA4bvhJjlko5i5lZEXFtxRL20bgdXqOlB27p5gBI82uWKWl7R8JiJnL/kWrU3fslUy3yTH\
5338  J/k+AcJ/U60tgUxhPE+20echd62yMZ8PYJjNCACLKZ8R6ZR9CS0uP3s1j76NAROxbRXzgTQMXMOA5bYgNEcCBXN2OX8fcMFyryhMsGdv5o1MOB0tUmI1HP6kW7BMDwRAv9l8arTuD87/0a4AVp5xHyEs8vjPAHN3\
5339  S26polZG9pq10Tk3bL/kk4XawuFR+FoL8xv8iCoJHzYY1qqkSgo/AJp8pq5pyB93abrKd4e0FaZX426cp6vxA1kXfXUohBtoWNGaw/Zcfi98mEWs4ewnfHi17cy+ks/0RLSs8+owsNLV1d7jAX4u+s+Pq+IWPhrV\
5340  Kk+niVdi6p/UV6vbT+2gnurMD1bFqoi+LuWzjT1+EhOaZGo6TdMv/wXshTKs\
5341  """)))
5342  ESP32S2ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b"""
5343  eNqVW3t33TQS/yo3TvNsu0i2ry13YfMALild6AvSwuacxZbtcvb05CRpIEkp+9lX87LG9i2wf9zGlqXRSDP6zUv9bee6u73eebRods5ujQs/A7+fzm6tVy/0wC+1v39223cHoU9sLo7gz0b4UIdff3brzQJagGQa\
5344  vvXVqHk3/JMvwmOVh1+YqktDSxF+Sz0bDFzSQGfD32JEJLAC5AMF54j7GtrMdSBn1HKapAcuQmsZugKNHOgAs3ZEsKJutg2tAw/fLQ4PvjOHB7TCwC2MaSeMBAbCrA6ezL3TE/qKPeu/0nM8I/wehFnhL/9RPyeM\
5345  dLAzXlbSECXjaeFxPl4UMtOovawmjFXpz/QQW3BXT+/mKwgUP4TWFBaRGJAjSGG+CvgdEr+dMBv6BRFUdWSla9V++Slb1WRBY67Wz0kbPG0zlkcb1l8gID/skC9EzhvCijsGhjdJO2vYchdX4WvSWAe9YOthV1EE\
5346  WWgM2lbzu3NJmJs3D49NaLQV8An/ZMZcd1FZcJqMNwhHWtrtvj9kEhboh6GW90320APdjNt4L2t47qdSrM7Oaftb+39uuQcNxgU4VrAq+4yGhI3gRXr8dIwLeOSqJIqqMkoEOAhe2gM5FUqIuX4/PJSnEyKPYwBA\
5347  mJocjMaKlBYMB7ChXcaLCXLrRG4KVCr1POBIxat3+lw06Z6CApaTCHfUswI0YY2r+AfgY8NB9BWtwnPbMMinr3lE4LJqNNylj6dHVE2AR6mO3Mhksqv4XIJ4V9w5jyvvlkpXWMNxfq0HHrGrUUORHqryimDGmA9E\
5348  AL7YQKCzK1QXJdaJDsZ9rc6uBzLj9vPxqGtiulWMIiqhZqrN4Y0cg8RPnwAUHIq2oA50RCiajQSmtE4OagJioX6EsAtSfJc9pBFTHEL9w/MHep2nOGVYSk5GJ2gzCEiaUQesQkuN7gpqUjvGHyIAirGpFlNOFwMd\
5349  6If7WrCpVezq70gjX0PDMa4prY/fV1Pb/QDxBbAv5TFmThOssbHqxCvY0v1ke3sjvkczPDlvlUA34rYhMWbUO9qtmj2FRtlmp0zjyHFgW+pGfsHhfTVbFx0UkEddqy1MmXq6y42eNEpLNkxxPrbH61ZNu7OrJ96K\
5350  MrDF1yQYPMeVzLoXJYuLt2iN+OTIEef54aBXCYOL4xG4rYOZIlvmMgJHB+DoPR07VB6wTHWdDOeC1tu7LmqsK+LhHilfS+1O2an5NgznNSUWvB5ZIhReMS/hS9uwGRWTKu1+RZs1chbUICaFyowEqgmBdis2jgVE\
5351  HD4STzYdHy95t00y+EI8V5+vJxVn9YIIxXzi2slJqHN5MgO0dHw6cM1Be2rGaJDwaPuy+6Ah4mFPXHE5+6gP4de0pFEfwxtot2ncOTnj4qj/dVwJzwVZkrZdgbIaA3qbbUdHyzMzDW+WSavIXdfNKVZOfXciiAr1\
5352  BxnsaUaDHWZBR/r52E+aYr/3l+qkHuEEr/NnmSeQgDWaLJgmm//48tnZ2RGfDMOA4lvx/L4ggdghyrnHXuWS4AU0wi/nTjUswua8MRlLKx3D0kgC4u44/yhhhcv3vtsFKo+SPfizmwMBb6pBVS8oaOtr9OJUnHIo\
5353  zlvDKljzU1FH09agWBTuWXTcFsBJtmBW6ymr36ACeNItsBONWH5LJ0lsmHa1Eb5tOLU+U55eGv0deZ7EaIhzYOgnnkcjaNpGvV0HW7W5R0A48w1AgfvpUQcyhq1QJZqMyzqBDrAofyRhWqqjS2yxew59JYin62Q/\
5354  M58e8QlJ915XJwMMPxSzbGsR9XJ9GBWj+9f65e0LFrLzVyxg56XN+JfkxWxskDAguLDmY/JEzT/X0z6PBpUw5tnnnhU5H1sK6bZWkXu/H0cJWM34mR2Fr5NDwBNzmlGMj94wy6RRowEcwNbr6HXEhzKxmEhp4hiM\
5355  RQyJgjB14gOB8xlW4ddJ5a2WxM/65UK/XOuXW/3CLhSi00Y9SQk0chShwRyjih6zJvKqnWviNqA48h9g3ccImXDIkMhzFcCCo847bevvwSVfvuJThKbSKS+g4P5mvUmhY3YZhUIsvX/+A0esxfY0Fjp8R9bTMlKI\
5356  BSKluLgV7SrH2uXLP4RJ8MeBDauyLssXCMPvL9l8Dgm0LYlUgoyacsETASK1RQz4W55RuGFAhIRbwYDYzDzj98n7L0o69b7jvUM6z67XL9uVsNCMTI6gFnmBdbp6yySyiMkS09aDG6tSBpGR09BYU4AErnXtle0v\
5357  WGubhkmF1VYla2PJ0TgAVPElHJUfYZdg6BNxmL4nzRCNol39QFoAXcHu+WUPo92LbVg07Am4L+kr2hwwo3Aoa1SssKcN7CnqcM0RMgPETOhgY/toLCof4aA2YzjAvgIHSxKJqAceDhHgzCK0B09Pjh6DJzhENY5N\
5358  Qnp48ulgSMmghDYVpbifyNpjpgS/xNStOzw8mOS4JgkxRvqdIcqAaadJR8fhixuZh8NRmm9OHvMzJlcvNld5ZIkHhbO4dlkajmlUuuiMQBtna40KpWsdV7ccbr8WHxjk1OXCZX7Bzcb8IgbM0RElu2Yei+/sloMX\
5359  /XfpOuwdRPDgHnAHovr2Uoh7eXL5Hj7d/ip9zY2Mz38deKFd/HmgXnAEBhGt4CEc1KQ+O1eg7FTyJurthgCxpTBt8OlxFKwvfZKUKTkF6I8NUPCEY0a7y19QoTvpcnQDotjAf624n/v3Bi8JaW/Qmeg7jJw78kjJ\
5360  S92Hs7zBkUZHa0TPhHuhTYAKw+AxdPOovPKfklGN8fMckgiJmkpBNP8qNt2+/+NMCfSp+jkLSHcZfW3iOayOU4q+/kj2hI0LgN+Upv4+Nz4ATv2sgpI9S6J7ujd4E8qhQixuBsOC3vOPbEzadZOgnF4Q1EGDnu3B\
5361  2fmaCYEWuNjTBeEcbo1i1na2kJS0FLx5cJd8Bi6QNZJ1kzQcBAZouzgSBQ3AadbIHjRf4qHWbvOUqrGxG8x0yss1Wp9qv4Z1GhucGwvI3u1/Ac7Q8v6Ks3HTGoTlmKMbFpsKMfLRinfs1+r0HPVMToTI2zXKAnuu\
5362  PLeqX+8vzdsvCez6ntO0qkxXMl1WPgfCtWZUHCuZbPWR6WbiBoGlC6X1k1G6HBhTmnDsUb7liME8xn7cegetyRFoTcLqj0mr3TuYEj+0kN55TgGX9eUNjAfl8l/h4yZGW1syOrviByptQaHlIln0WAPa3Rzr7B7r\
5363  vsr1IBtuqgqg0WnCPACk1nnEJBAj4S3pPn5Po49BbFGOijjb6EVA5FJhLQgk0CygEidpfhhsU51CCkpUdeNsVtdtSeNWnBecrbYVyzzaWfAvbzhDltJaPa1zp/weAgiTrrgMBoewuFr0qutQ+imOMc2SYH8sD6z2\
5364  MbVzzBRqNNeARIbzae1SIsZtZQg0M2DgGsfMgEvZX8UPbcGpI9zIdMUJc1MsuF1T8crWNNMpxPLCMxRyBocIVYp60moCG4akajURU7wfmLS8q/14V58zU80w5I2a1OpJz3ZGpEmj6FOkuXqucv+De3jFKc10NPc/\
5365  I3O59DCjPVjowXY0eFNqAbnuk0VGoIJipT0fse5V2r1MxFln6Y+r448n3UAHAV8wVQb645EYRNjlHTvS/obQCOKkTqqDAE5w5Jxy2ys7fx5yi258XE1zQ36/w1N5yulQjGVnJehEsjoQLblPyLp5yUjAcca6EnoD\
5366  CJr7a8Kspv4bY4pGDggyZQ140qq4mTaFYe4h2ekOE59baxDc8cfic830NgePCn1BIA0qYQLyTq/g3+wCztYi1hub/JQ9AAQZNJ0XCX3DmmHNYZ2LMQCG2JDmwgTyzMycPoK9veIgF3IeuAGG694lH4AiEqPC5+ku\
5367  cLGK0UjNv345zlVPpmpUogjUBo6BN6qc1Q0s3DiOMYo4y5A9V6u6XjtRNV9T2Lya/URf/DI2J0rvRTwMTpXVvR6SDTUKelGgKBXIGRCsDHq5HfMyTiFJg+XFTOqM4NBXjarpuJmCb+sET5QIFm073kykJQgzjPVc\
5368  j2gqNZzNa+NewtHY3KKy/xBal5LpRBI7UMxY/f5HPHlGYGGCOUMx/xlDvvgVXYAs8rPYJR4RMBC0Lp8yCAwlxDco4ft7GGnzOaox2s5W57I7X6tRmAKh0rlwUtzQOSL5/AfZ+AHcrk0CKDBboJYNFyf8pFxWwXxV\
5369  FvPL3awq955rd+yoAp5agDg8tLC2poQV9Kk4SkvlmuR8RMgjAbgdPhURY4JLdas/lQwZmFa/4Jy7W7cEG7dmWIorTsUBxYtZw16xZUHd/P3smqvoFSUhKr4uoE6SuyHEIRk/lSsE6Ibvvl59M9hbKigwHyLKaHxg\
5370  9NdyCeAdeNngNIGmePc56EyGOsNRxiy6/J1LHOcX+yQKP4ZjmXL7RCY7lABycxwVSYQ9Cp4LujKkEzu1QLtClRuG53T1SGWA8iPChCoLXH4gfAwbfSUMVNHCgOxaDr89l9qqds4QhAzQj4zP7h4Xryq5bFJzSI75\
5371  QyxKb+O6r1RZBjM3fnEfHRUMHMGy17pyI/ewUJdBL1ARDOdDfc4KgzF9f8I+hMYYtTereyM5aFTZwDjxNwY49/J2U24PbLyHxruYKxBsgwAFPTP7q7pfUsqqxgdbtM1xjrjh0wEHoWXkW0jla4QaDnACTlffSXuF\
5372  ESAUtMp/fEu0MKGZay2o6i8fDkW3frids//u9s2T5A6DZeClX/wXBuxTbruBzBLURSxXLwHzSQR3ePsRIHRISOKaeua6vsSUzy7nwItE458bUtUYjF5iPlBIy2HEBDsWtAfGTRkvgv2LE/HhdQuft/6NjtsLcFiu\
5373  YqgjoGP4lpMtD8DE22X/1ciL799E41vikrZLFUhwYoTDjBgl7nAKHAkhyN+ksbweZDzq8Bl71ZJNGFm2Fjvb8qm85/T+jh01hJjbDOChXn6Bi90n5Kkts+fst+sSOAfUWMnD+AqoG4J7dPaWDNZ8O3FUgaXo9IKO\
5374  iks5cEXLar9Zk95IKc+D98js8bwDhmGt3gvYhk0yR15senRbaO2/xHCiruLgGGUnOviX4HoBDws+hw2dQ/TQID3itlAHObuK8XP3x7kEWNGn0xVd0np7Lxkv3Dw0jAnfGsAApOA9LenOjkslNX80vkolKBwckh25\
5375  TMC4jMpiuCSC9g7NoegboG/Xbi3WXCqgkbAfPpX9yOh8O/twLiS6l2TEeOfVK2R9PwPoLV25wHzKHuWUdtbbKleKnK7WpUIm/kYiHF1xDlVdtDUEzqrW7jiFXfN9rI4qXFf1GV4gx0JwCkYwd9tCp6SCE+VC1pi7\
5376  Ds2dbB8ZN44gwbHr+FpVrdrkckbFl1YoJrxHUYcEh626a2DtGjOKud0GGAUJVjZKvJbThjcpoYNEMY3cmsebAO4o4g8Us5ohvV4ZSoeCN9JUo5v2irb378mFbrnM16phlu/wGDtc8Ei56leFB/C5l08mmoqbh9NB\
5377  GQa8KIR1EF+vAAbmbotdiY4zuiQF6R60vIPxKPe+n+fmnH9MpBt0Ul+JtimL4+FKgy++m36qiN4LNFebYD/AX+iWWawVYi2w5RpNyiowLWl383YDORiNbRRuWA6FqnRLXa2KQMkl2AkxfJaCBlRYx4Q/dvOskmL4\
5378  ED/sxmvtqKt42zxVTknBVtgMF09gxAdaO37tOP7EJEK5nfCaij+5kmf2NmVePImJEO7BTaM5xfh5RDLWUmINe/M9XT3XMEcr/5lBllaMhiaRlfFe7TxY4P+f+fe76/oK/heNNWVWmWVR5OFLd359dTc0lstlGhrb\
5379  +rqe/Hebvj3Y4S8jQkWaGpP//j8geRE3\
5380  """)))
5381  ESP32S3BETA2ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b"""
5382  eNqNW3t31Eay/yozY4wfmF21ZkbqJrnBDslgks1dQ4gxrM9ZWi0pJIf1MWayY3thP/tVvdSlh7n5w/aM1I+q6nr8qqr9n511db3eeTQpds6vzeL82ibn18n8bfMrUV98eHB+HYrHzdc4JjviUeb8ui6an7r5Hp41\
5383  A5MJvXGu+WubBxlNhGftDEsz2sHe0mB8mdNLD8+TdfOQX1YJ/U2SaTMi6y2Bo9LzhpUKxjYLmbIZksSdk3Y+/DScmaC+xFHwJn4hTqeRTd4Rt2ve1b7zeBe4nTQfXcOzazao0uYJyGCp9ziKHADP1XJMIIvIeyuM\
5384  SjNRzGpk+rpqZGZhvQWsA8SazoKOhnUl8hJfvX2p+WxohpnlULhrOnub3Ds9prc40v+ZkcOTOJi0Yp8MDgA4FHJQMkG4ajWNdavdlRl0Sh1Rl1yPPJe+ow/xCUr49GZUnT41T1NgZZbAmcKJjGpVckj0VkJsM645\
5385  DufVEZdKaqFPlusx1KWqv6fjv2SVluQd/GMRvLK2hf5+eCifjmEvnuMW7Woi9aI10gnrnW/Owcybt80b17BWeVJplHKmWMt61uuYG6uFXqR7StvmPJ3F3hnpQG1tZBt+QMtNc8rBMRf8rJ0U0jOe0VDpCm1X6bP+\
5386  +asN8Jx8pEY2E6ni5xyOa8WDF5FzsWFvyAXhc0/MR8rQPAo1Fddr9MHAmqDDSfKJFoA3xoMvW8EkrXBa8S+0XN35ul2m+/yiO2tNRJeKUFT5AG+UcFiQXQ08YJEtD9ftJ3JNZolfMhIPjg4/ksECKzpcRE/WLJfk\
5387  5CXNnbNsf9blyyfRE+DQZn4JRC9pHbA4kIR1R/ALFNdNHb1Dh2FgY3YntoR4cMA7e5ia/eZ/e87nXPd37wzsak4c9Gb+ZnbyHSlolVGQLXKmeuCPvyS0aj4kAMNXxj+hE8EGovFd0RDHriOkZoh3z0FIKK4pCQlG\
5388  l31hhcAkWn7bJY1YBuYj26G8i+1mlQBbg3tlNwpOJ53wwZcjnPcWKDjWtgvw1Ko/dQZmYEQobgaugrYgaU2IaTt/SDN6B6TXzv/k2uHPra1+0HhPyC+G6jEZqauRveNj/EPug5+9PO5wjn6IVdGH39soeBSJBoEX\
5389  KaOM+V0MsXYvUMPW3Sc4YzkyQ6+46L9fqYHLGJ1jeASa8PRBynP+wWUnHFFT5amr4f4AghLTRZtDu4zCrxMBt0X7Cca5Ssl0GiM2rifYihBnC14LhYfGHV00SttBZCcc8HuHmEYk4b1SPX5u091oP6B4EStVMarC\
5390  bONl9x/u8qZCyqRHSsVywpV+oMN1KsJZieQ+ZYkYNGaOL1XeIrbGOiyEQzfjEGx5RjBiM3zYjg4elQAgBLgbCE6wha8PQRqz1prI8p3djlwN4XbfqYypDkhPQJrJ9pWa2jEU1jqDlCgNYh0px/T0/IpJbt6UBR8P\
5391  q3z7PKxIpppWqybxUshPpYhpFyirL1P4SPKPNG4Ak+W7KWYtauW96sVdzPqW2e0I9fRQb8WK/II+vWz91GH7iXApLAR65hnzgC50JDh/aGgUWlvvUMXToOaAPytJ9+7yZa3lVsy/UUDffsGLjUTeim2qLJGuC318\
5392  wOH8Plv6nAMfaviD1H/ZeTkbWSD7QqyJeoRU1mzKOKCfAPv0STeZGITZsWBjeFvJJuo6/uCAheRnjnFEA+/etZ9IMVrHN9viJAkElIITAJ0tyuiKCs8O0DJEAYsQYACnjkCgAiPY4pyOD8kwWPdMtk32wP4nE85O\
5393  CsFo8JkNorBb7IfwJShewQAwB1R8wN4DlL54AEGAAyMq1t/Eax7yG86MOsCsuAOYpSvio5U0Lqdn2vHwYJqZSqMQpTNrAV/zs0qlG6afvSLeR3RWkMr/6VQTEwCkQxCLE8TSnNmEziLgHk/gy9b+7t5WVLZKSh5i\
5394  2I2ClEje2eJkzijdgXiLM0CGb34+OT8/4sCNLG8LcnjXwEif0Yxk8e7er+yO0206pG6WfQ2rNZ8C2n7yChSk+VVk/+IjTRjLqCNIhknFyZq1DN3DyW/Aa7NxMWeplWTVBfsp9Fecu6Dpg6WEDIoFRTq5bAbkkxeN\
5395  BBgqOUTUj37Z5qS1Pjk7UkhduTjQ+uBA3OI1HKNx1G8+fHRekAqXjHvgs+TQGDvhdI35HQh5S4JoxILuDggF/wRKKhY6TLsSUp8BxlKe+C6ZoPotRjBiW9gLj2YcTxZ7L3fhAB/N9uDP7gIWCknKS3dd2SWpWO0f\
5396  M65W9SOscDSaVZIOXrefSBsrsu26VsUhg2Y9AarmknQMIONPaJNsf6DkhWTMhhUmjQcn7oqyTFJbn6siSRptVz73KmgIfoC3XtJeCMQqY4gay059co/QUT8K4DHXYymNFSVVTtalkFwgX+FIymeprv3hE7Nn2zzM\
5397  z/bnydei1unemTtuIcpDdEuoX3Lyy/FMKFZgz/QXmPRv4PyQvCHkxQgoAj8+3OCfbDqlIwHfY5K7TpWVuXXTz3Ux6wIN9El4xFCVUx8CGi2axDgCCq+8UuhlIS1yN1+whjrs9zcZsjCEDT/MDtGKT7cIxXmFugs1\
5398  GyzaS+hxI3SomhQGsELmbJOo4fQIcQ24w7MMYwf5Xh/eO/3lUn+51l/W3QOHzMZJdWDqY15lfP1aVT+WhTB7AhY3ecFoCbEBqvYHCUVPuQyD0Pixymy5BFMm4/iRbPVDtDh0k/aW7CF2PB5i7PjISYGBzLu+XHcU\
5399  KVeINUdvshaVAgfvM5XKaOQu+NiA0/yCNrnwRmWbKYbKz/TN6eqjofnCDPAAWZpvs7jGQK5k/4pCRsMMCHDC5JiKcjgBX0GsgPlk/wqtpIz9azEA6Lez219yOqjAZWUKmlf3iTLD/pbw+uUfDELynBwK2HEVeg4M\
5400  EePqPQOmJLp7J6mmZK8oz4EbPW1meQqAgPvkUNDVZ++JpqKoovMHwA7lBqiU19nTaLfBaoURLm4pPrdFaVNsVuIlPpGZupwATkhAaxNL4shFgTckMihXeFbBuoZAD5KGzdHkmQg3wKoVxTGJRW4Z/YZPtN+oIqZG\
5401  e3KxEWPgpV+OdyqCf/z346NnAOkoHfwOSMkhFcTAHd5yqEvzwxLSQnjVomH7Foif8jiUZQIDTOxPvpRnsQL61h4yJOgkMr2sZ/BsgevuqBo3VWN7rSnLBRfbCVaHwwXj5iMvsfEibVH8Yhaq/1gnXeJFelE2OKfQ\
5402  faDa/oRO0lI5cLdNsKEM2Kbe7x/xM8xjDX65pGwSyouG34KxFPTsgNMh+uRk+uL8nD9iV6RQdUle9rrNBIhV/vKO9AwfA2CoqdwEy0GAbrfn5wcvOdxX4R8UBHgD0IsQG4IXM5gcvo2mr7PkTu84ZIwAQ67iNc4C\
5403  Q0x/nOXgwmxF/LbAGEtcBjFkHt2dt1xdLcH9lFP5YATj799rURwuD5V1OFssq6eQ6lYtNjJUTcimK07bwCpLrlpwvbHNZjgOwMl2MbrysIX6bCSD55p0LB24CCbDGJg0X8EWL1TybR4PEwPcizVXiMUl/R2FYnyf\
5404  DxfqTB6ECvDExaDpPj+ZRbS8F5GKsSp6WRfTRuxgpib6LiluQZJVqOeqwhJUxsP0FW6MPiNq832XSHgFQK0ap3YdBelCbKQGP4nkSdfV1yMbUy78gvNXM9icCuwjm1tOHgfJnqEYMjAilw6OICWgCBEFVC3MARia\
5405  RDoh0hqBJMtzcT9hpInbjFTawEotH3bJpRfcVT0v1OeqTd3bmrOh8DhgwOJSz/8LPO9/B+Bx+WDF1Yakj93/0amipLIMgdXsIzk4Lz2nsh05O+61o8q+ZX2OQMyZcdg5fP7hViCC7VxIgfqZ4UqOxcQ56dwB4azc\
5406  FuMb2cFB66K5Hcka9K0Xll3KfjzQfoq6Rcyg+ekNPJ0d3ZuxBeAJ794AF7OjbYAFzwlMmZBvYCYoVHi6oUWMgD1TywLzK/5AtzbAmC9nk/pbkMTuVldb91hVVDpjwmyGO1ZRSNgrLcT731NeBBwx7UrAnjaevhH5\
5407  AzjkaiMIuJhAWUjuEMBkY3RJncKJXXS9UQUpX/twSdAR18+kCHilpYcFjx0CjliExNJQsklf179KysNNYLyusdmqZaRn1QfkelEjFOLhVhpI2YNOrfspz8QMOKlf8ddK/GR2XxdA8bBa0ijsoittqIMDqjfxOfal\
5408  E74f0Sy6JQRMesX2yGcd9ym6IiiW3H0ED0fn/K0mxGZ7RMVfOUgOyLRVJJO9UNpdoWZ6inbWf2KFwUT96q/sanmlaa5FgBrYcsmk7O3sW9IWMiLpjpjq2ab77p40Zhd60FzRAmmjkRcLPTvJrlVbM5/Fbg0oDVQh\
5409  1I2vZ71hoPCwEAZo1Bpsg0HunN+waYaNOCC/XMt1pHSyjZ+3r+NvhYpsa6oQWjZgVX/AkFPuC5Wr73sCTcwppDdLtlmse0vTOaQUbCSRM2NN5/L84vRWqja57M+TUswHtz9GqOlrXe0/PZ63o3iPsfYVwEYzX70S\
5410  2u9zEqmcKgg9oNCvt3axlJlP4uWlYnHK930q8jQO4+3pLYe2jCJxp3MOo0uoYoejDWWAhgoXF2MNjXwY37C8NmdZQyW2ThpvVWJVQHU+MdMaKWfglR7OwG0W+7NtGiXhJOOkAspKdGvr9DXVw7QJtTeekCcYttyQ\
5411  8zKVsBVUr8DrdpMhV9rnsH+/1UlFpcJaKG+ay4MNz1D3GLDbUQ0Zd4rxUt0/QDFW5zsQ+pf7YI+xB4mI2Q1PLbvz1LAyUfZvaeC5jJ1U0k+uRgivfFSpQPbCTqNMVpsRjxFabU6JXTfXox4ykshImKj7WI2AYlH2\
5412  W8eS+ZUsZEu5cLglCJjbihhr6xhK483gsbUwCNp4wgXnZNSlXFxJ1ikMpaoyLNNZSQr7M/iWrW40xyNRV8M8ufB5I/q8/hJlIfskHoKbr47zl7r6/8gK5L9355GqyS6sVEk34ANXjUp9deVXVIwHe4gQ2AI9YCo7\
5413  X11GGblMTSSvuNbEZBvScTqr90jJaygMbZE3R83ktqvjq576toWDLUFTpN5X1e3dx1u+O8RIHWKMgVhgca0Ew/QOBHHBTksF4fDG50aQ22aiXmXx0kCDLK/1q5xdLTZoLrl7Y8doN1EmLQ8hOxPDgoykbIXE1z9Q\
5414  9z6fY7rxmu5ZOr6wqezIbugaDencCymBYhKyewZi/ruqWgXuUDEtcpQxKMP+3ASv7UeIBwAvQVMCtO8LvKUDx4XpQdn3Bp+5Z3ZxuU/HEbLve8kVbnn/WDb7VvL/rW5qaEdcZEKwJlblvMRC5Vc2XPFIV1+r8t3i\
5415  iCC9mzckfqKOQshiX8zj1SIJyQ4/YOHFs1KY2yE9DiN02obq3T0qZMB5hPaWFF+iwZo4jgNbxuPSTfoyPcDs6wGCOcyiAfn4Sb9oE1ivQUfA7xupiRd8OdRhVK2PlW9N1SX6KKnVvc6RaAeDcp6escezP19vyQW2\
5416  6St4eMMcBJVfsN8N5meaJkpW2hahtwYuimcZPpVsLKAyBfpBO59IV7XjPSz4C/MTbCHPHcIW6JTm3/wvOQ8s6iy0Tjj//cO2oRu9xv7H619/nN1g2QBoqSf/hQn71OgooCoICAQv5Fm6/kOncIO3prO1QifIE7fo\
5417  jf/wDdC4yw2RbKb9oJVSBa6Vf3Bc7nPKLLHZQneihO4kp7v5219zR4bv6X8lqeJjRr2AF3ylkkRxRJJemfybDVC5oHwOThBzF0kYMQEi0KkSF7gSgScrKVknuVn9wVnJxTClWn0TwaEv1eFRZmPyF51sw+T/ZtzM\
5418  kiGjv8KITuG8mjKvdIuZsgfwkaixZm+8rgV0ISpTn2O7XW5Xti03xNBL9uWWPEGn209J/iWZjk05/8egO0aC54tGlVWpxchdC5dHtkvkF5tsHO0jsiFI8XtMwNw8Toy1ipmukEiJAu6rFBM2y4LMErMYqL3Y6jgm\
5419  L1g2q75cb2F+bJ+fD4zgg5QEUXoYOGd4NYird5YTDSg72FR6LnJLKO3flmQ/XS0fyZ3UKXtu+5SvEDmSGtUfEg5yYMNVIZcTqu7/7HgaajAHFMnMiav27tAA/P7A5UG61OJeISP7c/DMeZONYQVqj6twSToW1C7l\
5420  zGayHzQ2THI1VmvqAZUrLpeL/3GrTrVkyv9HUEoDuODbX+kN15zxwmLijux9WUE6jYhZXBsMt+V/dijoyUWzkrvgRqJbQuqd8MFiLldKVn1AHXXJlEt9pSUMMy2HCUwBtHm+bm1lEy/GJi1PGOBgJMgnJDGceXsU\
5421  L416T9PI/4IxQ6sM4w6jKNKu2FlX+4TwOzlXvKdLZZd2PiIlL3eKWgjhl3lHcClm3HCjLeFJ6OVBrWvlXmC7MtvlnjQeIiSF2Y2OI/neLyv9TxXPeEHErYdKrSTqhPAEbPp/OuFz7xd49pUM3906Jchgq23ua3Oz\
5422  2HPiaCUdKocellLRvoovtRuXBARsD10lZIzGqCKocpF4j6O3XpWodDr7S3/tu64yO3HrbUbBPRL6By+KjiGk3fJKkqvEFEHd/BN1HeJFovs8fHl/xm3dTFU69N3uBG5O0n6weZjJgjXHWNpOomHAEOriTTQc/lBd\
5423  HlzccZG8lH9kFN6yzhKzSFNXWDsHE/wn4X9+XPsr+Fdhk+T5PHdJnjZvqov11Y08tGZp4GHp157/p1j1qnf4jV4oSXOXZfbz/wH6hAMl\
5424  """)))
5425  ESP32S3ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b"""
5426  eNqNW3t31MaS/yrjMTa2gb1qzYzUTbLBDsngJHv3AiGGsD5nabWkkBzWx5jJjs2F+9lX9eouPczmD8OM1I/qev6qquefdzfN9ebuw1l19/zaLM+vbXZ+nS3edP9k6osP986vQ/Wo+5rGFCc8ypxft1X313bfww/d\
5427  wGxGb5zr/rfdg4ImwrM4w9KMONhbGowvS3rp4Xm26R7yyyaj/7NspxtRDJbAUfl5d5QGxnYLmbobkqWdszgf/rqTmaC+pFHwJn2hk+6kY/KOuF33rvW9xwdw2ln30XVndt0GTd49AR6s9B4n6QRw5mY1xZBlOntk\
5428  RqMPUc1bPPR10/HMwnpLWAeINb0FHQ3rc+QFvnrzQp+zoxlm1mPmbkj2NrtzdkpvcaT/KyPHkrg/i2yfjQQAJxRykDNBThU1jXUr7soHdEodUZfcgDyXv6UP6Qly+OxmUp0+dU9zOMo8A5mCRCa1KjsmehshthvX\
5429  icN5JeJacS0MyXKDA/WpGu7p+H+ySkv8Dv6RMF5Z21J/Pz6WT6ewF89xy7iacL2KRjpjvfOdHMyie9u9cd3RGk8qjVwu1NGKgfU6Po3VTK/yQ6VtC57ObO+NdKC2Nh0b/kDLTSfl4PgU/CxOCvkrntFR6SptV/kP\
5430  Q/mrDVBOPlEjmwlX8XMJ4lrz4GU6udiwN+SC8LmnwyfK0DwqNRXX6/TBwJqgw1n2iRaAN8aDL1vDJK1wWvEvNF/d+SYu039+0Z+1IaJrRSiqfIA3ijnMyKEGNlbc0DGxJX6p0puCmIRzwk9ktnAgHTSSP7vf/VOS\
5431  rzS3zrLDWZcvHid/gEO7+TWQvqJ1wO6AH9adwD+gvm7H0Tt0GwY2Zqdia4gK93lnD1OL3/3vz1ja7XD33sC+/qRBrxev50+/IzVtCgq1VclUj7zyl5jWLMYEYBAr+C/04tiINb7PGjqx6zGpG+LdM2ASsmuHmASj\
5432  6yGzQmASLb/tk0ZHhsOnY4f6tmPD+wPlScHv5DOWej1x7P7sc1Yemc4Tm+HEOdiBEX64OfgK2oAYNaPz2sUDmjGQjV67/Itrh7+2tvpj660kSjTg2O0+G5hl74df3FIeg6/ix73Yyerowx8xHp4k6m0AZ8x4Y3Hb\
5433  yVjDl6hlm/4TnLGamKFXXA7fr9XAVYrTKVACTagEwO4F/+GyM46tufLZzXh/gEOZ6ePOsW0mKbSZwNyKPx3sK27upKiNKwm+ItQZAWylMNG0m0smaQeoDKN6MZZgngCF90oB+bnND/hhIPVLkKlJwRVmGy8E/Hib\
5434  OxVqTgekNMwkXOlHkqxTgc5KQPc5M8WAI5Ew05QRuHU2YiEqujlHYsszghHLYUk7kjpqACAJ8DcQo2AL3x4DN+bRpiiIOruXTjVG3UPHMqU3wD3BaqY4Ujpqp8BYdAk5URrENHIO7fn5FZPcvakrFg/re3we1sRT\
5435  TatVk3gpPE+jiIkL1M2XKXwoaUieNoDJ8t1U8wheea92edthfTzsXkJ8eqi3Ykx+SZ+OyZiiu6ozti7PiAdUoMe4xQNDu6CdDWQp3gUVBnxYTSp3m/+KNtvwsY2C+fYLnmsi4jZsSnWNdF1oqYHaLfbZxhcc8FCx\
5436  7+X+yw7L2XQEMitEmqg+SGXLFowDhumvzx/3U4lhdLFTkcbwtpJLtG36wwHLmJ0FcvyUQWyUa4geb77LGRLwJ4cxoKlVnRxQ5dnzWUYmYAeCB0DoYPPga2y+ywkdy8gwUvdMtc0OwepnM05NKoFmK0Kg8Lmyu+x9\
5437  8CWEzopxXwmQ+D77DFD16h74fY6FqFf/Ib7ymN9wWtTDY9UteCxf0zkio3E5PdNOxwXTzVQKhRCdjxbwNT9rVK5hhqkrgn0EZRVp/F/OM0nIGEfYszpBK53MZiSLgHs8hi+7RweHu0nXOoh3oay6U48aiXu1fLpg\
5438  aO6AudUrGPv656fn5yccqfHAewIV3nbY0Rc0I1u+vfMbu+B8j0TUT7CvYbXuU0DDz16CenT/VMX/sEAzBi9KANk4k3i6YR1D3/D0dzhpt3G1YJ7VZNIVOyl0VpywoN2DmYQC6gRVPrvsBpSz5x0HGBs5hNEPf9nj\
5439  fLV9+upEwXPl30DngwNmi8twDMFRu1n06LkA8NUMdOCzpM8YL0G2xvwBhLwhRnRsQV8HhIJzAhUV+xznWhkpzwhUKTd8G09Q+ZYToDDW9MLDOceQ5eGLAxDgw/khJgFLWChkOS/d92OXVFBr/SNG1Kp0dBxh8wV/\
5440  7WKK1seGbLttVWXIoFnPgK6FpBsjlPh3XIbtrwnkyCz7a1SZPIlO3BUll6S4vlQVkjzZrnwelM8Q8sDpBhl7JcCqThFqKin12R3CRMMggIJup9IZK2qqnKzLAQbiucKJ1M5yXfjDJ+bQIt+h4OnnR4vsa1Hs/PCV\
5441  O43A5AG6JdQwkf1qOgtK5ddX+gtkeVSzPZ6xYN/GJ5v4qdjZIYmA8zHZbUJlbY5e+pkuZF2ghT4ODxmfcrJDMCNCSAwjoPHKLYVB3hHhuvmCObThaLjJ+Ahj0PDj/BjN+GyXoJtXULtSs8GkvUQeN0GHqkchEyuZ\
5442  s0dxB4RHeGt0OhRlmJLjOy27t/rLpf5yrb9serV3lYTs+JRMGd/+qgoeq0pO+hSsbfacgRLiAlTr9xKInnDlBcHwI5XIctWlzqahI9np+2Rt6CTtR7KF1Op4gJHjA6cBBhLt9nLT06JSgdUSPclG9Ancuy9U8qKx\
5443  ukBjAy7zC6rkwmuVYuYYKD/TN6fLjobmy2HgDJCX+Zi3ddZxJfs3FDC6wwADZ0yOaShrE+AVxAT4nOxboYdUsG+tRtj84/zjLyUJKnA9mULm1T5RZtjXElS//JMBSFmSMwEjbsLAeSFaXL9jsJQlV+8kuZR8Ffk5\
5444  cqFn3SxP4Q8wnwgF3XzxjmiqqiY5fsDqUF2AEnlbPElGG6xWGDnFR4rOsRptqu1aXMQnslFXErwJGWhtZokdpSjwlljmGhKZQ6uGMA+chs3R3pkIN8KpDcUwiUNulZyGz7TTaBKeRntyqQNj4KVfTbcogn/0j9OT\
5445  HwDQUQL4HZBSQqEKw3Z4w2EuL49rKGrBq4iE7RsgfofHIS8zGGBSY/KFPEtFzzf2mN1GL4cZJDyjZ0tc925sTUkBdtCTslxisb1AdTxeMG0+8RI7LtIPxS9mqfxdm/WJF+4l3uCcSjeAkCAqfl6r0mDFFcN6P8ZH\
5446  IaoRiPQ0QqUsFZ8gBqB1HN8hrqBkDmTuMg7cMKeWx5hJSX7YqlTRxg28VRvYJQ95F4c6ySoPVDOhTpMKynSwEhdSH/BiDtAufJsMX6fHvZZxKBj7hVKFapwFZpj/NC/BgdmG8tAIirGkZRA9lsnZecul1BqcT70j\
5447  H4zg+6M7Eb/h8lBKB8liHT2HJLeJqMhQGaHYWXPCBjZZc7mCS4wxk+Eo4NohPlf+tVKfDYP6jCvRqWbgEowMUzDSfAVbPFdpt3k0TgpwL9ZbIRaX9LdUhfF9OV6oN3kUKMAPV6Ne++LpPOHkwwRSjFWxy7qUMmLj\
5448  MjfJc0kxCxKsSj1XpZWgsh2mr3JT9BlRm+/7RMIrwGjNNLWbxEgXUv80+FkiT5qtvp3YmPLg55y7mtHmVE2f2Nxy4jhK9AxFkJERuXwkgpwwIsQTULWwAExoMul/SEME0ivPlfyMQSZuM1FiAyu1LOyaiy64q3pe\
5449  qc9NTNtjjdlQcBwdwOJSz/4FZz76DqDj6t6aKw3ZELb/V69+kssyhFOLD+TJvXSa6jhyfjpoQtVDy/qcYJgz06Bz/Pz9RwEItncPBSpnhms4FpPmrHf1gzNyW01vZEeC1kVyO5Ew6MsuzLucciyUbNmjbplyZ356\
5450  A0/nJ3fmbAEo4YMbOMX8ZA9AwTOCUiaUW5gJChWebGkRI1DPtLLA4oo/0GUNMObL+az9FjhxsNvX1kNWFZXJmDCf445NYhI2Ryvx/neUFwFHTLsSrKeNd14L/wEacp0RGFzNoCQkVwdgsjG6hE7hxC773qiBbC8+\
5451  XBFwxPULKf9dae51D/Hykyu5/IhloWyb/9r+JgkPd33xlsZ2t5WRnlUfcOtFiyGfh1tpGBX3ekXuJzwTk9+sfclfG/GTxb4ufaKwImkUdtGVdtSBgNpteo6N6IyvRXSL7goBs0GVPZ2zTftUfRZUK241EiYCOX+r\
5452  CbHFIVHxNw6SIzJtk8hkL5T3V2iZnirO+mcqLpikX8OVXSuvNM2tMFDDWi6W1IOdfSRtKSOy/ogdPdv0392RLuxSD1ooWiBpNPJiqWdnxbXqZJbz1KYBpWnK3kWvHwbDQOFhIQzQqDXY9oLMubxh0wxbcUB+tZFb\
5453  SPlsDz/vXad/FSqy0VQhtGzBqv6EIWfcEKrX3w8YmpkzQMkrtlmseEuHOeQUbCSNM1Md5vr84uyjFGxK2Z8n5ZgN7n1IUNO3us5/drqIo3iPqb4VwEazWL8U2vc5hVROFZgekOnXuwdYxCxn6c5StTzjaz4NeRqH\
5454  8fbsI4e2giJxr00Oo2uoYIeTLeV/hsoWF1OtjHIc37CytmBe1xAass5b1VgTUJ1Oyh4meihFyr9tkfqxMYmScFJQ6ooVJbqsdfYrlcK0CcWLTngmGLbakvMyjRwrqD6B140mQ650eMLhtVYn9ZQGq6C8aSkPtjxD\
5455  XVrATkczPrhTB6/VlQNkY3N+F0L/6gjsMTUfETG7sdSKW6WGdYl6eCUD5TIlqWyYXE0Q3vikUoHshZ1Gna23Ex4jRG3O6bhuoUc9YCRREDNR97EWAaWi4veeJfMrWcjWcs9wVxAwNxQx1rYplKYLwVNrYRC0ScIV\
5456  52TUn1xeSdYpB8pVUVims5JU9mfwLbv9aI4iUXfBPLnwRcf6sv0SZaH4JB6C266O85e2+f/ICuS/DxaJqtkBrNRIH+A914xqfVvlN1SMe4eIENgCPWAqu1hfJh65Qk0kr7jRxBRb0nGS1Tuk5FcoC+2SN0fN5Iar\
5457  4xue+naFgy1BU6Ta17TxyuNHvijESB1ijIFYYHGtDMP0XQjigp1WCsLhRc+tILftTL0q0m2BDlle61clu1pszVxy38ZO0W4ST+IZQvFKDAsykjoyia97oO59Psd0o2OSAf/N9zSVHdktXZshnXsuBVBMQg5eAZv/\
5458  oWpWgXtTTIuIMgVl2J/b3639APEA4CVoSoDGfYW3ckBcmB7UQ2/wmbtlF5dHJI5QfD9IrnDL/VPZ7FvJ/3f7qaGdcJEZwZpUk/MSC5Vf2XLFI19/rYp3yxOC9G7RkfiJ+gmhSB0xj1eJJCQ7/ICFF89KYT6O6XEY\
5459  ofMYqg8OqZAB8gjxVhRfmsGKOI4DW0Zx6fZ8nd/H7OsegjnMogH5+NmwaBNYr0FHwO8bqYhXfBvUYVRtT5VvzdXd+cSp9Z2eSLSDQT7vvGKPZ3++3pU7azsv4eENnyCo/IL9bjA/0zRRstpGhB4NXBTPMnyq2VhA\
5460  ZSr0g3Yxk35qz3tY8Bfm77CFPHcIW6BHWn7zn+Q8sKiz1Drh/PcPYis3eY2jD9e//TS/wbIB0NLO/gUTjqjNUUFVEBAIXsCzdO+HpHCDl6WLjUIneCZuzxv//hug8YDbIcVc+0ErpQpcq3zvuNznlFliq4UuQwnd\
5461  WUlX8ve+5n4MX8//SlLFR4x6AS/4RiWJ4ogkvTLlN1ugckn5HEgQcxdJGDEBItCpEhe4DoGSlZSsl9ys/+Ss5GKcUq2/SeDQ10p4lNmY8nkv2zDl/zJuZs6Q0V9hRKdw3uzwWenaMmUP4CNRY83hdF0L6EJUpj6n\
5462  RrvcpowNN8TQK/blljxBr89PSf4lmY7NOf/HoDtFgucrRo1VqcXEPQtXpmPXeF5ssXG0T8iGIMUfKQFzizQx1SrmukIiJQq4q1LN2CwrMkvMYqD2YpvTlLxg2az5cr2Fz2OH53nPCD5ISRC5h4FzjteCuHpnOdGA\
5463  soPNpeMiN4Ty4e1I9tPN6qHcQd1hz22f8PUhR1yj+kPGQQ5suKnkWkLT/6mOp6EGc0DhzIJOFe8NjcDvj1wepAst7iUe5GgBnrnssjGsQB1yFS7Lp4LapchsLvtBY8NkV1O1pgFQueJyufgft+5VS3b4hwO1tH8r\
5464  vveV33DNGW8qZu7E7ssK0mdEzOJiMNyTn+pQ0JMrZjX3wI1EN/mFCAsWc7lasur71E+XTLnWl1nCONNymMBgN8vzDWsrm3gxNml4wgAHI4E/IUvhzNuTdFvUe5pG/heM2QeOO4yiSLtSX13tE8If5FzxXi6VXeJ8\
5465  REpebhNFCOFXZY9xOWbccJst40no5UGtW+VeYLu6OOCONAoRksLiRseR8vCXtTIL+BEmLoi49ViplUSdEB6DTf97L3we/gLPvpLhB7tnBBlss8ddbW4Ve04craRD9djDUio6VPGVduOSgIDtoauEjNEYVQRVLhJv\
5466  cQzWa9QvJWzxb8O1b7u67MStx4yCeyT0uy6KjiHk/fJKVqrEFEHd4hN1HdIdon0evtqfc1O3UJUOfZc7gzuTtB9sHuayYMsxlraTaBgwhLp0Bw2HP1AXB5e3XByv5feLcrait8Q80dRn1t37M/xt8H9/2Pgr+IWw\
5467  ycpyUbqszLs3zcXm6kYeWrMy8LD2G88/JVad6rv8Ri+U5aUrCvv5/wDWE/uZ\
5468  """)))
5469  ESP32C3ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b"""
5470  eNqVWmt7EzcW/itpEkjhaXcley4aaINNbZwLsNCHkg1rtsxoZlJomy3BLGG3+e+r91xGYwcb9kNiW9LoHJ3re47mv3uL5nKxd2er2hvPL63dnl/65Gb4N5hfmix8hr8qz8OP5BXmZ/PL2oXhKk9krMon4be7N5+P\
5471  5peFnV+6ij+b8FQ1GG2H6WIwCbsbTIZ9nQnfXVjthvPLkvYOf2HQDt/PL9s0/Gh5ZREm6xQ0+Glr5Tv+zKsjEA7fsE0RnqBRMF8+Buc/4ixyjAYH8/I0kQust/VpfsTMX3rzEAwswmhyb34R2A8MtYNRNdkJu7td\
5472  ZqAaHAaOBpP7h9vhQZOW8UROTrQ3P197mqswGog3gWFbhC9tmPGB86rNw3Ng6s/jsK5mIeLZts3zNRNKesxyUW3VdU7H5P3AfCYCG3SSg/zDdlDkEJ8/XAkv7oR0BuankY4Jn65Y0uAq0eLelZBmCqMeD0op/rb+\
5473  B0ifdw/0rm9NdkiWVQTlFEPeN6gzKKjwx0pZLLUMD/v2ZW5ZnHbISubl1h0EInWwXGcnwQTqjG2TxJBMMAftMZUqYRUZ8wh003tgdtIXnOX9C9CnsbBlHo7RZLyALCwTKw9/RcG2pOO8icriCasAJPFZ5Tc7kd0n\
5474  pYxn4k14LJ1hKY5TsnNYzzYOIq6RfdwGv7PD3lE8ZDkO/20wqiblUSeu60CswP72K3gALMmwLWBZW5zsY1HPpdOZZydU6briAUaCeGsZaesTVjykAXnbXLa0kbfKwP+8lc2GbGrgr22w6UydpmcY4RGcIxOPM2Jz\
5475  Ni7RE7c2Uoc+aqJGjBGpAYec1l/fAoz9zoSrfEq2KX6AFS6dwtzYMJ/e84dyMoqgs54l41ReRTTsOajEshqr8aMXQe5jyZjXW+GcZGcwk2FmsAOFy9Yug4HsMJFSj9/fvLBnkR0in0QikGWQdathA3tSmFQdKf8l\
5476  dlFrNCuHUZOnk6+XAf2WQGISkO7cScIXH0kssNYfOCyT+opngk2yEo3YAZS4Gl5ky7q/ZSMM12SZK7GtEDbhV4hX5Bzi3MQlQqVLNUGMgs5sCkml/MWYTZHTpzkPBEO7mPbZtSJRpnsm8h4iFHwRhyGI1A1ELsnT\
5477  p/3da/GgT0bfuPVyvIBuyhgqaDJVh63tvTjkaShu3hRsDss+Tg5XRd/2No9biGOmnUquY5PbgeMQ8SqJ91Uq8RjbFAIq1kMQ6E2hB3lqgpjf1D1OUz35xz4IISzxyeRE4ANfZ4o7CF887glT8hIiEdKIUCgbOUpB\
5478  sgvM3ZrIDDBGcZNjvB2ecvAj8fsD/1PYxujPif8ZTgCfhUXepZR8MLop7tWwma63lnYFtKjV8AlHFD/n9CCwXsmWNyeG5xNWA85Q5KMJpyXCTwKYBDwtYaYqvduwGSq6WwJrtrgrcM03937r29yEWd/kmnUjOQw+\
5479  2JDtz/goFNXyrX1SysXdPiyZadAafYIa0oJrJekSuJ2uZ4EiVQWh5JI4FU9K2uwJYqzIViMkJXo28R1O7BqherFzwewhkPl+zqOMenbFXgAxegkimIF1uPoOGx5MC5+V5bTvsLja4Qn2+jE8oeA0yMJ4wml0NW68\
5480  fwtLt1fvsOY1FJjeFNINK6NGZhX8VZdyvvxqwTGliHl2wcSdYhzxGphWW+6zZ7NXbLMo1Xk+rYstTijLm5v0TaRAhlvOxux+jLTOesiB/sBoITGBWVnDYiEsLj/eo9ZA6tufcUbrZYW3sLKU8gsCVuqjwKpydBux\
5481  rZzMz4Nvtdlp+wICfnHY2Racv/DvsEgj3esjDghQhEsff4YRxLNrUaGpl/bYeBg2fzLGkqMYB4rd8et+pAVr2U+IBvbt1bvnYeshzmL/CWgfHMnXgtfabYS1m3CvI+Sxx/CBH+GeqGCqN/Ch884PVFgZm7ddUQph\
5482  fSc5tK0/KJjZF99TT2id2Fz5F67BqoF82rsrGHMQPaarVZGxzP4XSAmQMD3zSI0fOHS5tEYNAzbVBJ0WsXmvEljyEizqKuKjTXS3oNSr/yiJ9QTojP4OhxCyYX99ceDigi2dVsmjzh7uS/WaroLOPFLm5WQbMwkt\
5483  w28Re0GpzQLyNpUCeCS54U8QWimgs7PCoJHxUHJeSYlwowBGqNcpl5XyyeReM/qqhNmqQKHUDoR1mTH53bGkmoQy+geBgTSyxMTzz2ihKpb87BGj0f9LYDvfsCZgrKHWRaR/RJBxoePtZhMkMyyvBEQMpwl/a0qR\
5484  bL3vR4KKBozemgErlioz8rZVM8r7ZiSDTP9HLlHOuUAhplNmVNFiB+hh3QroqQmFQdRCDVhM1NOTSHs5cFdZz4h7AfyLArc+/GWBu22vyPuntPCweAm92mI1GHCQaFYL1MH+57b3KzmgLZ+8eYaM+uzoFGI+vf0C\
5485  tvxifv4PTB6/eYjJh0ePMPnoVh995SfjQ/DxNqoL2AjCCLlmXyBjxgZeSvouJX0jmFMrsJGUjvmk970kCM81SMOWAjSCkgnPwqhwuqbZVMSMmDBDI3cv/7BSQ6rgNK6Tq5aet28owN8oIKjpL7ANgpipKKMrapcL\
5486  0q/O9skwtwQpaiGbu1HXLeQnYHIVrSu193Mk/JUQA60SUQmT59wd9LHqsIOuVeZfTQFAHYM6l06n0w1ygeCIsRSdR2MfaBtC65bNT7exq+uP8+jVWpE31CgwAnerzzCyLqKglmkbUSA3tlCkaENH/ZokD4teqZwR\
5487  x6afiVdUO5/lxyJ5dJdsvly5clptx61KLPbyCFQVFCLDIReImb/mD1XNG+oLao3h5JXmRrbvrqsjgAf2gyOiuqLxJI6H+oTbEefSjIACkN+ogBuyXLgdOV9gKQ7VyDwSX3+9IQd8RE24VCuGp/RzqD9v4WfR9Fp0\
5488  RCSJ/SGf70rdKd0VNAqo6rH0bBqfLTywp/9hH3TtPlQxPWBh+/Q73S7pE5K9CwsQZ/KXvHXnzOimwNyQCxqBFf0mw9tkucNw2vtNsL0ZI5CnCaB8rh1LZdcKySpd4zgN82zT6WaDrpt4BzGfz1Z6BQP1H7FDQyVy\
5489  LkVoDTNVVbLqSi6Bl/WdauvU59/4cwASHA/RmiaoBM3gljTh5hc0LO0/6/xvMpPMFzSDKEvNZXsY0x61DrqqH3Tb7xP9CQjQ3kCtBWVSFs7iY8vcfk9mhoSWT/yvCJyZWgx4avJn4Jx+Urvt+fxCkngh3XgEEdoz\
5490  /ZTBWAlsnJrEOFOBSLWUp0ibyN+1ZTkXlk5Ozb5dmO4DIZlygEXU9gATJp16DsQtK4xNu3YHO7sa3He2OacYI7mwTFWuYXckHC8SJ0REc8iA+dfwC6XcCuWs31QZeA6IRLxRRw7f6wnFJzqsnU6h3BQAvB5EX5Mr\
5491  JW8HqkIo2AD9ldLiK4sep0hNq3IA4iJdoRenXNT+IRoKQA+4cSjMAeIbQKWbbMAnzqApVyCEc9eDenTfssfVrBFt4kJkaLeJoYbTRYXBqCWNq1QrlmrB0zBG3wlWGiFk0OlEesiUvumSA6KyZ+QUcAJv0KDCaDiM\
5492  dK2cQLPCJggcnkAzdYsTqfQMOwWO5Tsv7FjppFrxYOM765ro1USOQB+FWNnvgM8VsWZbdInRHRB2QbeQWYwqcoN00ZlXD/FmZLsXy8qFAEtSLl1+6DVDOpXeaVn3OZCdyUzv6+Co2zOXPZG6hjPOmKJQqktK8pxx\
5493  7F2XyaGEdNBq+mAtiVcyzs7iEUiFJLxanmg0lTzrR4aJ5vDDfirhuVqMc3BtZiF9+5XkRuWEY1E3SeTQCocAGdRdRi+QwEv7h8AKasooSIGNAAT0MwapsIyYpyYsimeblo9L0MCJ13KArjt6v8dWJ9ZSE28JAv0+\
5494  THodRs1tQ1atEzGTxVfDMw6XqoSCUBkKkZYy/OAWossY7buivuISpa2vtGiqR1K1ODkyRZhcuofrOCzNGw7ObX3OBVYjTFbDDzc4nlNtl49fR0rIeY4a3Vcf4+hHROWN9LZ+kWBaHDGx7gJhE4+NaeP1H2i1hixg\
5495  dyydjJy8SG6XDDtSk6ykU7bnp2F5Jb4PG0YI1kAQrzNbTQP+wIslSIbXuISnHOuryuWKrO2Q2gMeoC5JKnWqlV5xLXfa6qJsCr7tAVO5QsDSZrgjTlbFO0hx3O/12oM691PWIwyzkLzZK88l2BDnHWr/tLC9mg7c\
5496  /oOEJcNskSUOX856iT+JdkyctPVL0F3wLnV9MNnkH0/oauTvsN1kdAV/l2tqvdApjIQ6LwUwngc0c60CVCfBJJ0vxqxrvT8jQWimdcMbR3LhR1dQ4vF0j9IuGcsrjit04WjlvRGK6FQbNoqyCSd8UAArL0yY4de6\
5497  VuBrCKDiIy0gbaXWovdhTRGDbyNlA/VSM8CamgHOrnQ7paaqqhWbs+axgmjITCXyQNo1rXhINArJwDXF793IspU7rq75Qm4T6J8L0KJPIIjuLieVdlsUIPqWfszSW3JCpyE7lTZhf7LQjEOThxG1wka8oB+TTBWc\
5498  SqWEDtD6SmnJRu31yomKi1r6yPQ6zeGqJzJLF/GNET/0E1xcWH/C77BoQfsHyYpV1RRRpYW2a3sNLD7tKXPEjwvEqMQeuo54zPGFvrklp0yu43Ek3OQl45/1fncc38ziQ8IrEv+OXqXaFsF3lzKV6QceQ8HbylYs\
5499  qgUhiefxwlNFyGnCPyFTPY9pFS7nzXv2Mto71b1dFpslqrZ1JzFmRt2B5YtQUUFb9Hk5lDBWyssXkPvyCpUFN+j3xlT+39rEwGiKzl2Ao02vzkzf9oSrCjJrw+BWvvTeHJLo8eobSVJEkWBOpP9O2TB/z2Zcydtn\
5500  atbSGd9jTggPYIhuSRVLrxWpi4dxZn/9So+9Efhg158qRFTZ9OkkdGlGNf/iXIzUz2/t/FtvWNV2Mm5Nb+gHyttV7b7cdumTVBLglb6w4Tn3Ovn6IyR9vahJIgWTHsidf8sI7OLgPjqfd1iD/XNU+fZscCBvGOn9\
5501  wZ9SmQpM1laT1b+hhF5TrZPmK/wfnNDh9wRYwG68pgzkF8J4tZwXQaUanCA1EKjg2k474dKGML1XIHzMVBTx5AUCekuIokvJWanJ5CZCjw38VSfSsSnUIwUFSzZZcPOYsLqmIR+JSxrp++VLuXfwkxjc6LFSKf+N\
5502  HPKab61xI4htPcy9/ZTJEdamyOYk6zgl91dpmnze9LduiAtJjVZRIfykRas/e/YArf7s9CZa/dkLWASa/dlxi3I9e/gAISN7NF903f69b7boTd+f3y3KC7zva02eJ9a6xISZ5nxx8bEbHCbOhcG6XJT0YjDEOiLX\
5503  2JPh/i7GZklhkqv/AdnBtYo=\
5504  """)))
5505  ESP32C6BETAROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b"""
5506  eNrNWlt7G7cR/SuyJEuJm68FyL3BiSXSpkTJtzqpY9Uu3XoX2FWdC79YpmK5Df97cebCXVIi5cc+UCKxu8Bg5syZC/a/+7P6arZ/f6van1wZO7my8VPl8Ts+5t3jyZUv4rf+5Kp0k6uCRvfiYPk8/sl+iH+SOJTF\
5507  //V2/OPl6YSenlw14XVOcxzGP+ZpnL8/i6PJ4eRiclWb+LU3qEY7cfZilwWoeqeTq9AbPTzdjg+atIyr9uIn3lsUg/inP9mfTDE9JruMM6TxR8N3uXweR+PidRTYuviliVd8lLxq8sk+CfXHk3hfiPdX/GzT5Pma\
5508  C7r0kPVC24yfEHLaJs8H4TNRWG+hufiJenPx4/v4/2gushRn2OMAwh+165j4v3BDVsHNi7rDuSzNKww6MuhK7W/rH0H7PHtc7/rUMFeSj6CeaBzX53mjOaOBnH+iK8PY8aEyPuybt7llddo+G5lvt8VJXCSM49x2\
5509  FCEQ4pO1qiEZ4Rqsx6tUCZvImGdYNz2EsKOu4izP77A+jcUp87iNOuMbCGEZYwK/nWMs6ThPorp4wSbAkvhf5XsLlT0kowyj3M7yFCYd41ZspwScIBtjHIsUtcxTkE63485cb7SkWlGNbsVDl8P410ZQ1SmPFpbV\
5510  U2Axh/ntHXgAkGQYC7itcWcHuKmdOkrn2QlVu4U7xkhUb5CRJpyx4aEN6NvmMqVtZasM/M9bmazPUIN8TY1Jx+o0HWDER7CPTDzOCOZse4vuuLHt6rBHoNVIMFoq7iZgKX99Cgj2Ky9c5UeETfED3FGkR4AbA/P7\
5511  Q38qO8tw97iDZOzKq4r6HQcVLgu4Gz86DPIQtwz5fiuSk+4MrmS40tuBwWXqIgNAdniRUrffndzZ81YcWj5pF4Euo64bpQ3MSTSpNlL5S8yiaDQrm1HI087X64B+C5GYBEsv3Enoi7ckCAz6A5vlpe7wlYhJNqIR\
5512  HMCIq/QiU4bulLUIHASZHGsoZNzIQRRj8HWs4YXCyHPeQ5d+ADiwhYCyBIDjpcoRZqPGvh7JFYQSt8eubPuvGeMUR/yJ/zFOY/TnyP8Le4VpEEceEPOeDPZEizW7pRPtgg5As+TThomk9ivhCRv2qW5yQJ4yoWcn\
5513  A9ZXnG5CMk/AmH3ehssHIyYgipQSGiVMdqPjRVqz32gU7wTly28lJvv68BcNnXh8xIKv2whtphKisikzRNUb8y4IuvnWAZuktt92o89YsTm4YT14f9EIt8Lre0frhNiKX8BEMbQxOWrOINTYUcFQsxf1AiJzJosd\
5514  Jm9FYcc/ZiwbwOq7vEaseT5nDkBeg+Dh5QqgUYT7jDrgCv8ry9Re4OZqhy8wLw4RSBxTHWviBVPljZG5DfrLsQSQMhJGejqRkvk6G2Is9Nu7Pd3drmuFVfi58w6N9VbjBZF30caJjUvadknmf2i3ytvUQxIL3uo9\
5515  cVrx6yqVsA/pXCJhf13ENcikjNxFASE5Qlgjqrn8AOKw84+49B4rp3tizLiFUPNGg2QtoRSJ8jmAUXJmIMQ0Y2UUmhmIsBC8KQ9YUCaZbQanctHNinq3tzqzSX9qpycGKMdDpjJOTs47wZY+kNKJmlmONfI5kW/5\
5516  8c5qsGe6vZnYSrMjd3h7hC9wMnwzqW+1VZWDe7BTOZpMI0k12evmDbT75nThqiBS5z/iJrXa+8dMrrBCkT6/TRBF2ArD1mFpmu3biRpBGJHGOSXd3eF7Ji/GL6TLfmRuVURrpnOj8xC27IcP84+vQARvwEb/BKQq\
5517  oRiKjdugwT345mPsBYGtQTkVUCT0f2KuqAWipZhVVbwkR+/PXLNUUG2VMQGBIHzvlq0XUM+5x+gnnrRIg0Kq0DpuM+S3b7eUzef/WT8nIdLfZ1IlGPrrN8eFL/gRol7NhuzpgdRs6Wqqla94EiUSVtLBgECEdZok\
5518  Zpum8h/Yy4hJIHKZ5hrngdV0CP9K8+cMrvJLwKVRD7M4lMvO+d/fM14q5VWH2qDpidyLK38ast0LkrH/SUpjGlmS4dXtAPfLfvKMc7AvVRjCmGc7NICLPUXki5OUuMfpleZ2OareXHIqKCVN+Edd8pO1O/B/bUGN\
5519  UFP3HkgRvl5WGiQk/MDZ+JRzcZI1Ba9qMbLIXFHAaeZq7ZgHoVHkGU3yf0CzhJ7ix7Tl2stvPzxY9foNGRMVwcuU/OLeS8Dw5WT6GuKf/gTWKR8/foKLT+49xcWnk+kz8PXbZ52mS5WfDU+RvXxobYDMD9uN3H8g\
5520  bpIJUUksLSWWgllDIbE2k1ibdL6XlB7Qs7in7nGyhdoEzwIe2B6I0Gebna30mvwVh+CzpUpIsyjNaDBWlbISZrHJXZjEHv2bU8SCtZ0yehbV2XJldef8gGC3JdmwVmRpMdA2Gr51hQDpGnusBa1adNEls+kRTSeB\
5521  cU3qEH90unSTyfg6OiqaUnVAW8wpgZyw9GvR05Qb6XwqWuYeCgol2okYW3Q8XbTopK8A6eB1pDfA0tJgyl+4K3K0mUBK6pSc509kW2hy2Hw5SUZ+XKTNsFFDtC0lSlSiqDMEZfwtws/5UzXrOjWXEgdcpZGKAbro\
5522  LOSy74L3h/BN40k7HssnLomnUhDDPOAl/MfNPteW2GSGW7GjWq6DZ7v3G/KgZ9QIqqSiyb6nn05/fo2fru60iWiRpE3ufb4rTiMVfnBSkll6Nm2fdR7JnH90wJ5rUUyU9ugEyn6gdK5zF925nW22MftbnnrhBWAn\
5523  lH6NSviFxcyAGxLLlcwmEu2mSpRAbyha0CtwYKLUcr+yyrUfp4qwFAq4OvFODcWGuWTALVszpeYcErr8G4ChyRihlrt24LQ88xVfiEi5oGHxElv4X+RKMpnRFYiZf0V5Txt3qGvRaIxP+EcVDnTE8UiZHrx2EgGN\
5524  PHkNfg9aWI38ryC1TCEBvq/zlxB+LFLW+avJhQRQJz2eRkGQ3oQIK5zHwUPQl0pGEiRXSe9+J50GImTaOTpHmMSlx7JeyjU7yMojipv0yDOZNExzDNxQnOzsKp2ieqnp2yWvXaaq15DvIRg0onFE10DXUBhC56XV\
5525  lRtZOes2c3pe0JBzSWF66i4jpZ51mIaG6I46JHegAABNPYzOM+zBXOwMwxs7kqgOs3YawLvIbFXKnkgJs5EBQboqXfAI+MVTyHgC452A1UZQ9GhzLl94lOoOxM3tGKncrQTKYFuNwuE81+EC4wgLurzoDno2iOq8\
5526  Cq21udfsF2qXKUkN6ajbPqUmO7Rlz/3PGEOBb9A2w2jckvTSCuEak37Hae1MG6CJVFCG3Rk786zYqiPKQrciX+0X2Bu12Ju2qrxMUE4U2gnfog76YneADB2BKeD6i+OLC6Hz0OlXGSaPi2X7Qnsl2ddUbRISnUHs\
5527  UqZdCWRmJGDouvLgYDFnInMiZvXHHCeFwTR1iU41bOveMjkVLmd66ORYSVslF3bcboHsV0sUpSdqjSEvu4wx0sh92o0hfC0IPnvXrsykfFiJarXwDlRdJ62EViV00nNKRwBF3fwmqQQ1N9R7AQ9IvZqYxP8zZqxA\
5528  6SM36XriqfDZi8X0v7Yd18ZIM3Fpus/9pNPpFK8iXQfhTSPorvrn7Pmqc0dpmb3LDXEf0IxswhBNLxfm3IFswlzrmDDgobqQPVZUKkkXc52ExqBVAJcOU4ZMLUJW/U93mdapvMqH79uV/FSqRTP/3I5+Bj/fsl5h\
5529  ZiK5e8zrNdpIXP/Y4KNEOlmrMWTw3aF0GXJyGjnJsOw3dbISWBm+6L9V4uc+MOmq07dHZ40GBH/SDSN1y0Hzgo1V5VJuNot07JgHqDuRSmPAcn5Aew0dd2Qc+KaTfcohBmi27u+IQ1XtYZc46QM9eKGDgyM2IofY\
5530  sYwsymMhFooGi7z8BjX/xq8F8GHfJ+EfwzI1lJO8HXcif9IimMRowlssesmzhHAyWrfSO1Qt1o/+DswmgzncWo5C9TTJ4ZzOSTrTyNNg86LRBLQQzkgnsyHbOOgpJ3RQyo+if/cxde2l/ywlDJ3gNEsgeScFQRCL\
5531  4d0EKoWoeV1rFk2ZwidNI+VQ3vS/0nulIow8qXil+lGBos3w2rUcS8jV89UMiU3gFGdXzh2M7KdagZs1zzWV5aDKGjmWc5ZGPKPFgxE8EFR3W5GtHLAt+h/kLnH9qaRa9B+JwuJMIEh/q1XglhyDpNJ/6/pfoQfo\
5532  N110pvOkPe3WP9Z7DYXJkaanUuVXxaZiaAmk9npxRBVM4DF+a+N01Q85Nb5oX0zwfT/aAkn4M35VQgvW30hdbK3atVZFy48ooNNG4t2+Zon4cUkmtAT0Ot5Gc3nLAPPQLpPrGTlCa/KWs9z1IemsfQGINwnHSPxH\
5533  emNnWxTfpzJjxlGypR1DpG2Fy1lVM8oZXrUHrqpCjhD+BaF1Kjwq5Y0vLxlBNHeqcxdZ20lRs62hkL9R6b98BCv6b1xXkFMhsVIO+KH05TtUEdwD3R9Sef/1ptUR07+XtypiOVd3+jXph45+1UZmtCkEP1p6SQtR\
5534  9Mnq6y9STJF6zqT3bQnPlwzmSl51UnBXXJ7vC69QQtCXs9q8bTjeDJFH7X4Kc7BJCYfMgJW/ufZQk4No0R9A/goE63jZsJIKq2+J/K6nvQqijF+X2NC2k7d5mgM5J9InqQRAhR8nnHJjkg8eIsHrqUjSrmDSE3n5\
5535  oOEs7OLkIdqU91lEFvkvMMT2uHcir7MwnIr0DylShcO1p2T10xcORiRfp00PvRc99s99yS+oSNPwQXnagGM97RcJVNU7Q5ig81Gq3/TgJZXDa9N5F8O3UYuoT15joLdSiGZKjlB1JmcAain8Dz3poTh1Tck5KLIQ\
5536  XVD/Vc8S8vb0x+SLkLLsoxSye1LeK9Hpk2Z9soJPdt3z1iN1LeC3ss8MAErDa0IIxaTiFgGMSb7ITwba4E+kFUUl8ovmJfD68jjGgSp7vfcGYe0N4PMPXH7SoJzPnh4/w+Vnk1mniU+UYPa/2aLXPf/1cVZe4KVP\
5537  a/I8sbZITLxST2cXnxeD/X6viIOhnJX6dihAFZ1pX4a7sxibJc4k8/8BksMRng==\
5538  """)))
5539  ESP32H2BETA1ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b"""
5540  eNrFWmtbG8cV/isYMCRunnZG2ts4MUi2QFyM66SOKa7cend2lzoXnoBFjNvov3fec9GsAMn+1g8CaXZ35sw573nPZfa/29PmZrr9eK3antwYO7mx4VPl4Ts+5t3R5MYX4Vt/clO6yU1Bo1thsHwR/mQ/hD9JGMrC\
5541  /2Y9/PHydEJPT27a+iynOXbDH/M8zN+fhtFkd3I1uWlM+NobVKONMHuxyQJUvcPJTd0bPT1cDw+atAyr9sIn3FsUg/CnP9meXGB6THYdZkjDj5bvcvksjIbFmyCwdeFLG674IHnV5pNtEuqP43BfHe6v+Nm2zfMl\
5542  F3TpIeuFthk+dZ3TNnk+CJ+JwnpzzYVP0JsLH9/H/2czkaU4xR4HEH4vrmPC/8INWQX3L+p2Z7I0rzDoyKArxd/WP4P2efaw3t2pYa4kH0E9wTiuz/MGcwYDOX+sK8PY4aEyPOzbt7llddo+G5lvt8VBWKQeh7nt\
5543  KECgDk82qoZkhGuwHq9SJWwiY06wbroLYUddxVme32F9GgtT5mEbTcY3EMIyxgR+O8dY0nGeRHXxkk2AJfG/yrfmKntKRhkGuZ3lKUw6xq3YTgk4QTbGOBYpGpmnIJ2uh5253mhBtaIa3YqHLofhrw2galIeLSyr\
5544  p8BiDvPbB/AAIMkwFnBb6053cFOcOkjn2QlVu4Xbx0hQby0jbX3Khoc2oG+by5Q2ylYZ+J+3MlmfoQb52gaTjtVpOsAIj2AfmXicEczZeIvuuLVxddijptVIMFoq7KbGUv7uFBDsV164yvcIm+IHuKNI9wA3Bub3\
5545  u/5Qdpbh7nEHydiVVxX1Ow4qXFbjbvzoMMhT3DLk+61ITrozuJLhSm8DBpepiwwA2eBFSt1+d3Jnz6M4tHwSF4Eug65bpQ3MSTSpNlL5S8yiaDS3NqOQp50v1wH9FiIxCZaeu5PQF29JEFjrD2yWl3rAVwIm2YhG\
5546  cAAj3qYXmbLuTtmIwLUgk2MNhYx7OYhiDL6ONbxQGHnBe+jSDwAHthBQlgBwuFQ5wmzQ2NcjuYJQ4rbYlW3/jDFOccQf+B/DNEZ/jvy/sFeYBnHkCTHvwWBLtNiwWzrRLugANEs+bZhIGn8rPGHDPtVNDshTJvTs\
5547  ZMD6CtNNSOYJGLPP23D5YMQERJFSQqOEyW50vEob9huN4p2gfP2txGTf7P6ioVN8R2Lksr3QfirhKpsySVS9MW+E0Juv7bBVGvttNwCNFZ6D+5cEBxStMCx8v7e3TI618AV8FAIcU6RmDkKQHUUMNYdRXyBKZ8rY\
5548  YApXLHa8ZMriAbK+y27EneczZgJkNwghXq4AIEX9mLEHdOF/ZZngC9xcbfAFZschwoljwmNlvGTCvDc+x9C/GFEALCPBpBe1ypS+zIwYq/vxbk93x3WtcAs/d94hs97tqEEUXsRosXJJG5fkKADtVnlMQCS94K0+\
5549  EtcV765SCf6QziUS/JfFXYN8yshdjLM9BDcinOtL0IedfcCl91g53RJjhi3UDW+0ltylLkWifAZglJwfCD1NWRmF5gciLARvyx0WlKlmncGpjLQM2tu3pzbpT3F+IoJyPGRG4xzlvBNz6QMxneiZBVkioBMBFx/v\
5550  rAaDpuur+O3dplz2dg9f4GL4ZlIfdVWVg0ewUjmaXASiarOz9g10++Zw7qggU+c/4Ca12fsjJljYoEhfrGbZco6vWyzb1AvTrH9+GkRDRBvnlHg3h++ZvRi9kC77kfl1TmOS7dzrOoQse3k5+/AaNPAGXPRPAKoS\
5551  gqH4uA4S3IJnHmEvCG4tSqoa8vR/YqZoBKCl2FRVvCBH789ct1RQbZUx/YAefO8zWweHpeceox950iKtFU+F1nKrAb8aL6CL2X+WT0hY9I+ZTwmA/u7NYdUrfoRYV9Mhe7gjRVt6O9fKb/kQZRJW8sF6hImQOiQh\
5552  3TSVv2T/IhKBXso010APoKZDeFaav2BklV+CLC8xD7M41MvO+d/fM1gqpVSH4qDtidzzK38astELkrH/UWpjGlmQ4fXnUxG/6CQnnIR9qcIQwbwE7QKh7RBBL0xS4h6nV9rPy1H1ZpJUQSlpwj+akp9s3I7/a0Q0\
5553  okzTeyJV+HJZaZCQ8AOn4xecjJOsKRhVq5F56ooKTlNXa8c8CI0ixWiT/zvBDibTH9PIsdffXj657e2rFU018CIbv3z0CiB8Nbk4g/CHP4FwyqOjY1w8fvQcF59PLk5A1W9POj2XKj8dHoImLqMFkPVhs4H2d8RJ\
5554  MuEoCaKlBFGQal1IkM0kyCad7yXlBfQs7ml6nGWhNMGzAAd2CA702epco/Sa9RW7oLKFQkjTJ01lMFaVshJmsclDGMTu/Ztzw4IVnjJ25sXZYmH14HyHQLcmmbAWZGkx0C4avnWFQOZk7L7Ws2rUeZPMpns0ncTE\
5555  pelw62OTbjIZ3wVIRVOqDmiLOWWOE5Z+FYDacgWZm0r0zE0UVEq0FzG3aPli3qOTxgLkg9eR3ACmpcGUv3BbZG+1WCW1Ss7zY9kYuhw2X8yPkRoXaTts1RSxp0RZShB1ioiMv0X9c/5cDbtsu6XEAVdppGKIzlsL\
5556  uey74P0hdtN4EsdD8cQ18YVUxDAQeAn/cbPPtSc2meJW7KiR6+DZ7v2GfOiEOkGVFDPZ9/TT6c+v8dM1nT4RLZLEvN7nm+I2UuLXTqoxS8+m8Vnnkcn5ZzvsuxZ1RGn3DqDsJ0rnOnfRndvZdh2zv+Wp534AfkLV\
5557  16qEX1jHDLgjsVjErKpcu3kSMWOzOlw7cFFquWFZ5dqQU0VYCgVcmHinhmLDXDPgFq2ZUncO2Vz+DcDQZoxQy207sFqe+YovBKRc0bB4iS38L3IlmUzpCsTMv6K8J8Ydalu0GuMT/lHVOzrieKRMd86cREAjT96B\
5558  35MIq5H/FbSWKSTA+E3+CsKPRcomfz25kgDqpMnTKgjS+xBhhfU4fAj6UslIaslV0offSZ+BKJl2jtYRJnHpvqyXcrkOrvKI4ibd80wmLRMdA7cuDjY2lVA31jmmGHPNa5ep6rXOtxAOWtE4QmxN11ATQuel1ZVb\
5559  WTnrdnN6XtCQc2PP9NRdRko9yzANDdEdTZ08gAIANPUwOtCwOzOxMwxv7EjiOsza6QBvIrNVKXsiJcxGBgTpqnS1R8gvnkPGAxjvAKw2gqJHqwuGwqNKdyBu7sRI0W4lVNY2ahQO57kEFxgHWNDleXvQs0FU51Ud\
5560  rc3NZj9Xu0xJakhH3f4pddmhLXvuf8YYanuDvhlGw5akmVYI15j0O05rp9oBTaR8MuzO2JlnxVYdUea6FfkaP8feKGLvIqryOkE5UWgrfI1a6PPdATIUbhVw/fn5xZXQed1pVRkmj6tF+0J7JdkXgVnTkOAMYpcy\
5561  7UogMyMFQ9uVBwfzOROZEzGrP+Y4KQymyUtwqmEsesvkULic6aGTZSWxRC7sOG6B7NdIFKUnGo0hr7qMMdLIfdiNIXytFnz27lyZSvlwK6o1wjtQdZNECa1K6KTdlI4Aiqb9TVIJ6myo91rDPePFxGRKT4COKHvk\
5562  5lxP3BQOezWf+9fYbG2NNBEX5vrUTzodTnEpUnQtpGkE2lX/nN1eFe4oJ7MPuR3uazQh23qIZperZ1zpt/VMi5h6wENNIRusqE6S7uUyCY1BkwD+XF8wXhoRsup/fMicTrVVPnwfV/IXUiqa2ac4+gnk/Jn1CjMV\
5563  yd0Rr9dqA3H5Y4MPEuZkrdaQtTeH0mLIyWPkHMOy0zTJrajK2EXbrRIn9zUzrnp8PDhrNRr4g24MaSIBzQo2VpVLrdnOc7F9HqDWRCpdAcvJAe217vgi48C3ndRTjjDAsU1/Q7ypikdd4qFP9NiFjg322IgcX8cy\
5564  Mq+NhVUoFMyT8nvU/Bu/FMBHfR+FfDouYvtvx52wn0QEkxht/RaLXvMsdX0wWrbSOxQt1o/+Dswmgxl8Wg5C9SzJ4ZTOSS7TytOg8qLV7LMQwkgn0yHbuNYzTuiglB9F/+ERdeul7yz1C53ftAsgeSfVQC0Ww5sJ\
5565  VAdR07rRFJrShI+aQ8qRvOl/pfdKQRhIUvFK5aMCRZvgjYsES8jV09UMWU3N+c2mnDcY2U91C27WvNA8liMqa2Rfzlda8YyIByN4IKhuRpGtiw0uan6Qu4T1LyTPov/IEuZnAbU0t6IC1+T4I5XmW9f/Cj0+v++i\
5566  M50n7WG3+LHeaxxM9jQ3lSK/KlZVQgsgtXcrIypfah7jdzYOb/sh58VX8bUE3/ejNZCEP+UXJbRa/Y3UxdZqXLQq+n1EAZ0eEu/2jCXixyWT0PrP63gM5fKOAeahXSZ303HE1eQtp7jLQ9JpfP2HNwnHSPwHel9n\
5567  XRTfpxpjyhlUpB1DpG2Fy1lVU0oYXsfjVlUhRwj/ktB6ITwqtY0vrxlBNHeqcxdZbKSo2ZZQyN+o7l88gBX9t64ryKGQWCnH+1D64h2qCG6Abg+ptv961eqI6d/LOxWhlms67Zr0sqNftZEZrQrBzxZe0UIUPb79\
5568  8otUUqSeU2l8W8LzNYO5khedFNwV1+bbwiuUEPTljDaPLcf7IfIs7qcwO6uUsMsMWPn7Cw81OYgWzQEkr0CwjpctK6mw+o7I73rKqyDK+GWJFV07eZen3ZETIn2S8n+U92HCC+5L8qlDIHg9D0niCiY9kFcPWs7C\
5569  rg6eokv5mEVkkf8CQ6yPewfyMgvDqUj/kApVOFwbSlY/feFgRPJl2vTQe9Fj/9yW/IIqNA0flKcNONbTfpFAVb1ThAk6F6XiTU9dUjm0Np03MXyMWkR98hIDvZNCNFNyhGoyOQBQS+F/3ZMGilPXlJyDIgvRBbVf\
5570  9SAhj0c/Jp+HlEUfpZDdk9peiU6fNMuTFXyyu563HKlLAb+WfWIAUBreEEIoJhWfEcCY5Iv8ZKAt/kT6UFQfv2xfAa+v9kMcqLKzrTcIa28An3/g8nGLWj57vn+CyyeTaaeHT5Rgtr9Zo5c9//VhWl7hlU9r8jyx\
5571  tkhMuNJcTK8+zQf7/V4RButyWuq7oQBVcKZtGe7OYmyWOJPM/gf0hhEn\
5572  """)))
5573  ESP32H2BETA2ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b"""
5574  eNrNWmtb20YW/isECLTZ24yt26QbsFMbY0iyaTcpSx/TVhpJbLotz0JMQ3br/77znoskG+zk434w2KPRmTNn3vOei/Tf/Xl1N99/ulXsz+6Mnd3Z8CnS8B0f89PJ7M5n4Vt/dpe72V1Go3thMH8V/iTfhj9RGErC\
5575  /2o7/PFyd0R3z+7q8jwlGYfhj3kR5PfnYTQ6nN3M7ioTvvYGxWgnSM92WYGiN53dlb3R8+l2uNHEeVi1Fz5hbpYNwp/+bH92BfEQdhskxOFHzbNcugijYfEqKGxd+FKHKz5oXtTpbJ+U+v00zCvD/ILvres0XXNB\
5576  lx6yXWib4VOWKW2T5UH5RAzWaywXPsFuLnx8H/+/Xogu2Rn2OIDy43YdE/5nbsgmeHhRd7iQpXmFQUcHXan9bf3XsD5LD+vdF43jitIRzBMOx/VZbjjOcEDOn+rKOOxwUx5u9vVFatmcts+HzNNtdhwWKSdBth0F\
5577  CJThzkrNEI1wDafHqxQRH5ExL7FufAhlR13DWZbvsD6NBZFp2EaV8ARCWMKYwG/nGEs6zkLUFq/5CLAk/hfpXmOy53Qow6C3syzCxBNMxXZywAm6McaxSFaJnIxsuh125nqjJdOKaXQrHrYchr82gKqKeTSzbJ4M\
5578  iznIt4/gAUCSYSxgWu3ODjCpFR208+yEat3MHWEkmLeUkbo844OHNWBvm4pI2+pWGPiftyKsz1CDfnUFoRN1mg4wwi3YRyIeZwRztp2iO65tuzrOo6TVSDFaKuymxFL+vggo9isvXKRjwqb4AWZk8RhwY2B+c+in\
5579  srMEsycdJGNXXk3U7ziocFmJ2fjRYZDnmDLk+VY0J9sZXElwpbeDAxfRWQKA7PAiuW6/K9zZy1YdWj5qF4Etg61rpQ3IJJrUM1L9c0hRNJqVzSjkaefrbUC/hUhMhKUbdxL64i0JAkv9gc3yUo/4SsAkH6IRHOAQ\
5580  V+lFRJZdkZUoXAoyOdZQyHiQgyjG4OtEwwuFkVe8hy79AHBgCwFlDgCHS4UjzAaLfTmSKwglbo9d2fbPGeMUR/yxfxvEGP058j9irzgaxJFnxLzHgz2xYsVu6cS6oAPQLPk0mKXyK7EJu/Wx7nBAbjKjG2cDNlaQ\
5581  NSOFZ6DLPu/BpYMRsw+FSYmLEiO7ofEmrthpNIR3IvLtVxKQfXX4i8ZNcRwJkA9vhMeqQojKxswQRW/CGyHoplsHfCSV/aobfSaKzcHDS4IAslroFY7fG6/XgwAEtIcAxxSpmYMQZMcWQ81h1BeI0pkydpjCFYsd\
5582  L5mzhoCs77IbceflgpkA2Q1CiJcrAEhWPmXsAV34X1gm+AyTix2+wOw4RDhxTHhsj9dMmA/G5zb0L0cUYMtIMOm1hmVKX2dBeELZme1pdruuFW7h+y5FCCxyL2oQhWdttNi4ZOfkOQrAukXaJiCSXvBWn4jrincX\
5583  sQR/aOciCf7r4q5BPmVkFkNtjOBGhHN7Dfqwi/e49A4rx3tymGELZcXWKSV3KXPRKF0AGDnnB0JPczZGpvmBKAvF6/yAFWWq2WZwKiOtoYudVckm/rkVT1SQT4ZMaJyiXHZCLn2gpRMzsx5r9HOi3/LtndVwnvH2\
5584  JnqDA27JDG/H+AInwzcT+9ZaRT54gnPKR7OrwFZ1cl5/D+t+P21cFXTq/HtM0lN7d8IUi1PI4lebFNlScK7wbFUuyfjEZmBR5GYINogLTL27w3fMXwxeqJa8ZYZtiEySnQc9BzmEt9fXi/ffgQW+BxX9ADwVwi8U\
5585  HrdBg8EIZf8Ee0Fsq1FRlagT+j8zUVSCz1zOVO27pEfvz1y2FLBrkTD7gB187xNbB2LiS4/RDyw0i0vFU6al3Ga8fxIvyJoX/1kvk+DonzKjEgb9/clh4Ru+hXhXEyI7PZCyLV7NttIVN6JcwkpGWI4gCMlDFBJO\
5586  U/hrdjGiEaicx6mGegA1HpKp0lcMrvxzwEWBz7AUh4rZOf/bO8ZLoaTqUB7UPdG7ufKHIZ97Rjr2P0h1TCNLOnz3iWTELzvJS87BPtdaCGBewnaGyDZFzAtCcsxxeqX+NASK3kJyKjhHHPGPKuc7K3fg/9YiGkGm\
5587  6j2TIny9rjRIMPiWs/ErzsVJ1xiMqsVIk7migNPM1doJD8KclQMY/g8Ilku9t3HLsrdfXT9bdfkNvGj698j49ZM3wOCb2dU51J/+DMrJT05OcfH0yQtcfDG7egmmvnjZaboU6dlwen7dHgDSPuw1UP6BOIiQbY4Q\
5588  Gkny32NOLQX+XkKslxBL13LWHTCnFLLHyS4KExAXsOHNFU/YlGbkhSZ82SFobKkG0sxJsxiMFcB0JV0KGz3GYdjxPzktzNjOMeOmqcuWa6pHlwcEuC3Jg7UWi7OBNtDwrasEkiZjj7SU1bNs+mM2HpM4CYZrHbpu\
5589  d/XTbDa5j4uCRKoNaIspEdGMtV+Lmzrd7MLEV1TdUv8ERRLtJefzFCtfNe056SlAP3gcWQ6QtDQY8xfuiIw3bDinFslleiq7QnfDpst5MWg2i+threfQ9pIoNwl6zhGK8Tcr/5W+0FPdEBxzqfVcoSGK8dt0FVLZ\
5590  d8b7Q9ym8agdD6UTl8NXUgwTX1o+KEoTUm2HzeaYSmWWXM/7y/PxKZOX1AQqpI5JvqGfTn9+iZ+u6rSIaJGo7U/4dFcKYqnuSye1mKV74/Ze55HC+a8P2Gltid3Z8Rew9zOlcpWddWU7W29D+gWLbvwAPQfUfLVq\
5591  +NklDEm9V8I8PHuwnCNR2lxtJlyHciW23Kss0p0W8bwdCgNck3inB8UHc8uYWz7NWBtzPn0CMNQJg9Ryx64iw/mCLwSk3LTz0WXzv8iVaDanK5kaFxFYYw51LGqN7xH/KMoDHXE8kscH506in5E778HvWQur5/5X\
5592  kEGkkADdV+m3UJ5+EkLezG4keDrp79QKgvghRFhhPY4dgr5YspFS8pT48V+ly0CUfCSLUyuJsHkkS8Y8CLL0COImHnvuJhAdV4LdMjve2VVO3dnmmEJlpJTseazHEfJxBIVa7I74Wh6JSUo0jXOri9eyeNLt6PSw\
5593  finrJ+qsGBspB21ANkxFk6oyegRLAHHqavRQwx4s5MCBAGNHEtpxvowpL0VFXqqiPVEU50cniRhUS1e79Ij62QsE7WOc4jHobQSsjTYEicyjTHegb27FSNVuKWC+VJMxsHypMB4eCU2t4Btn0egrLZia2p6CLiM5\
5594  B51hF/xNuzlmp/V8lHI4JpoqPCXOo5LK+hOOAl5Mn3IbtqFzJy0HajekuzuMJVO0veM8IvM/5u1zj3uLeuNo+5QUaT1zeAOkfvNggublomLSXrJNb8UVfBVIzenQjO+gKh4/UR+a3oqfkAyRTSB4roODRqoXqdmK\
5595  EZI2OQkeM2wLWtln4/6dLKpj/MxO2k0QOtH8xLFX4occI950GWGkwXnajRF8rRTY9e5dmUtpsBK1KH0XmFVRq6FVDR3Dw8SjsbSKBWLUw2sTjd/7kUJ6Kt1UKhTF3JRrUnP2khdUczjKiCwXKze9PyFBGaLH5MoF\
5596  N/zqcqHFQzngoSoTf88p3EjT8GHN8PkIOJVXDMxKctOi/+ExkykVNOnwXbuMv5L6zCw+tqMfQYmfWMyYDxLd3AmvV2vTbtNt3txqUKQT2B0yp8OQRNE4MAqfzNcjCUgCbhIp7PMWuU/cubWdHbWznT/2Engktnp/\
5597  LOxVo+7I9FlDc3fOd1dVc7fyUyWnqyKkA59JilLajCvhuT4ziaTjIs/zilSqyLrJtI7Eu6xk+6k0hytpSpZK5fGYn65zzJvICCks8YSqZOyrSZQfPribZ/rQ7oNQhtTSaG1BAXaQi0knJEctvkmdurzAPb+xTmV9\
5598  PFq34uAHelbyD0A6GizgjvJ4Up/wOCNM5JXEa06asloTw0x8PRTrQ3bcUp88WunX0NPj/uMT6qFLN5hLC6T09VJY+Em6cla6GHhbgAiXWF1LPmbeD10DSZe+JMb6Qu+Rio1YrtATptpCfKrOBp5bB1VHmtEnnxly\
5599  jpKzj115FmDE6YsVxFjzShNNzqnYLkcsnuL8MjjEppTd9ndbla08+mo6E8SEYf0ryYLoPwK4Zh94GlMUS4ZEG6HpiXXDLkv75oErzug9dtotSqynPJeC8nglKOPB5voKZQmg9n7F4oT7TaqvUUwfiOXB+27aNwV8\
5600  34+2gBB/xu8uaCH5b7ISH1LlOtDw4rxJ29fhrZ6zRny75ABal3kdb0OwPPaHHNpldD9NRjyMLjiMrwlT5+3rOLzDISVa76k22Bar9ynxn3NHuI3mhgjdSlBhO80pyn/XPv5U+3H08K8JoVeSGkvB4f0tZ+4kO1bZ\
5601  WdJ2N/TM1rFVZd6Q/y4/FhX7166ry1TCby5P3GH05RlqC25K7g+p5v5ykwJbf+c0pQgFVtXpocTXHfvqAZnRprg3WnplCl5/uvoyipQ3ZJ4zoTVLYL5lJBfy4pEiu+CCeV+4BLSPIXpsmrbtv4cUys243U9mDtYi\
5602  aRigVPiH0389bOppmJxf29HBvOaGXWb1VY3f9GGrYidh197QQZNXauoDbgw2qKOgilI7CLxqHiHe0Gnpc4moXcHEx/IGQM052c3xc2j3VHqJpPJf4Pbbk96xvFPCEAoonXHioAmsdnesfvrCtygA1gRBPALrndHW\
5603  9yXnAGT8QA4RIZyyvlJ2S/3h3hniAeUBVEDpg49YnhybzusQopSTQllfJqAXQzR2IShVibTh9Zzwv4ykleHUH0vONCiEEEdQVNR2fsrdI11cYseyV9JLHD0psZXa9E6zPkUZZPd9bQ02o03EQY2YXFyvInBQ/Mk3\
5604  ro758Wd5BnYp7TYXfRQkJ6/30EJPUMkkaKIn0xpN9OTkCO6enO6hnE5e4DLa6L2LqtNGJxow+3/cohcuf3w/z2/w2qU1aRpZm0UmXKmu5jcfm8F+v5eFwTKf5/p+JjAVPGlfhrtSjE0iZ6LF/wCAbuTq\
5605  """)))
5606  ESP32C2ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b"""
5607  eNq1Wmt7E8cV/iuObXBC8/SZ0V4HgpFARrbBFFKCCxVNdmd2XUhwYyMH00b/vfOei3YlWyb90A+WpdmZM2fO5T2X2f/szJrL2c7djXpnemns9NLGv7qI3/FnfjqcXvoyfkuml5WbXpY0ejsOVs/iR/59/EjjUB7/\
5608  N5vxw8vqlFZPL9vwugCN6kH8ME8j/WQWR9MH0/PpZWPi18GwHm9F6uU2M1APDqaXYTB+eLAZF5qsirsO4l+cW5bD+JFMd6anIA9iF5FCFn+0PMsV8zgaN28iw9bFL2184iPndVtMd4ip35/EeSHOr3lt2xbFmge6\
5609  9YjlQseMfyEUdEymB+ZzEdhgIbn4F+Xm4p9P8P/RXHgpj3HGIZjf6/Yx8X/pRiyC6zd1D+ayNe8w7PGgO3W/rX8E6TP1uN9V0lBXWozjZx2V4xKmG9UZFeT8E90Zyo6LqrjYt28Ly+K0CSuZp9tyP24SJpG2HUcT\
5610  CHFlo2JIx3gG7fEudcoqMuYI+2YPwOy4LzjL9B32p7FIsojHaHKeQBaWs03gt3NsSzrORFQWz1kF2BL/6+L2QmQPSSmjyLezTMJkE0zFcSqYE3hjG8cmZSN0SpLpZjyZG4yXRCui0aN4yHIUP200qibj0dKyeEps\
5611  5kDffgUPgCUZtgVMa93xLiZ1pCN3np1QpVu6xxiJ4g0y0oZjVjykAXnbQkjajrfawP+8FWIJmxr4axsQnajT9AwjLsE5cvE4IzZnuyl64tZ2u0MfgXYjxmireJqArfxVEmDsA29cF3tkm+IHmFFmezA3NswXD/yB\
5612  nCzH7EnPknEqryJKeg4qWBYwGz96CPIQU0Y83wrnJDuDJzmeDLagcCFd5jCQLd6k0uP3iTt70rFD26fdJpBllHWrsAGaBJOqI+W/AhW1RrNyGDV5Ovl6GdBvARKTYuuFOwl88ZHEAoP+wGF5q6/4SbRJVqIRO4AS\
5613  V+FFSIY+yUYYDmKZHGsoZFyLQRRj8HWi4YVC0TM+Qx9+YHBACzHKCgYcH9WObDZK7JuxPEEocbfZlW3ymm2c4ojf9z9EMkZ/jv2POCtUgzhyn5B3f3hbpNiwWzqRLuAAMEs+bRhImrASnnBgn+khh+QpU1o7HbK8\
5614  Irkp8TwFYiZ8DFcMxwxAFCklNEqY7EfH86xhv9Eo3gvKF/ckJvvmwS8aOrF8zIyvO4iRgxBQ2YwRoh5M+BRkusXGLquksff60Weitjm8Zj94f9kKtsLrB3vrmSDrqSCRQvBR0wZBx54URprAqCMQnjNebDF+qyH2\
5615  XGTG7MFefR/aCDhP5gwDSG0QP7w8gXWU4S4bHkwL/2vL6F5icr3FDxgaR4gljtGOhfGc0fLa4NzF/eVwAtc1EkkGSkjxfJ0E4QahN9vT7G5fK8DC606ECCRyJWQQfpddqLhxS9NtySEA0q2LLvuQ3IKPekf8Vly7\
5616  ziTygzuXSuRfF3QNkikjsygmpHsFG29dXJwBO+z8Ix69w87ZbVFmPEJoWDpBEpdQCUfFHIZRcXIg2DRjYZSaHAizYLytdplRxplNNk6FI1esMW2Em2XiJnvf7UA4UE1GDGicopz0Qi79zS96rGRim0RAWFqevsJ/\
5617  JoviWAVxZrtszNeyDA5KI4fzdg9f4Gf4ZjLfCayuhnegqmo8PY1Q1eav2zcQ8JuDhbcCTp3/iEmquHeHDLFQRJk9uxlnq4Vdr+BsE5bIbCqZjVUaG79zmEFEYMTdHr1j5GLRgKn8BwZWFZamOde6DdTp7dnZ/OMr\
5618  QMAbBJl/wJhqARcKjJvAwHj8kBzCyxDVWtRSARVC8p5RohHjJHuqOsku8TH4MxcsNSRa5ww9gAY/uClAUfZ54jH0iSmWWYC9L9KV9IuWvl6s2GE2//d6amSP/i5bat1wFrw6OW55zks6e4Z+D3alTstW06tixW8o\
5619  ebCSAgYEH+zTpjHDNLU/Y58i6IApVVmhsR2WmY3IMYpnbErVF0xp+CsvdqiMnfO/vWPrqBU/HcqAdiDsLp78acRaLom15JNUwTSytPWrLycdftkZjjjd+qNyQrjyLP62RBw7QISLRCrMcfqk/bJT1oO5pE/whizl\
5620  HwjiWNm4Xf+XzoQRUprBfam31/NKg2QA33PifcppN/GaATy17lgkqajVNEm1dsKDkGjjYAb/TywVDGVAXeMk3jQ/ZB2UXtw7u7/q3evkvHHrCtw+v/MS1vdyevoaDBy8B7RUh4dP8PDJnad4+HR6egQsfnvUa6vU\
5621  xfHoAMnJWSd65HZw9Yjru+IUuaCRhMpKQiXgM5QSSnMJpWnve8WZANZiTjPgXArVB9bCKnA2oJ3Pb85FK6+5XfUAuLVU62iSpAkLxmqsaKT8sOktaMLu/ZMzwJJFnbHRLOqv5drpq5NdsrYNyXe15srKoTbK8M3Y\
5622  x8ucWE0xM1Xnog9msz0iJ0Hv+gMjia66Ptx0Olk1jeJJTwB0voKSwymzvsZuOAklIVKmXpY/QhJ7GDskgmdw9orzfZtk/OVGRitELVSpaDvYYjlnRbpaZu2oVcF1TR5KGiL3M+yLzzL8XDxVNazdrRLJivGJzk+1\
5623  KUggXGuMYWNb9AEKWVMyMCDe0njajcdihwvYUylf4YWAFvyn8F5oA2s6w1SctpHngMr+fEPecERtm1qKj/wF/XT68xv8dE2vqUObpF1HwRfb4gBSjwcnBZSltVm31nkkXf7RLnnh+T30D/b2WQ0VgML0iZd94s62\
5624  myD/lmkvrBltAlRqrbL4PxUe7krhcZOH92dTsru2xhgy7vnMcXuxLrR9ppLAYYsP/NQ71RRr5oKtcVmdGfXSYMzFt7CGNme5WW6yAaCK3Nf8IJrKOQ1nQqL0v8iTdDqjJ+C5+JpSli6mUJOh1TiNfdt3uf5EyGxf\
5625  5EdW4peRNVcs735nUWP/AdiUqzUAtpviJdieCH9N8Wp6LuHPCRq2qv7sOluwAl0cA8TwMsknglSxhKapdAXoBx0bXR7Qcdlj2TLj4hqI5xGGTbbneX3LmMVmG8r9rW0Fxq1Njg3GXPD2VaZCDcVtwHor4kaQDPQM\
5626  FRwEXlnduZWd837jZeDFFAruwRkJWRYpIoPSDQYNOdGkEL6DzN105jovo/uHW/lcFA3NGzuWGN3CB7uG7TayUuV0IJxCnqRHRHblMHiE7/Ip+NyHDveBa2MIe3wD1JctimoHEOfGidTYVmJesJ1I4WueK2Yx4mga\
5627  9HjRyvOsERV6HTp1c2PYL+QuJEkG2bjf66SOOERlT/zPGEMpbtDjwmg8jzS+CHoG2Xeclc60VZlKuWPYk3EszyKte3wspCrMNX5heePO8k47ITbfoQgotWe9Qb3uxdEGkkUszC1ZXDScC5SHXlvJMG6cL2sWoqtI\
5628  s6bukonoCqKUKutzIJSRSKE/yoPDBc1UaCJeJROOnwJei+zDo0miRWqVHgiMMz70cqW0K2lLO+mOQMprJILSikbDx8s+ZIw1oh/0wwc/C2KZgytPZpL9r0S0RoAHom7SjkOrHDppDWVjGEXT/iopBjUg1HdbI026\
5629  nu+WEUhmDFaBckAsC9ldcVC46vmC9oeuMXodrcZ8TtJeN1L8iQQdBDWN2HWdnLDDq8AdJV/2Fvet24CGYRtGaEy5MOcErQ1zLSfCkIegazpgTWWOdBrXn/YSgAZKp2wvjTBZJ59uMahTaVSM3nU7+VOp9Mz8czf6\
5630  GdD8hf2M+U04d4e8X6vNvvXLhhcS52Sv1pC2t0fSGCjIY+TCwbLTNOlKQGXbRYOsFif3gbFWPb674Wo1Fvj9fgRpOvRBbWhYX3Uh1WK7SMUe8wD1FDKp6y2nBnTc0HNHNgXf9jJP17XUm2RLHKrurqXESe/rFQm1\
5631  +PdYjxxgJzKyqG4FWIjz8mbzP+c7fL6Z+yQQZJitljKSt5Ne9E87OyZO2vAW+14wlRD2x+s223hLdyh/g+GmwzkcW64t9ebH4U7NSUbTymLgedlq9lkKasTKfsSKDnojCSlU8qNMbh1Se10axXKNSbct7ZKl/CTl\
5632  QBCd4T0CKiKoy9xoDk2ZwifNIeUC3SRf61yp9SJSqtFSJaimol3rxnUoS+ard6E5EpvAKc62XBAYOU+9YnDWPNM8lmMqS+Qxk6d0YtkijFgEGet2x7KVy7BFY4N8Ju5/KqkW/UeeoIk43dMlSwLckPuKTPpmfScs\
5633  9bL7uofO9Fbag371Y73XYJjuaYYqbw/U5U2l0JKN2qulEZUvgcf4DYuDVU/k7Pi8e4nAJ36MCtn6Y36tQUvZX0lcrK3GdVpFz45AoNcX4tO+Zo54uaQTWgB6He/iubwRADp0yvRqUo7gmr7lLHd9XDruXtbhQ8Ix\
5634  Uv+R3q7ZFMEnVILMOI3qgMcQclvBCxbVjLKGV93lqIqQw4R/TtZ6KmBq2Ot8dcEWRLQzpV3mXU9E1Xb9SX76KzUFlq9LRf6t6zNyIBhWyWU8hL48QwXBTcydERX339y0OwL7C3kDItZyTa/zkp315Ks6MuP1lLx5\
5635  tPRCFULpk9VXVaSeIvEcS8/akj1fsDHX8lqSGnfNtfmO4AplBYncqxZd3/AawY57jUqzu3bakLGv9tdXHKps8IJVyF1huzz+L1ZdafVFjt/0NlZtJ+c3Gm7ou8kLN+2uXOLoSsr98ZZXJHi6uGM8J1zX64u028Fk\
5636  +17zLM7X9x+iz3iX1afnaChb2JwM9uWlE+3c/i7lqaC39pKs/iWCvojia3IcyGzAbrkjiQUk5zVqUI425CBP50XyVA+OER3o/pKqNr0nyeRy2fRel/BdsCLEkzcN6MURQpeKA1OTS+9ej43GXUikb+LUIyXZoIBC\
5637  KIFWbaN3AEV3WWOKRSRZdk2K1AOp6hXfdOWNSbrBNeiqw62x0WRt/lF8Zu1T/t2QeVAcKr9YIqR/yD3ApzT4SFmBt2vz5+1L2OvLxxH+6/z17TeIZm9gO3/H4yctKvj86eMjPD6aznpdeEICs/PtBr2R+ePHWXWO\
5638  9zKtKYrU2jI18UlzOjv/vBhMkkEZB0M1q/QFThhVdKYdGe5TMQOXuSKf/xdxa/YZ\
5639  """)))
5640  
5641  
5642  def _main():
5643      try:
5644          main()
5645      except FatalError as e:
5646          print('\nA fatal error occurred: %s' % e)
5647          sys.exit(2)
5648  
5649  
5650  if __name__ == '__main__':
5651      _main()