Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not able to erase section of memory on STM32F103 #73

Open
GeorgeIoak opened this issue Jan 16, 2024 · 13 comments
Open

Not able to erase section of memory on STM32F103 #73

GeorgeIoak opened this issue Jan 16, 2024 · 13 comments

Comments

@GeorgeIoak
Copy link

GeorgeIoak commented Jan 16, 2024

I'm working on a project where I have an application loaded at 0x08005000 and I have reserved 22 pages. I was running some tests on how to erase that section of memory and I can't seem to get it to work without getting an error when it tries to create the bytearray of pages to erase:

C:\Users\george>py -m stm32loader -p COM20 -f F1 -V -e -a 0x08005000 -l 0x5800
Open port COM20, baud 115200
Activating bootloader (select UART)
*** Command: Get
    Bootloader version: 0x22
    Available commands: 0x0, 0x1, 0x2, 0x11, 0x21, 0x31, 0x43, 0x63, 0x73, 0x82, 0x92
Bootloader version: 0x22
*** Command: Get ID
Chip id: 0x410 (STM32F10x Medium-density)
*** Command: Read memory
*** Command: Read memory
Device UID: FF35-066B-30474630-43152645
Flash size: 128 KiB
*** Command: Erase memory
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "C:\Python312\Lib\site-packages\stm32loader\__main__.py", line 41, in <module>
    main()
  File "C:\Python312\Lib\site-packages\stm32loader\__main__.py", line 37, in main
    stm32loader_main(*sys.argv[1:])
  File "C:\Python312\Lib\site-packages\stm32loader\main.py", line 248, in main
    loader.perform_commands()
  File "C:\Python312\Lib\site-packages\stm32loader\main.py", line 148, in perform_commands
    self.stm32.erase_memory(pages)
  File "C:\Python312\Lib\site-packages\stm32loader\bootloader.py", line 624, in erase_memory
    page_numbers = bytearray(pages)
                   ^^^^^^^^^^^^^^^^
ValueError: byte must be in range(0, 256)

C:\Users\george>py -m stm32loader --version
0.7.1
@GeorgeIoak
Copy link
Author

I'm a little confused by the explanation in the AN3155 app note but I think we should be sending the page number of each page after we've sent the number of pages to erase:

Erase Memory command specifications:
1. The bootloader receives a byte that contains N, the number of pages to be erased – 1. 
N = 255 is reserved for global erase requests. For 0 ≤ N ≤ 254, N + 1 pages are erased.
2. The bootloader receives (N + 1) bytes, each byte containing a page number.

So if I pass in the starting address of 0x08005000 the pages_from_range should return a list starting with page 20 going to page 42

@GeorgeIoak
Copy link
Author

So either I've stumbled on a bug or the example on the main page is wrong:

stm32loader --erase --address 0x08000000 --length 0x2000 --port /dev/cu.usbserial-A5XK3RJT

Because if I pass the offset from 0x08000000 the erase command will work:

C:\Users\george>py -m stm32loader -p COM20 -f F1 -V -e -a 0x5000 -l 0x5800
Open port COM20, baud 115200
Activating bootloader (select UART)
*** Command: Get
    Bootloader version: 0x22
    Available commands: 0x0, 0x1, 0x2, 0x11, 0x21, 0x31, 0x43, 0x63, 0x73, 0x82, 0x92
