mirror of
https://github.com/espressif/esptool.git
synced 2025-10-18 18:01:15 +08:00
fix: Do not use padding for merged IntelHex files
Split merged hex files into multiple temporary binary files for commands like `write_flash` and `image_info` and others that support IntelHex files. Splitting is done based on the gaps in the addresses. This should prevent overriding the flash region between the files. Closes https://github.com/espressif/esptool/issues/1075
This commit is contained in:

committed by
Radim Karniš

parent
98688abde1
commit
739669ffca
@@ -281,6 +281,14 @@ Intel Hex format offers distinct advantages when compared to the binary format,
|
||||
* **Size**: Data is carefully allocated to specific memory addresses eliminating the need for unnecessary padding. Binary images often lack detailed addressing information, leading to the inclusion of data for all memory locations from the file's initial address to its end.
|
||||
* **Validity Checks**: Each line in an Intel Hex file has a checksum to help find errors and make sure data stays unchanged.
|
||||
|
||||
When using a merged Intel Hex file with the ``write_flash`` or ``image_info`` commands, the file is automatically split into temporary raw binary files at the gaps between input files.
|
||||
|
||||
.. note::
|
||||
|
||||
The ``image_info`` command will show the information for the first file in a merged Intel Hex file.
|
||||
|
||||
The splitting behavior of Intel Hex files offers an advantage during flashing: since no padding is used between sections, flash sectors between input files remain unerased. This can significantly improve flashing speed compared to using a merged raw binary file.
|
||||
|
||||
.. code:: sh
|
||||
|
||||
esptool.py --chip {IDF_TARGET_NAME} merge_bin --format hex -o merged-flash.hex --flash_mode dio --flash_size 4MB 0x1000 bootloader.bin 0x8000 partition-table.bin 0x10000 app.bin
|
||||
|
@@ -1291,7 +1291,12 @@ class AutoHex2BinAction(argparse.Action):
|
||||
with open(value, "rb") as f:
|
||||
# if hex file was detected replace hex file with converted temp bin
|
||||
# otherwise keep the original file
|
||||
value = intel_hex_to_bin(f).name
|
||||
converted = intel_hex_to_bin(f)
|
||||
if len(converted) != 1:
|
||||
print(
|
||||
"Note: Detected merged IntelHex file, processing only first file"
|
||||
)
|
||||
value = converted[0][1].name
|
||||
except IOError as e:
|
||||
raise argparse.ArgumentError(self, e)
|
||||
setattr(namespace, self.dest, value)
|
||||
@@ -1326,8 +1331,7 @@ class AddrFilenamePairAction(argparse.Action):
|
||||
"and the binary filename to write there",
|
||||
)
|
||||
# check for intel hex files and convert them to bin
|
||||
argfile = intel_hex_to_bin(argfile, address)
|
||||
pairs.append((address, argfile))
|
||||
pairs.extend(intel_hex_to_bin(argfile, address))
|
||||
|
||||
# Sort the addresses and check for overlapping
|
||||
end = 0
|
||||
|
@@ -11,7 +11,7 @@ import os
|
||||
import re
|
||||
import struct
|
||||
import tempfile
|
||||
from typing import IO, Optional, Tuple
|
||||
from typing import IO, List, Optional, Tuple
|
||||
|
||||
from intelhex import HexRecordError, IntelHex
|
||||
|
||||
@@ -45,9 +45,46 @@ def align_file_position(f, size):
|
||||
f.seek(align, 1)
|
||||
|
||||
|
||||
def intel_hex_to_bin(file: IO[bytes], start_addr: Optional[int] = None) -> IO[bytes]:
|
||||
"""Convert IntelHex file to temp binary file with padding from start_addr
|
||||
If hex file was detected return temp bin file object; input file otherwise"""
|
||||
def _find_subsequences(addresses: List[int]) -> List[Tuple[int, int]]:
|
||||
"""Find continuous subsequences in a list of addresses"""
|
||||
if not addresses:
|
||||
return []
|
||||
|
||||
sorted_seq = sorted(addresses)
|
||||
|
||||
subsequences = []
|
||||
start = sorted_seq[0]
|
||||
|
||||
for prev, num in zip(sorted_seq, sorted_seq[1:]):
|
||||
if num != prev + 1:
|
||||
# Found a gap, save the current subsequence
|
||||
subsequences.append((start, prev))
|
||||
start = num
|
||||
|
||||
# Add the last subsequence
|
||||
subsequences.append((start, sorted_seq[-1]))
|
||||
|
||||
return subsequences
|
||||
|
||||
|
||||
def _split_intel_hex_file(ih: IntelHex) -> List[Tuple[int, IO[bytes]]]:
|
||||
"""Split an IntelHex file into multiple temporary binary files based on the gaps
|
||||
in the addresses"""
|
||||
subsequences = _find_subsequences(ih.addresses())
|
||||
bins: List[Tuple[int, IO[bytes]]] = []
|
||||
for start, end in subsequences:
|
||||
bin = tempfile.NamedTemporaryFile(suffix=".bin", delete=False)
|
||||
ih.tobinfile(bin, start=start, end=end)
|
||||
bin.seek(0) # make sure the file is at the beginning
|
||||
bins.append((start, bin))
|
||||
return bins
|
||||
|
||||
|
||||
def intel_hex_to_bin(
|
||||
file: IO[bytes], start_addr: Optional[int] = None
|
||||
) -> List[Tuple[Optional[int], IO[bytes]]]:
|
||||
"""Convert IntelHex file to list of temp binary files
|
||||
If not hex file return input file otherwise"""
|
||||
INTEL_HEX_MAGIC = b":"
|
||||
magic = file.read(1)
|
||||
file.seek(0)
|
||||
@@ -56,14 +93,12 @@ def intel_hex_to_bin(file: IO[bytes], start_addr: Optional[int] = None) -> IO[by
|
||||
ih = IntelHex()
|
||||
ih.loadhex(file.name)
|
||||
file.close()
|
||||
bin = tempfile.NamedTemporaryFile(suffix=".bin", delete=False)
|
||||
ih.tobinfile(bin, start=start_addr)
|
||||
return bin
|
||||
return _split_intel_hex_file(ih) # type: ignore
|
||||
else:
|
||||
return file
|
||||
return [(start_addr, file)]
|
||||
except (HexRecordError, UnicodeDecodeError):
|
||||
# file started with HEX magic but the rest was not according to the standard
|
||||
return file
|
||||
return [(start_addr, file)]
|
||||
|
||||
|
||||
def LoadFirmwareImage(chip, image_file):
|
||||
|
@@ -97,7 +97,7 @@ class TestImageInfo:
|
||||
), "Wrong flash pins drive settings"
|
||||
|
||||
assert "Minimal chip revision: v0.0" in out, "Wrong min revision"
|
||||
assert "Maximal chip revision: v0.0" in out, "Wrong min revision"
|
||||
assert "Maximal chip revision: v0.0" in out, "Wrong max revision"
|
||||
|
||||
# Segments
|
||||
assert (
|
||||
@@ -194,9 +194,9 @@ class TestImageInfo:
|
||||
assert "Compile time: Apr 25 2023 00:13:32" in out
|
||||
|
||||
def test_intel_hex(self):
|
||||
# This bootloader binary is built from "hello_world" project
|
||||
# with default settings, IDF version is v5.2.
|
||||
# File is converted to Intel Hex using merge_bin
|
||||
# Convert and merge two files to Intel Hex using merge_bin
|
||||
# Run image_info on the resulting Intel Hex file
|
||||
# Verify that image info is shown for the first file
|
||||
|
||||
def convert_bin2hex(file):
|
||||
subprocess.check_output(
|
||||
@@ -205,12 +205,14 @@ class TestImageInfo:
|
||||
"-m",
|
||||
"esptool",
|
||||
"--chip",
|
||||
"esp32",
|
||||
"esp32c3",
|
||||
"merge_bin",
|
||||
"--format",
|
||||
"hex",
|
||||
"0x0",
|
||||
"".join([IMAGES_DIR, os.sep, "bootloader_esp32_v5_2.bin"]),
|
||||
os.path.join(IMAGES_DIR, "bootloader_esp32c3.bin"),
|
||||
"0x8000",
|
||||
os.path.join(IMAGES_DIR, "esp32c3_header_min_rev.bin"),
|
||||
"-o",
|
||||
file,
|
||||
]
|
||||
@@ -219,12 +221,29 @@ class TestImageInfo:
|
||||
fd, file = tempfile.mkstemp(suffix=".hex")
|
||||
try:
|
||||
convert_bin2hex(file)
|
||||
out = self.run_image_info("esp32", file, "2")
|
||||
assert "File size: 26768 (bytes)" in out
|
||||
assert "Bootloader information" in out
|
||||
assert "Bootloader version: 1" in out
|
||||
assert "ESP-IDF: v5.2-dev-254-g1950b15" in out
|
||||
assert "Compile time: Apr 25 2023 00:13:32" in out
|
||||
out = self.run_image_info("esp32c3", file, "2")
|
||||
|
||||
# Only the first files should be shown with Note
|
||||
assert (
|
||||
"Note: Detected merged IntelHex file, processing only first file" in out
|
||||
)
|
||||
# Header
|
||||
assert "Entry point: 0x403c0000" in out, "Wrong entry point"
|
||||
assert "Segments: 4" in out, "Wrong num of segments"
|
||||
assert "Flash size: 2MB" in out, "Wrong flash size"
|
||||
assert "Flash freq: 40m" in out, "Wrong flash frequency"
|
||||
assert "Flash mode: DIO" in out, "Wrong flash mode"
|
||||
|
||||
# Extended header
|
||||
assert "WP pin: 0xee (disabled)" in out, "Wrong WP pin"
|
||||
assert "Chip ID: 5 (ESP32-C3)" in out, "Wrong chip ID"
|
||||
assert (
|
||||
"clk_drv: 0x0, q_drv: 0x0, d_drv: 0x0, "
|
||||
"cs0_drv: 0x0, hd_drv: 0x0, wp_drv: 0x0" in out
|
||||
), "Wrong flash pins drive settings"
|
||||
|
||||
assert "Minimal chip revision: v0.0" in out, "Wrong min revision"
|
||||
assert "Maximal chip revision: v0.0" in out, "Wrong max revision"
|
||||
finally:
|
||||
try:
|
||||
# make sure that file was closed before removing it
|
||||
|
Reference in New Issue
Block a user