Skip to main content
Visitor II
February 16, 2023
Question

The STM32G0 series microcontroller enters undescribed mode.

  • February 16, 2023
  • 7 replies
  • 5369 views

I have been sitting with this question for more than a week, I have already run out of strength, I cannot understand how it works and what I am doing wrong ...

And so, I have an STM32G030F6P6 microcontroller. My program needs to do two things with flash memory, namely:

1) Save 8 bit number. I do it like this:

	FLASH->KEYR = 0x45670123;
	FLASH->KEYR = 0xCDEF89AB;
 
	while (FLASH->SR & FLASH_SR_BSY1);
 
	FLASH->SR |= FLASH_SR_OPTVERR;
	FLASH->SR |= FLASH_SR_FASTERR;
	FLASH->SR |= FLASH_SR_MISERR;
	FLASH->SR |= FLASH_SR_PGSERR;
	FLASH->SR |= FLASH_SR_SIZERR;
	FLASH->SR |= FLASH_SR_PGAERR;
	FLASH->SR |= FLASH_SR_WRPERR;
	FLASH->SR |= FLASH_SR_PROGERR;
	FLASH->SR |= FLASH_SR_OPERR;
 
	FLASH->CR |= FLASH_CR_PER; //Page erase enable
	FLASH->CR |= (15<<FLASH_CR_PNB_Pos); //Page number selection
	FLASH->CR |= FLASH_CR_EOPIE; //End-of-operation interrupt enable
	FLASH->CR |= FLASH_CR_STRT; // Start erase operation
 
	while (FLASH->SR & FLASH_SR_BSY1);
 
	if (FLASH->SR & FLASH_SR_EOP)
	{
		FLASH->SR |= FLASH_SR_EOP;
	}
 
	FLASH->CR &= ~FLASH_CR_PER; //Page erase disabled
	//--------------------------------------------------
	while (FLASH->SR & FLASH_SR_BSY1);
 
	FLASH->SR |= FLASH_SR_OPTVERR;
	FLASH->SR |= FLASH_SR_FASTERR;
	FLASH->SR |= FLASH_SR_MISERR;
	FLASH->SR |= FLASH_SR_PGSERR;
	FLASH->SR |= FLASH_SR_SIZERR;
	FLASH->SR |= FLASH_SR_PGAERR;
	FLASH->SR |= FLASH_SR_WRPERR;
	FLASH->SR |= FLASH_SR_PROGERR;
	FLASH->SR |= FLASH_SR_OPERR;
 
	FLASH->CR |= FLASH_CR_PG; //Flash memory programming enable
	FLASH->CR |= (15<<FLASH_CR_PNB_Pos); //Page number selection
	FLASH->CR |= FLASH_CR_EOPIE; //End-of-operation interrupt enable
 
	*(__IO uint32_t*)0x08007800=(uint32_t)mydata;
	*(__IO uint32_t*)0x08007804=0;
 
	while (FLASH->SR & FLASH_SR_BSY1);
 
	if (FLASH->SR & FLASH_SR_EOP)
	{
		FLASH->SR |= FLASH_SR_EOP;
	}
	FLASH->CR &= ~FLASH_CR_PG; //Page programming disabled
	FLASH->CR &= ~FLASH_CR_EOPIE; //End-of-operation interrupt disabled
 
	FLASH->CR |= FLASH_CR_LOCK;

2) Set read protection memory. I do it like this:

 if ((FLASH->OPTR & FLASH_OPTR_RDP) != 0xBB)
 {
 	 FLASH->KEYR = 0x45670123;
 	 FLASH->KEYR = 0xCDEF89AB;
 
 	 FLASH->OPTKEYR = 0x08192A3B;
 	 FLASH->OPTKEYR = 0x4C5D6E7F;
 
 	 while (FLASH->SR & FLASH_SR_BSY1);
 
 	 FLASH->OPTR |= (0xBB << FLASH_OPTR_RDP_Pos);
 
 	 while (FLASH->SR & FLASH_SR_BSY1);
 	 FLASH->CR |= FLASH_CR_OPTSTRT;
 	 while (FLASH->SR & FLASH_SR_BSY1);
 	 FLASH->CR |= FLASH_CR_LOCK;
 }

