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 18, 2022

    Found it. Nowhere in the HAL documentation does it say you have to first HAL_FLASH_Unlock() then HAL_FLASH_OB_Unlock();

    But that is what you have to do.

    At least it is not crashing.

    Kip

    KiptonMAuthor
    Explorer
    April 18, 2022

    I read the address 0x1FFF7800 into a uint32_t, Change the bits I need to the correct value.

    Put it into a uint64_t as the lower 4 bytes, then did a bit inversion into the upper 4 bytes. And tried to write. It returned a 1. 0 is O.K.

    No clue from the Documentation.

    KiptonMAuthor
    Explorer
    April 18, 2022

    It gets into FLASH_WaitForLastOperation(uint32_t Timeout)|

    And it appears the Flash Status register has the WRPERR bit set. It says it is set by hardware when an address to be erased/programmed belongs to a write protected part (by WRP, PCROP, or RDP Level 1) of the Flash Memory.

    I thought the purpose of the HAL_FLASH_OB_Unlock() was to unlock the OB memory.

    Again nothing in the HAL documentation about this.

    KiptonMAuthor
    Explorer
    April 18, 2022

    WRPERR is for user area. Not the OB area. Why am I getting a WRPERR error?

    KiptonMAuthor
    Explorer
    April 18, 2022

    I think I traced down the latest error.

    In the routine below when I write the first uint32_t (in line 13) CFGBSY is set to 1 it never checks to see if it is cleared. So it tries to write the second one (in line 20) while it is busy, which generates an error.

    /**
     * @brief Program double-word (64-bit) at a specified address.
     * @param Address Specifies the address to be programmed.
     * @param Data Specifies the data to be programmed.
     * @retval None
     */
    static void FLASH_Program_DoubleWord(uint32_t Address, uint64_t Data)
    {
     /* Set PG bit */
     SET_BIT(FLASH->CR, FLASH_CR_PG);
     
     /* Program first word */
     *(uint32_t *)Address = (uint32_t)Data;
     
     /* Barrier to ensure programming is performed in 2 steps, in right order
     (independently of compiler optimization behavior) */
     __ISB();
     
     /* Program second word */
     *(uint32_t *)(Address + 4U) = (uint32_t)(Data >> 32U);
    }
     
    KiptonMAuthor
    Explorer
    April 18, 2022

    So I decided to call Program Memory with FLASH_TYPEPROGRAM_FAST since it uses a different routine.

    But when it gets to the other code FLASH_Program_Fast(Address, (uint32_t)Data); I cannot enter it. It goes directly to HardFault_Handler(void); I looked at the variables and the (uint32_t)Data said it was "Optimized out".

    KiptonMAuthor
    Explorer
    April 18, 2022

    It looks like after I write the first uint32_t in Flash_Program_DoubleWord() The FLASH->SR CFGBSY (bit 18) never resets. I would have thought it would reset when it was finished programming. The documentation does not address it. It just says it is "reset when program operation completes or is interrupted by an error."

    Explorer
    April 18, 2022

    Data variable as "Optimized Out" usually is beacuse of compilation optimization. The variable is there but the symbol for the debugger is removed by the compiler. Tweak the compilation flags to use -O0 and -g3 (on project properties).

    Is the FLASH peripheral clock enabled? Look for HAL_RCC_FLASH_CLK_ENABLE.

    Also, you might need to adjust flash latency depending on your system clock

    KiptonMAuthor
    Explorer
    April 18, 2022

    There is no string "HAL_RCC_FLASH_CLK_ENABLE" and no "HAL_RCC_FLA" and no "FLASH_CLK" I cannot find anything in there with that string or with the part of the string in the UM1940 or in RM0444.

    I cannot find anything about a FLASH clock in RCC or in Flash sections.

    The Optimized out variable meant I could not easily see it. I am pretty sure it is correct. I can put it into a print statement and see it on the serial port terminal.

    My system clock for the STM32G031 is 64MHz. I do not see anywhere to set the latency in the MX program.

    Explorer
    April 19, 2022

    RCC's AHBENR register does have a bit to control power to FLASH. On runtime you can check if the bit is set by adding a watch at RCC->AHBENR. Sometimes you need to reference it through the address eg.:

    *(uint32_t *)(0x40021038)

    FLASH is on bit 8 of the register. If clock to the peripheral is disabled access to any of it's registers triggers a HardFault.

    You can activate the peripheral by directly enabling on your code:

    RCC->AHBENR |= RCC_AHBENR_FLASH; // If this define is not set use the numerical value 0x00000100

    This is explained on the G031 reference manual page 197 https://www.st.com/resource/en/reference_manual/rm0444-stm32g0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf

    Seems like latency is only related to memory reading, my bad :)

    You can however manually wait for the writing operation to be done before issue another one:

    while(FLASH->SR & FLASH_SR_BSY1){
     // write operation ongoing, wait
     __asm("nop");
    }

    Also, check that the region you are trying to write is not protected. And don't forget to erase the memory before re-writing (FLASH write changes a bit from 1 to 0, but not the inverse).

    KiptonMAuthor
    Explorer
    April 19, 2022

    So assuming that I have to figure out how to erase the Option bytes let's see how the ST documentation tells us to do it.

    First thing the HAL talks about blocks and sectors. RM0444 talks about blocks and pages. I am guessing a page and sector are the same thing. If they are why can ST not call them the same thing in two different documents?

    But page 69 of RM0444 says "A Main memory block containing 128 pages of 2K bytes, each page with 8 rows of 256 bytes" So what is a row for? The Option Bytes is 128 bytes. That is half a row.

    What do I erase?

    There is a function HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError);

    First thing is I have to figure out what FLASH_EraseInitTypeDef is.

    /**
     * @brief FLASH Erase structure definition
     */
    typedef struct
    {
     uint32_t TypeErase; /*!< Mass erase or page erase.
     This parameter can be a value of @ref FLASH_Type_Erase */
     uint32_t Banks; /*!< Select bank to erase.
     This parameter must be a value of @ref FLASH_Banks
     (FLASH_BANK_BOTH should be used only for mass erase) */
     uint32_t Page; /*!< Initial Flash page to erase when page erase is enabled
     This parameter must be a value between 0 and (FLASH_PAGE_NB - 1) */
     uint32_t NbPages; /*!< Number of pages to be erased.
     This parameter must be a value between 1 and (FLASH_PAGE_NB - value of initial page)*/
    } FLASH_EraseInitTypeDef;

    So what is TypeErase? It is a FLASH_Type_Erase. So what is a FLASH_Type_Erase?

    So it is a mass erase or a sector erase based on FLASHEx_Type_Erase. So what is that?

    It looks like 2 choices. FLASH_TYPEERASE_SECTORS or FLASH_TYPEERASE_MASSERASE. I do not want to erase everything, so it must be a sector.

    So what is a Bank? It is a FLASH_Bank. What is a FLASH_Bank?

    According to the UM1940 the only thing is FLASH_BANK_1

    However according to the stm32g0xx_hal-flash.h there is a possibility for FLASH_BANK_2 but I do not know what that means DBANK Support. It is not mentioned in the HAL.

    /** @defgroup FLASH_Banks FLASH Banks
     * @{
     */
    #define FLASH_BANK_1 FLASH_CR_MER1 /*!< Bank 1 */
    #if defined(FLASH_DBANK_SUPPORT)
    #define FLASH_BANK_2 FLASH_CR_MER2 /*!< Bank 2 */
    #endif /* FLASH_DBANK_SUPPORT */

    So the next item is Page. What is a page? it has to be a number between 0 and (FLASH_PAGE_NB - 1). What is the number for the Option Bytes? Am I supposed to just guess?

    Wait the HAL says the value is Sector not Page. So the data structures are even wrong. Between the two documents.

    So I still do not know how to erase the flash at 0x1FFF7800 so I can rewrite nBOOT0. nBOOT1, nBOOT_SEL. So I can boot this processor so I can flash it from the UART.

    I have been programming for over 45 years. I am not a beginner at this stuff. But I cannot program without documentation that is correct.. Especially when this is unique to your microcontroller, and only your microcontroller. 3 days is too long to sit here trying to figure this out, which should be simple if it were documented right. It is like you have software people doing this who have no experience using the code. They write something and do not worry about how to actually do something with it.

    KiptonMAuthor
    Explorer
    April 19, 2022

    I am back to the RM0444 Rev 5. This cannot be this hard.

    It lays out the memory on page 69 ad 70. Table 9>

    The Main Memory has Page numbers (which the HAL calls Sectors) But the Information Block does NOT have page numbers even though looking at Table 10 it appears the information Block is part of Main Memory Bank 1.

    Going to section 3.4.2 there is NO mention of ERASING the Option Bytes.

    I think I may have found something.

    I was using the HAL routine. HAL_FLASH_Program() where the last variable passed is a uint64_t Data.

    Looking at the code it write the 64-bytes to the memory. with a a write to the lower 32-bits first, then the upper 32-bits.

    The second 32-bits generated a write error WRPERR.

    Reading RM0444 again it says. on page 90

    Any modification of the value of one option is automatically performed by erasing user option byte pages first, and then programming all the option bytes with the values contained in the Flash memory option registers.

    The complementary values are automatically computed and written into the complemented option bytes upon setting the OPTSTRT bit

    So I should NOT write the upper 32-bits. They are automatically computed an written, if I am to believe RM0444.