cancel
Showing results for 
Search instead for 
Did you mean: 

How to change the Read Out Protection on STM32H7

B.Montanari
ST Employee

How to change the Read Out Protection on STM32H7?

  1. Introduction

STM32H7 microcontrollers offer three levels of read-out protection: level 0 (no protection), level 1 (Flash memory, backup SRAM, and backup registers protected), and level 2 (same as level 1, but with permanent protection by locking the option bytes). It's important to note that level 2 should only be considered for the final product, not during development stages.
At level 0, no read-out protection is enabled, and all read and write operations on the flash memory and backup SRAM are possible in all boot configurations. Option bytes on the microcontroller can also be changed.
Level 1 sets read protection for the Flash memory. Access to protected memories is only allowed when booting from User Flash memory, otherwise a system hard fault is generated, blocking all code execution until the next power-on reset. Option bytes are still configurable at this level, making it possible to revert the read-out protection to level 0 either via system bootloader mode or regular SWD/JTAG interface.
Level 2 provides the same protection as level 1, but with permanent protection by locking the option bytes. Once level 2 protection is set, it cannot be undone, and the microcontroller will remain permanently protected, the system bootloader is also unreachable in this mode.
Regressing from level 1 to level 0 protection causes a mass erase of the Flash memory and backup SRAM, only option bytes will not be erased in this process.
In this article we will showcase this functionality using the firmware to modify it. You will be able to configure any other option bytes using this method as well, but the focus will be on read out protection.
  1. Microcontroller Configuration
First, let’s create a STM32CubeIDE project for the STM32H7 series and then add our logic.
 After opening the software, click on File -> New -> STM32 Project:
390.png
After the menu loads, search for STM32H723ZG, you can easily locate it with the it with the corresponding Nucleo board and press ‘next’.
391.png
Name your project (avoid using special characters and spaces) and click finish with the default settings.
392.png
Now we will setup the microcontroller peripherals the logic will use, including the SWD interface, USART and GPIO:
To setup the microcontroller’s SWD go to System Core > SYS and set the default Timebase source as SysTick
394.png
We also need to enable the Debug Serial Wire, under Trace and Debug > DEBUG
395.png
Next, we will configure USART3, which is connected on this Nucleo board to the on-board ST-LINK. With the help of ST-Link’s virtual COM port, we will then use this link to relay messages. Go to Connectivity > USART3 and set it to Asynchronous mode. The default basic parameters under configuration tab should work, but feel free to adjust it to your preferences, just remember that this will be used later when configuring your terminal.
396.png
In the nucleo 144 boards the USART3 is wired to the pins PD9 and PD8, thus you must manually reassign them since the Cube MX software will configure RX and TX pins to PB11 and PB10. You need to left click the PD9 pin in your microcontroller and select the USART3_RX option. That must also be done for the PD8 pin and the USART3_TX.
397.png
After setting up the USART3 with the default settings you will notice that a clock configuration error was generated. To solve this issue simply press the “Resolve Clock Issues” button in the Clock Configuration tab.
399.png
After clock issues are solved, we can go back to Pinout & Configuration tab to keep setting up our microcontroller.
For the button pin and LED, we’ll use the hardware available in the NUCLEO board, this means that PC13 will be the push button and we can configure it by clicking and selecting ‘EXTI_13’. After that, setup a user label for the button doing the following: System Core > GPIO > PC13 and under User Label type ‘B1 [Blue PushButton]’. 
400.png
As for the LED, we will use the PB0, as it controls the onboard Green LED. On the dropdown, scroll down and select GPIO_Output, and similarly to the button pin, go to System Core > GPIO > PB0 and under User Label type ‘LD1 [Green Led]’.
401.png
It is now possible to either click on the Device configuration tool code generation icon in the top bar or press Alt+K. This will generate a basic project with all the configuration we selected using the GUI.
403.png
  1. Coding
With the base code ready, it is time to start adding the application code. First include the stdio.h library in the private includes section so we can easily print to the vcom terminal.
#include "stdio.h"
In the private function prototypes, we will create a function to allow us to transmit to the USART simply using the printf function.
int __io_putchar(int ch)
{
      HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 100);
      return ch;
}
Under the USER CODE BEGIN 0 section we will create the regression function in a way that it runs from the microcontroller’s RAM instead of Flash
void __attribute__((__section__(".RamFunc"))) RDP_Regression(void) {
      __disable_irq();
      printf("Mass Erase Start\r\n");

      HAL_FLASH_Unlock();
      HAL_FLASH_OB_Unlock();

              MODIFY_REG(FLASH->OPTSR_PRG, FLASH_OPTSR_RDP, OB_RDP_LEVEL_0);

      HAL_FLASH_OB_Launch();
}

