Skip to main content
arnold_w
Senior II
March 11, 2019
Question

On what address do the Option Bytes on STM32F446 reside on?

  • March 11, 2019
  • 19 replies
  • 3775 views

I want to read the Option Bytes values from my STM32F446 in order to debug strange behavior. On what addresses do they reside? According to the Reference Manual they reside on addresses 0x1FFFC000 and 0x1FFFC008, but when I look at the implementation of HAL_FLASHEx_OBGetConfig it is reading from addresses OPTCR_BYTE0_ADDRESS, OPTCR_BYTE1_ADDRESS, OPTCR_BYTE2_ADDRESS, and OPTCR_BYTE3_ADDRESS (0x40023C14 - 0x40023C17). Can someone please clarify?

This topic has been closed for replies.

19 replies

arnold_w
arnold_wAuthor
Senior II
December 2, 2019

I tried the following code:

#define OPTION_BYTES_MASK (0b10000000111111111111111111101100)
#define OPTION_BYTES_FROM_FLASH ((((uint32_t) *((volatile uint16_t*)0x1FFFC008)) << 16) | \
 (((uint32_t) *((volatile uint16_t*)0x1FFFC000)) << 0))
#define DESIRED_OPTION_BYTES ((((uint32_t) OB_PCROP_DESELECTED) << 24) | \
 (((uint32_t) 0xFF) << 16) | \
 (((uint32_t) OB_RDP_LEVEL_0) << 8) | \
 (((uint32_t) (OB_STDBY_NO_RST | OB_STOP_NO_RST | OB_IWDG_SW | OB_BOR_LEVEL3)) << 0))
 
void updateOptionBytesIfNecessary() {
 if ((DESIRED_OPTION_BYTES != (OPTION_BYTES_FROM_FLASH & OPTION_BYTES_MASK)) ||
 (DESIRED_OPTION_BYTES != (FLASH->OPTCR & OPTION_BYTES_MASK))) {
 FLASH->OPTKEYR = FLASH_OPT_KEY1; // Unlock option bytes
 FLASH->OPTKEYR = FLASH_OPT_KEY2; // Unlock option bytes
 
 while(FLASH->SR & FLASH_SR_BSY) { } // Wait until busy flag goes low
 FLASH->OPTCR = DESIRED_OPTION_BYTES; // Update the Option Bytes
 FLASH->OPTCR |= FLASH_OPTCR_OPTSTRT; // Set the option start bit
 while(FLASH->SR & FLASH_SR_BSY) { } // Wait until busy flag goes low
 FLASH->OPTCR |= FLASH_OPTCR_OPTLOCK; // Lock the flash
 }
}

The code above can successfully update nRST_STDBY, rRST_STOP, WDG_SW, BOR-level, Read Protection Level 1 -> Read Protection Level 0, and nWRT as long as SPRMOD is 0. If SPRMOD is 1, then it will not update SPRMOD or nWRT. Does anybody know what's wrong?

arnold_w
arnold_wAuthor
Senior II
December 2, 2019

I guess I have found the reason why SPRMOD can't be changed from 1 to 0. From the reference manual, section "Proprietary code readout protection (PCROP)":

"The deactivation of the SPRMOD and/or the unprotection of PCROPed user sectors can only occur when, at the same time, the RDP level changes from 1 to 0. If this condition is not respected, the user option byte modification is canceled and the write error WRPERR flag is set. The modification of the users option bytes (BOR_LEV, RST_STDBY, ..) is allowed since none of the active nWRPi bits is reset and SPRMOD is kept active."

Does this mean setting SPRMOD 1 -> 0 needs to be done in 3 steps, first setting Read Protection (RDP) to level 1, then setting SPRMOD to 0, and then setting Read Protection (RDP) back to level 0?

waclawek.jan
Super User
December 2, 2019

AFAIK this is what STLink Utility does too. This implicitly involves bulkerase, so it is rather time consuming.

I'm not sure why do you want to do this, but at this point it sounds that you already know more than me on this issue, so I can't help you anymore... :)

JW

arnold_w
arnold_wAuthor
Senior II
December 3, 2019

I think I solved it!!! A little disappointing that the HAL-function doesn't take the SPRMOD == 1 special case into account.

#define LOG_STRING(__STRING__,__POST_TRANSMISSION_OPTION__)
 
#define OPTION_BYTES_MASK (0b10000000111111111111111111101100)
#define OPTION_BYTES_FROM_FLASH ((((uint32_t) *((volatile uint16_t*)0x1FFFC008)) << 16) | \
 (((uint32_t) *((volatile uint16_t*)0x1FFFC000)) << 0))
