mirror of
https://github.com/espressif/esptool.git
synced 2025-10-16 23:06:31 +08:00
feat: Add support for k, M suffix for flash size
This commit is contained in:
@@ -41,7 +41,8 @@ The ``dump-mem`` command will dump a region from the chip's memory space to a fi
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
esptool.py dump-mem 0x40000000 65536 iram0.bin
|
esptool.py dump-mem 0x40000000 64k iram0.bin
|
||||||
|
|
||||||
|
|
||||||
.. _load-ram:
|
.. _load-ram:
|
||||||
|
|
||||||
|
@@ -108,6 +108,8 @@ The read-flash command allows reading back the contents of flash. The arguments
|
|||||||
esptool.py -p PORT -b 460800 read-flash 0 0x200000 flash_contents.bin
|
esptool.py -p PORT -b 460800 read-flash 0 0x200000 flash_contents.bin
|
||||||
|
|
||||||
|
|
||||||
|
Size can be specified in bytes, or with suffixes like ``k`` and ``M``. So ``0x200000`` in example can be replaced with ``2M``.
|
||||||
|
|
||||||
It is also possible to autodetect flash size by using ``ALL`` as size. The above example with autodetection would look like this:
|
It is also possible to autodetect flash size by using ``ALL`` as size. The above example with autodetection would look like this:
|
||||||
|
|
||||||
::
|
::
|
||||||
@@ -135,11 +137,11 @@ To erase the entire flash chip (all data replaced with 0xFF bytes):
|
|||||||
|
|
||||||
esptool.py erase-flash
|
esptool.py erase-flash
|
||||||
|
|
||||||
To erase a region of the flash, starting at address 0x20000 with length 0x4000 bytes (16KB):
|
To erase a region of the flash, starting at address 0x20000 with length 16 kB (0x4000 bytes):
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
esptool.py erase-region 0x20000 0x4000
|
esptool.py erase-region 0x20000 16k
|
||||||
|
|
||||||
The address and length must both be multiples of the SPI flash erase sector size. This is 0x1000 (4096) bytes for supported flash chips.
|
The address and length must both be multiples of the SPI flash erase sector size. This is 0x1000 (4096) bytes for supported flash chips.
|
||||||
|
|
||||||
|
@@ -44,6 +44,7 @@ import typing as t
|
|||||||
from esptool.cmds import (
|
from esptool.cmds import (
|
||||||
chip_id,
|
chip_id,
|
||||||
detect_chip,
|
detect_chip,
|
||||||
|
detect_flash_size,
|
||||||
dump_mem,
|
dump_mem,
|
||||||
elf2image,
|
elf2image,
|
||||||
erase_flash,
|
erase_flash,
|
||||||
@@ -81,6 +82,7 @@ from esptool.targets import CHIP_DEFS, CHIP_LIST, ESP32ROM
|
|||||||
from esptool.util import (
|
from esptool.util import (
|
||||||
FatalError,
|
FatalError,
|
||||||
NotImplementedInROMError,
|
NotImplementedInROMError,
|
||||||
|
flash_size_bytes,
|
||||||
)
|
)
|
||||||
from itertools import chain, cycle, repeat
|
from itertools import chain, cycle, repeat
|
||||||
|
|
||||||
@@ -271,6 +273,18 @@ def check_flash_size(esp: ESPLoader, address: int, size: int) -> None:
|
|||||||
f"(set size {size:#x} from address {address:#010x} goes past 16MB "
|
f"(set size {size:#x} from address {address:#010x} goes past 16MB "
|
||||||
f"by {address + size - 0x1000000:#x} bytes)."
|
f"by {address + size - 0x1000000:#x} bytes)."
|
||||||
)
|
)
|
||||||
|
# Check if we are writing/reading past detected flash size
|
||||||
|
if not esp.secure_download_mode:
|
||||||
|
detected_size_str = detect_flash_size(esp)
|
||||||
|
if not detected_size_str:
|
||||||
|
return
|
||||||
|
detected_size = flash_size_bytes(detected_size_str)
|
||||||
|
if address + size > detected_size:
|
||||||
|
raise FatalError(
|
||||||
|
f"Can't access flash regions larger than detected flash size "
|
||||||
|
f"(set size {size:#x} from address {address:#010x} goes past "
|
||||||
|
f"{detected_size_str} by {address + size - detected_size:#x} bytes)."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
############################### GLOBAL OPTIONS AND MAIN ###############################
|
############################### GLOBAL OPTIONS AND MAIN ###############################
|
||||||
@@ -544,7 +558,7 @@ def load_ram_cli(ctx, filename):
|
|||||||
|
|
||||||
@cli.command("dump-mem")
|
@cli.command("dump-mem")
|
||||||
@click.argument("address", type=AnyIntType())
|
@click.argument("address", type=AnyIntType())
|
||||||
@click.argument("size", type=AnyIntType())
|
@click.argument("size", type=AutoSizeType(allow_all=False))
|
||||||
@click.argument("output", type=click.Path())
|
@click.argument("output", type=click.Path())
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def dump_mem_cli(ctx, address, size, output):
|
def dump_mem_cli(ctx, address, size, output):
|
||||||
|
@@ -58,13 +58,29 @@ class AnyIntType(click.ParamType):
|
|||||||
|
|
||||||
|
|
||||||
class AutoSizeType(AnyIntType):
|
class AutoSizeType(AnyIntType):
|
||||||
"""Similar to AnyIntType but allows 'all' as a value to e.g. read whole flash"""
|
"""Similar to AnyIntType but allows 'k', 'M' suffixes for kilo(1024), Mega(1024^2)
|
||||||
|
and 'all' as a value to e.g. read whole flash"""
|
||||||
|
|
||||||
|
def __init__(self, allow_all: bool = True):
|
||||||
|
self.allow_all = allow_all
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
def convert(
|
def convert(
|
||||||
self, value: str, param: click.Parameter | None, ctx: click.Context
|
self, value: str, param: click.Parameter | None, ctx: click.Context
|
||||||
) -> Any:
|
) -> Any:
|
||||||
if value.lower() == "all":
|
if self.allow_all and value.lower() == "all":
|
||||||
return value
|
return value
|
||||||
|
# Handle suffixes like 'k', 'M' for kilo, mega
|
||||||
|
if value[-1] in ("k", "M"):
|
||||||
|
try:
|
||||||
|
num = arg_auto_int(value[:-1])
|
||||||
|
except ValueError:
|
||||||
|
raise click.BadParameter(f"{value!r} is not a valid integer")
|
||||||
|
if value[-1] == "k":
|
||||||
|
num *= 1024
|
||||||
|
elif value[-1] == "M":
|
||||||
|
num *= 1024 * 1024
|
||||||
|
return num
|
||||||
return super().convert(value, param, ctx)
|
return super().convert(value, param, ctx)
|
||||||
|
|
||||||
|
|
||||||
@@ -388,6 +404,13 @@ def parse_port_filters(
|
|||||||
def parse_size_arg(esp: ESPLoader, size: int | str) -> int:
|
def parse_size_arg(esp: ESPLoader, size: int | str) -> int:
|
||||||
"""Parse the flash size argument and return the size in bytes"""
|
"""Parse the flash size argument and return the size in bytes"""
|
||||||
if isinstance(size, int):
|
if isinstance(size, int):
|
||||||
|
if not esp.secure_download_mode:
|
||||||
|
detected_size = flash_size_bytes(detect_flash_size(esp))
|
||||||
|
if detected_size and size > detected_size:
|
||||||
|
raise FatalError(
|
||||||
|
f"Specified size {size:#x} is greater than detected flash size "
|
||||||
|
f"{detected_size:#x}.",
|
||||||
|
)
|
||||||
return size
|
return size
|
||||||
if size.lower() != "all":
|
if size.lower() != "all":
|
||||||
raise FatalError(f"Invalid size value: {size}. Use an integer or 'all'.")
|
raise FatalError(f"Invalid size value: {size}. Use an integer or 'all'.")
|
||||||
|
@@ -439,6 +439,7 @@ class TestFlashing(EsptoolTestCase):
|
|||||||
self.run_esptool("write-flash 0x0 images/one_kb.bin")
|
self.run_esptool("write-flash 0x0 images/one_kb.bin")
|
||||||
self.verify_readback(0, 1024, "images/one_kb.bin")
|
self.verify_readback(0, 1024, "images/one_kb.bin")
|
||||||
|
|
||||||
|
@pytest.mark.skipif(arg_chip != "esp32", reason="Don't need to test multiple times")
|
||||||
def test_short_flash_deprecated(self):
|
def test_short_flash_deprecated(self):
|
||||||
out = self.run_esptool(
|
out = self.run_esptool(
|
||||||
"--before default_reset write_flash 0x0 images/one_kb.bin --flash_size keep"
|
"--before default_reset write_flash 0x0 images/one_kb.bin --flash_size keep"
|
||||||
@@ -877,6 +878,13 @@ class TestFlashSizes(EsptoolTestCase):
|
|||||||
assert "File 'images/one_kb.bin'" in output
|
assert "File 'images/one_kb.bin'" in output
|
||||||
assert "will not fit" in output
|
assert "will not fit" in output
|
||||||
|
|
||||||
|
@pytest.mark.skipif(arg_chip != "esp32", reason="Don't need to test multiple times")
|
||||||
|
def test_read_past_end_fails(self):
|
||||||
|
output = self.run_esptool_error(
|
||||||
|
"read-flash 0xffffff 1 out.bin"
|
||||||
|
) # 0xffffff is well past the end of the flash in most cases (16MB)
|
||||||
|
assert "Can't access flash regions larger than detected flash size" in output
|
||||||
|
|
||||||
def test_write_no_compression_past_end_fails(self):
|
def test_write_no_compression_past_end_fails(self):
|
||||||
output = self.run_esptool_error(
|
output = self.run_esptool_error(
|
||||||
"write-flash -u -fs 1MB 0x280000 images/one_kb.bin"
|
"write-flash -u -fs 1MB 0x280000 images/one_kb.bin"
|
||||||
@@ -913,7 +921,7 @@ class TestFlashSizes(EsptoolTestCase):
|
|||||||
# readback with no-stub and flash-size set
|
# readback with no-stub and flash-size set
|
||||||
try:
|
try:
|
||||||
self.run_esptool(
|
self.run_esptool(
|
||||||
f"--no-stub read-flash -fs detect {offset} 1024 {dump_file.name}"
|
f"--no-stub read-flash -fs detect {offset} 1k {dump_file.name}"
|
||||||
)
|
)
|
||||||
with open(dump_file.name, "rb") as f:
|
with open(dump_file.name, "rb") as f:
|
||||||
rb = f.read()
|
rb = f.read()
|
||||||
|
Reference in New Issue
Block a user