Still under the same comment session, add the simple function to blink the on-board LED at a given time interval.
void ToggleLED(uint16_t n) // Function for toggling the on-board LED
{
      if(HAL_GPIO_ReadPin(LD1_GPIO_Port, LD1_Pin))
      {
             printf("\033[5;32mLED\033[0m \r\n");
             HAL_GPIO_WritePin(LD1_GPIO_Port, LD1_Pin, GPIO_PIN_RESET);
      }
      else
      {
             printf("\033[1;32mLED\033[0m \r\n");
             HAL_GPIO_WritePin(LD1_GPIO_Port, LD1_Pin, GPIO_PIN_SET);
      }
      HAL_Delay(n);
}
Already inside the main function and under the USER CODE BEGIN 1 label we will define a structure to edit the option bytes.
FLASH_OBProgramInitTypeDef OptionsBytesStruct; //Define a structure to store the Options Bytes configuration for the Flash memory
The next code portion should be placed between the USER CODE BEGIN 3 and END 3. After the RDP level check, the small decision branch happens, if it’s at level 1 the code will blink the led at a 500ms rate until the button is pressed. Upon button press, it will start the regression process by unlocking the flash memory and the Option bytes, and finally call the RDP_Regression function, which was added earlier and executed from RAM memory.  It is important to remember that upon RDP regression, a full mass erase is performed.
printf("\033[0m");
      HAL_FLASHEx_OBGetConfig(&OptionsBytesStruct); // Get the current Options Bytes configuration for the Flash memory

      // Check if the RDP level is set to Level 1
      if (OptionsBytesStruct.RDPLevel == OB_RDP_LEVEL_1) {
             printf("RDP LVL1 \r\n");
             printf("wait BT1 to be pressed\r\n");
             // Loop until button BT1 is pressed
             while (HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin) != GPIO_PIN_SET) {
                   ToggleLED(500);
             }
             printf("wait BT1 to be released\r\n");
             // Loop until button BT1 is released
             while (HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin) != GPIO_PIN_RESET) {
                   ;
             }
             printf("RDP regression LV0 init\r\n");
             // Call RDP_Regression() function
            RDP_Regression();
      }

Then the code performs a check every 2 seconds to decide when the microcontroller’s RDP level should be set to 1. While it waits for a button press and release, the code blinks the led at a 2s rate. When the button is released the RDP level is set to 1.
   //if RDP is set to 0, wait for a button press to set it to level 1
             printf("\033[96mPress BT1 to change Option Bytes\033[0m \r\n");
             if (HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin) != GPIO_PIN_RESET) {
                   printf("BT1 pressed, please release it to proceed\r\n");
                   while (HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin) != GPIO_PIN_RESET)
                          ;
                   while (HAL_FLASH_Unlock() != HAL_OK) {
                          printf("Waiting Flash Unlock\r\n");
                   }
                   while (HAL_FLASH_OB_Unlock() != HAL_OK) {
                          printf("Waiting OB Unlock\r\n");
                   }

                   OptionsBytesStruct.OptionType = OPTIONBYTE_RDP; //Configure the RDP
                   OptionsBytesStruct.RDPLevel = OB_RDP_LEVEL_1;
                   while (HAL_FLASHEx_OBProgram(&OptionsBytesStruct) != HAL_OK) {
                          printf("Waiting OB Program\r\n");
                   }
                   printf("RDP will now be set to level 1\r\n");
                   printf("OBLaunch\r\n");

                   HAL_FLASH_OB_Launch();
             } else {
                   ToggleLED(2000);
             }

      }
      /* USER CODE END 3 */
  1. Testing
To test this code, we must follow a specific flow. First you should program the NUCLEO board with the code created. After building, click on the green button on the STM32CubeIDE, highlighted below:
404.png
After this, the NUCLEO board must be disconnected from the USB’s computer and reconnected to complete a full power cycle.
Note: The reason the power cycle is to prevent the debug connection by the ST-Link from interfering with the RDP entry/exit.
To confirm this, you can open a terminal, such as tera term and configure it to match the USART2 configuration we setup earlier, just to recall, it was: 115200/8/N/1. The terminal should display the following:
406.png
Now press and hold the blue button until the following message appears:
407.png
 Release the blue button to change the RDP level to 1. The MCU will reset itself and display the new RDP level message with a faster LED blink rate.
408.png
Press and release the blue button again to restore the RDP level to 0, causing a mass erase of the MCU.
409.png
At this point the Flash memory is automatically mass erased.
Warning: This may take up to 30 seconds, if you reset the microcontroller before the FLASH memory is fully erased the RDP byte will stay locked at Level 1 and your program will be deleted, so it is very important so stand still while the FLASH is being mass erased.
To confirm the RDP regression, use the STM32CubeProg (GUI or CLI) to read the memory. Here it is demonstrated using the CLI to read the option byte.
Open your windows command prompt and type the following commands as shown
cd C:\Program Files\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin
STM32_Programmer_CLI.exe -c port=swd -ob displ

410.png
If the RDP regression worked, you’ll see a message like the following:
411.png
By using the command
STM32_Programmer_cli.exe -c port=swd mode=HOTPLUG -r32 0x08000000 1000
you can see the contents of the flash memory, which as we expected, are all FFFF FFFF:
412.png
In order to see the RDP protection taking place, you can re-do the steps (reprogram your code using the CubeIDE, power cycle, press the button to set RDP level 1). Now, you can reissue the option byte command
STM32_Programmer_CLI.exe -c port=swd -ob displ
to view the RDP status. Be aware that this will trigger the read-out protection and after the execution of this command your microcontroller will halt completely and recover only after a power cycle.
413.png
To further explore, you can try to check the Flash contents again using the command
STM32_Programmer_cli.exe -c port=swd mode=HOTPLUG -r32 0x08000000 1000
, but this time you’ll receive an error message as the microcontroller has protected the Flash content, and again the micro will halt until it is power cycled.
414.png
Note: You can follow the same process to set RDP to level 2, but keep in mind that this action is irreversible and should only be done at your final production. If you need to update your firmware after RDP level 2, you should first implement your own bootloader (see IAP bootloader example https://www.st.com/content/ccc/resource/technical/document/application_note/27/38/37/58/c2/8c/40/07/DM00161366.pdf/files/DM00161366.pdf/jcr:content/translations/en.DM00161366.pdf ) because RDP level 2 will disable the built-in System Bootloader.
  1. Conclusion
In this article you learned how to configure option bytes for the STM32H7 series, more specifically the Read-Out Protection feature, allowing your application to activate the FLASH protection and even revert to a state of no protection whenever necessary.
 
Version history
Last update:
‎2023-04-14 09:52 AM
Updated by: