Skip to main content
Explorer
April 18, 2022
Solved

What causes HAL_FLASH_OB_Unlock() to call HardFaultHandler()? It calls WRITE_REG(FLASH->OPTKEYR, FLASH_OPTKEY1); and jumps directly to HardFaultHandler().

  • April 18, 2022
  • 20 replies
  • 11712 views

The HAL_FLASH documentation is especially poor.

There in no overview of what you have to do.

You would think (because of lack of anything saying different) you have to

HAL_FLASH_OB_Unlock()

HAL_FLASH_Program(...)

HAL_FLASH_OB_Lock()

The first command HAL_FLASH_OB_Unlock() crashes.

HAL_FLASH_Program(...) says the format is

HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data)

Nowhere in the HAL Documentation's (UM1940) 1441 pages does it say what "TypeProgram" is, what it is used for, or what it should be.

It says TypeProgram indicates the way to program at a specific address. The value can be a value of FLASH Type Program. What is FLASH Type Program?

On page 234 it has

FLASH Type Program

FLASH_TYPEPROGRAM_BYTE

FLASH_TYPEPROGRAM_HALFWORD

FLASH_TYPEPROGRAM_WORD

FLASH_TYPEPROGRAM_DOUBLEWORD

I cannot program that with a byte or a halfword, or a word. it can only be a word.

Looking into the code for

HAL_FLASH_Program() in stm32g0xx_hal_flash.c it says

/**

 * @brief Program double word or fast program of a row at a specified address.

 * @param TypeProgram Indicate the way to program at a specified address.

 *           This parameter can be a value of @ref FLASH_Type_Program

 * @param Address Specifies the address to be programmed.

 * @param Data Specifies the data to be programmed

 *        This parameter is the data for the double word program and the address where

 *        are stored the data for the row fast program depending on the TypeProgram:

 *        TypeProgram = FLASH_TYPEPROGRAM_DOUBLEWORD (64-bit)

 *        TypeProgram = FLASH_TYPEPROGRAM_FAST (32-bit).

 *

 * @retval HAL_StatusTypeDef HAL Status

 */

So one of the options is FLASH_TYPEPROGRAM_FAST. But that is not on the list on page 234! Why?

The macro

#define IS_FLASH_TYPEPROGRAM(__VALUE__)        (((__VALUE__) == FLASH_TYPEPROGRAM_DOUBLEWORD) || \

                            ((__VALUE__) == FLASH_TYPEPROGRAM_FAST))

It does not say any of the other values are valid from Page 234.

I still do not know why HAL_FLASH_OB_Unlock() crashes. It is the first step.