After, if you restart the power supply, the microcontroller goes into some kind of incomprehensible state and it can no longer be programmed. It generally ceases to be determined by the programmer through the SWD interface. I also see that my program is not running. (I know this because it should flash the LED)

At the same time, I noticed that after I program the microcontroller, but do not turn off the power, I have the opportunity to connect to it and even erase the memory or write a new program to it. But no matter what I do after a power reset, everything leads to one result - the inability to connect to this microcontroller in any way.

I also noticed that after programming with this program, when I am in debug mode, sometimes (I did not find a pattern) the program can go into the address space> 0x1fff0000 and crashes there.

I don’t know what exactly affects the microcontroller writing to memory or setting read protection. But if I remove these two functions from the program, everything works without problems.

Thus, I have already spoiled more than 10 microcontrollers, and in order to continue to deal with this problem, I do not want to sacrifice new ones.

I use the original ST LINK V2 programmer and original STM32G030F6P6 microcontrollers, as well as the latest versions of STM32CubeIDE 1.11.2, STM32CubeProgrammer v2.12.0 and STM32 ST-LINK Utility.

The programmer is connected according to this scheme

If I forgot to mention something, I am ready to answer any questions. If you need to provide any diagrams, code snippets or any other information, just ask.

I welcome any idea, any opportunity to get to the bottom of the truth! If you need to "kill" more controllers - I'm ready!

