{"id":8934,"date":"2025-01-27T20:17:04","date_gmt":"2025-01-28T04:17:04","guid":{"rendered":"https:\/\/visualgdb.com\/w\/?p=8934"},"modified":"2025-01-30T15:00:53","modified_gmt":"2025-01-30T23:00:53","slug":"debugging-stm32h7r-s-applications-in-external-memory","status":"publish","type":"post","link":"https:\/\/visualgdb.com\/tutorials\/arm\/stm32\/stm32h7rs\/application\/","title":{"rendered":"Debugging STM32H7R\/S Applications in External Memory"},"content":{"rendered":"<p>This tutorial shows how to create an application project for the STM32H7R\/S device, that will reside in an external memory, and will be launched by a bootloader. Before you begin, follow <a href=\"https:\/\/visualgdb.com\/tutorials\/arm\/stm32\/stm32h7rs\/bootloader\">this tutorial<\/a> to create a basic bootloader project.<\/p>\n<ol>\n<li>In the solution containing the bootloader project, right-click on the solution node and select &#8220;Add-&gt;New Project&#8221;:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/01-prj2.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8935\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/01-prj2.png\" alt=\"\" width=\"1809\" height=\"1069\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/01-prj2.png 1809w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/01-prj2-300x177.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/01-prj2-1024x605.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/01-prj2-768x454.png 768w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/01-prj2-1536x908.png 1536w\" sizes=\"(max-width: 1809px) 100vw, 1809px\" \/><\/a><\/li>\n<li>Select the same project type and device as you did when creating bootloader, but this time pick the <strong>Appli<\/strong> project instead of <strong>Boot <\/strong>in the <strong>Sample Selection<\/strong> page:<br \/>\n<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/02-app.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8936\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/02-app.png\" alt=\"\" width=\"1109\" height=\"867\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/02-app.png 1109w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/02-app-300x235.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/02-app-1024x801.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/02-app-768x600.png 768w\" sizes=\"(max-width: 1109px) 100vw, 1109px\" \/><\/a><\/li>\n<li>In order to program the external memory, we will need to use the <a href=\"https:\/\/www.st.com\/en\/development-tools\/stm32cubeprog.html\">STM32CubeProgrammer<\/a> tool that requires the ELF files to have the <strong>.elf<\/strong> extension. This can be accomplished by adding the following line at the end of <strong>CMakeLists.txt<\/strong>:\n<pre>set_target_properties(&lt;Executable Name&gt; PROPERTIES SUFFIX \".elf\")<\/pre>\n<p><a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/03-elf.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8937\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/03-elf.png\" alt=\"\" width=\"1809\" height=\"1069\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/03-elf.png 1809w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/03-elf-300x177.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/03-elf-1024x605.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/03-elf-768x454.png 768w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/03-elf-1536x908.png 1536w\" sizes=\"(max-width: 1809px) 100vw, 1809px\" \/><\/a><\/li>\n<li>Build the project and verify that it got placed into XSPI2, and the FLASH memory is empty. If you still see the stats from the bootloader project, right-click on the <strong>STM32H7S_App<\/strong> project (with triangular CMake icon) and select &#8220;Set as startup project&#8221;:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/04-build-1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8947\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/04-build-1.png\" alt=\"\" width=\"1809\" height=\"1069\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/04-build-1.png 1809w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/04-build-1-300x177.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/04-build-1-1024x605.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/04-build-1-768x454.png 768w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/04-build-1-1536x908.png 1536w\" sizes=\"(max-width: 1809px) 100vw, 1809px\" \/><\/a><\/li>\n<li>Now we can test programming the FLASH memory with STM32CubeProgrammer. Go to its binary directory, and run the following command line:\n<pre>STM32_Programmer_CLI.exe -c port=SWD reset=HWrst -el ExternalLoader\\MX25UW25645G_NUCLEO-H7S3L8.stldr -w &lt;full path to STM32H7S_App.elf&gt;<\/pre>\n<p><a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/05-programmed.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8939\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/05-programmed.png\" alt=\"\" width=\"1599\" height=\"892\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/05-programmed.png 1599w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/05-programmed-300x167.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/05-programmed-1024x571.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/05-programmed-768x428.png 768w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/05-programmed-1536x857.png 1536w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/05-programmed-800x445.png 800w\" sizes=\"(max-width: 1599px) 100vw, 1599px\" \/><\/a><\/li>\n<li>Once the programming succeeds, you can configure VisualGDB to run STM32CubeProgrammer automatically before each debug session. Add the following command as a pre-debug step:<br \/>\n<table style=\"border-collapse: collapse; width: 100%;\">\n<tbody>\n<tr>\n<td style=\"width: 50%;\">Command<\/td>\n<td style=\"width: 50%;\">&lt;full path to STM32_Programmer_CLI.exe&gt;<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 50%;\">Arguments<\/td>\n<td style=\"width: 50%;\">STM32_Programmer_CLI.exe -c port=SWD reset=HWrst -el ExternalLoader\\MX25UW25645G_NUCLEO-H7S3L8.stldr -w $(TargetPath)<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 50%;\">Working directory<\/td>\n<td style=\"width: 50%;\">&lt;directory of STM32_Programmer_CLI.exe&gt;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/06-program.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8940\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/06-program.png\" alt=\"\" width=\"1759\" height=\"1041\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/06-program.png 1759w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/06-program-300x178.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/06-program-1024x606.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/06-program-768x455.png 768w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/06-program-1536x909.png 1536w\" sizes=\"(max-width: 1759px) 100vw, 1759px\" \/><\/a><\/li>\n<li>If we try to just debugging the application, GDB will try to directly program it into the FLASH memory and will fail. It will also assume that the FLASH memory is actually RAM that can be directly modified to set breakpoints, which is not the case. To work around it, change the following settings via VisualGDB Project Properties -&gt; Debug Settings:<br \/>\n<table style=\"border-collapse: collapse; width: 100%;\">\n<tbody>\n<tr>\n<td style=\"width: 50%;\">Program FLASH memory<\/td>\n<td style=\"width: 50%;\">Never<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 50%;\">Reset device<\/td>\n<td style=\"width: 50%;\">Before programming + After programming<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><a id=\"noprogram\"><\/a>Also add &#8220;<strong>mon gdb_breakpoint_override hard<\/strong>&#8221; to the startup GDB commands (requires using the ST fork of OpenOCD and not the discontinued H7R\/S OpenOCD branch):<br \/>\n<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/07-openocd.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8941\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/07-openocd.png\" alt=\"\" width=\"1759\" height=\"1041\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/07-openocd.png 1759w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/07-openocd-300x178.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/07-openocd-1024x606.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/07-openocd-768x455.png 768w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/07-openocd-1536x909.png 1536w\" sizes=\"(max-width: 1759px) 100vw, 1759px\" \/><\/a><\/li>\n<li>Set a breakpoint in the application&#8217;s <strong>main()<\/strong> function and press F5. VisualGDB will launch the application and the breakpoint will trigger:<\/li>\n<li><a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/08-bkpt.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8942\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/08-bkpt.png\" alt=\"\" width=\"1809\" height=\"1069\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/08-bkpt.png 1809w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/08-bkpt-300x177.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/08-bkpt-1024x605.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/08-bkpt-768x454.png 768w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/08-bkpt-1536x908.png 1536w\" sizes=\"(max-width: 1809px) 100vw, 1809px\" \/><\/a>If you continue the application and connect the other port of the board to USB, it will get recognized as a virtual serial port:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/devmgr.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8949\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/devmgr.png\" alt=\"\" width=\"976\" height=\"486\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/devmgr.png 976w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/devmgr-300x149.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/devmgr-768x382.png 768w\" sizes=\"(max-width: 976px) 100vw, 976px\" \/><\/a><\/li>\n<li>Now we will show how to merge both the bootloader and the application into the same CMake project. Apply the following changes to CMakeLists.txt files:\n<ol>\n<li>Add <strong><span class=\"blob-code-inner blob-code-marker \" data-code-marker=\"+\">ALIAS BOOTLOADER_BSP<\/span> <\/strong>to the <a href=\"https:\/\/visualgdb.com\/reference\/cmake\/find_bsp\"><strong>find_bsp()<\/strong><\/a> statement in the bootloader&#8217;s CMakeLists.txt file.<\/li>\n<li>Add <strong><span class=\"blob-code-inner blob-code-marker \" data-code-marker=\"+\">BSP_ALIAS BOOTLOADER_BSP<\/span>\u00a0 <\/strong>to the <strong><span class=\"blob-code-inner blob-code-marker js-skip-tagsearch\" data-code-marker=\"-\">bsp_include_directories<\/span>()<\/strong>, <strong><span class=\"blob-code-inner blob-code-marker js-skip-tagsearch\" data-code-marker=\"-\">bsp_compile_definitions<\/span>()<\/strong> and <strong><span class=\"blob-code-inner blob-code-marker \" data-code-marker=\" \">add_bsp_based_executable<\/span>( )<\/strong> statements. In this particular example, many include directories imported from the STM32 SDK are not needed as long as the project references the correct frameworks, so we have removed them in our <a href=\"https:\/\/github.com\/sysprogs\/tutorials\/commit\/4835feda7a41a7c09edc559a406705dd53143bfa\">example<\/a>.<\/li>\n<li>Copy the <strong>find_bsp()<\/strong> statement and everything below it from the application&#8217;s CMakeLists.txt file.<\/li>\n<\/ol>\n<p>This way you will end up with one <strong>CMakeLists.txt<\/strong> file defining two separate BSPs, each of them having one application. You can use this <a href=\"https:\/\/github.com\/sysprogs\/tutorials\/commit\/4835feda7a41a7c09edc559a406705dd53143bfa\">git commit<\/a> as a reference for the changes necessary to merge both projects.<\/li>\n<li>Open VisualGDB Project Properties for the bootloader project and add a custom debug step similar to the one added before. Use <strong>$(TargetDir)\/(application name)<\/strong> instead of <strong>$(TargetPath)<\/strong>, as <strong>$(TargetPath)<\/strong> will now resolve to the bootloader binary:<br \/>\n<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/10-customstep.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8944\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/10-customstep.png\" alt=\"\" width=\"1759\" height=\"1041\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/10-customstep.png 1759w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/10-customstep-300x178.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/10-customstep-1024x606.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/10-customstep-768x455.png 768w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/10-customstep-1536x909.png 1536w\" sizes=\"(max-width: 1759px) 100vw, 1759px\" \/><\/a><\/li>\n<li>As the main application from VisualGDB&#8217;s view is now the bootloader, you can keep the FLASH programming turned on and don&#8217;t need to reset the device after programming. However, you still need to force the hardware breakpoints via <strong>gdb_breakpoint_override<\/strong>:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/11-openocd.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8945\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/11-openocd.png\" alt=\"\" width=\"1759\" height=\"1041\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/11-openocd.png 1759w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/11-openocd-300x178.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/11-openocd-1024x606.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/11-openocd-768x455.png 768w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/11-openocd-1536x909.png 1536w\" sizes=\"(max-width: 1759px) 100vw, 1759px\" \/><\/a><\/li>\n<li>Finally, go to the <strong>Embedded Debug Tweaking<\/strong> page, expand the <strong>Related Executables<\/strong> list: and add the application there, checking the <strong>Symbols<\/strong> and <strong>Live Watch<\/strong> checkboxes, but not the <strong>FLASH<\/strong> checkbox. This will ensure that you can step set breakpoints in both the bootloader and the main application, but GDB will rely on the STM32CubeProgrammer (via a custom pre-debug step) to program the application into the external FLASH memory: <a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/12-elf2.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8946\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/12-elf2.png\" alt=\"\" width=\"1759\" height=\"1041\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/12-elf2.png 1759w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/12-elf2-300x178.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/12-elf2-1024x606.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/12-elf2-768x455.png 768w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/12-elf2-1536x909.png 1536w\" sizes=\"(max-width: 1759px) 100vw, 1759px\" \/><\/a><\/li>\n<li>Having bootloader and application symbols loaded at the same time can cause confusion if both of them have variables with the same name (e.g. <strong>TickCount<\/strong>). If this happens, you can limit the debug symbols to application only by selecting it as the startup target (console icon in Solution Explorer), disabling the FLASH memory programming and enabling reset-after-program as shown <a href=\"#noprogram\">here<\/a>:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/startup.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8956\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/startup.png\" alt=\"\" width=\"1809\" height=\"392\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/startup.png 1809w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/startup-300x65.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/startup-1024x222.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/startup-768x166.png 768w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2025\/01\/startup-1536x333.png 1536w\" sizes=\"(max-width: 1809px) 100vw, 1809px\" \/><\/a>This will reuse the existing bootloader, program the application and reset the device so that the bootloader takes control and launches the app.<\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>This tutorial shows how to create an application project for the STM32H7R\/S device, that will reside in an external memory,<\/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":[61],"_links":{"self":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/8934"}],"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=8934"}],"version-history":[{"count":6,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/8934\/revisions"}],"predecessor-version":[{"id":8957,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/8934\/revisions\/8957"}],"wp:attachment":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/media?parent=8934"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/categories?post=8934"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/tags?post=8934"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}