mirror of
https://github.com/0xFEEDC0DE64/arduino-esp32.git
synced 2025-07-29 18:27:15 +02:00
update IDF libraries and includes
This commit is contained in:
477
tools/esptool.py
477
tools/esptool.py
@ -98,7 +98,6 @@ class ESPLoader(object):
|
||||
# Some comands supported by ESP32 ROM bootloader (or -8266 w/ stub)
|
||||
ESP_SPI_SET_PARAMS = 0x0B
|
||||
ESP_SPI_ATTACH = 0x0D
|
||||
|
||||
ESP_CHANGE_BAUDRATE = 0x0F
|
||||
ESP_FLASH_DEFL_BEGIN = 0x10
|
||||
ESP_FLASH_DEFL_DATA = 0x11
|
||||
@ -131,14 +130,11 @@ class ESPLoader(object):
|
||||
|
||||
UART_DATA_REG_ADDR = 0x60000078
|
||||
|
||||
# SPI peripheral "command" bitmasks
|
||||
SPI_CMD_READ_ID = 0x10000000
|
||||
|
||||
# Memory addresses
|
||||
IROM_MAP_START = 0x40200000
|
||||
IROM_MAP_END = 0x40300000
|
||||
|
||||
# The number of bytes in the response that signify command status
|
||||
# The number of bytes in the UART response that signify command status
|
||||
STATUS_BYTES_LENGTH = 2
|
||||
|
||||
def __init__(self, port=DEFAULT_PORT, baud=ESP_ROM_BAUD, do_connect=True):
|
||||
@ -271,7 +267,7 @@ class ESPLoader(object):
|
||||
""" Try connecting repeatedly until successful, or giving up """
|
||||
print 'Connecting...'
|
||||
|
||||
for _ in xrange(4):
|
||||
for _ in xrange(10):
|
||||
# issue reset-to-bootloader:
|
||||
# RTS = either CH_PD or nRESET (both active low = chip in reset)
|
||||
# DTR = GPIO0 (active low = boot to flasher)
|
||||
@ -283,8 +279,7 @@ class ESPLoader(object):
|
||||
time.sleep(0.05)
|
||||
self._port.setDTR(False)
|
||||
|
||||
# worst-case latency timer should be 255ms (probably <20ms)
|
||||
self._port.timeout = 0.3
|
||||
self._port.timeout = 0.1
|
||||
last_exception = None
|
||||
for _ in xrange(4):
|
||||
try:
|
||||
@ -362,10 +357,9 @@ class ESPLoader(object):
|
||||
self.flash_finish(reboot)
|
||||
|
||||
""" Read SPI flash manufacturer and device id """
|
||||
@stub_function_only
|
||||
def flash_id(self):
|
||||
resp = self.check_command("get flash id", self.ESP_GET_FLASH_ID)
|
||||
return struct.unpack('<I', resp)[0]
|
||||
SPIFLASH_RDID = 0x9F
|
||||
return self.run_spiflash_command(SPIFLASH_RDID, b"", 24)
|
||||
|
||||
def parse_flash_size_arg(self, arg):
|
||||
try:
|
||||
@ -420,7 +414,7 @@ class ESPLoader(object):
|
||||
|
||||
self._port.timeout = 20
|
||||
t = time.time()
|
||||
print "Unc size %d comp size %d comp blocks %d" % (size, compsize, num_blocks)
|
||||
print "Compressed %d bytes to %d..." % (size, compsize)
|
||||
self.check_command("enter compressed flash mode", self.ESP_FLASH_DEFL_BEGIN,
|
||||
struct.pack('<IIII', erase_blocks * self.ESP_FLASH_BLOCK, num_blocks, self.ESP_FLASH_BLOCK, offset))
|
||||
if size != 0 and not self.IS_STUB:
|
||||
@ -495,7 +489,6 @@ class ESPLoader(object):
|
||||
while len(data) < length:
|
||||
p = self.read()
|
||||
data += p
|
||||
print "Read %d bytes total %d" % (len(p), len(data))
|
||||
self.write(struct.pack('<I', len(data)))
|
||||
if progress_fn and (len(data) % 1024 == 0 or len(data) == length):
|
||||
progress_fn(len(data), length)
|
||||
@ -542,6 +535,151 @@ class ESPLoader(object):
|
||||
self.check_command("set SPI params", ESP32ROM.ESP_SPI_SET_PARAMS,
|
||||
struct.pack('<IIIIII', fl_id, total_size, block_size, sector_size, page_size, status_mask))
|
||||
|
||||
def run_spiflash_command(self, spiflash_command, data=b"", read_bits=0):
|
||||
"""Run an arbitrary SPI flash command.
|
||||
|
||||
This function uses the "USR_COMMAND" functionality in the ESP
|
||||
SPI hardware, rather than the precanned commands supported by
|
||||
hardware. So the value of spiflash_command is an actual command
|
||||
byte, sent over the wire.
|
||||
|
||||
After writing command byte, writes 'data' to MOSI and then
|
||||
reads back 'read_bits' of reply on MISO. Result is a number.
|
||||
"""
|
||||
|
||||
# SPI_USR register flags
|
||||
SPI_USR_COMMAND = (1 << 31)
|
||||
SPI_USR_MISO = (1 << 28)
|
||||
SPI_USR_MOSI = (1 << 27)
|
||||
|
||||
# SPI registers, base address differs ESP32 vs 8266
|
||||
base = self.SPI_REG_BASE
|
||||
SPI_CMD_REG = base + 0x00
|
||||
SPI_USR_REG = base + 0x1C
|
||||
SPI_USR1_REG = base + 0x20
|
||||
SPI_USR2_REG = base + 0x24
|
||||
SPI_W0_REG = base + self.SPI_W0_OFFS
|
||||
|
||||
# following two registers are ESP32 only
|
||||
if self.SPI_HAS_MOSI_DLEN_REG:
|
||||
# ESP32 has a more sophisticated wayto set up "user" commands
|
||||
def set_data_lengths(mosi_bits, miso_bits):
|
||||
SPI_MOSI_DLEN_REG = base + 0x28
|
||||
SPI_MISO_DLEN_REG = base + 0x2C
|
||||
if mosi_bits > 0:
|
||||
self.write_reg(SPI_MOSI_DLEN_REG, mosi_bits - 1)
|
||||
if miso_bits > 0:
|
||||
self.write_reg(SPI_MISO_DLEN_REG, miso_bits - 1)
|
||||
else:
|
||||
|
||||
def set_data_lengths(mosi_bits, miso_bits):
|
||||
SPI_DATA_LEN_REG = SPI_USR1_REG
|
||||
SPI_MOSI_BITLEN_S = 17
|
||||
SPI_MISO_BITLEN_S = 8
|
||||
mosi_mask = 0 if (mosi_bits == 0) else (mosi_bits - 1)
|
||||
miso_mask = 0 if (miso_bits == 0) else (miso_bits - 1)
|
||||
self.write_reg(SPI_DATA_LEN_REG,
|
||||
(miso_mask << SPI_MISO_BITLEN_S) | (
|
||||
mosi_mask << SPI_MOSI_BITLEN_S))
|
||||
|
||||
# SPI peripheral "command" bitmasks for SPI_CMD_REG
|
||||
SPI_CMD_USR = (1 << 18)
|
||||
|
||||
# shift values
|
||||
SPI_USR2_DLEN_SHIFT = 28
|
||||
|
||||
if read_bits > 32:
|
||||
raise FatalError("Reading more than 32 bits back from a SPI flash operation is unsupported")
|
||||
if len(data) > 64:
|
||||
raise FatalError("Writing more than 64 bytes of data with one SPI command is unsupported")
|
||||
|
||||
data_bits = len(data) * 8
|
||||
flags = SPI_USR_COMMAND
|
||||
if read_bits > 0:
|
||||
flags |= SPI_USR_MISO
|
||||
if data_bits > 0:
|
||||
flags |= SPI_USR_MOSI
|
||||
set_data_lengths(data_bits, read_bits)
|
||||
self.write_reg(SPI_USR_REG, flags)
|
||||
self.write_reg(SPI_USR2_REG,
|
||||
(7 << SPI_USR2_DLEN_SHIFT) | spiflash_command)
|
||||
if data_bits == 0:
|
||||
self.write_reg(SPI_W0_REG, 0) # clear data register before we read it
|
||||
else:
|
||||
if len(data) % 4 != 0: # pad to 32-bit multiple
|
||||
data += b'\0' * (4 - (len(data) % 4))
|
||||
words = struct.unpack("I" * (len(data) / 4), data)
|
||||
next_reg = SPI_W0_REG
|
||||
for word in words:
|
||||
self.write_reg(next_reg, word)
|
||||
next_reg += 4
|
||||
self.write_reg(SPI_CMD_REG, SPI_CMD_USR)
|
||||
|
||||
def wait_done():
|
||||
for _ in xrange(10):
|
||||
if (self.read_reg(SPI_CMD_REG) & SPI_CMD_USR) == 0:
|
||||
return
|
||||
raise FatalError("SPI command did not complete in time")
|
||||
wait_done()
|
||||
|
||||
status = self.read_reg(SPI_W0_REG)
|
||||
return status
|
||||
|
||||
def read_status(self, num_bytes=2):
|
||||
"""Read up to 24 bits (num_bytes) of SPI flash status register contents
|
||||
via RDSR, RDSR2, RDSR3 commands
|
||||
|
||||
Not all SPI flash supports all three commands. The upper 1 or 2
|
||||
bytes may be 0xFF.
|
||||
"""
|
||||
SPIFLASH_RDSR = 0x05
|
||||
SPIFLASH_RDSR2 = 0x35
|
||||
SPIFLASH_RDSR3 = 0x15
|
||||
|
||||
status = 0
|
||||
shift = 0
|
||||
for cmd in [SPIFLASH_RDSR, SPIFLASH_RDSR2, SPIFLASH_RDSR3][0:num_bytes]:
|
||||
status += self.run_spiflash_command(cmd, read_bits=8) << shift
|
||||
shift += 8
|
||||
return status
|
||||
|
||||
def write_status(self, new_status, num_bytes=2, set_non_volatile=False):
|
||||
"""Write up to 24 bits (num_bytes) of new status register
|
||||
|
||||
num_bytes can be 1, 2 or 3.
|
||||
|
||||
Not all flash supports the additional commands to write the
|
||||
second and third byte of the status register. When writing 2
|
||||
bytes, esptool also sends a 16-byte WRSR command (as some
|
||||
flash types use this instead of WRSR2.)
|
||||
|
||||
If the set_non_volatile flag is set, non-volatile bits will
|
||||
be set as well as volatile ones (WREN used instead of WEVSR).
|
||||
|
||||
"""
|
||||
SPIFLASH_WRSR = 0x01
|
||||
SPIFLASH_WRSR2 = 0x31
|
||||
SPIFLASH_WRSR3 = 0x11
|
||||
SPIFLASH_WEVSR = 0x50
|
||||
SPIFLASH_WREN = 0x06
|
||||
SPIFLASH_WRDI = 0x04
|
||||
|
||||
enable_cmd = SPIFLASH_WREN if set_non_volatile else SPIFLASH_WEVSR
|
||||
|
||||
# try using a 16-bit WRSR (not supported by all chips)
|
||||
# this may be redundant, but shouldn't hurt
|
||||
if num_bytes == 2:
|
||||
self.run_spiflash_command(enable_cmd)
|
||||
self.run_spiflash_command(SPIFLASH_WRSR, struct.pack("<H", new_status))
|
||||
|
||||
# also try using individual commands (also not supported by all chips for num_bytes 2 & 3)
|
||||
for cmd in [SPIFLASH_WRSR, SPIFLASH_WRSR2, SPIFLASH_WRSR3][0:num_bytes]:
|
||||
self.run_spiflash_command(enable_cmd)
|
||||
self.run_spiflash_command(cmd, struct.pack("B", new_status & 0xFF))
|
||||
new_status >>= 8
|
||||
|
||||
self.run_spiflash_command(SPIFLASH_WRDI)
|
||||
|
||||
|
||||
class ESP8266ROM(ESPLoader):
|
||||
""" Access class for ESP8266 ROM bootloader
|
||||
@ -556,8 +694,9 @@ class ESP8266ROM(ESPLoader):
|
||||
ESP_OTP_MAC1 = 0x3ff00054
|
||||
ESP_OTP_MAC3 = 0x3ff0005c
|
||||
|
||||
SPI_CMD_REG_ADDR = 0x60000200
|
||||
SPI_W0_REG_ADDR = 0x60000240
|
||||
SPI_REG_BASE = 0x60000200
|
||||
SPI_W0_OFFS = 0x40
|
||||
SPI_HAS_MOSI_DLEN_REG = False
|
||||
|
||||
FLASH_SIZES = {
|
||||
'512KB':0x00,
|
||||
@ -619,7 +758,7 @@ class ESP8266ROM(ESPLoader):
|
||||
class ESP8266StubLoader(ESP8266ROM):
|
||||
""" Access class for ESP8266 stub loader, runs on top of ROM.
|
||||
"""
|
||||
FLASH_WRITE_SIZE = 8192 # matches MAX_WRITE_BLOCK in stub_loader.c
|
||||
FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c
|
||||
IS_STUB = True
|
||||
|
||||
def __init__(self, rom_loader):
|
||||
@ -646,10 +785,11 @@ class ESP32ROM(ESPLoader):
|
||||
# ESP32 uses a 4 byte status reply
|
||||
STATUS_BYTES_LENGTH = 4
|
||||
|
||||
SPI_CMD_REG_ADDR = 0x60003000
|
||||
SPI_W0_REG_ADDR = 0x60003040
|
||||
SPI_REG_BASE = 0x60002000
|
||||
EFUSE_REG_BASE = 0x6001a000
|
||||
|
||||
EFUSE_BASE = 0x6001a000
|
||||
SPI_W0_OFFS = 0x80
|
||||
SPI_HAS_MOSI_DLEN_REG = True
|
||||
|
||||
FLASH_SIZES = {
|
||||
'1MB':0x00,
|
||||
@ -661,7 +801,7 @@ class ESP32ROM(ESPLoader):
|
||||
|
||||
def read_efuse(self, n):
|
||||
""" Read the nth word of the ESP3x EFUSE region. """
|
||||
return self.read_reg(self.EFUSE_BASE + (4 * n))
|
||||
return self.read_reg(self.EFUSE_REG_BASE + (4 * n))
|
||||
|
||||
def chip_id(self):
|
||||
word16 = self.read_efuse(16)
|
||||
@ -687,7 +827,7 @@ class ESP32ROM(ESPLoader):
|
||||
class ESP32StubLoader(ESP32ROM):
|
||||
""" Access class for ESP32 stub loader, runs on top of ROM.
|
||||
"""
|
||||
FLASH_WRITE_SIZE = 8192 # matches MAX_WRITE_BLOCK in stub_loader.c
|
||||
FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c
|
||||
STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM
|
||||
IS_STUB = True
|
||||
|
||||
@ -739,6 +879,7 @@ class ImageSegment(object):
|
||||
data += "\x00" * (4 - pad_mod)
|
||||
self.data = data
|
||||
self.file_offs = file_offs
|
||||
self.include_in_checksum = True
|
||||
|
||||
def copy_with_new_addr(self, new_addr):
|
||||
""" Return a new ImageSegment with same data, but mapped at
|
||||
@ -782,9 +923,7 @@ class BaseFirmwareImage(object):
|
||||
""" Load the next segment from the image file """
|
||||
file_offs = f.tell()
|
||||
(offset, size) = struct.unpack('<II', f.read(8))
|
||||
if not is_irom_segment:
|
||||
if offset > 0x40200000 or offset < 0x3ffe0000 or size > 65536:
|
||||
print('WARNING: Suspicious segment 0x%x, length %d' % (offset, size))
|
||||
self.warn_if_unusual_segment(offset, size, is_irom_segment)
|
||||
segment_data = f.read(size)
|
||||
if len(segment_data) < size:
|
||||
raise FatalError('End of file reading segment 0x%x, length %d (actual length %d)' % (offset, size, len(segment_data)))
|
||||
@ -792,6 +931,11 @@ class BaseFirmwareImage(object):
|
||||
self.segments.append(segment)
|
||||
return segment
|
||||
|
||||
def warn_if_unusual_segment(self, offset, size, is_irom_segment):
|
||||
if not is_irom_segment:
|
||||
if offset > 0x40200000 or offset < 0x3ffe0000 or size > 65536:
|
||||
print('WARNING: Suspicious segment 0x%x, length %d' % (offset, size))
|
||||
|
||||
def save_segment(self, f, segment, checksum=None):
|
||||
""" Save the next segment to the image file, return next checksum value if provided """
|
||||
f.write(struct.pack('<II', segment.addr, len(segment.data)))
|
||||
@ -806,6 +950,16 @@ class BaseFirmwareImage(object):
|
||||
align_file_position(f, 16)
|
||||
return ord(f.read(1))
|
||||
|
||||
def calculate_checksum(self):
|
||||
""" Calculate checksum of loaded image, based on segments in
|
||||
segment array.
|
||||
"""
|
||||
checksum = ESPLoader.ESP_CHECKSUM_MAGIC
|
||||
for seg in self.segments:
|
||||
if seg.include_in_checksum:
|
||||
checksum = ESPLoader.checksum(seg.data, checksum)
|
||||
return checksum
|
||||
|
||||
def append_checksum(self, f, checksum):
|
||||
""" Append ESPLoader checksum to the just-written image """
|
||||
align_file_position(f, 16)
|
||||
@ -819,7 +973,7 @@ class BaseFirmwareImage(object):
|
||||
""" Returns True if an address starts in the irom region.
|
||||
Valid for ESP8266 only.
|
||||
"""
|
||||
return ESPLoader.IROM_MAP_START <= addr < ESPLoader.IROM_MAP_END
|
||||
return ESP8266ROM.IROM_MAP_START <= addr < ESP8266ROM.IROM_MAP_END
|
||||
|
||||
def get_irom_segment(self):
|
||||
irom_segments = [s for s in self.segments if self.is_irom_addr(s.addr)]
|
||||
@ -836,6 +990,9 @@ class BaseFirmwareImage(object):
|
||||
|
||||
class ESPFirmwareImage(BaseFirmwareImage):
|
||||
""" 'Version 1' firmware image, segments loaded directly by the ROM bootloader. """
|
||||
|
||||
ROM_LOADER = ESP8266ROM
|
||||
|
||||
def __init__(self, load_file=None):
|
||||
super(ESPFirmwareImage, self).__init__()
|
||||
self.flash_mode = 0
|
||||
@ -858,7 +1015,7 @@ class ESPFirmwareImage(BaseFirmwareImage):
|
||||
# IROM data goes in its own plain binary file
|
||||
irom_segment = self.get_irom_segment()
|
||||
if irom_segment is not None:
|
||||
with open("%s0x%05x.bin" % (basename, irom_segment.addr), "wb") as f:
|
||||
with open("%s0x%05x.bin" % (basename, irom_segment.addr - ESP8266ROM.IROM_MAP_START), "wb") as f:
|
||||
f.write(irom_segment.data)
|
||||
|
||||
# everything but IROM goes at 0x00000 in an image file
|
||||
@ -866,7 +1023,7 @@ class ESPFirmwareImage(BaseFirmwareImage):
|
||||
with open("%s0x00000.bin" % basename, 'wb') as f:
|
||||
self.write_common_header(f, normal_segments)
|
||||
checksum = ESPLoader.ESP_CHECKSUM_MAGIC
|
||||
for segment in self.segments:
|
||||
for segment in normal_segments:
|
||||
checksum = self.save_segment(f, segment, checksum)
|
||||
self.append_checksum(f, checksum)
|
||||
|
||||
@ -875,6 +1032,9 @@ class OTAFirmwareImage(BaseFirmwareImage):
|
||||
""" 'Version 2' firmware image, segments loaded by software bootloader stub
|
||||
(ie Espressif bootloader or rboot)
|
||||
"""
|
||||
|
||||
ROM_LOADER = ESP8266ROM
|
||||
|
||||
def __init__(self, load_file=None):
|
||||
super(OTAFirmwareImage, self).__init__()
|
||||
self.version = 2
|
||||
@ -888,16 +1048,17 @@ class OTAFirmwareImage(BaseFirmwareImage):
|
||||
#
|
||||
# the file is saved in the image with a zero load address
|
||||
# in the header, so we need to calculate a load address
|
||||
irom_offs = load_file.tell()
|
||||
irom_segment = self.load_segment(load_file, True)
|
||||
irom_segment.addr = irom_offs + self.IROM_MAP_START
|
||||
# for actual mapped addr, add ESP8266ROM.IROM_MAP_START + flashing_Addr + 8
|
||||
irom_segment.addr = 0
|
||||
irom_segment.include_in_checksum = False
|
||||
|
||||
first_flash_mode = self.flash_mode
|
||||
first_flash_size_freq = self.flash_size_freq
|
||||
first_entrypoint = self.entrypoint
|
||||
# load the second header
|
||||
self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC)
|
||||
(magic, segments, self.flash_mode, self.flash_size_freq, self.entrypoint) = struct.unpack('<BBBBI', load_file.read(8))
|
||||
|
||||
segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC)
|
||||
|
||||
if first_flash_mode != self.flash_mode:
|
||||
print('WARNING: Flash mode value in first header (0x%02x) disagrees with second (0x%02x). Using second value.'
|
||||
@ -918,7 +1079,7 @@ class OTAFirmwareImage(BaseFirmwareImage):
|
||||
""" Derive a default output name from the ELF name. """
|
||||
irom_segment = self.get_irom_segment()
|
||||
if irom_segment is not None:
|
||||
irom_offs = irom_segment.addr - self.IROM_MAP_START
|
||||
irom_offs = irom_segment.addr - ESP8266ROM.IROM_MAP_START
|
||||
else:
|
||||
irom_offs = 0
|
||||
return "%s-0x%05x.bin" % (os.path.splitext(input_file)[0],
|
||||
@ -951,6 +1112,9 @@ class ESP32FirmwareImage(BaseFirmwareImage):
|
||||
and because of new flash mapping capabilities the flash-mapped regions
|
||||
can be placed in the normal image (just @ 64kB padded offsets).
|
||||
"""
|
||||
|
||||
ROM_LOADER = ESP32ROM
|
||||
|
||||
def __init__(self, load_file=None):
|
||||
super(ESP32FirmwareImage, self).__init__()
|
||||
self.flash_mode = 0
|
||||
@ -974,6 +1138,9 @@ class ESP32FirmwareImage(BaseFirmwareImage):
|
||||
""" Derive a default output name from the ELF name. """
|
||||
return "%s.bin" % (os.path.splitext(input_file)[0])
|
||||
|
||||
def warn_if_unusual_segment(self, offset, size, is_irom_segment):
|
||||
pass # TODO: add warnings for ESP32 segment offset/size combinations that are wrong
|
||||
|
||||
def save(self, filename):
|
||||
padding_segments = 0
|
||||
with open(filename, 'wb') as f:
|
||||
@ -1268,24 +1435,25 @@ def write_flash(esp, args):
|
||||
for address, argfile in args.addr_filename:
|
||||
argfile.seek(0,2) # seek to end
|
||||
if address + argfile.tell() > flash_end:
|
||||
raise FatalError("File %s (length %d) at offset %d will not fit in %d bytes of flash." +
|
||||
"Use --flash-size argument, or change flashing address."
|
||||
raise FatalError(("File %s (length %d) at offset %d will not fit in %d bytes of flash. " +
|
||||
"Use --flash-size argument, or change flashing address.")
|
||||
% (argfile.name, argfile.tell(), address, flash_end))
|
||||
argfile.seek(0)
|
||||
|
||||
for address, argfile in args.addr_filename:
|
||||
print 'Erasing flash...'
|
||||
image = argfile.read()
|
||||
# Update header with flash parameters
|
||||
if address == 0 and image[0] == '\xe9':
|
||||
image = image[0:2] + flash_info + image[4:]
|
||||
calcmd5 = hashlib.md5(image).hexdigest()
|
||||
uncsize = len(image)
|
||||
if args.compress:
|
||||
uncimage = argfile.read()
|
||||
calcmd5 = hashlib.md5(uncimage).hexdigest()
|
||||
uncsize = len(uncimage)
|
||||
uncimage = image
|
||||
image = zlib.compress(uncimage, 9)
|
||||
blocks = div_roundup(len(image), esp.FLASH_WRITE_SIZE)
|
||||
esp.flash_defl_begin(len(uncimage),len(image), address)
|
||||
else:
|
||||
image = argfile.read()
|
||||
calcmd5 = hashlib.md5(image).hexdigest()
|
||||
uncsize = len(image)
|
||||
blocks = div_roundup(len(image), esp.FLASH_WRITE_SIZE)
|
||||
esp.flash_begin(blocks * esp.FLASH_WRITE_SIZE, address)
|
||||
argfile.seek(0) # in case we need it again
|
||||
@ -1302,16 +1470,20 @@ def write_flash(esp, args):
|
||||
else:
|
||||
# Pad the last block
|
||||
block = block + '\xff' * (esp.FLASH_WRITE_SIZE - len(block))
|
||||
# Fix sflash config data
|
||||
if address == 0 and seq == 0 and block[0] == '\xe9':
|
||||
block = block[0:2] + flash_info + block[4:]
|
||||
header_block = block
|
||||
esp.flash_block(block, seq)
|
||||
image = image[esp.FLASH_WRITE_SIZE:]
|
||||
seq += 1
|
||||
written += len(block)
|
||||
t = time.time() - t
|
||||
print '\rWrote %d bytes at 0x%08x in %.1f seconds (%.1f kbit/s)...' % (written, address, t, written / t * 8 / 1000)
|
||||
speed_msg = ""
|
||||
if args.compress:
|
||||
if t > 0.0:
|
||||
speed_msg = " (effective %.1f kbit/s)" % (uncsize / t * 8 / 1000)
|
||||
print '\rWrote %d bytes (%d compressed) at 0x%08x in %.1f seconds%s...' % (uncsize, written, address, t, speed_msg)
|
||||
else:
|
||||
if t > 0.0:
|
||||
speed_msg = " (%.1f kbit/s)" % (written / t * 8 / 1000)
|
||||
print '\rWrote %d bytes at 0x%08x in %.1f seconds%s...' % (written, address, t, speed_msg)
|
||||
res = esp.flash_md5sum(address, uncsize)
|
||||
if res != calcmd5:
|
||||
print 'File md5: %s' % calcmd5
|
||||
@ -1339,14 +1511,13 @@ def image_info(args):
|
||||
print('Entry point: %08x' % image.entrypoint) if image.entrypoint != 0 else 'Entry point not set'
|
||||
print '%d segments' % len(image.segments)
|
||||
print
|
||||
checksum = ESPLoader.ESP_CHECKSUM_MAGIC
|
||||
idx = 0
|
||||
for seg in image.segments:
|
||||
idx += 1
|
||||
print 'Segment %d: %r' % (idx, seg)
|
||||
checksum = ESPLoader.checksum(seg.data, checksum)
|
||||
print
|
||||
print 'Checksum: %02x (%s)' % (image.checksum, 'valid' if image.checksum == checksum else 'invalid!')
|
||||
calc_checksum = image.calculate_checksum()
|
||||
print 'Checksum: %02x (%s)' % (image.checksum,
|
||||
'valid' if image.checksum == calc_checksum else 'invalid - calculated %02x' % calc_checksum)
|
||||
|
||||
|
||||
def make_image(args):
|
||||
@ -1377,7 +1548,7 @@ def elf2image(args):
|
||||
image.entrypoint = e.entrypoint
|
||||
image.segments = e.sections # ELFSection is a subclass of ImageSegment
|
||||
image.flash_mode = {'qio':0, 'qout':1, 'dio':2, 'dout': 3}[args.flash_mode]
|
||||
image.flash_size_freq = ESP8266ROM.FLASH_SIZES[args.flash_size]
|
||||
image.flash_size_freq = image.ROM_LOADER.FLASH_SIZES[args.flash_size]
|
||||
image.flash_size_freq += {'40m':0, '26m':1, '20m':2, '80m': 0xf}[args.flash_freq]
|
||||
|
||||
if args.output is None:
|
||||
@ -1404,8 +1575,9 @@ def erase_flash(esp, args):
|
||||
|
||||
def erase_region(esp, args):
|
||||
print 'Erasing region (may be slow depending on size)...'
|
||||
t = time.time()
|
||||
esp.erase_region(args.address, args.size)
|
||||
print 'Erase completed successfully.'
|
||||
print 'Erase completed successfully in %.1f seconds.' % (time.time() - t)
|
||||
|
||||
|
||||
def run(esp, args):
|
||||
@ -1468,6 +1640,19 @@ def verify_flash(esp, args, flash_params=None):
|
||||
raise FatalError("Verify failed.")
|
||||
|
||||
|
||||
def read_flash_status(esp, args):
|
||||
print ('Status value: 0x%04x' % esp.read_status(args.bytes))
|
||||
|
||||
|
||||
def write_flash_status(esp, args):
|
||||
fmt = "0x%%0%dx" % (args.bytes * 2)
|
||||
args.value = args.value & ((1 << (args.bytes * 8)) - 1)
|
||||
print (('Initial flash status: ' + fmt) % esp.read_status(args.bytes))
|
||||
print (('Setting flash status: ' + fmt) % args.value)
|
||||
esp.write_status(args.value, args.bytes, args.non_volatile)
|
||||
print (('After flash status: ' + fmt) % esp.read_status(args.bytes))
|
||||
|
||||
|
||||
def version(args):
|
||||
print __version__
|
||||
|
||||
@ -1590,6 +1775,20 @@ def main():
|
||||
'flash_id',
|
||||
help='Read SPI flash manufacturer and device ID')
|
||||
|
||||
parser_read_status = subparsers.add_parser(
|
||||
'read_flash_status',
|
||||
help='Read SPI flash status register')
|
||||
|
||||
parser_read_status.add_argument('--bytes', help='Number of bytes to read (1-3)', type=int, choices=[1,2,3], default=2)
|
||||
|
||||
parser_write_status = subparsers.add_parser(
|
||||
'write_flash_status',
|
||||
help='Write SPI flash status register')
|
||||
|
||||
parser_write_status.add_argument('--non-volatile', help='Write non-volatile bits (use with caution)', action='store_true')
|
||||
parser_write_status.add_argument('--bytes', help='Number of status bytes to write (1-3)', type=int, choices=[1,2,3], default=2)
|
||||
parser_write_status.add_argument('value', help='New value', type=arg_auto_int)
|
||||
|
||||
parser_read_flash = subparsers.add_parser(
|
||||
'read_flash',
|
||||
help='Read SPI flash content')
|
||||
@ -1652,6 +1851,8 @@ def main():
|
||||
if hasattr(args, "ucIsHspi"):
|
||||
print "Attaching SPI flash..."
|
||||
esp.flash_spi_attach(args.ucIsHspi,args.ucIsLegacy)
|
||||
else:
|
||||
esp.flash_spi_attach(0, 0)
|
||||
if hasattr(args, "flash_size"):
|
||||
print "Configuring flash size..."
|
||||
esp.flash_set_parameters(flash_size_bytes(args.flash_size))
|
||||
@ -1718,94 +1919,96 @@ class AddrFilenamePairAction(argparse.Action):
|
||||
|
||||
# Binary stub code (see flasher_stub dir for source & details)
|
||||
ESP8266ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b"""
|
||||
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\
|
||||
""")))
|
||||
ESP32ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b"""
|
||||
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\
|
||||
""")))
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
Reference in New Issue
Block a user