If anyone has any ideas how to bring broken microcontrollers back to life (any idea) I'm willing to try.

    This topic has been closed for replies.

    7 replies

    Visitor II
    February 16, 2023

    Is it a dual or single bank flash?

    If not dual, your code must execute in RAM as the flash is disconnected during erase/write.

    red15530Author
    Visitor II
    February 16, 2023

    It is single bank flash

    Graduate II
    February 16, 2023

    Remove from this scheme all pullups. For example G0F6 have BOOT0 and PA14 SWCLK on same pin usw. 1FFF0000 is on empty flash normal boot step into system mem ...

    red15530Author
    Visitor II
    February 16, 2023

    All programming pins in my scheme are no pull-up, pull-down

    Graduate II
    February 16, 2023

    Doing multiple RMW to clear the errors is massively inefficient. Speed perhaps not the goal here, but the code size.

    I'd be masking the new pattern on to OTPR, not just or-ing on additional bits to get a non-viable value.

    Perhaps try with the library, or take some cues from it.

    /**
     * @brief Set user & RDP configuration
     * @note !!! Warning : When enabling OB_RDP level 2 it is no more possible
     * to go back to level 1 or 0 !!!
     * @param UserType The FLASH User Option Bytes to be modified.
     * This parameter can be a combination of @ref FLASH_OB_USER_Type
     * @param UserConfig The FLASH User Option Bytes values.
     * This parameter can be a combination of:
     * @arg @ref FLASH_OB_USER_BOR_ENABLE(*)
     * @arg @ref FLASH_OB_USER_BOR_LEVEL(*)
     * @arg @ref FLASH_OB_USER_RESET_CONFIG(*)
     * @arg @ref FLASH_OB_USER_nRST_STOP
     * @arg @ref FLASH_OB_USER_nRST_STANDBY
     * @arg @ref FLASH_OB_USER_nRST_SHUTDOWN(*)
     * @arg @ref FLASH_OB_USER_IWDG_SW
     * @arg @ref FLASH_OB_USER_IWDG_STOP
     * @arg @ref FLASH_OB_USER_IWDG_STANDBY
     * @arg @ref FLASH_OB_USER_WWDG_SW
     * @arg @ref FLASH_OB_USER_SRAM_PARITY
     * @arg @ref FLASH_OB_USER_BANK_SWAP(*)
     * @arg @ref FLASH_OB_USER_DUAL_BANK(*)
     * @arg @ref FLASH_OB_USER_nBOOT_SEL
     * @arg @ref FLASH_OB_USER_nBOOT1
     * @arg @ref FLASH_OB_USER_nBOOT0
     * @arg @ref FLASH_OB_USER_INPUT_RESET_HOLDER(*)
     * @param RDPLevel specifies the read protection level.
     * This parameter can be one of the following values:
     * @arg @ref OB_RDP_LEVEL_0 No protection
     * @arg @ref OB_RDP_LEVEL_1 Memory Read protection
     * @arg @ref OB_RDP_LEVEL_2 Full chip protection
     * @note (*) availability depends on devices
     * @retval None
     */
    static void FLASH_OB_OptrConfig(uint32_t UserType, uint32_t UserConfig, uint32_t RDPLevel)
    {
     uint32_t optr;
     
     /* Check the parameters */
     assert_param(IS_OB_USER_TYPE(UserType));
     assert_param(IS_OB_USER_CONFIG(UserType, UserConfig));
     assert_param(IS_OB_RDP_LEVEL(RDPLevel));
     
     /* Configure the RDP level in the option bytes register */
     optr = FLASH->OPTR;
     optr &= ~(UserType | FLASH_OPTR_RDP);
     FLASH->OPTR = (optr | UserConfig | RDPLevel);
    }

    Yes, these protection methods are a bit of a minefield, plenty of opportunity to brick devices. Have code on-board that you can debug and interact with, like a old-school monitor type application, so you can dump, and peek/poke registers and memory, without needing a debug pod.

    red15530Author
    Visitor II
    February 17, 2023

    Thanks to everyone who is trying to help. Sorry for not answering right away, I needed time and this is what I came up with:

    1) Now I have one microcontroller connected to debugging with which I can test, so if you have any ideas, write!

    2) If you run any program (without any interruptions - this is important), then everything works fine, the program is executed, debugging works, it stops at breakpoints. When debugging is disabled, everything continues to work.

    3) If you configure at least one any interrupt (I tried it from ADC, DMA, TIM), then something unknown happens. As soon as an interrupt should occur, the program jumps to >0x1fff0000 and stays there forever. Next, I spent a lot of time reading about how interrupts work. As I understood from the documentation:

    There is a table of interrupt vectors. RM0454 Rev 5 page 250/989

    0693W00000aH7HWQA0.pngWhen and how it is written, I did not quite understand, but as I understand it, it is written to the address that lies in the VTOR register

    0693W00000aH7HbQAK.pngIn the end, I put a breakpoint before starting the interrupt and looked at this register

    0693W00000aH7HvQAK.pngI saw 0 there, which seemed strange to me ... It turns out that the addresses of the interrupt vectors are from address 0?

    Next, I unpaused the program so that an interrupt occurred and also hung on the address space> 0x1fff0000, but if you pause in the VTOR register, it will already be just 0x1fff0000. Coincidence? Don't think. I also noticed that in FLASH the Latency register was set to 2. I don’t know what this is connected with, but it’s a fact.

    0693W00000aH7IUQA0.png4) I realized that all this goes away not when I set the read protection, but when I write to the last page of the flash memory. What can happen that the controller goes into such a state?

    5) I deliberately do not turn off the power of this MK now, because I believe that after that it will also cease to be detected by the SWD interface.

    6) Maybe I forgot to mention something, so ask.

    7) Also, if you remove the FLASH entry from the program, then it works absolutely fine on another same microcontroller. That is, it's all about writing to memory. To be honest, it doesn’t fit in my head what can be done when writing to memory in order to spoil the microcontroller in such a way ... I thought you could get only two states, either everything works or it doesn’t work. And here it turns out that everything seems to be working, until the interrupt is triggered. And again, even the complete erasure of flash memory does not bring the microcontroller out of this state. I don't understand. After all, I could not change other parts of the memory, except for flash, or could I?

    Graduate II
    February 17, 2023

    Try use HAL

    c:\Users\YOU\STM32Cube\Repository\STM32Cube_FW_G0_V1.5.0\Projects\NUCLEO-G031K8\Examples\FLASH\FLASH_EraseProgram\

    red15530Author
    Visitor II
    February 18, 2023

    Thank you! Is there a similar LL library? Or maybe someone wrote memory programming in pure CMSIS?

    red15530Author
    Visitor II
    February 23, 2023

    Thank you all very much for your help. At the cost of another microcontroller, I finally figured out what was going on! I was wrong when I thought it was because of writing to flash. As a result, what I have now:

    We do all the points below without turning off the power.

    1) We program with any program.

    2) We put L1 protection through proprietary utilities. (STM32 ST-LINK Utility or STM32CubeProgrammer, it doesn't matter, the effect is the same), while the program stops executing for some reason.

    3) We remove L1 protection through proprietary utilities. The memory is accordingly erased. (STM32 ST-LINK Utility or STM32CubeProgrammer, it doesn't matter, the effect is the same)

    4) After that, if you program with any program where there are interruptions, then after any interruption we go to the area> 0x1fff0000 and hang there endlessly.

    If you program the program without interruptions, everything works properly.

    Next, there are two scenarios:

    - If after that you turn off and turn on the power of the microcontroller and at the same time the program is flashed without interruptions, then all work is restored and now you can flash with any program, even with interruptions, even without them. Everything is working normally.

    - If after that turn off and turn on the power supply of the microcontroller, but leave the program where the interrupts were, we get a corpse that is not detected by SWD, not by UART.

    Note 1: If you program with interrupts and put L1 on and then turn the power off and on, then everything will work fine with L1 protection.

    It follows from this that the microcontroller can be transferred to such a state only if you put L1, then remove it, and then, without turning off and on the power, program it with interrupts, which will lead to the microcontroller going to the memory area> 1FFF0000. Then we turn the power off and on and see that at startup (after about 100ms) an interrupt comes (it happened in my program) and then the program goes to the > 1FFF0000 area, from where, as I understand it, SWD is no longer available. But at the same time, in the area> 1FFF0000, the microcontroller somehow freezes, since even the bootloader does not work.

    Note 2: Tested with a clean program and a simple blinking LED

    Here is the project: https://drive.google.com/file/d/1mtBmZHD-A3YecBccCb6P2EF2i0ArUBNG/view

    - programmed, blinking works

    - set L1

    - removed L1 (memory erased)

    - programmed the same program, by interrupt I go to 0x1fff0000

    - in this state, turn off and turn on the power and after that the microcontroller is dead

    Used STM32CubeIDE 1.11.2, STM32 ST-LINK Utility and ST link V2 programmer. PC win7 x64. I used ST-LINK Utility because I can't get STM32CubeProgrammer to work on Win7. Connecting the programmer according to the scheme: https://developer.arm.com/documentation/101761/1-0/Debug-and-trace-interface/Typical-SWD-circuit. If I forgot to mention something, - just ask.

    Graduate II
    March 11, 2023

    Nothing is "dead" there, just use the proper connection mode:

    Connect mode: Under Reset.

    Reset mode : Hardware reset

    The NRST has to be connected to the debugger and remove all of those 10k pull-up/down resistors, if you have those. The correct schematic is shown in AN5096.

    FLASH->SR |= FLASH_SR_OPTVERR;
    FLASH->SR |= FLASH_SR_FASTERR;
    FLASH->SR |= FLASH_SR_MISERR;
    FLASH->SR |= FLASH_SR_PGSERR;
    FLASH->SR |= FLASH_SR_SIZERR;
    FLASH->SR |= FLASH_SR_PGAERR;
    FLASH->SR |= FLASH_SR_WRPERR;
    FLASH->SR |= FLASH_SR_PROGERR;
    FLASH->SR |= FLASH_SR_OPERR;

    First, do not use an RMW operation ( |= ). Second, use a single operation for all the flags.

    FLASH->SR = FLASH_SR_OPTVERR | FLASH_SR_FASTERR | FLASH_SR_MISERR |
    	FLASH_SR_PGSERR | FLASH_SR_SIZERR | FLASH_SR_PGAERR |
    	FLASH_SR_WRPERR | FLASH_SR_PROGERR | FLASH_SR_OPERR;