Debugging Bootloaders on the STM32H7R/S devices
This tutorial shows how to create and debug a basic bootloader project on the NUCLEO-H7S3L8 board. We will show how to change the option bytes to resolve memory mapping problems, get debugging working, and will give a quick overview of the STM32H7R/S project structure.
The STM32H7R/S devices come with a relatively small built-in FLASH memory and are designed to run most of the code from an external memory. The NUCLEO-H7S3L8 board contains a STM32H7S3 chip with 64KB of FLASH memory, that will contain the bootloader, and a much bigger 256MB OctoSPI FLASH module that will store the main program.
Before you begin, install VisualGDB 6.0 or later and update the OpenOCD (ST fork) package to the latest version.
- Start Visual Studio and locate the VisualGDB Embedded Project Wizard:
- Enter the name and location for your project:
- Proceed with creating a CMake-based application. MSBuild will also work just fine, however CMake allows joining the bootloader and the main application into the same logical project, sharing the common building and debugging settings between them:
- Select your toolchain and the target device. In this tutorial we are targeting the STM32H7S3L8 device:
- The regular “Blinking LED” sample does not demonstrate how to configure the FLASH memory, however most STM32CubeMX examples do include a bootloader for the corresponding board. In this tutorial we select the USB_Device\CDC_Standalone\Boot project, however any other bootloader for the same board should work as well:
- On the Debug Method page select OpenOCD (ST Fork) and make sure that you are using the latest OpenOCD package (2025-01-22 or later):Warning: The previously released OpenOCD (STM32H7R/S) package is no longer required and has been discontinued since ST merged the STM32H7R/S support into their main OpenOCD repository. If you have it installed, please uninstall it via VisualGDB Package Manager and use OpenOCD (ST Fork) instead.
- Click the “Test” button to verify that the connection to your device works:
- Locate the call to BOOT_Application() from main(), press F12 to go into BOOT_Application, and set a breakpoint there after the MapMemory() call. If this is the first time you are using the board, it will fail with BOOT_ERROR_MAPPEDMODEFAIL: This happens because the bootloader example relies on the high-speed mode of the external memory interface, that requires the XSPI2_HSLV option bit (see this thread). You can change it via STM32CubeProgrammer, or by adding extra code to the bootloader (see below).
- If you would like to use STM32CubeProgrammer, launch it and click “Connect” to connect to the device:
- Then click the “Option Bytes” button (OB), expand User Configuration 1 and set XSPI2_HSLV:
- You can also do by adding the following code to main() after HAL_Init():
FLASH_OBProgramInitTypeDef ob = { 0, }; HAL_FLASHEx_OBGetConfig(&ob); if (!(ob.USERConfig1 & FLASH_OBW1SR_XSPI2_HSLV_Msk)) { HAL_FLASH_Unlock(); HAL_FLASH_OB_Unlock(); ob.OptionType = OPTIONBYTE_USER; ob.USERType = OB_USER_XSPI2_HSLV; ob.USERConfig1 |= FLASH_OBW1SR_XSPI2_HSLV_Msk; if (HAL_FLASHEx_OBProgram(&ob) != HAL_OK) asm("bkpt 255"); HAL_NVIC_SystemReset(); }
WARNING: We advise removing the option byte programming code from main() after it has finished its work. Accidentally changing other option bytes could make the device unusable, or lead to other hard-to-troubleshoot errors.
- Now that you have changed the option byte, you can run the bootloader again, and the MapMemory() call will succeed.
- Running beyond MapMemory() would require programming the main application into the external memory, that will be shown in a separate tutorial. Before we do that, however, we will give an overview of the STM32H7R/S memory manager. Locate the stm32_extmem.c file in Solution Explorer. Note how it’s a part of the ST’s SDK and is reused between multiple projects:
- Note how it has conditional checks for EXTMEM_DRIVER_xxx macros. Click on the first one and press F12 to go to definition. This opens the stm32_extmem_conf.h file that is defined in each project separately: If you would like to enable other memory types, you would need to change the definitions, and adjust the related code to match your board (using Find All References on either macro should give a quick overview of the related code).
- You can also use the memory manager API to read/write external memory (e.g. to create an OpenOCD FLASH plugin so that you won’t need to have STM32CubeProgrammer installed):
Now that you got the bootloader working, follow this tutorial to create and debug a basic application in the external memory.