cancel
Showing results for 
Search instead for 
Did you mean: 

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

KiptonM
Lead

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

37 REPLIES 37

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.

JKaz.1
Associate III

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 🙂

KiptonM
Lead

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.

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...

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.

That looks like they're remaining it to be clearer but they are defining the new name as the old name to maintain backwards compatibility. I don't really see anything wrong with that philosophy. I'd rather full words describing something rather than undecipherable abbreviations. Buuut that's an entirely different discussion.

Ok, so I can set the Protection Level to 1... sometimes. I haven't actually figured out what particular things make it correctly work sometimes and not others, but I am making NO changes to the actual function that sets the protection level. It seems to do with how I construct other parts of the code? Seriously, this took all of 5 minutes to get working on the H7, wtf guys.

Actually, it just occurred to me you might not be using an IDE that lets you just Right-Click and "Go to definition", which makes tracing these define renames rather trivial. In that case yes, I could see why that would be annoying. For me it's just another few mouse clicks away.

I made the mistake of Accidentally running Piranas code on my STM32G031 Nucleo Board.

I forgot that you can not step through the code, then go make some fixes. Because when you get out of the debugger the processor starts running. So it worked and now it looks like I can program the Nucleo board from the STMCubeIDE , but it will not run.

I think it is starting automatically in the bootloader.

On page AN2606 there is a Table 2 pattern 11 (Page 28) which is for the STM32G0.

They have 4 patterns.

The First pattern is nBoot0(bit) = 0 nBoot1(bit) = 1, nBOOT0_SEL(bit) = 1 and BOOT_LOCK(bit) = 0

This is the pattern Pirahna used, which makes it always go to the bootloader.

I planned to use the Second Pattern

Boot0(pin) = 1, nBoot1(bit) = 1, nBOOT0_SEL(bit) = 0 I could run like normal when coming out of reset PA14/Boot0 pin was 0 and go into bootloader mode when coming out of reset PA14/Boot0 was 1.

Now I cannot get out of the bootloader. It looks like I can still load code, but I can not execute it.

I think I have to write some code for the UART to end bootloader and start the code to set the Option bits the way I need them.

Anyway, I am making progress, and the code worked, but it programmed the bits not the way I needed them. The code is fixed, and I think loaded into Flash. Now I have to leave bootmode and execute it.

You can use the STM32CubeProgrammer and connect to the device using Mode: Under reset, Reset Mode: Hardware reset.

Doing that, you should be able to erase the flash to wipe out your program and then go to the Option bytes page and fix the boot settings.

Thanks, that worked. Never used the Programmer, so I did not know it could set the option bytes.