{"id":8916,"date":"2025-01-27T20:16:57","date_gmt":"2025-01-28T04:16:57","guid":{"rendered":"https:\/\/visualgdb.com\/w\/?p=8916"},"modified":"2025-01-27T20:24:31","modified_gmt":"2025-01-28T04:24:31","slug":"debugging-bootloaders-on-the-stm32h7r-s-devices","status":"publish","type":"post","link":"https:\/\/visualgdb.com\/tutorials\/arm\/stm32\/stm32h7rs\/bootloader\/","title":{"rendered":"Debugging Bootloaders on the STM32H7R\/S devices"},"content":{"rendered":"<p>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.<\/p>\n<p>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.<\/p>\n<p>Before you begin, install VisualGDB 6.0 or later and update the OpenOCD (ST fork) package to the latest version.<\/p>\n<ol>\n<li>Start Visual Studio and locate the VisualGDB Embedded Project Wizard:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/01-newprj.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8918\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/01-newprj.png\" alt=\"\" width=\"1268\" height=\"844\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/01-newprj.png 1268w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/01-newprj-300x200.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/01-newprj-1024x682.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/01-newprj-768x511.png 768w\" sizes=\"(max-width: 1268px) 100vw, 1268px\" \/><\/a><\/li>\n<li>Enter the name and location for your project:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/02-path.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8919\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/02-path.png\" alt=\"\" width=\"1268\" height=\"844\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/02-path.png 1268w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/02-path-300x200.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/02-path-1024x682.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/02-path-768x511.png 768w\" sizes=\"(max-width: 1268px) 100vw, 1268px\" \/><\/a><\/li>\n<li>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:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/03-cmake.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8920\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/03-cmake.png\" alt=\"\" width=\"1109\" height=\"867\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/03-cmake.png 1109w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/03-cmake-300x235.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/03-cmake-1024x801.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/03-cmake-768x600.png 768w\" sizes=\"(max-width: 1109px) 100vw, 1109px\" \/><\/a><\/li>\n<li>Select your toolchain and the target device. In this tutorial we are targeting the STM32H7S3L8 device:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/04-device.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8921\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/04-device.png\" alt=\"\" width=\"1109\" height=\"867\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/04-device.png 1109w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/04-device-300x235.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/04-device-1024x801.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/04-device-768x600.png 768w\" sizes=\"(max-width: 1109px) 100vw, 1109px\" \/><\/a><\/li>\n<li>The regular &#8220;Blinking LED&#8221; 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 <strong>USB_Device\\CDC_Standalone\\Boot<\/strong> project, however any other bootloader for the same board should work as well: <a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/05-standalone.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8922\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/05-standalone.png\" alt=\"\" width=\"1109\" height=\"867\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/05-standalone.png 1109w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/05-standalone-300x235.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/05-standalone-1024x801.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/05-standalone-768x600.png 768w\" sizes=\"(max-width: 1109px) 100vw, 1109px\" \/><\/a><\/li>\n<li>On the Debug Method page select <strong>OpenOCD (ST Fork) <\/strong>and make sure that you are using the latest OpenOCD package (<strong>2025-01-22<\/strong> or later):<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/06-debug.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8923\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/06-debug.png\" alt=\"\" width=\"1109\" height=\"867\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/06-debug.png 1109w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/06-debug-300x235.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/06-debug-1024x801.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/06-debug-768x600.png 768w\" sizes=\"(max-width: 1109px) 100vw, 1109px\" \/><\/a><span style=\"text-decoration: underline;\"><strong>Warning<\/strong><\/span>: The previously released <strong>OpenOCD (STM32H7R\/S) package <\/strong>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 <strong>VisualGDB Package Manager <\/strong>and use <strong>OpenOCD (ST Fork)<\/strong> instead.<\/li>\n<li>Click the &#8220;Test&#8221; button to verify that the connection to your device works:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/07-test.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8924\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/07-test.png\" alt=\"\" width=\"1090\" height=\"799\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/07-test.png 1090w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/07-test-300x220.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/07-test-1024x751.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/07-test-768x563.png 768w\" sizes=\"(max-width: 1090px) 100vw, 1090px\" \/><\/a><\/li>\n<li>Locate the call to <strong>BOOT_Application()<\/strong> from <strong>main()<\/strong>, press F12 to go into BOOT_Application, and set a breakpoint there after the <strong>MapMemory()<\/strong> call. If this is the first time you are using the board, it will fail with <strong>BOOT_ERROR_MAPPEDMODEFAIL<\/strong>: <a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/08-problem.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8925\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/08-problem.png\" alt=\"\" width=\"1745\" height=\"1038\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/08-problem.png 1745w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/08-problem-300x178.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/08-problem-1024x609.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/08-problem-768x457.png 768w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/08-problem-1536x914.png 1536w\" sizes=\"(max-width: 1745px) 100vw, 1745px\" \/><\/a>This happens because the bootloader example relies on the high-speed mode of the external memory interface, that requires the <strong>XSPI2_HSLV<\/strong> option bit (see <a href=\"https:\/\/community.st.com\/t5\/stm32-mcus-boards-and-hardware\/nucleo-h7s3l8-booting-to-application-via-bootloader\/td-p\/662075\">this thread<\/a>). You can change it via STM32CubeProgrammer, or by adding extra code to the bootloader (see below).<\/li>\n<li>If you would like to use STM32CubeProgrammer, launch it and click &#8220;Connect&#8221; to connect to the device:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/09-connect.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8926\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/09-connect.png\" alt=\"\" width=\"1202\" height=\"697\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/09-connect.png 1202w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/09-connect-300x174.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/09-connect-1024x594.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/09-connect-768x445.png 768w\" sizes=\"(max-width: 1202px) 100vw, 1202px\" \/><\/a><\/li>\n<li>Then click the &#8220;Option Bytes&#8221; button (OB), expand <strong>User Configuration 1<\/strong> and set <strong>XSPI2_HSLV:<\/strong><br \/>\n<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/10-byte.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8927\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/10-byte.png\" alt=\"\" width=\"1202\" height=\"697\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/10-byte.png 1202w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/10-byte-300x174.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/10-byte-1024x594.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/10-byte-768x445.png 768w\" sizes=\"(max-width: 1202px) 100vw, 1202px\" \/><\/a><\/li>\n<li>You can also do by adding the following code to <strong>main()<\/strong> after <strong>HAL_Init()<\/strong>:\n<pre>FLASH_OBProgramInitTypeDef ob = { 0, };\r\nHAL_FLASHEx_OBGetConfig(&amp;ob);\r\nif (!(ob.USERConfig1 &amp; FLASH_OBW1SR_XSPI2_HSLV_Msk))\r\n{\r\n    HAL_FLASH_Unlock();\r\n    HAL_FLASH_OB_Unlock();\r\n    ob.OptionType = OPTIONBYTE_USER;\r\n    ob.USERType = OB_USER_XSPI2_HSLV;\r\n    ob.USERConfig1 |= FLASH_OBW1SR_XSPI2_HSLV_Msk;\r\n    if (HAL_FLASHEx_OBProgram(&amp;ob) != HAL_OK)\r\n        asm(\"bkpt 255\");\r\n    HAL_NVIC_SystemReset();\r\n}<\/pre>\n<p><span style=\"text-decoration: underline;\"><strong>WARNING:<\/strong><\/span> We advise removing the option byte programming code from <strong>main()<\/strong> after it has finished its work. Accidentally changing other option bytes could make the device unusable, or lead to other hard-to-troubleshoot errors.<\/li>\n<li>Now that you have changed the option byte, you can run the bootloader again, and the <strong>MapMemory()<\/strong> call will succeed.<br \/>\n<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/11-mapped.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8928\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/11-mapped.png\" alt=\"\" width=\"1745\" height=\"1038\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/11-mapped.png 1745w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/11-mapped-300x178.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/11-mapped-1024x609.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/11-mapped-768x457.png 768w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/11-mapped-1536x914.png 1536w\" sizes=\"(max-width: 1745px) 100vw, 1745px\" \/><\/a><\/li>\n<li>Running beyond <strong>MapMemory() <\/strong>would require programming the main application into the external memory, that will be shown in a <a href=\"https:\/\/visualgdb.com\/tutorials\/arm\/stm32\/stm32h7rs\/application\/\">separate tutorial<\/a>. Before we do that, however, we will give an overview of the STM32H7R\/S memory manager. Locate the <strong>stm32_extmem.c<\/strong> file in Solution Explorer. Note how it&#8217;s a part of the ST&#8217;s SDK and is reused between multiple projects:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/12-extmem.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8929\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/12-extmem.png\" alt=\"\" width=\"1745\" height=\"1038\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/12-extmem.png 1745w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/12-extmem-300x178.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/12-extmem-1024x609.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/12-extmem-768x457.png 768w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/12-extmem-1536x914.png 1536w\" sizes=\"(max-width: 1745px) 100vw, 1745px\" \/><\/a><\/li>\n<li>Note how it has conditional checks for <strong>EXTMEM_DRIVER_xxx<\/strong> macros. Click on the first one and press F12 to go to definition. This opens the <strong>stm32_extmem_conf.h<\/strong> file that is defined in each project separately: <a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/13-conf.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8930\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/13-conf.png\" alt=\"\" width=\"1745\" height=\"1038\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/13-conf.png 1745w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/13-conf-300x178.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/13-conf-1024x609.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/13-conf-768x457.png 768w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/13-conf-1536x914.png 1536w\" sizes=\"(max-width: 1745px) 100vw, 1745px\" \/><\/a>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).<\/li>\n<li>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&#8217;t need to have STM32CubeProgrammer installed):<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/14-api.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8931\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/14-api.png\" alt=\"\" width=\"1809\" height=\"1069\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/14-api.png 1809w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/14-api-300x177.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/14-api-1024x605.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/14-api-768x454.png 768w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/14-api-1536x908.png 1536w\" sizes=\"(max-width: 1809px) 100vw, 1809px\" \/><\/a><\/li>\n<\/ol>\n<p>Now that you got the bootloader working, follow <a href=\"https:\/\/visualgdb.com\/tutorials\/arm\/stm32\/stm32h7rs\/application\/\">this tutorial<\/a> to create and debug a basic application in the external memory.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This tutorial shows how to create and debug a basic bootloader project on the NUCLEO-H7S3L8 board. We will show how<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[89],"tags":[],"_links":{"self":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/8916"}],"collection":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/comments?post=8916"}],"version-history":[{"count":4,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/8916\/revisions"}],"predecessor-version":[{"id":8954,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/8916\/revisions\/8954"}],"wp:attachment":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/media?parent=8916"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/categories?post=8916"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/tags?post=8916"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}