pyocd / pyOCD

Open source Python library for programming and debugging Arm Cortex-M microcontrollers
https://pyocd.io
Apache License 2.0
1.13k stars 484 forks source link

FlashEraseFailure on STM32G431KB Nucleo #1343

Closed Finwood closed 2 years ago

Finwood commented 2 years ago

When trying to flash images onto the STM32G431KB Nucleo board through the builtin ST-Link V3, PyOCD fails with a flash erase sector failure (result code 0x1).

The behavior is reproducible both from the shell and through the Python API.

~/src/py-aeric cli ❯ pyocd flash --erase sector --base-address 0x0800a800 --target stm32g431kbtx ../g4-nucleo/build/G4-Nucleo.bin
0000755 I Loading /home/lasse/src/g4-nucleo/build/G4-Nucleo.bin at 0x0800a800 [load_cmd]
[===                                               ]   7%
0006354 C flash erase sector failure (address 0x08010000; result code 0x1) [__main__]

Traceback (most recent call last):
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/__main__.py", line 161, in run
    status = cmd.invoke()
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/subcommands/load_cmd.py", line 117, in invoke
    programmer.program(filename,
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/flash/file_programmer.py", line 170, in program
    self._loader.commit()
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/flash/loader.py", line 289, in commit
    perf = builder.program(chip_erase=chipErase,
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/flash/builder.py", line 509, in program
    flash_operation = self._sector_erase_program_double_buffer(progress_cb)
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/flash/builder.py", line 911, in _sector_erase_program_double_buffer
    self.flash.erase_sector(sector.addr)
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/flash/flash.py", line 371, in erase_sector
    raise FlashEraseFailure('flash erase sector failure', address=address, result_code=result)
pyocd.core.exceptions.FlashEraseFailure: flash erase sector failure (address 0x08010000; result code 0x1)
flit commented 2 years ago

Hi @Finwood, thanks for the report. I don't have a G431 board, so I'll have to investigate indirectly.

flit commented 2 years ago

I looked at buying a Nucleo-G431K8, but there aren't any available from US distributors. ☹️

Based on the flash algo source code, a result code of 0x1 means the flash controller reported a programming error. Now the question is why does the controller report an error?

Could you please try these tests? (I'm kind of shooting blindly!)

Fyi, you can see the flash algo source code from the Keil.STM32G4xx_DFP.1.4.0 CMSIS-Pack. Download from the CMSIS-Pack list and extract as a zip (change ext if needed). The algo source is under CMSIS/Flash/STM32G4xx_Flash/.

Finwood commented 2 years ago

Could you please try these tests? (I'm kind of shooting blindly!)

Sure thing!

~/src/py-aeric cli ❯ pyocd erase --target stm32g431kbtx --chip
0000752 I Erasing chip... [eraser]
0000937 I Done [eraser]
~/src/py-aeric cli ❯ pyocd erase --target stm32g431kbtx --sector 0x0800a000+0x2000
0000876 I Erasing sector 0x0800a000 (2048 bytes) [eraser]
0000904 I Erasing sector 0x0800a800 (2048 bytes) [eraser]
0000932 I Erasing sector 0x0800b000 (2048 bytes) [eraser]
0000960 I Erasing sector 0x0800b800 (2048 bytes) [eraser]

On a hunch I've tried the above with the address range of my original issue, which failed:

~/src/py-aeric cli ❯ pyocd erase --target stm32g431kbtx --sector 0x0800a800-0x08016800
0000887 I Erasing sector 0x0800a800 (2048 bytes) [eraser]
0000919 I Erasing sector 0x0800b000 (2048 bytes) [eraser]
0000948 I Erasing sector 0x0800b800 (2048 bytes) [eraser]
0000977 I Erasing sector 0x0800c000 (2048 bytes) [eraser]
0001006 I Erasing sector 0x0800c800 (2048 bytes) [eraser]
0001034 I Erasing sector 0x0800d000 (2048 bytes) [eraser]
0001062 I Erasing sector 0x0800d800 (2048 bytes) [eraser]
0001089 I Erasing sector 0x0800e000 (2048 bytes) [eraser]
0001117 I Erasing sector 0x0800e800 (2048 bytes) [eraser]
0001145 I Erasing sector 0x0800f000 (2048 bytes) [eraser]
0001173 I Erasing sector 0x0800f800 (2048 bytes) [eraser]
0001201 I Erasing sector 0x08010000 (2048 bytes) [eraser]
0006209 C flash erase sector failure (address 0x08010000; result code 0x1) [__main__]
Traceback (most recent call last):
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/__main__.py", line 161, in run
    status = cmd.invoke()
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/subcommands/erase_cmd.py", line 97, in invoke
    eraser.erase(addresses)
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/flash/eraser.py", line 76, in erase
    self._sector_erase(addresses)
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/flash/eraser.py", line 145, in _sector_erase
    flash.erase_sector(sector_addr)
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/flash/flash.py", line 371, in erase_sector
    raise FlashEraseFailure('flash erase sector failure', address=address, result_code=result)
pyocd.core.exceptions.FlashEraseFailure: flash erase sector failure (address 0x08010000; result code 0x1)

Same with -Osmart_flash=0:

~/src/py-aeric cli ❯ pyocd erase --target stm32g431kbtx --sector 0x0800a800-0x08016800 -Osmart_flash=0
0000872 I Erasing sector 0x0800a800 (2048 bytes) [eraser]
0000901 I Erasing sector 0x0800b000 (2048 bytes) [eraser]
0000929 I Erasing sector 0x0800b800 (2048 bytes) [eraser]
0000957 I Erasing sector 0x0800c000 (2048 bytes) [eraser]
0000984 I Erasing sector 0x0800c800 (2048 bytes) [eraser]
0001012 I Erasing sector 0x0800d000 (2048 bytes) [eraser]
0001040 I Erasing sector 0x0800d800 (2048 bytes) [eraser]
0001068 I Erasing sector 0x0800e000 (2048 bytes) [eraser]
0001096 I Erasing sector 0x0800e800 (2048 bytes) [eraser]
0001124 I Erasing sector 0x0800f000 (2048 bytes) [eraser]
0001151 I Erasing sector 0x0800f800 (2048 bytes) [eraser]
0001179 I Erasing sector 0x08010000 (2048 bytes) [eraser]
0006187 C flash erase sector failure (address 0x08010000; result code 0x1) [__main__]
Traceback (most recent call last):
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/__main__.py", line 161, in run
    status = cmd.invoke()
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/subcommands/erase_cmd.py", line 97, in invoke
    eraser.erase(addresses)
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/flash/eraser.py", line 76, in erase
    self._sector_erase(addresses)
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/flash/eraser.py", line 145, in _sector_erase
    flash.erase_sector(sector_addr)
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/flash/flash.py", line 371, in erase_sector
    raise FlashEraseFailure('flash erase sector failure', address=address, result_code=result)
pyocd.core.exceptions.FlashEraseFailure: flash erase sector failure (address 0x08010000; result code 0x1)

Adding -Osmart_flash=0 -Okeep_unwritten=0 to my original line does not make a difference:

~/src/py-aeric cli ❯ pyocd flash --erase sector --base-address 0x0800a800 --target stm32g431kbtx -Osmart_flash=0 -Okeep_unwritten=0 ../g4-nucleo/build/G4-Nucleo.bin
0000736 I Loading /home/lasse/src/g4-nucleo/build/G4-Nucleo.bin at 0x0800a800 [load_cmd]
[===                                               ]   6%
0006228 C flash erase sector failure (address 0x08010000; result code 0x1) [__main__]
Traceback (most recent call last):
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/__main__.py", line 161, in run
    status = cmd.invoke()
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/subcommands/load_cmd.py", line 117, in invoke
    programmer.program(filename,
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/flash/file_programmer.py", line 170, in program
    self._loader.commit()
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/flash/loader.py", line 289, in commit
    perf = builder.program(chip_erase=chipErase,
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/flash/builder.py", line 509, in program
    flash_operation = self._sector_erase_program_double_buffer(progress_cb)
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/flash/builder.py", line 911, in _sector_erase_program_double_buffer
    self.flash.erase_sector(sector.addr)
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/flash/flash.py", line 371, in erase_sector
    raise FlashEraseFailure('flash erase sector failure', address=address, result_code=result)
pyocd.core.exceptions.FlashEraseFailure: flash erase sector failure (address 0x08010000; result code 0x1)

Writing to (and erasing) memory up to 0x0800FFFF works as expected.

Is it possible that (some part of) PyOCD only expects 64 KiB of memory?

flit commented 2 years ago

Thanks for the tests! (And sorry, I meant to include the original failing address range but got the numbers wrong. 😅)

If pyocd had a limit of 64 KiB on the flash memory, it wouldn't even ask the flash algo to erase the sector.

It's behaving as if sectors starting at 0x08010000 are write protected. Could you check the option bytes for any settings that might cause this? Since this device has ECC, it's also possible that there is somehow an ECC error. Unfortunately the flash algo doesn't return a status code indicating which error occurred.

Another option is to attempt to manually erase the 0x08010000 sector using pyocd commander. This would allow you to check which error status bit is set. The FLASH peripheral registers will be accessible using the reg and wreg commands, eg reg FLASH.SR to read the status and wreg FLASH.KEYR 0x45670123 ; wreg FLASH.KEYR 0xcdef89ab to unlock flash programming.

Finwood commented 2 years ago

I am quite confident no memory protection is active:

~/src/py-aeric cli ❯ openocd -f interface/stlink.cfg -f target/stm32g4x.cfg -c "init" -c "reset halt" -c "flash write_image erase unlock ../g4-nucleo/build/G4-Nucleo.bin 0x0800a800 bin" -c "reset run" -c "shutdown"
Open On-Chip Debugger 0.11.0
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
Info : clock speed 2000 kHz
Info : STLINK V3J9M3 (API v3) VID:PID 0483:374E
Info : Target voltage: 3.284881
Info : stm32g4x.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : starting gdb server for stm32g4x.cpu on 3333
Info : Listening on port 3333 for gdb connections
Info : Unable to match requested speed 2000 kHz, using 1000 kHz
Info : Unable to match requested speed 2000 kHz, using 1000 kHz
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x0800021c msp: 0x20008000
Info : device idcode = 0x20016468 (STM32G43/G44xx - Rev Z : 0x2001)
Info : flash size = 128kbytes
Info : flash mode : single-bank
Warn : Adding extra unprotect range, 0x08016660 .. 0x080167ff
Warn : Adding extra erase range, 0x08016660 .. 0x080167ff
auto erase enabled
auto unlock enabled
wrote 48736 bytes from file ../g4-nucleo/build/G4-Nucleo.bin in 1.275998s (37.299 KiB/s)

As per your suggestion, I have tried to manually erase the sector in question using the commander. Result: success! Unfortunately this is only a workaround and does not solve the issue.

(I've added empty lines for readability)

~/src/py-aeric cli ❯ pyocd commander
Connected to STM32G431KBTx [Running]: 004A00243137510739383538

pyocd> reg -f FLASH.CR; reg -f FLASH.SR
FLASH.CR @ 40022014 = c0000000
  PG[0] = 0 (0)
  PER[1] = 0 (0)
  MER1[2] = 0 (0)
  PNB[9:3] = 00 (0000000)
  STRT[16] = 0 (0)
  OPTSTRT[17] = 0 (0)
  FSTPG[18] = 0 (0)
  EOPIE[24] = 0 (0)
  ERRIE[25] = 0 (0)
  RDERRIE[26] = 0 (0)
  OBL_LAUNCH[27] = 0 (0)
  SEC_PROT1[28] = 0 (0)
  OPTLOCK[30] = 1 (1)
  LOCK[31] = 1 (1)
FLASH.SR @ 40022010 = 00000000
  EOP[0] = 0 (0)
  OPERR[1] = 0 (0)
  PROGERR[3] = 0 (0)
  WRPERR[4] = 0 (0)
  PGAERR[5] = 0 (0)
  SIZERR[6] = 0 (0)
  PGSERR[7] = 0 (0)
  MISERR[8] = 0 (0)
  FASTERR[9] = 0 (0)
  RDERR[14] = 0 (0)
  OPTVERR[15] = 0 (0)
  BSY[16] = 0 (0)

pyocd> rw 0x0800fff0 32
0800fff0:  da022800 71b9f44f 9a06e7ad 2b0700d3    |..(.q..O....+...|
08010000:  f44fd802 e7a67196 6f00f5b3 f240d902    |.O....q.o....@..|

pyocd> wreg FLASH.KEYR 0x45670123; wreg FLASH.KEYR 0xcdef89ab
writing 0x45670123 to 0x40022008:32 (KEYR)
writing 0xcdef89ab to 0x40022008:32 (KEYR)

pyocd> reg FLASH.CR; reg FLASH.SR
FLASH.CR @ 40022014 = 40000000
FLASH.SR @ 40022010 = 00000000

pyocd> wreg -r FLASH.CR 0x40010102
writing 0x40010102 to 0x40022014:32 (CR)
FLASH.CR @ 40022014 = 40010102
  PG[0] = 0 (0)
  PER[1] = 1 (1)
  MER1[2] = 0 (0)
  PNB[9:3] = 20 (0100000)
  STRT[16] = 1 (1)
  OPTSTRT[17] = 0 (0)
  FSTPG[18] = 0 (0)
  EOPIE[24] = 0 (0)
  ERRIE[25] = 0 (0)
  RDERRIE[26] = 0 (0)
  OBL_LAUNCH[27] = 0 (0)
  SEC_PROT1[28] = 0 (0)
  OPTLOCK[30] = 1 (1)
  LOCK[31] = 0 (0)

pyocd> reg FLASH.CR; reg FLASH.SR
FLASH.CR @ 40022014 = 40000102
FLASH.SR @ 40022010 = 00000000

pyocd> rw 0x0800fff0 32
0800fff0:  da022800 71b9f44f 9a06e7ad 2b0700d3    |..(.q..O....+...|
08010000:  ffffffff ffffffff ffffffff ffffffff    |................|

pyocd>

The page size seems to be correct, though:

~/src/py-aeric cli ❯ pyocd cmd -c read32 0x0800fff0 32
0800fff0:  da022800 71b9f44f 9a06e7ad 2b0700d3    |..(.q..O....+...|
08010000:  f44fd802 e7a67196 6f00f5b3 f240d902    |.O....q.o....@..|

~/src/py-aeric cli ❯ pyocd erase --target stm32g431kbtx --sector 0x0800f800-0x080107ff -L 'pyocd.flash.*=debug'
0000744:DEBUG:flash:algo init and load to 0x20000200
0000869:DEBUG:flash:algo call init(addr=134217728, clock=0, op=1)
0000888:DEBUG:flash:init result = 0
0000888:INFO:eraser:Erasing sector 0x0800f800 (2048 bytes)
0000888:DEBUG:flash:call erase_sector(800f800)
0000920:DEBUG:flash:erase_sector result = 0
0000920:INFO:eraser:Erasing sector 0x08010000 (2048 bytes)
0000920:DEBUG:flash:call erase_sector(8010000)
0000928:DEBUG:flash:erase_sector result = 1
0005932:CRITICAL:__main__:flash erase sector failure (address 0x08010000; result code 0x1)
Traceback (most recent call last):
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/__main__.py", line 150, in run
    status = cmd.invoke()
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/subcommands/erase_cmd.py", line 97, in invoke
    eraser.erase(addresses)
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/flash/eraser.py", line 76, in erase
    self._sector_erase(addresses)
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/flash/eraser.py", line 145, in _sector_erase
    flash.erase_sector(sector_addr)
  File "/home/lasse/.local/share/virtualenvs/py-aeric-xfOJeMmh/lib/python3.9/site-packages/pyocd/flash/flash.py", line 374, in erase_sector
    raise FlashEraseFailure('flash erase sector failure', address=address, result_code=result)
pyocd.core.exceptions.FlashEraseFailure: flash erase sector failure (address 0x08010000; result code 0x1)

~/src/py-aeric cli ❯ pyocd cmd -c read32 0x0800fff0 32
0800fff0:  ffffffff ffffffff ffffffff ffffffff    |................|
08010000:  f44fd802 e7a67196 6f00f5b3 f240d902    |.O....q.o....@..|

Further ideas? 🤔

flit commented 2 years ago

Thanks for testing the manual erase and comparing with OpenOCD, that helps narrow it down a lot. This strongly implies there's a bug in the Keil G431 flash algo, so I'll need to review the code in detail.

These register values would be helpful to verify exactly what the flash algo code is doing:

In the meantime, one more thing to try would be to add a reset halt command after connecting when you try the manual erase. This will mirror the exact conditions under which the flash algo is run.

For reference, here are the Init() and EraseSector() functions from the G431 flash algo (minus a few lines that are ifdef'd out, and not including helper functions).

int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {

  FLASH->KEYR = FLASH_KEY1;                              /* Unlock Flash operation */
  FLASH->KEYR = FLASH_KEY2;

  /* Wait until the flash is ready */
  while (FLASH->SR & FLASH_SR_BSY);

  flashBase = adr;
  flashSize = ((*((u32 *)FLASHSIZE_BASE)) & 0x0000FFFF) << 10;
  flashBankSize = flashSize >> 1;
  flashPageSize = GetFlashPageSize();

  if ((FLASH->OPTR & 0x10000) == 0x00000) {              /* Test if IWDG is running (IWDG in HW mode) */
    /* Set IWDG time out to ~32.768 second */
    IWDG->KR  = 0x5555;                                  /* Enable write access to IWDG_PR and IWDG_RLR */
    IWDG->PR  = 0x06;                                    /* Set prescaler to 256 */
    IWDG->RLR = 4095;                                    /* Set reload value to 4095 */
  }

  return (0);
}

int EraseSector (unsigned long adr) {
  u32 b, p;

  b = GetFlashBankNum(adr);                              /* Get Bank Number 0..1  */
  p = GetFlashPageNum(adr);                              /* Get Page Number 0..127 */

  FLASH->SR  = FLASH_PGERR;                              /* Reset Error Flags */

  FLASH->CR  = (FLASH_CR_PER |                           /* Page Erase Enabled */
                (p <<  3)    |                           /* page Number. 0 to 127 for each bank */
                (b << 11)     );
  FLASH->CR |=  FLASH_CR_STRT;                           /* Start Erase */
  DSB();

  while (FLASH->SR & FLASH_SR_BSY);

  if (FLASH->SR & FLASH_PGERR) {                         /* Check for Error */
    FLASH->SR  = FLASH_PGERR;                            /* Reset Error Flags */
    return (1);                                          /* Failed */
  }

  return (0);                                            /* Done */
}
flit commented 2 years ago

Frustratingly, I didn't find anything in the flash algo code. Plenty of things I'd do differently, but no errors.

I also looked over the openocd code and didn't find anything notably different for the sector erase operation. (I didn't review the setup code yet since it is surprisingly large and complex.)

Some questions:

I rebuilt the flash algo (attached below) with a change to return the FLASH.SR value after an error, which will appear as the result code in the FlashEraseFailure exception description. This should at least let us see the actual cause of the failure.

stm32g43x_128_with_errs.zip

Probably the easiest way to switch to the modified algo is to create a pyocd_user.py script in the project directory:

def will_connect():
    # Look up the flash memory region.
    rgn = target.memory_map.get_first_matching_region(start=0x08000000)

    # Set the path to the .FLM flash algorithm.
    rgn.flm = "stm32g43x_128_with_errs.flm"

(And make sure the .flm file extracted from the .zip is also in the project directory.)

In the meantime, I will check with my STMicro contacts to see if I can get a Nucleo-G431KB board. US distributors still don't have any in stock.

Thanks

Finwood commented 2 years ago

Thanks, I'll be able to take a look on Tuesday (Europe) and will come back to you then. Keep up the good work!

Finwood commented 2 years ago

FLASHSIZE_BASE and DBGMCU.IDCODE are as expected:

~/src/py-aeric cli ❯ pyocd cmd -c read32 0x1fff_75e0 -c reg DBGMCU.IDCODE
1fff75e0:  ffff0080    |....|
DBGMCU.IDCODE @ e0042000 = 20016468

Your pyocd_user.py did not work out of the box, I had to reset rgn.algo to make it work. I assume this is what you intended, but please correct me if I am mistaken.

def will_connect(board):
    # Look up the flash memory region.
    rgn = target.memory_map.get_first_matching_region(start=0x08000000)

    # Set the path to the .FLM flash algorithm.
    rgn.flm = "/home/lasse/src/py-aeric/stm32g43x_128_with_errs.flm"
    rgn.algo = None

The revised flash algorithm did not reproduce the error, therefore the FLASH.SR contents remain a mystery.

Something seems to be wrong with mapping addresses to page numbers, though: below you find the start of each sector before and after pyocd erase --sector 0x0800f800-0x080107ff. Instead of pages 31 and 32, PyOCD erased pages 9 and 32.

~/src/py-aeric cli ❯ pyocd cmd -c read32 0x0800_0000 16 \
                               -c read32 0x0800_0800 16 \
                               -c ... \
                               -c read32 0x0801_f000 16 \
                               -c read32 0x0801_f800 16
08000000:  20008000 0800021d 08000219 08000219    | ...............|  <-- page 0
08000800:  3063737f 08000800 08000a00 00000078    |0cs............x|
08001000:  e000ed00 05fa0004 4606b573 4812b920    |........F..sH.. |
08001800:  20000764 09896800 f002b508 f7ffff79    | ..d..h........y|
08002000:  f004d10b 4b27fc41 4298681b 4b26d305    |....K'.AB.h.K&..|
08002800:  1c5e2300 0383eb00 685b4605 0686eb00    |.^#.....h[F.....|
08003000:  f04ffc37 82a00900 9f0768e2 d20b4592    |.O.7......h...E.|
08003800:  f97af001 4a0e2304 3010f8ad 5355f641    |.z..J.#.0...SU.A|
08004000:  79237962 92004914 4642a802 fbb4f003    |y#yb..I.FB......|
08004800:  4ff0e92d ed2d8843 f5b38b04 b0875fe0    |O..-.-.C......_.|  <-- page 9
08005000:  fafcf002 772b2300 f8c46963 681b611c    |....w+#...ich.a.|
08005800:  60010170 00004770 e000ed88 2300b507    |`..p..Gp....#...|
08006000:  f1049901 f0000010 4620fdbf fefff7ff    |........F ......|
08006800:  24009301 48164915 46239400 f7ff2240    |$...H.I.F#...."@|
08007000:  205cf883 2f04b2f6 e8dfd81e 0309f007    | \../...........|
08007800:  42a3d101 1a80d101 2800bd10 e7fad1f3    |B.......(.......|
08008000:  f000462a 4606fd01 d1e12800 46506921    |..F*F.....(.FPi!|
08008800:  f96af000 d1021c43 b103682b bd386023    |.j.....C..h+.8`#|
08009000:  fc02fa0c f303fa21 43014091 471cea4f    |.......!C.@.G..O|
08009800:  080020c1 08001e59 08001dd5 00000008    |.. ....Y........|
0800a000:  6573206c 6f742074 00732520 00737542    |es lot t.s% .suB|
0800a800:  3063737f 0800a800 0800aa00 00000078    |0cs............x|
0800b000:  bd70b008 e7fa2401 40021000 20000c00    |.p....$.@... ...|
0800b800:  f4227280 ea4242e6 688d2205 0320f023    |."r..BB.h.".. .#|
0800c000:  3380f423 f7ff60eb 4604fe2d 019b682b    |3..#..`.F..-..h+|
0800c800:  20006cb8 f04fb508 f3830350 f3bf8811    | .l..O.....P....|
0800d000:  41ffe92d 9201460f b9204604 512ff240    |A..-..F.. F.Q/.@|
0800d800:  4b0ff9c5 681b6aea 429a6adb 2401bf28    |K...h.j.B.j.$..(|
0800e000:  08014c45 20006db4 20006d38 20006cc4    |..LE .m. .m8 .l.|
0800e800:  c002f81a ea0c4240 43080000 0002f80a    |......B@C.......|
0800f000:  b33cfe3d 9b044922 3000f8c9 72f8f641    |.<.=..I"0...r..A|
0800f800:  2304e9cd fa51b2e2 f5b4f484 f2006f01    |#....Q........o.|  <-- page 31
08010000:  f44fd802 e7a67196 6f00f5b3 f240d902    |.O....q.o....@..|  <-- page 32
08010800:  46594620 f814f004 32016832 46032a03    |FYF ....2.h2F.*.|
08011000:  f0002c00 4f3180e7 80bcf8df 040cf106    |..,.O1..........|
08011800:  9b0b930f 2520bb2d 0a10f04f f640950b    |....% .-...O.@..|
08012000:  930100e2 f202fa00 91002301 f7fc4628    |..........#...F(|
08012800:  f1a70807 00e40818 bf2845a0 ab0946a0    |.........(E...F.|
08013000:  d1eb2b02 9068f8df f7ff2000 eb00f9fd    |..+..h.... .....|
08013800:  46382104 ff62f7ff 60304604 d1d52800    |F8!..b..`0F...(.|
08014000:  60334613 2043f891 460c4607 3301b10a    |`3F. C..F.F.3...|
08014800:  f202fa20 0002eb43 f020bf08 47700001    |... ...C. ..Gp..|
08015000:  52437325 696d2043 74616d73 72006863    |RCs%im Ctamsr.hc|
08015800:  72206f66 65757165 25207473 72662075    |r ofeuqe% tsrf u|
08016000:  00000000 00000000 00000000 00000000    |................|
08016800:  ffffffff ffffffff ffffffff ffffffff    |................|
08017000:  ffffffff ffffffff ffffffff ffffffff    |................|
08017800:  ffffffff ffffffff ffffffff ffffffff    |................|
08018000:  ffffffff ffffffff ffffffff ffffffff    |................|
08018800:  ffffffff ffffffff ffffffff ffffffff    |................|
08019000:  ffffffff ffffffff ffffffff ffffffff    |................|
08019800:  ffffffff ffffffff ffffffff ffffffff    |................|
0801a000:  ffffffff ffffffff ffffffff ffffffff    |................|
0801a800:  ffffffff ffffffff ffffffff ffffffff    |................|
0801b000:  ffffffff ffffffff ffffffff ffffffff    |................|
0801b800:  ffffffff ffffffff ffffffff ffffffff    |................|
0801c000:  ffffffff ffffffff ffffffff ffffffff    |................|
0801c800:  ffffffff ffffffff ffffffff ffffffff    |................|
0801d000:  ffffffff ffffffff ffffffff ffffffff    |................|
0801d800:  ffffffff ffffffff ffffffff ffffffff    |................|
0801e000:  ffffffff ffffffff ffffffff ffffffff    |................|
0801e800:  ffffffff ffffffff ffffffff ffffffff    |................|
0801f000:  ffffffff ffffffff ffffffff ffffffff    |................|
0801f800:  6d766ea5 00000000 676f6c10 76656c5f    |mvn.....gol.vel_|  <-- page 63

~/src/py-aeric cli ❯ pyocd erase --sector 0x0800f800-0x080107ff
0000906:INFO:eraser:Erasing sector 0x0800f800 (2048 bytes)
0000934:INFO:eraser:Erasing sector 0x08010000 (2048 bytes)

~/src/py-aeric cli ❯ pyocd cmd -c read32 0x0800_0000 16 \
                               -c read32 0x0800_0800 16 \
                               -c ... \
                               -c read32 0x0801_f000 16 \
                               -c read32 0x0801_f800 16
08000000:  20008000 0800021d 08000219 08000219    | ...............|  <-- page 0
08000800:  3063737f 08000800 08000a00 00000078    |0cs............x|
08001000:  e000ed00 05fa0004 4606b573 4812b920    |........F..sH.. |
08001800:  20000764 09896800 f002b508 f7ffff79    | ..d..h........y|
08002000:  f004d10b 4b27fc41 4298681b 4b26d305    |....K'.AB.h.K&..|
08002800:  1c5e2300 0383eb00 685b4605 0686eb00    |.^#.....h[F.....|
08003000:  f04ffc37 82a00900 9f0768e2 d20b4592    |.O.7......h...E.|
08003800:  f97af001 4a0e2304 3010f8ad 5355f641    |.z..J.#.0...SU.A|
08004000:  79237962 92004914 4642a802 fbb4f003    |y#yb..I.FB......|
08004800:  ffffffff ffffffff ffffffff ffffffff    |................|  <-- page 9
08005000:  fafcf002 772b2300 f8c46963 681b611c    |....w+#...ich.a.|
08005800:  60010170 00004770 e000ed88 2300b507    |`..p..Gp....#...|
08006000:  f1049901 f0000010 4620fdbf fefff7ff    |........F ......|
08006800:  24009301 48164915 46239400 f7ff2240    |$...H.I.F#...."@|
08007000:  205cf883 2f04b2f6 e8dfd81e 0309f007    | \../...........|
08007800:  42a3d101 1a80d101 2800bd10 e7fad1f3    |B.......(.......|
08008000:  f000462a 4606fd01 d1e12800 46506921    |..F*F.....(.FPi!|
08008800:  f96af000 d1021c43 b103682b bd386023    |.j.....C..h+.8`#|
08009000:  fc02fa0c f303fa21 43014091 471cea4f    |.......!C.@.G..O|
08009800:  080020c1 08001e59 08001dd5 00000008    |.. ....Y........|
0800a000:  6573206c 6f742074 00732520 00737542    |es lot t.s% .suB|
0800a800:  3063737f 0800a800 0800aa00 00000078    |0cs............x|
0800b000:  bd70b008 e7fa2401 40021000 20000c00    |.p....$.@... ...|
0800b800:  f4227280 ea4242e6 688d2205 0320f023    |."r..BB.h.".. .#|
0800c000:  3380f423 f7ff60eb 4604fe2d 019b682b    |3..#..`.F..-..h+|
0800c800:  20006cb8 f04fb508 f3830350 f3bf8811    | .l..O.....P....|
0800d000:  41ffe92d 9201460f b9204604 512ff240    |A..-..F.. F.Q/.@|
0800d800:  4b0ff9c5 681b6aea 429a6adb 2401bf28    |K...h.j.B.j.$..(|
0800e000:  08014c45 20006db4 20006d38 20006cc4    |..LE .m. .m8 .l.|
0800e800:  c002f81a ea0c4240 43080000 0002f80a    |......B@C.......|
0800f000:  b33cfe3d 9b044922 3000f8c9 72f8f641    |.<.=..I"0...r..A|
0800f800:  2304e9cd fa51b2e2 f5b4f484 f2006f01    |#....Q........o.|  <-- page 31
08010000:  ffffffff ffffffff ffffffff ffffffff    |................|  <-- page 32
08010800:  46594620 f814f004 32016832 46032a03    |FYF ....2.h2F.*.|
08011000:  f0002c00 4f3180e7 80bcf8df 040cf106    |..,.O1..........|
08011800:  9b0b930f 2520bb2d 0a10f04f f640950b    |....% .-...O.@..|
08012000:  930100e2 f202fa00 91002301 f7fc4628    |..........#...F(|
08012800:  f1a70807 00e40818 bf2845a0 ab0946a0    |.........(E...F.|
08013000:  d1eb2b02 9068f8df f7ff2000 eb00f9fd    |..+..h.... .....|
08013800:  46382104 ff62f7ff 60304604 d1d52800    |F8!..b..`0F...(.|
08014000:  60334613 2043f891 460c4607 3301b10a    |`3F. C..F.F.3...|
08014800:  f202fa20 0002eb43 f020bf08 47700001    |... ...C. ..Gp..|
08015000:  52437325 696d2043 74616d73 72006863    |RCs%im Ctamsr.hc|
08015800:  72206f66 65757165 25207473 72662075    |r ofeuqe% tsrf u|
08016000:  00000000 00000000 00000000 00000000    |................|
08016800:  ffffffff ffffffff ffffffff ffffffff    |................|
08017000:  ffffffff ffffffff ffffffff ffffffff    |................|
08017800:  ffffffff ffffffff ffffffff ffffffff    |................|
08018000:  ffffffff ffffffff ffffffff ffffffff    |................|
08018800:  ffffffff ffffffff ffffffff ffffffff    |................|
08019000:  ffffffff ffffffff ffffffff ffffffff    |................|
08019800:  ffffffff ffffffff ffffffff ffffffff    |................|
0801a000:  ffffffff ffffffff ffffffff ffffffff    |................|
0801a800:  ffffffff ffffffff ffffffff ffffffff    |................|
0801b000:  ffffffff ffffffff ffffffff ffffffff    |................|
0801b800:  ffffffff ffffffff ffffffff ffffffff    |................|
0801c000:  ffffffff ffffffff ffffffff ffffffff    |................|
0801c800:  ffffffff ffffffff ffffffff ffffffff    |................|
0801d000:  ffffffff ffffffff ffffffff ffffffff    |................|
0801d800:  ffffffff ffffffff ffffffff ffffffff    |................|
0801e000:  ffffffff ffffffff ffffffff ffffffff    |................|
0801e800:  ffffffff ffffffff ffffffff ffffffff    |................|
0801f000:  ffffffff ffffffff ffffffff ffffffff    |................|
0801f800:  6d766ea5 00000000 676f6c10 76656c5f    |mvn.....gol.vel_|  <-- page 63

If you can arrange shipping, I could send you a board from Germany, we have a few spare ones.

flit commented 2 years ago

Thanks for the offer to send a board! Let me check with my ST contacts first, and I'll let you know.

Good to see that FLASHSIZE_BASE and DBGMCU.IDCODE are as expected. I didn't really expect any difference, but it's good to eliminate a couple variables.

And sorry that the user script didn't work immediately. That's quite unexpected that the updated flash algo didn't fail. Of course, erasing sector 9 is even more unexpected. 😦 I will walk through the pyocd side of the code to double check the correct addresses are passed into the flash algo.

Since sector 32 was actually erased (in the second memory dump), I guess you used the updated algo for the tests, and it also exhibits the same sector address anomalies?

I'll try to reproduce this issue by running the same flash algo on an STM32L475 board. The OpenOCD stm32l4xx.c flash driver is used for G4xx devices, too, with only minor differences for things like OPT bytes.

flit commented 2 years ago

Doesn't look like ST can get me a Nucleo-G431KB, so I'll take you up your offer of sending a board. 😄 Why don't you email me so we can discuss non-publicly? (See my GH profile for email address.)

Also, I did try testing with the L475. The G431 flash algos do actually work ok on the L475, it seems. Sector erase did not fail with an error. It did reproduce the same incorrect erased sector error, though instead of incorrectly erasing sector 9 it was 11.

elfmimi commented 2 years ago

I can't spot where any bug is hiding. But I think using older version of packs will likely do some difference. Maybe Keil.STM32G4xx_DFP.1.2.0.pack or Keil.STM32G4xx_DFP.1.1.0.pack.

flit commented 2 years ago

Update (now that I have a board thanks for @Finwood! 😄): I can produce the erase failure. Interestingly, version 1.2.0 of the STM32G4xx_DFP pack does work. Good suggestion @elfmimi!

Unfortunately the flash driver was nearly rewritten in 1.3.0 (also combining all variants into a single driver with config by macro), so diff'ing produces too many changes to be helpful.

Continuing debug…

flit commented 2 years ago

First discovery: Looks like the Keil folks forgot to update the .FLM files in the 1.4.0 release of the DFP.

Version 1.3.0 had a broken GetFlashType() (that matches against DBGMCU.IDCODE & 0xfff) switch statement with case 0x468 | 0x479:. This was fixed in the 1.4.0 source to separate case statements. But the .FLM file wasn't rebuilt, I guess. This is why the version of the algo I modified to return the error code didn't fail the same way.

Here's the disassembly of GetFlashType() from 1.4.0 that shows it always returns 1 for a low 12-bit of IDCODE of 0x468 (the value from the G431KB).

000001e8 <GetFlashType>:
 1e8: 480f          ldr r0, [pc, #60]   ; (228 <GetFlashBankMode+0x2c>)
 1ea: 6800          ldr r0, [r0, #0]
 1ec: f3c0 000b     ubfx    r0, r0, #0, #12
 1f0: f5a0 6080     sub.w   r0, r0, #1024   ; 0x400
 1f4: 3879          subs    r0, #121    ; 0x79
 1f6: d000          beq.n   1fa <GetFlashType+0x12>
 1f8: 2001          movs    r0, #1
 1fa: 4770          bx  lr

Tomorrow onto the messed up sector erase number! 🚀

flit commented 2 years ago

Alright, after rebuilding the algo from the 1.4.0 using the included MDK project, everything checks out. No further errors.

I wrote a script that runs a specified flash algo through a test sequence to verify that it can erase all sectors, and only the requested sector is erased. Eventually this script will be added to the pyocd repo under scripts/. Hopefully this script will make things a lot faster and simpler next time there's buggy flash algo!

Tomorrow I'll report the issue to the Keil team. Until a fixed version of the pack is released, you'll have to manually override the flash algo (using either method from earlier in this issue). The working .flm file is attached here.

Thanks for sticking with me! 😄 And thanks again for sending the board. Sorry the issue isn't one that I can simply fix myself in pyocd—this is unfortunate side of the CMSIS-Pack story.

STM32G43x-4x_128.zip

Finwood commented 2 years ago

Update: as of 2022-06-29, this bug is solved upstream in Keil.STM32G4xx_DFP.1.5.0.pack 🎉