Skip to content

Commit 19967b5

Browse files
Dzarda7radimkarnis
authored andcommitted
change: make command independent on status byte length
This commit adds passing of expected response data length to check_command, which all command independent on status byte length. This enables the --after no-reset-no-sync option for all chips when stub is running and unifies read_reg verification.
1 parent 0b3460f commit 19967b5

File tree

4 files changed

+65
-52
lines changed

4 files changed

+65
-52
lines changed

esptool/loader.py

Lines changed: 63 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -291,9 +291,6 @@ class ESPLoader(object):
291291
IROM_MAP_START = 0x40200000
292292
IROM_MAP_END = 0x40300000
293293

294-
# The number of bytes in the UART response that signify command status
295-
STATUS_BYTES_LENGTH = 4
296-
297294
# Bootloader flashing offset
298295
BOOTLOADER_FLASH_OFFSET = 0x0
299296

@@ -518,36 +515,54 @@ def command(
518515
raise FatalError("Response doesn't match request.")
519516

520517
def check_command(
521-
self, op_description, op=None, data=b"", chk=0, timeout=DEFAULT_TIMEOUT
518+
self,
519+
op_description,
520+
op=None,
521+
data=b"",
522+
chk=0,
523+
resp_data_len=0,
524+
timeout=DEFAULT_TIMEOUT,
522525
):
523526
"""
524527
Execute a command with 'command', check the result code and throw an appropriate
525528
FatalError if it fails.
526529
527530
Returns the "result" of a successful command.
528531
"""
529-
val, data = self.command(op, data, chk, timeout=timeout)
530532

531-
# things are a bit weird here, bear with us
533+
# The status bytes are first two bytes after the expected response data,
534+
# ROM bootloaders of the chips (except ESP8266) return another 2 bytes,
535+
# which are reserved for future use, these are ignored here.
536+
STATUS_BYTES_LENGTH = 2
532537

533-
# the status bytes are the last 2/4 bytes in the data (depending on chip)
534-
if len(data) < self.STATUS_BYTES_LENGTH:
535-
raise FatalError(
536-
f"Failed to {op_description}. "
537-
f"Only got {len(data)} byte status response."
538-
)
539-
status_bytes = data[-self.STATUS_BYTES_LENGTH :]
540-
# only care if the first one is non-zero. If it is, the second byte is a reason.
541-
if byte(status_bytes, 0) != 0:
538+
# Execute the command and get the result
539+
val, data = self.command(op, data, chk, timeout=timeout)
540+
541+
# Check if we have enough data,
542+
# including the expected response data and status bytes
543+
if len(data) < resp_data_len + STATUS_BYTES_LENGTH:
544+
# If we don't have enough data, check the first 2 bytes as status
545+
status_bytes = data[0:2]
546+
# Only care if the first byte is non-zero.
547+
# If it is, the second byte is a reason.
548+
if status_bytes[0] != 0:
549+
raise FatalError.WithResult(f"Failed to {op_description}", status_bytes)
550+
else:
551+
raise FatalError(
552+
f"Failed to {op_description}. "
553+
f"Only got {len(data)} byte status response."
554+
)
555+
# The status bytes are positioned after the expected response data
556+
# (first two bytes after resp_data_len are the status bytes)
557+
status_bytes = data[resp_data_len : resp_data_len + STATUS_BYTES_LENGTH]
558+
# Only care if the first byte is non-zero.
559+
# If it is, the second byte is a reason.
560+
if status_bytes[0] != 0:
542561
raise FatalError.WithResult(f"Failed to {op_description}", status_bytes)
543562

544-
# if we had more data than just the status bytes, return it as the result
545-
# (this is used by the md5sum command, maybe other commands?)
546-
if len(data) > self.STATUS_BYTES_LENGTH:
547-
return data[: -self.STATUS_BYTES_LENGTH]
563+
if resp_data_len > 0:
564+
return data[:resp_data_len]
548565
else:
549-
# otherwise, just return the 'val' field which comes from the reply header
550-
# (this is used by read_reg)
551566
return val
552567

553568
def flush_input(self):
@@ -856,17 +871,10 @@ def _post_connect(self):
856871

857872
def read_reg(self, addr, timeout=DEFAULT_TIMEOUT):
858873
"""Read memory address in target"""
859-
# we don't call check_command here because read_reg() function is called
860-
# when detecting chip type, and the way we check for success
861-
# (STATUS_BYTES_LENGTH) is different for different chip types (!)
862-
val, data = self.command(
863-
self.ESP_CMDS["READ_REG"], struct.pack("<I", addr), timeout=timeout
874+
command = struct.pack("<I", addr)
875+
return self.check_command(
876+
"read target memory", self.ESP_CMDS["READ_REG"], command, timeout=timeout
864877
)
865-
if byte(data, 0) != 0:
866-
raise FatalError.WithResult(
867-
"Failed to read register address %08x" % addr, data
868-
)
869-
return val
870878

871879
def write_reg(self, addr, value, mask=0xFFFFFFFF, delay_us=0, delay_after_us=0):
872880
"""Write to memory address in target"""
@@ -1099,11 +1107,25 @@ def parse_security_flags(flags_value):
10991107
parsed_flags[flag_name] = (flags_value & flag_mask) != 0
11001108
return parsed_flags
11011109

1102-
res = self.check_command(
1103-
"get security info", self.ESP_CMDS["GET_SECURITY_INFO"], b""
1104-
)
1105-
esp32s2 = True if len(res) == 12 else False
1106-
res = struct.unpack("<IBBBBBBBB" if esp32s2 else "<IBBBBBBBBII", res)
1110+
# Try with 20 bytes first (most chips), fall back to 12 bytes (ESP32-S2)
1111+
try:
1112+
res = self.check_command(
1113+
"get security info",
1114+
self.ESP_CMDS["GET_SECURITY_INFO"],
1115+
b"",
1116+
resp_data_len=20,
1117+
)
1118+
res = struct.unpack("<IBBBBBBBBII", res)
1119+
esp32s2 = False
1120+
except FatalError:
1121+
res = self.check_command(
1122+
"get security info",
1123+
self.ESP_CMDS["GET_SECURITY_INFO"],
1124+
b"",
1125+
resp_data_len=12,
1126+
)
1127+
res = struct.unpack("<IBBBBBBBB", res)
1128+
esp32s2 = True
11071129

11081130
security_info = {
11091131
"flags": res[0],
@@ -1347,20 +1369,21 @@ def flash_defl_finish(self, reboot=False):
13471369
def flash_md5sum(self, addr, size):
13481370
# the MD5 command returns additional bytes in the standard
13491371
# command reply slot
1372+
RESP_DATA_LEN = 32
1373+
RESP_DATA_LEN_STUB = 16
13501374
timeout = timeout_per_mb(MD5_TIMEOUT_PER_MB, size)
13511375
res = self.check_command(
13521376
"calculate md5sum",
13531377
self.ESP_CMDS["SPI_FLASH_MD5"],
13541378
struct.pack("<IIII", addr, size, 0, 0),
1379+
resp_data_len=RESP_DATA_LEN_STUB if self.IS_STUB else RESP_DATA_LEN,
13551380
timeout=timeout,
13561381
)
13571382

1358-
if len(res) == 32:
1383+
if not self.IS_STUB:
13591384
return res.decode("utf-8") # already hex formatted
1360-
elif len(res) == 16:
1361-
return hexify(res).lower()
13621385
else:
1363-
raise FatalError("MD5Sum command returned unexpected result: %r" % res)
1386+
return hexify(res).lower()
13641387

13651388
@stub_and_esp32_function_only
13661389
def change_baud(self, baud):
@@ -1756,7 +1779,6 @@ class StubMixin:
17561779
"""
17571780

17581781
FLASH_WRITE_SIZE = 0x4000 # Default value, can be overridden
1759-
STATUS_BYTES_LENGTH = 2
17601782
IS_STUB = True
17611783

17621784
def __init__(self, rom_loader):

esptool/targets/esp32.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@ class ESP32ROM(ESPLoader):
2525
DROM_MAP_START = 0x3F400000
2626
DROM_MAP_END = 0x3F800000
2727

28-
# ESP32 uses a 4 byte status reply
29-
STATUS_BYTES_LENGTH = 4
30-
3128
SPI_REG_BASE = 0x3FF42000
3229
SPI_USR_OFFS = 0x1C
3330
SPI_USR1_OFFS = 0x20
@@ -393,6 +390,7 @@ def read_flash_slow(self, offset, length, progress_fn):
393390
"read flash block",
394391
self.ESP_CMDS["READ_FLASH_SLOW"],
395392
struct.pack("<II", offset + len(data), block_len),
393+
resp_data_len=BLOCK_LEN,
396394
)
397395
except FatalError:
398396
log.note("Consider specifying the flash size argument.")

esptool/targets/esp8266.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ class ESP8266ROM(ESPLoader):
2929

3030
UART_CLKDIV_REG = 0x60000014
3131

32-
STATUS_BYTES_LENGTH = 2
33-
3432
XTAL_CLK_DIVIDER = 2
3533

3634
FLASH_SIZES = {

test/test_esptool.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,19 +1075,14 @@ def test_stub_reuse_with_synchronization(self):
10751075
) # do sync before (without reset it talks to the flasher stub)
10761076
assert "Manufacturer:" in res
10771077

1078-
@pytest.mark.skipif(arg_chip != "esp8266", reason="ESP8266 only")
10791078
def test_stub_reuse_without_synchronization(self):
10801079
"""
10811080
Keep the flasher stub running and reuse it the next time
10821081
without synchronization.
1083-
1084-
Synchronization is necessary for chips where the ROM bootloader has different
1085-
status length in comparison to the flasher stub.
1086-
Therefore, this is ESP8266 only test.
10871082
"""
10881083
res = self.run_esptool("--after no-reset-stub flash-id")
10891084
assert "Manufacturer:" in res
1090-
res = self.run_esptool("--before no-reset-no-sync flash-id")
1085+
res = self.run_esptool("--before no-reset-no-sync flash-id", preload=False)
10911086
assert "Manufacturer:" in res
10921087

10931088

0 commit comments

Comments
 (0)