Skip to content

Commit

Permalink
Bugfix: erase_memory erases in 1KiB pages, not 256 bytes
Browse files Browse the repository at this point in the history
This should be made configurable at some point, because this differs
among STM32 parts.

256 Bytes remains the maximum data transfer length (chunk).
  • Loading branch information
florisla committed Apr 19, 2019
1 parent dadab77 commit 023ef02
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 26 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ v0.4.0
* #9: Support data writes smaller than 256 bytes. By NINI1988.
* #10: Make stm32loader useful as a library.
* #4: Bring back support for progress bar.
* #11: Support page erase in extended (two-byte addressing) erase mode
* #12: Allow to supply the serial port as an environment variable
* #12: Allow to supply the serial port as an environment variable.
* #11: Support paged erase in extended (two-byte addressing) erase mode.
Note: this is not yet tested on hardware.
* Start using code linting and unit tests.
* Start using Continuous Integration (Travis CI).

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ Not currently supported
* Command-line argument for readout protection
* Command-line argument for write protection/unprotection
* STM8 devices (ST UM0560)
* Paged flash erase for devices with page size <> 1 KiB
* Other bootloader protocols (e.g. I2C, HEX -> implemented in stm32flash)


Expand All @@ -128,3 +129,4 @@ Future work
* Try Azure pipelines for CI
* Support PyPy, PyPy3
* Drop Python2 support; start using intenum for commands and replies
* Determine flash page size or make this configurable
30 changes: 16 additions & 14 deletions stm32loader/bootloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,8 @@ class Reply:
"F7": 0x1FF0F442,
}

PAGE_SIZE = 256 # bytes
DATA_TRANSFER_SIZE = 256 # bytes
FLASH_PAGE_SIZE = 1024 # bytes

def __init__(self, connection, verbosity=5, show_progress=None):
"""
Expand Down Expand Up @@ -315,7 +316,7 @@ def get_flash_size(self, device_family):
"""Return the MCU's flash size in bytes."""
flash_size_address = self.FLASH_SIZE_ADDRESS[device_family]
flash_size_bytes = self.read_memory(flash_size_address, 2)
flash_size = flash_size_bytes[0] + flash_size_bytes[1] * self.PAGE_SIZE
flash_size = flash_size_bytes[0] + flash_size_bytes[1] * self.DATA_TRANSFER_SIZE
return flash_size

def get_uid(self, device_id):
Expand All @@ -337,7 +338,7 @@ def read_memory(self, address, length):
Supports maximum 256 bytes.
"""
if length > self.PAGE_SIZE:
if length > self.DATA_TRANSFER_SIZE:
raise DataLengthError("Can not read more than 256 bytes at once.")
self.command(self.Command.READ_MEMORY, "Read memory")
self.write_and_ack("0x11 address failed", self._encode_address(address))
Expand All @@ -361,7 +362,7 @@ def write_memory(self, address, data):
nr_of_bytes = len(data)
if nr_of_bytes == 0:
return
if nr_of_bytes > self.PAGE_SIZE:
if nr_of_bytes > self.DATA_TRANSFER_SIZE:
raise DataLengthError("Can not write more than 256 bytes at once.")
self.command(self.Command.WRITE_MEMORY, "Write memory")
self.write_and_ack("0x31 address failed", self._encode_address(address))
Expand Down Expand Up @@ -456,8 +457,9 @@ def write_protect(self, pages):
"""Enable write protection on the given flash pages."""
self.command(self.Command.WRITE_PROTECT, "Write protect")
nr_of_pages = (len(pages) - 1) & 0xFF
checksum = reduce(operator.xor, pages, nr_of_pages)
self.write_and_ack("0x63 write protect failed", nr_of_pages, pages, checksum)
page_numbers = bytearray(pages)
checksum = reduce(operator.xor, page_numbers, nr_of_pages)
self.write_and_ack("0x63 write protect failed", nr_of_pages, page_numbers, checksum)
self.debug(10, " Write protect done")

def write_unprotect(self):
Expand Down Expand Up @@ -493,11 +495,11 @@ def read_memory_data(self, address, length):
Length may be more than 256 bytes.
"""
data = bytearray()
page_count = int(math.ceil(length / float(self.PAGE_SIZE)))
self.debug(5, "Read %d pages at address 0x%X..." % (page_count, address))
with self.show_progress("Reading", maximum=page_count) as progress_bar:
chunk_count = int(math.ceil(length / float(self.DATA_TRANSFER_SIZE)))
self.debug(5, "Read %d chunks at address 0x%X..." % (chunk_count, address))
with self.show_progress("Reading", maximum=chunk_count) as progress_bar:
while length:
read_length = min(length, self.PAGE_SIZE)
read_length = min(length, self.DATA_TRANSFER_SIZE)
self.debug(
10,
"Read %(len)d bytes at 0x%(address)X"
Expand All @@ -516,13 +518,13 @@ def write_memory_data(self, address, data):
Data length may be more than 256 bytes.
"""
length = len(data)
page_count = int(math.ceil(length / float(self.PAGE_SIZE)))
chunk_count = int(math.ceil(length / float(self.DATA_TRANSFER_SIZE)))
offset = 0
self.debug(5, "Write %d pages at address 0x%X..." % (page_count, address))
self.debug(5, "Write %d chunks at address 0x%X..." % (chunk_count, address))

with self.show_progress("Writing", maximum=page_count) as progress_bar:
with self.show_progress("Writing", maximum=chunk_count) as progress_bar:
while length:
write_length = min(length, self.PAGE_SIZE)
write_length = min(length, self.DATA_TRANSFER_SIZE)
self.debug(
10,
"Write %(len)d bytes at 0x%(address)X"
Expand Down
15 changes: 5 additions & 10 deletions tests/integration/test_stm32bootloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,29 +40,24 @@ def stm32(serial_connection):
def test_erase_with_page_erases_only_that_page(stm32):
stm32.reset_from_system_memory()
base = 0x08000000
before, middle, after = base + 0, base + 256, base + 512
before, middle, after = base + 0, base + 1024, base + 2048

# erase full device
# erase full device and check that it reset data bytes
stm32.erase_memory()

# check that erase was successful
assert all(byte == 0xFF for byte in stm32.read_memory(before, 16))
assert all(byte == 0xFF for byte in stm32.read_memory(middle, 16))
assert all(byte == 0xFF for byte in stm32.read_memory(after, 16))

# write zeros to three pages (and verify success)
# write zeros to three pages and verify data has changed
stm32.write_memory(before, bytearray([0x00] * 16))
stm32.write_memory(middle, bytearray([0x00] * 16))
stm32.write_memory(after, bytearray([0x00] * 16))
assert all(byte == 0x00 for byte in stm32.read_memory(before, 16))
assert all(byte == 0x00 for byte in stm32.read_memory(middle, 16))
assert all(byte == 0x00 for byte in stm32.read_memory(after, 16))

# erase only the middle page
stm32.erase_memory(pages=[0])

# check that middle page is erased, others are not
# erase only the middle page and check only that one's bytes are rest
stm32.erase_memory(pages=[1])
assert all(byte == 0x00 for byte in stm32.read_memory(before, 16))
assert all(byte == 0xFF for byte in stm32.read_memory(middle, 256))
assert all(byte == 0x00 for byte in stm32.read_memory(after, 16))

0 comments on commit 023ef02

Please sign in to comment.