cancel
Showing results for 
Search instead for 
Did you mean: 

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

arnold_w
Senior

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?

19 REPLIES 19

IMO all bits described in Table 9 - which are also "mirrored" in FLASH_OPTCR - are subject to programming according to section 3.6.2. In other words, you program then all at once.

Terminology is always confusing - this is a live topic, with a huge amount of concepts and details, which are sometimes hard to describe precisely without resorting to excessive explanation; and with many changes as the chips are evolving and are updated and their features expanded. Users are often expected to have prior knowledge to fill out the gaps. This is not to say documentation can't or shouldn't be improved; it's just that it's a hard task. Maybe you might want to suggest particular improvements yourself.

You may not be aware of the fact that this is a primarily user driven forum, with casual ST presence. I am not ST. If you want to pick up the issue of imprecise terminology used in the manuals, maybe you want to contact ST directly, through the web support form, or through your FAE.

JW

@Imen DAHMEN​ , could perhaps 3.6.2 in RM0390 - and all other analogous chapters in other STM32 RMs - be made less ambiguous by omitting the "user" from "user option bytes", as 3.6.2. appears to refer to programming of all option bytes, whereas "user option bytes" appear to designate only a portion of the overall option bytes?

@Vincent Onde​  ,

Can't this issue be clarified within an AN, together with an overview of the various features which can be enabled/disabled using the Option bits/bytes accross all the STM32 families?

JW

I'm on vacation right now, but when I get back I will try to change all bits and see if I can successfully update them using the instructions in 3.6.2 (and hence, not use the HAL-functions). Thank you for your help.

arnold_w
Senior

Accidental double-post.

arnold_w
Senior

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?

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?

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
Senior

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

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

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.