Bootloader version: 0x22
*** Command: Get ID
Chip id: 0x410 (STM32F10x Medium-density)
*** Command: Read memory
*** Command: Read memory
Device UID: FF35-066B-30474630-43152645
Flash size: 128 KiB
*** Command: Erase memory
    Erase memory done
    ```

@dbeinder
Copy link

dbeinder commented Jan 16, 2024

This is a bug, or at least an inconsistency in the --address argument. In selective erase mode, the address starts at 0 for the start of the flash, otherwise the pages are calculated incorrectly:

# Erase from address to address + length.
start_address = self.configuration.address
end_address = self.configuration.address + self.configuration.length
pages = self.stm32.pages_from_range(start_address, end_address)
self.stm32.erase_memory(pages)
# Assemble the list of pages to erase.
first_page = start // self.flash_page_size
last_page = end // self.flash_page_size
pages = list(range(first_page, last_page))

@florisla florisla added the bug label Jan 16, 2024
@florisla
Copy link
Owner

Thanks for reporting this. Not sure if I should change the behavior or the docs...

I think changing the behavior would make the --address argument more consistent with other uses.

@dbeinder
Copy link

I'd prefer the intuitive way, to have --address always refer to a memory mapped location. Of course, that would require stm32loader to know the flash base addresses for all chips to do selective erase. It would be a nice feature in itself - for many families the fixed fallback value of the --address argument is probably a bad idea anyway.

The easiest would be to add a separate, mandatory argument like --erase-start which can then start at 0 for start-of-flash.

@GeorgeIoak
Copy link
Author

If I had a vote I too would prefer that --address refers to the actual location as I think that's easier on the user and eliminates any confusion.

@florisla
Copy link
Owner

@GeorgeIoak Thanks, the more input the better! But can you clarify if by 'the actual location' you mean the offset relative to start of flash ('implemented behavior') or the absolute address ('documented behavior'). Bot addressing modes are equally 'real' in my mind ;-)

@GeorgeIoak
Copy link
Author

Sorry for the confusion, I mean the actual location, 0x08005000 because that's what you enter in the linker script.

@GeorgeIoak
Copy link
Author

GeorgeIoak commented Jan 18, 2024

I was hoping that maybe I could ask for some clarification on the actual erase command and what the bootloader expects. When I issue this, py -m stm32loader -p COM20 -f F1 -V -e -a 0x5000 -l 0x5800 what is the actual data sent and returned?

I know after 0x43, 0xBC the STM32 should send an ACK and then AN3155 says to send

  • the number of pages to be erased – 1
  • The bootloader receives (N + 1) bytes, each byte containing a page number
  • checksum, XOR (N, N+1 bytes))

In my example I calculate the First Page = 20 and the last page = 42 so 23 total pages so if I'm thinking correctly we should be sending:

  • 22
  • 20, 21, 22, ...42
  • 23

I believe that I've calculated the checksum correctly but maybe not. After sending this we should expect to receive an ACK back from the STM32

But I see that we're sending

  • 21
  • 20, 21, 22, ...41
  • 20

So shouldn't we be erasing page 42 and with the current code it looks like we're only going to erase up to page 41.

@florisla
Copy link
Owner

florisla commented Jan 18, 2024

Hi, I thought you were on to a bug... but on second thought I think your calculation is off.

Length 0x5800 is decimal 22528, which is 22 KiB (22 * 1024). So the page count is 22. Sending page_count_minus_one == 21 and then page indices 20 to 41 is what you asked stm32loader to do.

To also erase page 42 you would supply -l 0x5c00.

In your calculation, the first page is 20. Start address plus length results in page 42. But that is not the page index of the last page to be erased. 42 is the page index of the first page which is no longer included. The 'last' page which gets erased is 41. This is due to zero-based counting; 20 ends with a zero but it is first page to be erased.

@florisla florisla added this to the Next-version milestone Feb 19, 2024
@theeuwke
Copy link

my biggest issue, is that erase and program use a different addressing method.

For programming, you have to use an absolute address in the memory map (thus for F1 devices: 0x8000000)
but for erasing, you have to use a relative address within the flash area, because else the page calculation goes wrong:

doesn't work:

stm32loader -f F1 --reset-active-high --port /dev/ttyUSB0 --address 0x8000000 --erase --length 0x400
Activating bootloader (select UART)
Bootloader version: 0x22
Chip id: 0x410 (STM32F10x Medium-density)
Device UID: FF55-0669-53578271-87023735
Flash size: 64 KiB
pages: [131072]
page_count: 0
page_numbers: [131072]
Traceback (most recent call last):
File "/home/naaijkens/.local/bin/stm32loader", line 8, in
sys.exit(main())
File "/home/naaijkens/.local/lib/python3.10/site-packages/stm32loader/main.py", line 37, in main
stm32loader_main(*sys.argv[1:])
File "/home/naaijkens/.local/lib/python3.10/site-packages/stm32loader/main.py", line 249, in main
loader.perform_commands()
File "/home/naaijkens/.local/lib/python3.10/site-packages/stm32loader/main.py", line 149, in perform_commands
self.stm32.erase_memory(pages)
File "/home/naaijkens/.local/lib/python3.10/site-packages/stm32loader/bootloader.py", line 627, in erase_memory
bytearray(pages)
ValueError: byte must be in range(0, 256)

works:

stm32loader -f F1 --reset-active-high --port /dev/ttyUSB0 --address 0x0 --erase --length 0x800
Activating bootloader (select UART)
Bootloader version: 0x22
Chip id: 0x410 (STM32F10x Medium-density)
Device UID: FF55-0669-53578271-87023735
Flash size: 64 KiB
pages: [0, 1]
page_count: 1
page_numbers: [0, 1]

for programing; it's the other way around:

fail:

stm32loader -f F1 --reset-active-high --port /dev/ttyUSB0 --address 0 --length 0x400 .pio/build/genericSTM32F103RB/firmware.bin -w
Activating bootloader (select UART)
Bootloader version: 0x22
Chip id: 0x410 (STM32F10x Medium-density)
Device UID: FF55-0669-53578271-87023735
Flash size: 64 KiB
Write 243 chunks at address 0x0...
Traceback (most recent call last):
File "/home/naaijkens/.local/bin/stm32loader", line 8, in
sys.exit(main())
File "/home/naaijkens/.local/lib/python3.10/site-packages/stm32loader/main.py", line 37, in main
stm32loader_main(*sys.argv[1:])
File "/home/naaijkens/.local/lib/python3.10/site-packages/stm32loader/main.py", line 249, in main
loader.perform_commands()
File "/home/naaijkens/.local/lib/python3.10/site-packages/stm32loader/main.py", line 161, in perform_commands
self.stm32.write_memory_data(self.configuration.address, binary_data)
File "/home/naaijkens/.local/lib/python3.10/site-packages/stm32loader/bootloader.py", line 761, in write_memory_data
self.write_memory(address, data[offset : offset + write_length])
File "/home/naaijkens/.local/lib/python3.10/site-packages/stm32loader/bootloader.py", line 590, in write_memory
self.write_and_ack("0x31 programming failed", nr_of_bytes - 1, data, checksum)
File "/home/naaijkens/.local/lib/python3.10/site-packages/stm32loader/bootloader.py", line 377, in write_and_ack
return self._wait_for_ack(message)
File "/home/naaijkens/.local/lib/python3.10/site-packages/stm32loader/bootloader.py", line 838, in _wait_for_ack
raise CommandError("NACK " + info)
stm32loader.bootloader.CommandError: NACK 0x31 programming failed

works:

stm32loader -f F1 --reset-active-high --port /dev/ttyUSB0 --address 0x8000000 --length 0x400 .pio/build/genericSTM32F103RB/firmware.bin -w
Activating bootloader (select UART)
Bootloader version: 0x22
Chip id: 0x410 (STM32F10x Medium-density)
Device UID: FF55-0669-53578271-87023735
Flash size: 64 KiB
Write 243 chunks at address 0x8000000...
Writing ████████████████∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙ 124/243

So, my issue with this, is that you cannot 'partially erase' the flash and flash a file in 1 command. Also, the length parameter is ignored while flashing afaik.

@florisla
Copy link
Owner

I see how that's inconvenient, but I'm reluctant to change addressing semantics which can break existing use cases.

Is there a particular reason you prefer executing one command instead of two?

Also, what is the alternative to ignoring the length parameter while flashing? You can't flash any content that's not present... Or would you expect to write padded with zeros?

@theeuwke
Copy link

theeuwke commented Jan 10, 2025

Well, you could change it, and inform users to pin an older version - or add a 'page parameter' over keep the address parameter. Now its inconsistent.

Usually, flashing a device is a 'single command' operation - but I have some use cases where I don't want to erase the whole device (virtual eeprom, bootloader).

I don't really get the last comment. First of alll, I would expect by default that only the affecting pages are erased (see 2nd remark). Moreover, in some cases I only want to partially flash a binary, with the length command you could limit the amount of written bytes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants