florisla / stm32loader

Flash firmware to STM32 microcontrollers using Python.
GNU General Public License v3.0
111 stars 55 forks source link

Not able to erase section of memory on STM32F103 #73

Open GeorgeIoak opened 10 months ago

GeorgeIoak commented 10 months ago

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 commented 10 months ago

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 commented 10 months ago

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 commented 10 months ago

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: https://github.com/florisla/stm32loader/blob/c56da3ddf552df033f5445963b0a0fce0259125f/stm32loader/main.py#L144-L148 https://github.com/florisla/stm32loader/blob/c56da3ddf552df033f5445963b0a0fce0259125f/stm32loader/bootloader.py#L805-L808

florisla commented 10 months ago

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 commented 10 months ago

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 commented 10 months ago

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 commented 10 months ago

@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 commented 10 months ago

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

GeorgeIoak commented 10 months ago

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

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:

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

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 commented 10 months ago

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.