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

38 REPLIES 38

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

Why is this so hard? The documentation *****. ST does not seem to care.

I have searched the internet, and the HAL code and I still cannot figure out how to rewrite the nBOOT0, nBOOT1, nBOOT_SEL bits of the Option bytes.

First thing is I cannot see how to erase it so I can change it. It looks like I can only erase a sector. But it does not say what a sector is. It seems kind of stupid to have to erase everything from 0x1FFF7800 through 01FFF7870. It is not clear what is page or what is a bank, in the Option Bytes. What do II have to erase to change those three bit?

Is there a way to erase just the 64-bits? So I can rewrite them? Or do I have to erase the 128 bytes? Or is the minimum amount to erase 2K? If it is 2K then I have to save all those other 2047 bytes so I can change the 3 bits in the third byte?

The HAL has a routine HAL_FLASHEx_OBProgram() but I can not determine from looking at the source code that it deletes the memory before programming. So I can not tell from the code if it erases or or not, but it looks like it does not erase.

Again poor documentation everywhere. I guess I am the ONLY person in the WORLD that needs to do this.

KiptonM
Lead

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.

KiptonM
Lead

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.

KiptonM
Lead

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

KiptonM
Lead

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.

KiptonM
Lead

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.

If you want to program the Option bits you might not need to erase a page. One of the reasons I usually don't use the HAL libraries is that, documentation is poor. And if you want to deviate a bit it usually breaks the library down.
The reference manual I linked in the previous comment is pretty complete though. Try looking on it for boot1 and you might have a better view of the process.
The hal follows the reference nomeclature so bit FLASH on register AHBENR from RCC should be named as RCC_AHBENR_FLASH on the C libraries
Close to the definition of OPTKEYR you might find the bitfield of the register and how to write it.
KiptonM
Lead

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.

Piranha
Chief II

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;
}