#define DESIRED_OPTION_BYTES ((((uint32_t) OB_PCROP_DESELECTED) << 24) | \
 (((uint32_t) 0xFF) << 16) | \
 (((uint32_t) OB_RDP_LEVEL_0) << 8) | \
 (((uint32_t) (OB_STDBY_NO_RST | OB_STOP_NO_RST | OB_IWDG_SW | OB_BOR_LEVEL3)) << 0))
 
void updateOptionBytesIfNecessary() {
 if (DESIRED_OPTION_BYTES == (OPTION_BYTES_FROM_FLASH & OPTION_BYTES_MASK)) {
 LOG_STRING("\r\nOption bytes are already up-to-date", SWITCH_TO_Rx_MODE_WHEN_DONE);
 return;
 }
 
 LOG_STRING("\r\nOption bytes need updating", STAY_IN_Tx_MODE_WHEN_DONE);
 
 uint8_t SPRMOD = (uint8_t)(OPTION_BYTES_FROM_FLASH >> 31);
 uint8_t RDP_LEVEL = (((uint8_t)(OPTION_BYTES_FROM_FLASH >> 8)) == OB_RDP_LEVEL_0) ? OB_RDP_LEVEL_0 :
 ((((uint8_t)(OPTION_BYTES_FROM_FLASH >> 8)) == OB_RDP_LEVEL_2) ? OB_RDP_LEVEL_2 : OB_RDP_LEVEL_1);
 Bool_t flashWillBeErased = (Bool_t)((SPRMOD == 1) || (RDP_LEVEL != OB_RDP_LEVEL_0));
 FLASH->OPTKEYR = FLASH_OPT_KEY1; // Unlock option bytes
 FLASH->OPTKEYR = FLASH_OPT_KEY2; // Unlock option bytes
 while(FLASH->SR & FLASH_SR_BSY) { } // Wait until busy flag goes low
 
 if (SPRMOD == 1) {
 LOG_STRING("\r\nSPRMOD was 1. Weird special case needed!!!", STAY_IN_Tx_MODE_WHEN_DONE);
 // SPRMOD is 1. We need to change the Read Protection (RDP) level to 1 (weird, but that's what the Ref Manual says)
 if (RDP_LEVEL != OB_RDP_LEVEL_1) {
 LOG_STRING("\r\***_RDP_LEVEL_1 was not used. Will need to set to OB_RDP_LEVEL_1", STAY_IN_Tx_MODE_WHEN_DONE);
// FLASH->OPTCR = ((FLASH->OPTCR & 0xFFFF00FF) | (((uint32_t)(OB_RDP_LEVEL_1)) << 8)) & OPTION_BYTES_MASK; // Doesn't work for mysterious reasons!!!
 *(volatile uint8_t*)OPTCR_BYTE1_ADDRESS = OB_RDP_LEVEL_1; // Weird, we must only write to the particular byte in the FLASH->OPTCR register
 FLASH->OPTCR |= FLASH_OPTCR_OPTSTRT; // Set the option start bit
 while(FLASH->SR & FLASH_SR_BSY) { } // Wait until busy flag goes low
 } else {
 LOG_STRING("\r\***_RDP_LEVEL_1 was used. No need to update it", STAY_IN_Tx_MODE_WHEN_DONE);
 }
 } else {
 LOG_STRING("\r\nSPRMOD was 0. No special case needed", STAY_IN_Tx_MODE_WHEN_DONE);
 }
 
 if (flashWillBeErased) {
 LOG_STRING("\r\nEntire flash will now be erased, don't expect any more logging messages like this one", SWITCH_TO_Rx_MODE_WHEN_DONE);
 }
 FLASH->OPTCR = DESIRED_OPTION_BYTES; // Update the Option Bytes
 FLASH->OPTCR |= FLASH_OPTCR_OPTSTRT; // Set the option start bit
 while(FLASH->SR & FLASH_SR_BSY) { } // Wait until busy flag goes low
 FLASH->OPTCR |= FLASH_OPTCR_OPTLOCK; // Lock the flash
 LOG_STRING("\r\nNow finished updating option bytes", SWITCH_TO_Rx_MODE_WHEN_DONE);
}

waclawek.jan
Super User
December 3, 2019

Arnold,

Thanks for sharing this!

 // FLASH->OPTCR = ((FLASH->OPTCR & 0xFFFF00FF) | (((uint32_t)(OB_RDP_LEVEL_1)) << 8)) & OPTION_BYTES_MASK; // Doesn't work for mysterious reasons!!!
 *(volatile uint8_t*)OPTCR_BYTE1_ADDRESS = OB_RDP_LEVEL_1; // Weird, we must only write to the particular byte in the FLASH->OPTCR register

Hummm.

I wonder - your skills are way off of those of the usual Cube/HAL users, why do you use it?

Jan

arnold_w
arnold_wAuthor
Senior II
December 4, 2019

Thank you for the kind words. I usually use Cube/HAL as a starting point (they're quite good when they work), but when I'm finished with my code usually there aren't many similarities with the original code left.