Kip

    This topic has been closed for replies.
    Best answer by Piranha

    You are kind of reading the reference manual, but many things seem to be invisible...

    3.4.1 FLASH option byte description:

    "The option bytes can be read from the Flash memory locations listed in Table 16 or from the Option byte registers"

    And look at option byte detailed description after the table 16 - all of the bits are marked as read-only (r).

    3.4.2 FLASH option byte programming, Modifying user options:

    "2. Write the desired values in the FLASH option registers."

    3.7.8 FLASH option register (FLASH_OPTR):

    All of the bits are marked as read/write (rw).

    And here is my code on STM32L4, which does what you are trying to implement:

    #define BSP_FLASH_OPTR ( (0xFFEFF8AA & ~(FLASH_OPTR_nSWBOOT0 | FLASH_OPTR_IWDG_STDBY | FLASH_OPTR_IWDG_SW)) )
     
    void BSP_SyncOptionBytes(void)
    {
    	if (FLASH->OPTR == BSP_FLASH_OPTR) {
    		return;
    	}
     
    	if (FLASH->CR & FLASH_CR_LOCK) {
    		FLASH->KEYR = DEV_FLASH_KEY1;
    		FLASH->KEYR = DEV_FLASH_KEY2;
    	}
    	if (FLASH->CR & FLASH_CR_OPTLOCK) {
    		FLASH->OPTKEYR = DEV_FLASH_OPTKEY1;
    		FLASH->OPTKEYR = DEV_FLASH_OPTKEY2;
    	}
     
    	FLASH->OPTR = BSP_FLASH_OPTR;
    	FLASH->CR = FLASH_CR_OPTSTRT;
    	while (FLASH->SR & FLASH_SR_BSY);
     
    	FLASH->CR = FLASH_CR_OBL_LAUNCH;
    }

    20 replies

    KiptonMAuthor
    Explorer
    April 19, 2022

    It looks like I have to go to the HAL_FLASHEx routines.

    and try to figure out if the HAL_FLASHEx_OBProgram() subroutine will work.

    It needs to be passed FLASH_OBProgramInitTypeDef pointer.

    Let's see if the definition matches the HAL since we know it does not in the last case.

    /**
     * @brief FLASH Option Bytes Program structure definition
     */
    typedef struct
    {
     uint32_t OptionType; /*!< Option byte to be configured.
     This parameter can be a combination of the values of @ref FLASH_OB_Type */
     uint32_t WRPArea; /*!< Write protection area to be programmed (used for OPTIONBYTE_WRP).
     Only one WRP area could be programmed at the same time.
     This parameter can be value of @ref FLASH_OB_WRP_Area */
     uint32_t WRPStartOffset; /*!< Write protection start offset (used for OPTIONBYTE_WRP).
     This parameter must be a value between 0 and [FLASH_PAGE_NB - 1]*/
     uint32_t WRPEndOffset; /*!< Write protection end offset (used for OPTIONBYTE_WRP).
     This parameter must be a value between WRPStartOffset and [FLASH_PAGE_NB - 1] */
     uint32_t RDPLevel; /*!< Set the read protection level (used for OPTIONBYTE_RDP).
     This parameter can be a value of @ref FLASH_OB_Read_Protection */
     uint32_t USERType; /*!< User option byte(s) to be configured (used for OPTIONBYTE_USER).
     This parameter can be a combination of @ref FLASH_OB_USER_Type */
     uint32_t USERConfig; /*!< Value of the user option byte (used for OPTIONBYTE_USER).
     This parameter can be a combination of
     @ref FLASH_OB_USER_BOR_ENABLE(*),
     @ref FLASH_OB_USER_BOR_LEVEL(*),
     @ref FLASH_OB_USER_RESET_CONFIG(*),
     @ref FLASH_OB_USER_nRST_STOP,
     @ref FLASH_OB_USER_nRST_STANDBY,
     @ref FLASH_OB_USER_nRST_SHUTDOWN(*),
     @ref FLASH_OB_USER_IWDG_SW,
     @ref FLASH_OB_USER_IWDG_STOP,
     @ref FLASH_OB_USER_IWDG_STANDBY,
     @ref FLASH_OB_USER_WWDG_SW,
     @ref FLASH_OB_USER_SRAM_PARITY,
     @ref FLASH_OB_USER_BANK_SWAP(*),
     @ref FLASH_OB_USER_DUAL_BANK(*),
     @ref FLASH_OB_USER_nBOOT_SEL,
     @ref FLASH_OB_USER_nBOOT1,
     @ref FLASH_OB_USER_nBOOT0,
     @ref FLASH_OB_USER_INPUT_RESET_HOLDER(*)
     @note (*) availability depends on devices */
    #if defined(FLASH_PCROP_SUPPORT)
     uint32_t PCROPConfig; /*!< Configuration of the PCROP (used for OPTIONBYTE_PCROP).
     This parameter must be a combination of @ref FLASH_OB_PCROP_ZONE
     and @ref FLASH_OB_PCROP_RDP. Note that once set, Pcrop erase on RDP level 1 regression
     (PCROP_RDP bit) can not be reset. It will be reset by mass erase */
     uint32_t PCROP1AStartAddr; /*!< PCROP Start address (used for OPTIONBYTE_PCROP). It represents first address of start block
     to protect. Make sure this parameter is multiple of PCROP granularity: 512 Bytes.*/
     uint32_t PCROP1AEndAddr; /*!< PCROP End address (used for OPTIONBYTE_PCROP). It represents first address of end block
     to protect. Make sure this parameter is multiple of PCROP granularity: 512 Bytes.*/
     uint32_t PCROP1BStartAddr; /*!< PCROP Start address (used for OPTIONBYTE_PCROP). It represents first address of start block
     to protect. Make sure this parameter is multiple of PCROP granularity: 512 Bytes.*/
     uint32_t PCROP1BEndAddr; /*!< PCROP End address (used for OPTIONBYTE_PCROP). It represents first address of end block
     to protect. Make sure this parameter is multiple of PCROP granularity: 512 Bytes.*/
    #if defined(FLASH_DBANK_SUPPORT)
     uint32_t PCROP2AStartAddr; /*!< PCROP Start address (used for OPTIONBYTE_PCROP). It represents first address of start block
     to protect. Make sure this parameter is multiple of PCROP granularity: 512 Bytes.*/
     uint32_t PCROP2AEndAddr; /*!< PCROP End address (used for OPTIONBYTE_PCROP). It represents first address of end block
     to protect. Make sure this parameter is multiple of PCROP granularity: 512 Bytes.*/
     uint32_t PCROP2BStartAddr; /*!< PCROP Start address (used for OPTIONBYTE_PCROP). It represents first address of start block
     to protect. Make sure this parameter is multiple of PCROP granularity: 512 Bytes.*/
     uint32_t PCROP2BEndAddr; /*!< PCROP End address (used for OPTIONBYTE_PCROP). It represents first address of end block
     to protect. Make sure this parameter is multiple of PCROP granularity: 512 Bytes.*/
    #endif /* FLASH_DBANK_SUPPORT */
    #endif /* FLASH_PCROP_SUPPORT */
    #if defined(FLASH_SECURABLE_MEMORY_SUPPORT)
     uint32_t BootEntryPoint; /*!< Allow to force a unique boot entry point to Flash or system Flash */
     uint32_t SecSize; /*!< This parameter defines securable memory area width in number of pages starting from Flash base address.
     This parameter must be a value between [0] and [FLASH_PAGE_NB],
     [0] meaning no secure area defined, [1] meaning first page only protected, etc... */
    #if defined(FLASH_DBANK_SUPPORT)
     uint32_t SecSize2; /*!< This parameter defines securable memory area width in number of pages starting from 2nd Bank start address.
     This parameter must be a value between [0] and [FLASH_PAGE_NB],
     [0] meaning no secure area defined, [1] meaning first page only protected, etc... */
    #endif /* FLASH_SECURABLE_MEMORY_SUPPORT */
    #endif /* FLASH_DBANK_SUPPORT */
    } FLASH_OBProgramInitTypeDef;

    You have got to be kidding me. I need to change/verify the bits nBOOT0, nBOOT1 and nBOOT_SEL in the USER read and option protection bytes. FML.

    Let's get started

    What is Option_Type? It is FLASH_OB_Type. What is FLASH_OB_Type? (Of course the HAL calls it FLASHEx_Option_Type. Please Fix This.)

    The HAL says the values are OPTIONBYTE_WRP, OPTIONBYTE_RDP, OPTIONBYTE_USER, and OPTIONBYTE_BOR.

    And as I have come to expected it does not match what is in the code.

    /** @defgroup FLASH_OB_Type FLASH Option Bytes Type
     * @{
     */
    #define OPTIONBYTE_WRP 0x00000001U /*!< WRP option byte configuration */
    #define OPTIONBYTE_RDP 0x00000002U /*!< RDP option byte configuration */
    #define OPTIONBYTE_USER 0x00000004U /*!< USER option byte configuration */
    #if defined(FLASH_PCROP_SUPPORT)
    #define OPTIONBYTE_PCROP 0x00000008U /*!< PCROP option byte configuration */
    #endif /* FLASH_PCROP_SUPPORT */
    #if defined(FLASH_SECURABLE_MEMORY_SUPPORT)
    #define OPTIONBYTE_SEC 0x00000010U /*!< SEC option byte configuration */
    #endif /* FLASH_SECURABLE_MEMORY_SUPPORT */
     
    #if defined(FLASH_PCROP_SUPPORT) && defined(FLASH_SECURABLE_MEMORY_SUPPORT)
    #define OPTIONBYTE_ALL (OPTIONBYTE_WRP | OPTIONBYTE_RDP | OPTIONBYTE_USER | \
     OPTIONBYTE_PCROP | OPTIONBYTE_SEC) /*!< All option byte configuration */
    #else
    #define OPTIONBYTE_ALL (OPTIONBYTE_WRP | OPTIONBYTE_RDP | OPTIONBYTE_USER) /*!< All option byte configuration */
    #endif /* FLASH_PCROP_SUPPORT && FLASH_SECURABLE_MEMORY_SUPPORT */

    OPTIONBYTE_BOR is in the HAL but not here.

    OPTIONBYTE_PCROP is here but not in the HAL.

    OPTIONBYTE_SEC is in the table...

    Fortunately today I am only interested in OPTIONBYTE_USER which is in both the code and in the HAL documentation.

    Not interested in WRPArea.

    Not interested in WRPStartOffset

    This is ridiculous.

    This is where I think ST gives this code to people straight out of school, who have no experience with programming. And no supervision. They make it too complicated.

    Why have one routine so complicated? Split it up how it makes sense. You should have one routine for OPTIONBYTE_USER, another for OPTIONBYTE_WRP, and another for the other bytes that have nothing in common with the others.

    I will continue after lunch...

    KiptonMAuthor
    Explorer
    April 19, 2022

    Really fter going through al the values I see I only need USERConfig.

    Why would it not be better to have HAL_FLASHEx_OBProgramUser(uint32_t USERConfig)???

    In my case I want to keep all the other bytes in the USER bytes (a.k.a User and read protection bytes) the same but make sure nBOOT0, nBOOT1 are each 1 ad nBOOT_SEL is 0. The easiest way is

    uint32_t User_read_protectionOB = *((uint32_t *)0x1FFF7800);

    uint32_t newUser =  (User_read_protectionOB & 0xF8FFFFFF) | 0x06000000;

    FLASH_OBProgramInitTypeDef FOBP;

    memset(&FOBP,0,sizeof(FOBP));

    FOBP.OptionType = OPTIONBYTE_USER;

    FOBP.USERConfig = newUser;

    HAL_FLASHEx_OBProgram(&FOBP);

    And it does not work...

    Here is my code:

    void Flash_Allow_Reprogramming2(void)
    {
    	/*
    	 * To be able to program the flash with BOOT0 pin high, nBOOT1 must be 1 and
    	 * nBOOT_SEL must be 0. The default is nBOOT1 = 1 and nBOOT_SEL = 1. So we must
    	 * check both and fix them if they are not right.
    	 */
    	char buf[50];
    	HAL_StatusTypeDef rc;
    	uint32_t User_read_protectionOB = *((uint32_t *)0x1FFF7800);
    	uint32_t newUser = (User_read_protectionOB & 0xF8FFFFFF) | 0x06000000;
     
    	if (User_read_protectionOB != newUser) // if nBOOT0 != 1 or nBOOT1 != 1 or nBOOT_SEL != 0 we have to rewrite this dword
    	{
     
    		rc = HAL_FLASH_Unlock();
    		if (rc != HAL_OK)
    		{
    			sprintf(buf,"Error with HAL_FLASH_Unlock() %u\r\n",rc);
    			print_debug_str(buf);
     
    		}
     
    		rc = HAL_FLASH_OB_Unlock();
    		if (rc != HAL_OK)
    		{
    			sprintf(buf,"Error with HAL_FLASH_OB_Unlock() %u\r\n",rc);
    			print_debug_str(buf);
     
    		}
     
    		FLASH_OBProgramInitTypeDef FOBP;
    		memset(&FOBP,0,sizeof(FOBP)); // make all bytes in structure 0
    		FOBP.OptionType = OPTIONBYTE_USER;
    		FOBP.USERConfig = newUser;
     
    		rc = HAL_FLASHEx_OBProgram(&FOBP);
    		if (rc != HAL_OK)
    		{
    			sprintf(buf,"Error with HAL_FLASH_Program() %u\r\n",rc);
    			print_debug_str(buf);
    		}
     
    		rc = HAL_FLASH_OB_Lock();
    		if (rc != HAL_OK)
    		{
    			sprintf(buf,"Error with HAL_FLASH_OB_Lock() %u\r\n",rc);
    			print_debug_str(buf);
    		}
     
    		rc = HAL_FLASH_Lock();
    		if (rc != HAL_OK)
    		{
    			sprintf(buf,"Error with HAL_FLASH_Lock() %u\r\n",rc);
    			print_debug_str(buf);
    		}
     
    		print_debug_str("About to OBLaunch and Reboot\r\n");
    		HAL_Delay(200); // let it print
    		HAL_FLASH_OB_Launch();
     
    		// Should not get here.
    		print_debug_str("HAL_FLASH_OB_Launch() did not reboot!\r\n");
    	}
     
    }

    Everything returns HAL_OK.

    But HAL_FLASH_OB_Launch() does not reboot.

    RM0444 page 90 says:

    Option bytes loading is performed in two cases:

    – when OBL_LAUNCH bit of the FLASH control register (FLASH_CR) is set

    – after a power reset (BOR reset or exit from Standby/Shutdown modes)

    Option byte loader performs a read of the options block and stores the data into internal option registers. These internal registers configure the system and can be read by software. Setting OBL_LAUNCH generates a reset so the option byte loading is performed under system reset

    It did not reset. So it is not working.

    KiptonMAuthor
    Explorer
    April 19, 2022

    Well I got to thinking. The HAL_FLASH_OB_Launch();needs to set a bit. I probably should not unlock everything. So I changed the code to get rid of the unlocks.

    void Flash_Allow_Reprogramming2(void)
    {
    	/*
    	 * To be able to program the flash with BOOT0 pin high, nBOOT1 must be 1 and
    	 * nBOOT_SEL must be 0. The default is nBOOT1 = 1 and nBOOT_SEL = 1. So we must
    	 * check both and fix them if they are not right.
    	 */
    	char buf[50];
    	HAL_StatusTypeDef rc;
    	uint32_t User_read_protectionOB = *((uint32_t *)0x1FFF7800);
    	uint32_t newUser = (User_read_protectionOB & 0xF8FFFFFF) | 0x06000000;
     
    	uint32_t x = *((uint32_t *) 0x1FFF7800);
    	// 0x
    	sprintf(buf,"*0x1FFF7800: 0x%08lX\r\n",x);
    	print_debug_str(buf);
     
    	if (User_read_protectionOB != newUser) // if nBOOT0 != 1 or nBOOT1 != 1 or nBOOT_SEL != 0 we have to rewrite this dword
    	{
     
    		rc = HAL_FLASH_Unlock();
    		if (rc != HAL_OK)
    		{
    			sprintf(buf,"Error with HAL_FLASH_Unlock() %u\r\n",rc);
    			print_debug_str(buf);
     
    		}
     
    		rc = HAL_FLASH_OB_Unlock();
    		if (rc != HAL_OK)
    		{
    			sprintf(buf,"Error with HAL_FLASH_OB_Unlock() %u\r\n",rc);
    			print_debug_str(buf);
     
    		}
     
    		FLASH_OBProgramInitTypeDef FOBP;
    		memset(&FOBP,0,sizeof(FOBP)); // make all bytes in structure 0
    		FOBP.OptionType = OPTIONBYTE_USER;
    		FOBP.USERConfig = newUser;
     
    		rc = HAL_FLASHEx_OBProgram(&FOBP);
    		if (rc != HAL_OK)
    		{
    			sprintf(buf,"Error with HAL_FLASH_Program() %u\r\n",rc);
    			print_debug_str(buf);
    		}
    #if 0
    		rc = HAL_FLASH_OB_Lock();
    		if (rc != HAL_OK)
    		{
    			sprintf(buf,"Error with HAL_FLASH_OB_Lock() %u\r\n",rc);
    			print_debug_str(buf);
    		}
     
    		rc = HAL_FLASH_Lock();
    		if (rc != HAL_OK)
    		{
    			sprintf(buf,"Error with HAL_FLASH_Lock() %u\r\n",rc);
    			print_debug_str(buf);
    		}
    #endif
    		print_debug_str("About to OBLaunch and Reboot\r\n");
    		HAL_Delay(200); // let it print
    		HAL_FLASH_OB_Launch();
     
    		// Should not get here.
    		print_debug_str("HAL_FLASH_OB_Launch() did not reboot!\r\n");
    	}
     
    }

    Now the system reboots, but it did not change the data. And it goes into an infinite loop, of rebooting, seeing the code is not right, and trying to rewrite. Then rebooting.

    Unfortunately I can not get the debugger to stop it. Or break into it. So it looks like I essentially bricked the Nucleo board.

    KiptonMAuthor
    Explorer
    April 19, 2022

    I got the debugger to get control by going to Project->Properties->Run/Debug Settings and Edit current configuration. Under the Debugger tab where it says Reset behavior, change from None to Connect under reset.

    That seemed to work. But to be safer, I put some HAL_Delay() in my code to give me more time for the debugger to take over.

    So now the routine is:

    void Flash_Allow_Reprogramming2(void)
    {
    	/*
    	 * To be able to program the flash with BOOT0 pin high, nBOOT1 must be 1 and
    	 * nBOOT_SEL must be 0. The default is nBOOT1 = 1 and nBOOT_SEL = 1. So we must
    	 * check both and fix them if they are not right.
    	 */
    	char buf[80];
    	HAL_StatusTypeDef rc;
    	uint32_t User_read_protectionOB = *((uint32_t *)0x1FFF7800);
    	uint32_t newUser = (User_read_protectionOB & 0xF8FFFFFF) | 0x06000000;
     
     
     
    	if (User_read_protectionOB != newUser) // if nBOOT0 != 1 or nBOOT1 != 1 or 
     // nBOOT_SEL != 0 we have to rewrite this dword
    	{
     
    		uint32_t x = *((uint32_t *) 0x1FFF7800);
     
    		sprintf(buf,"\r\nStarting Flash_Allow_Reprogramming2() *0x1FFF7800: 0x%08lX\r\n",x);
    		print_debug_str(buf);
    		HAL_Delay(100); // Give it time to finish printing.
     
    		rc = HAL_FLASH_Unlock();
    		if (rc != HAL_OK)
    		{
    			sprintf(buf,"Error with HAL_FLASH_Unlock() %u\r\n",rc);
    			print_debug_str(buf);
     
    		}
     
    		rc = HAL_FLASH_OB_Unlock();
    		if (rc != HAL_OK)
    		{
    			sprintf(buf,"Error with HAL_FLASH_OB_Unlock() %u\r\n",rc);
    			print_debug_str(buf);
     
    		}
     
    		FLASH_OBProgramInitTypeDef FOBP;
    		memset(&FOBP,0,sizeof(FOBP)); // make all bytes in structure 0
    		FOBP.OptionType = OPTIONBYTE_USER;
    		FOBP.USERConfig = newUser;
     
    		rc = HAL_FLASHEx_OBProgram(&FOBP);
    		if (rc != HAL_OK)
    		{
    			sprintf(buf,"Error with HAL_FLASH_Program() %u\r\n",rc);
    			print_debug_str(buf);
    		}
     
    		print_debug_str("About to OBLaunch and Reboot\r\n");
    		HAL_Delay(5000); // let it print and delay so the debugger can interrupt if not working right
    		HAL_FLASH_OB_Launch();
     
    		// Should not get here.
    		print_debug_str("HAL_FLASH_OB_Launch() did not reboot!\r\n");
    		HAL_Delay(1000);
    	}
     
    }

    It is rebooting as expected in the HAL_FLASH_OB_Launch(); subroutine, but the USER option bytes are not changing.

    So I do not know what is going on.

    PiranhaAnswer
    Graduate II
    April 19, 2022

    You are kind of reading the reference manual, but many things seem to be invisible...

    3.4.1 FLASH option byte description:

    "The option bytes can be read from the Flash memory locations listed in Table 16 or from the Option byte registers"

    And look at option byte detailed description after the table 16 - all of the bits are marked as read-only (r).

    3.4.2 FLASH option byte programming, Modifying user options:

    "2. Write the desired values in the FLASH option registers."

    3.7.8 FLASH option register (FLASH_OPTR):

    All of the bits are marked as read/write (rw).

    And here is my code on STM32L4, which does what you are trying to implement:

    #define BSP_FLASH_OPTR ( (0xFFEFF8AA & ~(FLASH_OPTR_nSWBOOT0 | FLASH_OPTR_IWDG_STDBY | FLASH_OPTR_IWDG_SW)) )
     
    void BSP_SyncOptionBytes(void)
    {
    	if (FLASH->OPTR == BSP_FLASH_OPTR) {
    		return;
    	}
     
    	if (FLASH->CR & FLASH_CR_LOCK) {
    		FLASH->KEYR = DEV_FLASH_KEY1;
    		FLASH->KEYR = DEV_FLASH_KEY2;
    	}
    	if (FLASH->CR & FLASH_CR_OPTLOCK) {
    		FLASH->OPTKEYR = DEV_FLASH_OPTKEY1;
    		FLASH->OPTKEYR = DEV_FLASH_OPTKEY2;
    	}
     
    	FLASH->OPTR = BSP_FLASH_OPTR;
    	FLASH->CR = FLASH_CR_OPTSTRT;
    	while (FLASH->SR & FLASH_SR_BSY);
     
    	FLASH->CR = FLASH_CR_OBL_LAUNCH;
    }

    KiptonMAuthor
    Explorer
    April 20, 2022

    Interesting. I was using the HAL code.

    Give me a chance to try your way. At this point after a week of trying I do not care how to do it, I just want it to work.

    Thanks for the pointer.

    Visitor II
    April 20, 2022

    Yea, I'm having the same difficulty as your Kipton. I have code that works perfectly fine on the H7, but doing the same flow path on the G0 results in Timeouts then hardfaults.

    {	
    	__HAL_RCC_FLASH_CLK_ENABLE();
     
    	FLASH_OBProgramInitTypeDef flashDef;
    	memset(&flashDef, 0, sizeof(FLASH_OBProgramInitTypeDef));	
    	HAL_FLASHEx_OBGetConfig(&flashDef);
    	
    	if ((flashDef.USERConfig & OB_BOR_ENABLE) == 0)
    	{
    		for (int i = 0; i < 10; i++)
    		{
    			if (HAL_FLASH_Unlock() == HAL_OK)
    				break;
    		}		
    		
    		for (int i = 0; i < 10; i++)
    		{
    			if (HAL_FLASH_OB_Unlock() == HAL_OK)
    				break;
    		}	
    	
    		memset(&flashDef, 0, sizeof(FLASH_OBProgramInitTypeDef));
    		flashDef.OptionType = OPTIONBYTE_USER;
    		flashDef.USERType = OB_USER_BOR_EN;
    		flashDef.USERConfig = OB_BOR_ENABLE;
    	
    		for (int i = 0; i < 10; i++)
    		{
    			if (HAL_FLASHEx_OBProgram(&flashDef) == HAL_OK)
    				break;
    		}
    		
    		HAL_FLASH_OB_Launch();
    	
    		for (int i = 0; i < 10; i++)
    		{
    			if (HAL_FLASH_OB_Lock() == HAL_OK)
    				break;
    		}
    	
    		for (int i = 0; i < 10; i++)
    		{
    			if (HAL_FLASH_Lock() == HAL_OK)
    				break;
    		}
    	}
    }

    This code times out at the end of HAL_FLASHEx_OBProgram during the "WaitForLastOperation" function. If I continue on after that it will HardFault in the OB_Launch function.

    This is also what I'm working today when I get into the office. If I figure anything out I'll post :)

    KiptonMAuthor
    Explorer
    April 20, 2022

    This is the other thing that really makes me angry with ST.

    I cannot find DEV_FLASH_KEY1.

    In stm32g0xx.hal_flash.h it is called FLASH_KEY1

    Same with DEV_FLASH_KEY1 -- FLASH_KEY2

    Same with DEV_FLASH_OPTKEY1 -- FLASH_OPTKEY1

    Same with DEV_FLASH_OPTKEY2 -- FLASH_OPTKEY2

    They rename #defines all the time. Just pick a name and stick with it.

    My program cannot find FLASH_OPTR_nSWBOOT0, it calls it FLASH_OPTR_nBOOT0 in stm32g031xx.h

    It cannot find FLASH_SR_BSY it calls it FLASH_SR_BSY1 in stm32g031xx.h

    Why does it have to be so complicated? Piranha's code is clear and easy to read.

    ST's code is this:

    /**
     * @brief Program Option bytes.
     * @param pOBInit Pointer to an @ref FLASH_OBProgramInitTypeDef structure that
     * contains the configuration information for the programming.
     * @note To configure any option bytes, the option lock bit OPTLOCK must be
     * cleared with the call of @ref HAL_FLASH_OB_Unlock() function.
     * @note New option bytes configuration will be taken into account only
     * - after an option bytes launch through the call of @ref HAL_FLASH_OB_Launch()
     * - a Power On Reset
     * - an exit from Standby or Shutdown mode.
     * @retval HAL Status
     */
    HAL_StatusTypeDef HAL_FLASHEx_OBProgram(FLASH_OBProgramInitTypeDef *pOBInit)
    {
     uint32_t optr;
     HAL_StatusTypeDef status;
     
     /* Check the parameters */
     assert_param(IS_OPTIONBYTE(pOBInit->OptionType));
     
     /* Process Locked */
     __HAL_LOCK(&pFlash);
     
     pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;
     
     /* Write protection configuration */
     if ((pOBInit->OptionType & OPTIONBYTE_WRP) != 0x00U)
     {
     /* Configure of Write protection on the selected area */
     FLASH_OB_WRPConfig(pOBInit->WRPArea, pOBInit->WRPStartOffset, pOBInit->WRPEndOffset);
     }
     
     /* Option register */
     if ((pOBInit->OptionType & (OPTIONBYTE_RDP | OPTIONBYTE_USER)) == (OPTIONBYTE_RDP | OPTIONBYTE_USER))
     {
     /* Fully modify OPTR register with RDP & user data */
     FLASH_OB_OptrConfig(pOBInit->USERType, pOBInit->USERConfig, pOBInit->RDPLevel);
     }
     else if ((pOBInit->OptionType & OPTIONBYTE_RDP) != 0x00U)
     {
     /* Only modify RDP so get current user data */
     optr = FLASH_OB_GetUser();
     FLASH_OB_OptrConfig(optr, optr, pOBInit->RDPLevel);
     }
     else if ((pOBInit->OptionType & OPTIONBYTE_USER) != 0x00U)
     {
     /* Only modify user so get current RDP level */
     optr = FLASH_OB_GetRDP();
     FLASH_OB_OptrConfig(pOBInit->USERType, pOBInit->USERConfig, optr);
     }
     else
     {
     /* nothing to do */
     }
     
    #if defined(FLASH_PCROP_SUPPORT)
     /* PCROP Configuration */
     if ((pOBInit->OptionType & OPTIONBYTE_PCROP) != 0x00U)
     {
     /* Check the parameters */
     assert_param(IS_OB_PCROP_CONFIG(pOBInit->PCROPConfig));
     
     if ((pOBInit->PCROPConfig & (OB_PCROP_ZONE_A | OB_PCROP_RDP_ERASE)) != 0x00U)
     {
     /* Configure the 1A Proprietary code readout protection */
     FLASH_OB_PCROP1AConfig(pOBInit->PCROPConfig, pOBInit->PCROP1AStartAddr, pOBInit->PCROP1AEndAddr);
     }
     
     if ((pOBInit->PCROPConfig & OB_PCROP_ZONE_B) != 0x00U)
     {
     /* Configure the 1B Proprietary code readout protection */
     FLASH_OB_PCROP1BConfig(pOBInit->PCROP1BStartAddr, pOBInit->PCROP1BEndAddr);
     }
     
    #if defined(FLASH_DBANK_SUPPORT)
     if ((pOBInit->PCROPConfig & OB_PCROP_ZONE2_A) != 0x00U)
     {
     /* Configure the 2A Proprietary code readout protection */
     FLASH_OB_PCROP2AConfig(pOBInit->PCROP2AStartAddr, pOBInit->PCROP2AEndAddr);
     }
     
     if ((pOBInit->PCROPConfig & OB_PCROP_ZONE2_B) != 0x00U)
     {
     /* Configure the 2B Proprietary code readout protection */
     FLASH_OB_PCROP2BConfig(pOBInit->PCROP2BStartAddr, pOBInit->PCROP2BEndAddr);
     }
    #endif /* FLASH_DBANK_SUPPORT */
     }
    #endif /* FLASH_PCROP_SUPPORT */
     
    #if defined(FLASH_SECURABLE_MEMORY_SUPPORT)
     /* Securable Memory Area Configuration */
     if ((pOBInit->OptionType & OPTIONBYTE_SEC) != 0x00U)
     {
    #if defined(FLASH_DBANK_SUPPORT)
     /* Configure the securable memory area protection */
     FLASH_OB_SecMemConfig(pOBInit->BootEntryPoint, pOBInit->SecSize, pOBInit->SecSize2);
    #else
     /* Configure the securable memory area protection */
     FLASH_OB_SecMemConfig(pOBInit->BootEntryPoint, pOBInit->SecSize);
    #endif /* FLASH_DBANK_SUPPORT */
     }
    #endif /* FLASH_SECURABLE_MEMORY_SUPPORT */
     
     /* Wait for last operation to be completed */
     status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
     
     if (status == HAL_OK)
     {
     /* Set OPTSTRT Bit */
     SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT);
     
     /* Wait for last operation to be completed */
     status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
     
     /* If the option byte program operation is completed, disable the OPTSTRT Bit */
     CLEAR_BIT(FLASH->CR, FLASH_CR_OPTSTRT);
     }
     
     /* Process Unlocked */
     __HAL_UNLOCK(&pFlash);
     
     /* return status */
     return status;
    }

    And I cannot get the ST code to work.

    Visitor II
    April 20, 2022

    Pirahna's code is clear and easy to read, but in ST's defense, those aren't the #defines in the L4 framework, I think those are ones he renamed. I just checked the L4, G0 and H7 frameworks and they all have identical #defines for FLASH_KEY and FLASH_OPTKEY.

    If you dig into the WaitForLastOperation function, there is a FLASH_SR_BSY2 for dual bank parts, which presumably isn't on the L4 that Pirahna is using. In that regard, I'd prefer that they rename BSY to BSY1 so that if you are copying code between different processor families it will fail, such that it requires you to look into and to realize that the new processor has dual banks and thus requires another check. Otherwise the behavior might be extremely difficult to figure out.

    That being said, yea the HAL functions for this are definitely not working. The same flow path works great on the H7 processor I have, but it's failing on the G0. Funny enough, I CAN right the RDP value, that works. Trying to set BOR_EN is failing though...

    Edit: Let me amend that statement. I can set the RDP... but only if I build my project in Release configuration rather then Debug. Pretty sure the only real difference between those two are the optimization levels, -O3 vs -O0. BOR still doesn't get enabled though. I set up my BOR writing function identically to how I set up the RDP one...

    KiptonMAuthor
    Explorer
    April 20, 2022

    I have seen this in a lot of places

    This happens to be from stm32g0xx_hal_uart.h

    /** @defgroup UART_Request_Parameters UART Request Parameters
     * @{
     */
    #define UART_AUTOBAUD_REQUEST USART_RQR_ABRRQ /*!< Auto-Baud Rate Request */
    #define UART_SENDBREAK_REQUEST USART_RQR_SBKRQ /*!< Send Break Request */
    #define UART_MUTE_MODE_REQUEST USART_RQR_MMRQ /*!< Mute Mode Request */
    #define UART_RXDATA_FLUSH_REQUEST USART_RQR_RXFRQ /*!< Receive Data flush Request */
    #define UART_TXDATA_FLUSH_REQUEST USART_RQR_TXFRQ /*!< Transmit data flush Request */
     
     
     
    /** @defgroup UART_Flags UART Status Flags
     * Elements values convention: 0xXXXX
     * - 0xXXXX : Flag mask in the ISR register
     * @{
     */
    #define UART_FLAG_TXFT USART_ISR_TXFT /*!< UART TXFIFO threshold flag */
    #define UART_FLAG_RXFT USART_ISR_RXFT /*!< UART RXFIFO threshold flag */
    #define UART_FLAG_RXFF USART_ISR_RXFF /*!< UART RXFIFO Full flag */
    #define UART_FLAG_TXFE USART_ISR_TXFE /*!< UART TXFIFO Empty flag */
    #define UART_FLAG_REACK USART_ISR_REACK /*!< UART receive enable acknowledge flag */
    #define UART_FLAG_TEACK USART_ISR_TEACK /*!< UART transmit enable acknowledge flag */
    #define UART_FLAG_WUF USART_ISR_WUF /*!< UART wake-up from stop mode flag */
    #define UART_FLAG_RWU USART_ISR_RWU /*!< UART receiver wake-up from mute mode flag */
    #define UART_FLAG_SBKF USART_ISR_SBKF /*!< UART send break flag */
    #define UART_FLAG_CMF USART_ISR_CMF /*!< UART character match flag */
    #define UART_FLAG_BUSY USART_ISR_BUSY /*!< UART busy flag */
    #define UART_FLAG_ABRF USART_ISR_ABRF /*!< UART auto Baud rate flag */
    #define UART_FLAG_ABRE USART_ISR_ABRE /*!< UART auto Baud rate error */
    #define UART_FLAG_RTOF USART_ISR_RTOF /*!< UART receiver timeout flag */
    #define UART_FLAG_CTS USART_ISR_CTS /*!< UART clear to send flag */
    #define UART_FLAG_CTSIF USART_ISR_CTSIF /*!< UART clear to send interrupt flag */
    #define UART_FLAG_LBDF USART_ISR_LBDF /*!< UART LIN break detection flag */
    #define UART_FLAG_TXE USART_ISR_TXE_TXFNF /*!< UART transmit data register empty */
    #define UART_FLAG_TXFNF USART_ISR_TXE_TXFNF /*!< UART TXFIFO not full */
    #define UART_FLAG_TC USART_ISR_TC /*!< UART transmission complete */
    #define UART_FLAG_RXNE USART_ISR_RXNE_RXFNE /*!< UART read data register not empty */
    #define UART_FLAG_RXFNE USART_ISR_RXNE_RXFNE /*!< UART RXFIFO not empty */
    #define UART_FLAG_IDLE USART_ISR_IDLE /*!< UART idle flag */
    #define UART_FLAG_ORE USART_ISR_ORE /*!< UART overrun error */
    #define UART_FLAG_NE USART_ISR_NE /*!< UART noise error */
    #define UART_FLAG_FE USART_ISR_FE /*!< UART frame error */
    #define UART_FLAG_PE USART_ISR_PE /*!< UART parity error */

    With the renaming it makes it harder to figure out what is happening when things do not work.

    I thought Pirahna was using a driver I was not using that had done a similar renaming.

    Graduate II
    April 21, 2022

    > I cannot find DEV_FLASH_KEY1.

    These key constants are indeed defined by me. The reason for this is simple - ST has not defined those in register definition files. Such constants are only defined in HAL files, not even LL. The best would be to define those with the same CMSIS style FLASH_KEYR_KEYR_KEY1 (PERIPHERAL_REGISTER_BITFIELD_VALUE) in register definition files, but...

    > cannot find FLASH_OPTR_nSWBOOT0, it calls it FLASH_OPTR_nBOOT0

    Those two are not the same! But in this case it doesn't matter because that is a code from my project. Just redefine BSP_FLASH_OPTR with your desired values. I defined it as a hexadecimal default value for my MCU "minus" some specific bits, but that's just how I choose to format it.

    Explorer
    April 21, 2022
    stm32g0xx.h (or similar) should have the direct definitions of registers and bitfields, might make it easier to adapt Piranha's code.
    Graduate II
    April 21, 2022

    Registers and bit fields are there, but not the specific field values. In this case KEY1 and KEY2 are just a two specific values for a KEYR bit field.

    KiptonMAuthor
    Explorer
    April 21, 2022

    With thanks to Piranha I have it working. Here is my version of his code.

    #define CLEAR_BITS (~(FLASH_OPTR_nBOOT_SEL))
    #define SET_BITS (FLASH_OPTR_nBOOT0 | FLASH_OPTR_nBOOT1)
    #define BSP_FLASH_OPTR ((FLASH->OPTR & CLEAR_BITS) | SET_BITS)
     
    void BSP_SyncOptionBytes(void)
    {
    	char buf[100];
     
    	uint32_t x = *((uint32_t *) 0x1FFF7800);
    	sprintf(buf,"\r\nStarting BSP_SyncOptionBytes() *0x1FFF7800: 0x%08lX\r\n",x);
    	print_debug_str(buf);
     
     
    	sprintf(buf,"FLASH_OPTR %08lX, BSP_FLASH_OPTR %08lX\r\n",FLASH->OPTR, BSP_FLASH_OPTR);
    	print_debug_str(buf);
    	HAL_Delay(5000); // Give it time to finish printing.
     
     
    	if (FLASH->OPTR == BSP_FLASH_OPTR) {
    		return;
    	}
     
    	if (FLASH->CR & FLASH_CR_LOCK) {
    		FLASH->KEYR = FLASH_KEY1;
    		FLASH->KEYR = FLASH_KEY2;
    	}
    	if (FLASH->CR & FLASH_CR_OPTLOCK) {
    		FLASH->OPTKEYR = FLASH_OPTKEY1;
    		FLASH->OPTKEYR = FLASH_OPTKEY2;
    	}
     
    	FLASH->OPTR = BSP_FLASH_OPTR;
    	FLASH->CR = FLASH_CR_OPTSTRT;
    	while (FLASH->SR & FLASH_SR_BSY1);
     
    	FLASH->CR = FLASH_CR_OBL_LAUNCH;
    }

    Thanks all who helped. This was a tough one. Of course I did not help that I did not understand at first that you had to write to registers as opposed to Flash.

    We know the HAL code does not work for the STM32G031K6T6.

    Visitor II
    May 10, 2022

    Hey KiptonM,

    That looks great! I am hoping you could help me as well...

    I am using a Nucleo - L476RG board and would like to enter DFU mode from the software.

    I just dont want to copy and paste your code until i understand more about what i am doing.

    From the Documentation:

    https://www.st.com/resource/en/reference_manual/rm0351-stm32l47xxx-stm32l48xxx-stm32l49xxx-and-stm32l4axxx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf

    and

    https://www.st.com/resource/en/application_note/cd00167594-stm32-microcontroller-system-memory-boot-mode-stmicroelectronics.pdf

    it says i need to use Pattern 7:

    Boot0(pin) = 1, nBoot1(bit) = 1 and BFB2(bit) = 0

    to enter DFU mode.

    How could I achieve that using the code snippet you posted above?

    Would simply changing the defines above to:

    #define CLEAR_BITS    (~(FLASH_OPTR_nBOOT_SEL))

    #define SET_BITS     (FLASH_OPTR_BFB2 | FLASH_OPTR_nBOOT1)

    #define BSP_FLASH_OPTR  ((FLASH->OPTR & CLEAR_BITS) | SET_BITS)

    also, what is the purpse of:

    uint32_t x = *((uint32_t *) 0x1FFF7800); ?

    Is it in use in the code?

    Thanks for your help

    Almog

    KiptonMAuthor
    Explorer
    June 6, 2022

    Sorry I have been out for all of May and just got to checking this today.

    Boot0(pin) = 1, nBoot1(bit) = 1 and BFB2(bit) = 0

    to enter DFU mode.

     

    How could I achieve that using the code snippet you posted above?

     

    Would simply changing the defines above to:

     

    #define CLEAR_BITS    (~(FLASH_OPTR_nBOOT_SEL))

    #define SET_BITS     (FLASH_OPTR_BFB2 | FLASH_OPTR_nBOOT1)

    #define BSP_FLASH_OPTR  ((FLASH->OPTR & CLEAR_BITS) | SET_BITS)

    I do not think so. In your case you want to clear BFB2(bit) = 0

    #define CLEAR_BITS    (~(FLASH_OPTR_BFB2))

    And you want to set Boot0(pin) = 1, nBoot1(bit) = 1

    #define SET_BITS     (FLASH_OPTR_nBOOT_SEL | FLASH_OPTR_nBOOT1)

    You combine the same way

    BSP_FLASH_OPTR  ((FLASH->OPTR & CLEAR_BITS) | SET_BITS)

    also, what is the purpose of:

    uint32_t x = *((uint32_t *) 0x1FFF7800); ?

    Is it in use in the code?

    Let's step through that code.

    0x1FFF7800 is a number;

    (uint32_t *) 0x1FFF7800) says that number is an address for 32-bit unsigned data.

    *((uint32_t *) 0x1FFF7800) says get the value stored in that 32-bit unsigned address.

    uint32_t x = *((uint32_t *) 0x1FFF7800); says get the value stored in that 32-bit unsigned address and put it in x.

    I am then printing the value to see what it is. (For debugging purposes)

    Why am I interested in the data in that location? Page 81 of RM0444 says that is the Flash memory address of the "User and read protection option bytes"

    That is what gets changed by the code. I wanted to see what it was before I started.

    Graduate
    October 20, 2024

    It's not very clear from the driver documentation that you have to unlock the flash separately and as a prerequisite to unlocking the flash option bytes, but that's what fixed it for me.

     

    Here's code that works for me now, to change the BOR level in the option bytes:

     HAL_FLASHEx_OBGetConfig(&OB_to_be_programmed);
     if((OB_to_be_programmed.UserConfig & FLASH_OPTR_BOR_LEV_Msk) ==0){	//Default is zero for 1.65V
    
    	 OB_to_be_programmed.OptionType = OPTIONBYTE_USER;
    	 OB_to_be_programmed.UserType = FLASH_OPTR_BOR_LEV;
    	 OB_to_be_programmed.UserConfig |= OB_BOR_LEVEL_3; //BOR_LEVEL_3 is 2.5V
    
    	 status = HAL_FLASH_Unlock();
     	 status = HAL_FLASH_OB_Unlock();
     	 status = HAL_FLASHEx_OBProgram(&OB_to_be_programmed);
     	 status = HAL_FLASH_OB_Lock();
     	 status = HAL_FLASH_Unlock();
    }

     I tested out the reset voltage before I added this code, and it was about 1.65V.   After I added this code it was 2.5V.