Skip to content

Commit 6f0d779

Browse files
committed
feat: Add support for k, M suffix for flash size
1 parent f26a7bb commit 6f0d779

File tree

5 files changed

+55
-7
lines changed

5 files changed

+55
-7
lines changed

docs/en/esptool/advanced-commands.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ The ``dump-mem`` command will dump a region from the chip's memory space to a fi
4141

4242
::
4343

44-
esptool.py dump-mem 0x40000000 65536 iram0.bin
44+
esptool.py dump-mem 0x40000000 64k iram0.bin
45+
4546

4647
.. _load-ram:
4748

docs/en/esptool/basic-commands.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ The read-flash command allows reading back the contents of flash. The arguments
108108
esptool.py -p PORT -b 460800 read-flash 0 0x200000 flash_contents.bin
109109

110110

111+
Size can be specified in bytes, or with suffixes like ``k`` and ``M``. So ``0x200000`` in example can be replaced with ``2M``.
112+
111113
It is also possible to autodetect flash size by using ``ALL`` as size. The above example with autodetection would look like this:
112114

113115
::
@@ -135,11 +137,11 @@ To erase the entire flash chip (all data replaced with 0xFF bytes):
135137

136138
esptool.py erase-flash
137139

138-
To erase a region of the flash, starting at address 0x20000 with length 0x4000 bytes (16KB):
140+
To erase a region of the flash, starting at address 0x20000 with length 16 kB (0x4000 bytes):
139141

140142
::
141143

142-
esptool.py erase-region 0x20000 0x4000
144+
esptool.py erase-region 0x20000 16k
143145

144146
The address and length must both be multiples of the SPI flash erase sector size. This is 0x1000 (4096) bytes for supported flash chips.
145147

esptool/__init__.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
from esptool.cmds import (
4545
chip_id,
4646
detect_chip,
47+
detect_flash_size,
4748
dump_mem,
4849
elf2image,
4950
erase_flash,
@@ -81,6 +82,7 @@
8182
from esptool.util import (
8283
FatalError,
8384
NotImplementedInROMError,
85+
flash_size_bytes,
8486
)
8587
from itertools import chain, cycle, repeat
8688

@@ -271,6 +273,18 @@ def check_flash_size(esp: ESPLoader, address: int, size: int) -> None:
271273
f"(set size {size:#x} from address {address:#010x} goes past 16MB "
272274
f"by {address + size - 0x1000000:#x} bytes)."
273275
)
276+
# Check if we are writing/reading past detected flash size
277+
if not esp.secure_download_mode:
278+
detected_size_str = detect_flash_size(esp)
279+
if not detected_size_str:
280+
return
281+
detected_size = flash_size_bytes(detected_size_str)
282+
if address + size > detected_size:
283+
raise FatalError(
284+
f"Can't access flash regions larger than detected flash size "
285+
f"(set size {size:#x} from address {address:#010x} goes past "
286+
f"{detected_size_str} by {address + size - detected_size:#x} bytes)."
287+
)
274288

275289

276290
############################### GLOBAL OPTIONS AND MAIN ###############################
@@ -544,7 +558,7 @@ def load_ram_cli(ctx, filename):
544558

545559
@cli.command("dump-mem")
546560
@click.argument("address", type=AnyIntType())
547-
@click.argument("size", type=AnyIntType())
561+
@click.argument("size", type=AutoSizeType(allow_all=False))
548562
@click.argument("output", type=click.Path())
549563
@click.pass_context
550564
def dump_mem_cli(ctx, address, size, output):

esptool/cli_util.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,29 @@ def convert(
5858

5959

6060
class AutoSizeType(AnyIntType):
61-
"""Similar to AnyIntType but allows 'all' as a value to e.g. read whole flash"""
61+
"""Similar to AnyIntType but allows 'k', 'M' suffixes for kilo(1024), Mega(1024^2)
62+
and 'all' as a value to e.g. read whole flash"""
63+
64+
def __init__(self, allow_all: bool = True):
65+
self.allow_all = allow_all
66+
super().__init__()
6267

6368
def convert(
6469
self, value: str, param: click.Parameter | None, ctx: click.Context
6570
) -> Any:
66-
if value.lower() == "all":
71+
if self.allow_all and value.lower() == "all":
6772
return value
73+
# Handle suffixes like 'k', 'M' for kilo, mega
74+
if value[-1] in ("k", "M"):
75+
try:
76+
num = arg_auto_int(value[:-1])
77+
except ValueError:
78+
raise click.BadParameter(f"{value!r} is not a valid integer")
79+
if value[-1] == "k":
80+
num *= 1024
81+
elif value[-1] == "M":
82+
num *= 1024 * 1024
83+
return num
6884
return super().convert(value, param, ctx)
6985

7086

@@ -388,6 +404,13 @@ def parse_port_filters(
388404
def parse_size_arg(esp: ESPLoader, size: int | str) -> int:
389405
"""Parse the flash size argument and return the size in bytes"""
390406
if isinstance(size, int):
407+
if not esp.secure_download_mode:
408+
detected_size = flash_size_bytes(detect_flash_size(esp))
409+
if detected_size and size > detected_size:
410+
raise FatalError(
411+
f"Specified size {size:#x} is greater than detected flash size "
412+
f"{detected_size:#x}.",
413+
)
391414
return size
392415
if size.lower() != "all":
393416
raise FatalError(f"Invalid size value: {size}. Use an integer or 'all'.")

test/test_esptool.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,7 @@ def test_short_flash(self):
439439
self.run_esptool("write-flash 0x0 images/one_kb.bin")
440440
self.verify_readback(0, 1024, "images/one_kb.bin")
441441

442+
@pytest.mark.skipif(arg_chip != "esp32", reason="Don't need to test multiple times")
442443
def test_short_flash_deprecated(self):
443444
out = self.run_esptool(
444445
"--before default_reset write_flash 0x0 images/one_kb.bin --flash_size keep"
@@ -877,6 +878,13 @@ def test_write_past_end_fails(self):
877878
assert "File 'images/one_kb.bin'" in output
878879
assert "will not fit" in output
879880

881+
@pytest.mark.skipif(arg_chip != "esp32", reason="Don't need to test multiple times")
882+
def test_read_past_end_fails(self):
883+
output = self.run_esptool_error(
884+
"read-flash 0xffffff 1 out.bin"
885+
) # 0xffffff is well past the end of the flash in most cases (16MB)
886+
assert "Can't access flash regions larger than detected flash size" in output
887+
880888
def test_write_no_compression_past_end_fails(self):
881889
output = self.run_esptool_error(
882890
"write-flash -u -fs 1MB 0x280000 images/one_kb.bin"
@@ -913,7 +921,7 @@ def test_read_nostub_high_offset(self):
913921
# readback with no-stub and flash-size set
914922
try:
915923
self.run_esptool(
916-
f"--no-stub read-flash -fs detect {offset} 1024 {dump_file.name}"
924+
f"--no-stub read-flash -fs detect {offset} 1k {dump_file.name}"
917925
)
918926
with open(dump_file.name, "rb") as f:
919927
rb = f.read()

0 commit comments

Comments
 (0)