cancel
Showing results for 
Search instead for 
Did you mean: 

How to debug the STM32N6 using VS Code

B.Montanari
ST Employee

Summary

This article provides a detailed guide on using VS Code to debug code running from external serial flash on the STM32N6. It covers setting up the development environment, setting up the project with CMake, configuring the debugger within VS Code, and validating the setup.

Introduction

To debug code running from external serial flash effectively, the external memory must be configured in memory-mapped mode. The first stage bootloader (FSBL), running from AXI-SRAM2, initializes the XSPI interface, enables memory-mapped mode, and jumps to the application stored in external flash. Debugging starts with placing the debugger program on this external memory, allowing seamless execution and inspection directly from flash.

A key limitation to be aware of is that breakpoints cannot be set in application code located in external memory before the FSBL configures the memory mapping. Once the FSBL completes this setup and the debugger enters debug mode, breakpoints function normally. This guide covers environment setup, debugger configuration, project setup with CMake, and tips to work around these constraints for an efficient debugging workflow in VS Code.

1. Prerequisites

This article requires that you have the following installed:

2. Hardware setup

The hardware used to showcase is the STM32N6570-DK. Make sure that you have it in DEV boot mode to program the code.

BMontanari_0-1756124872300.png

3. Create an STM32N6 CMake project

  • Create a new STM32CubeMX project, open VS Code, and click on [Launch STM32CubeMX]:
BMontanari_1-1756124872303.png
  • Select the [STM32N657X0H3Q]:
BMontanari_2-1756124872310.png
  • Select [Secure domain only]:
BMontanari_3-1756124872310.png
 
Configure the project to execute code from the external serial NOR flash. If you need assistance with this setup, we have published an article that explains the process in detail. You can find it here: How to execute code from the external serial NOR using the STM32N6.
  • Before the code generation, select the CMake toolchain:
BMontanari_4-1756124872318.png
  • In VS Code, import the project folder:
BMontanari_5-1756124872320.png
  • Configure the preset [Debug] by pressing Ctrl+Shift+P and type/select [Select Configure Preset] then click on [Debug]:
BMontanari_6-1756124872323.png

 

BMontanari_7-1756124872324.png

 

  • Configure CMake discovered Project as STM32Cube Project:
BMontanari_8-1756124872326.png
  • Add [PROJECT_NAME_Appli] and [PROJECT_NAME_FSBL] to your environment, save and close:
BMontanari_9-1756124872327.png

 

4. Code editing and post build commands

In the [Appli] project, locate the main.c file and add the toggle LED function call in its main loop:

/* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
        HAL_GPIO_TogglePin(GREEN_LED_GPIO_Port, GREEN_LED_Pin);
        HAL_Delay(200);
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

As described here, it is possible to add postbuild steps to generate the *.bin files and sign the binaries for both projects. However, in VS Code the launch.json configuration used for debugging embedded applications only supports *.elf files. To address this limitation, a custom postbuild command is included in the build process:

# Convert the signed Appli .bin back to .elf
add_custom_command(
  TARGET ${CMAKE_PROJECT_NAME}
  POST_BUILD
  COMMAND ${CMAKE_OBJCOPY} -I binary -O elf32-littlearm -B arm --change-addresses=0x70100000 ${APPLI_OUTPUT} ${CMAKE_BINARY_DIR}/Appli-trusted.elf
  COMMENT "Converting signed .bin back to .elf in address [0x70100000]"
)

This custom command converts the signed *.bin output back into an *.elf file, mapping it to the correct memory addresses (in this case 0x70100000 for [Appli]). This ensures that the resulting .elf file contains the necessary information for proper debugger functionality.

In short, these postbuild commands can be added to the CMakeLists.txt files of [FSBL] and [Appli] projects:

BMontanari_10-1756124872331.png

4.1 Edit the FSBL post build

# Specify the output ELF file
set(TARGET_ELF "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.elf")
# Command to generate .bin file
add_custom_command(
    TARGET ${CMAKE_PROJECT_NAME}
    POST_BUILD
    COMMAND ${CMAKE_OBJCOPY}
-O binary ${TARGET_ELF} ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.bin
    COMMENT "Generating .bin file"
)

# Define the paths to the STM32 Signing Tool and the input/output files
set(SIGNING_TOOL "C:/Program Files/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/STM32_SigningTool_CLI.exe")

# Define the input and output files for FSBL
set(FSBL_INPUT "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.bin")
set(FSBL_OUTPUT "${CMAKE_BINARY_DIR}/FSBL-trusted.bin")

# Add a post-build step to sign the FSBL binary
add_custom_command(
    TARGET ${CMAKE_PROJECT_NAME}
    POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E echo y > input.txt
    COMMAND "${SIGNING_TOOL}" -bin "${FSBL_INPUT}" -nk -of 0x80000000 -t fsbl -o "${FSBL_OUTPUT}" -hv 2.3 -dump "${FSBL_OUTPUT}" < input.txt
    WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
    COMMENT "Signing FSBL binary: ${FSBL_INPUT} -> ${FSBL_OUTPUT}"
)

# Convert the signed FSBL .bin back to .elf
add_custom_command(
    TARGET ${CMAKE_PROJECT_NAME}
    POST_BUILD
    COMMAND ${CMAKE_OBJCOPY} -I binary -O elf32-littlearm -B arm --change-addresses=0x70000000 ${FSBL_OUTPUT} ${CMAKE_BINARY_DIR}/FSBL-trusted.elf
    COMMENT "Converting signed FSBL .bin back to .elf in address [0x70000000]"
)

4.2 Edit the Appli post build

# Specify the output ELF file
set(TARGET_ELF "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.elf")
# Command to generate .bin file
add_custom_command(
    TARGET ${CMAKE_PROJECT_NAME}
    POST_BUILD
    COMMAND ${CMAKE_OBJCOPY} -O binary ${TARGET_ELF} ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.bin
    COMMENT "Generating .bin file"
)

# Define the paths to the STM32 Signing Tool and the input/output files
set(SIGNING_TOOL "C:/Program Files/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/STM32_SigningTool_CLI.exe")
# Define the input and output files for Appli
set(APPLI_INPUT "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.bin")
set(APPLI_OUTPUT "${CMAKE_BINARY_DIR}/Appli-trusted.bin")

# Add a post-build step to sign the Appli binary
add_custom_command(
    TARGET ${CMAKE_PROJECT_NAME}
    POST_BUILD
    # Step 1: Create a file with 'y'
    COMMAND ${CMAKE_COMMAND} -E echo y > "${CMAKE_BINARY_DIR}/input.txt"
    # Step 2: Run the signing tool, redirecting input from the file
    COMMAND "${SIGNING_TOOL}" -bin "${APPLI_INPUT}" -nk -of 0x80000000 -t fsbl -o "${APPLI_OUTPUT}" -hv 2.3 -dump "${APPLI_OUTPUT}" < "${CMAKE_BINARY_DIR}/input.txt"
    WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
    COMMENT "Signing Appli binary: ${APPLI_INPUT} -> ${APPLI_OUTPUT}"
)

# Convert the signed Appli .bin back to .elf
add_custom_command(
  TARGET ${CMAKE_PROJECT_NAME}
  POST_BUILD
  COMMAND ${CMAKE_OBJCOPY} -I binary -O elf32-littlearm -B arm --change-addresses=0x70100000 ${APPLI_OUTPUT} ${CMAKE_BINARY_DIR}/Appli-trusted.elf
  COMMENT "Converting signed .bin back to .elf in address [0x70100000]"
)

4.3 Linker script for execute in place (XIP)

The default linker script generated by the CMake for the [Appli] project is configured for the selected operation configured in the STM32CubeMX’s EXTMEM_MANAGER, where the selected was XIP. Double check that the linker script used in [Appli] CMakeLists.txt is indeed the correct one:

BMontanari_11-1756124872336.png

Note: STM32CubeMX version 6.15 has a minor bug on the linker script creation for CMake that causes a build error on VS Code. I recommend changing the content of said linker script with the equivalent one available in our GitHub here or use the attached on in this article (available at the end of the article.)

5. Build the project

Now that all steps are configured, it is time to build the application, locate the [CMake] icon on the left, then expand the [PROJECT STATUS] to locate the [Build] [All] to rebuild the projects. Alternatively, press Ctrl+Shift+P and then type/click [CMake: Clean Rebuild] for a clean and rebuild action.

BMontanari_12-1756124872338.png
 
You should see something similar to the following in the Build Analyzer (lower left corner) and Build Output window:
BMontanari_13-1756124872342.png

 

6. Create and modify the launch.json file

  • Open the [Run and Debug] view (Ctrl+Shift+D.)
  • Click create a launch.json file.
BMontanari_14-1756124872342.png

 

  • Select STM32Cube: STLink GDB Server from the list of debuggers provided by STM32CubeExtension.
BMontanari_15-1756124872343.png

 

  • The extension generates a default launch.json with configurations tailored for STLINK debugging.

Edit the generated launch.json to support debugging with all features enabled:

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "stlinkgdbtarget",
            "request": "launch",
            "name": "STM326570-DK Debug",
            "origin": "snippet",
            "cwd": "${workspaceFolder}",
            "preBuild": "${command:st-stm32-ide-debug-launch.build}",
            "runEntry": "BOOT_Application", // Set in BOOT_Application
            "svdPath": "C:/ST/STM32CubeCLT_1.18.0/STMicroelectronics_CMSIS_SVD/STM32N657.svd", // Replace with your .svd path
            "imagesAndSymbols": [
                {
                    "symbolFileName": "${workspaceFolder}/Appli/build/XIP_Appli.elf", // Replace this with your path and your_Appli.elf symbol
                    "imageFileName": "${workspaceFolder}/Appli/build/Appli-trusted.elf",
                },

                {
                    "symbolFileName": "${workspaceFolder}/FSBL/build/XIP_FSBL.elf", // Replace this with your path and your_FSBL.elf symbol
                    "imageFileName": "${workspaceFolder}/FSBL/build/XIP_FSBL.elf", // Replace this with your path and your_FSBL.elf image
                }
            ],
            "serverExtLoader": [
                {
                    "loader": "C:/Program Files/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/ExternalLoader/MX66UW1G45G_STM32N6570-DK.stldr", // Replace with your external loader path
                    "initialize": false
                }
            ]
        }
    ]
}

6.1 Explanation of the launch.json file changes

  • Changed the debug configuration name:

The "name" field was updated from a generic label to "STM326570-DK Debug" to identify the target board clearly and make the configuration easier to recognize in the VS Code interface.

  • Changed the runEntry function:

As placing breakpoints in code positioned in the external memory region before the memory is configured in memory-mapped mode, a change on the entry point has to be made. Set the first breakpoint in the FSBL's function called “BOOT_Application” instead of the usual "main" as this is the first function after the memory is in memory-mapped mode. 

  • Added svdPath:

A new "svdPath" field was introduced, pointing to the SVD (system view description) file for the STM32N657 device. This enables the debugger to display peripheral registers and improves the debugging experience by providing detailed hardware information in the UI.

  • Explicitly defined imagesAndSymbols:  

Instead of using a dynamic command to resolve the binary, the configuration now explicitly lists both "symbolFileName" and "imageFileName" for two separate images (application and FSBL). This allows for multi-image debugging and ensures that the correct symbol and image files are used, which is especially important when dealing with signed binaries.

  • Added serverExtLoader: 

A "serverExtLoader" section was added, specifying the path to an external loader (*.stldr file). This is required for programming external memory so that the debugger can access and debug code stored outside the MCUs.

7. Start debugging

  • Connect your STM32 board to your PC using the STLINK debugger.
BMontanari_16-1756124872343.png

 

  • In VS Code, select the debug configuration you created.
BMontanari_17-1756124872344.png

 

  • Press F5 or click the green [Start Debugging] button.
  • The application should halt at the breakpoint set in the BOOT_Application:
BMontanari_18-1756124872352.png
 
The code starts in the FSBL application running from an address within AXISRAM2. After the code stops here, press F5. The green LED should start blinking on the board.
By pausing the code execution, you can confirm that it is running from the external flash by inspecting the function addresses in the call stack. At this stage, all standard debugging features become available, allowing you to:
  • Set breakpoints.
  • Step through your program.
  • Inspect variables, registers, and memory.
  • View call stacks.
BMontanari_19-1756124872360.png

Note: Remember that keeping existing breakpoints between debug sessions can cause failure messages when entering debug mode. If a failure message appears when attempting to enter debug mode, try power cycling the board.

Conclusion

By following these steps, you can successfully debug code running on the STM32N6 using VS Code. This tutorial provides a practical approach, covering environment setup, debugger configuration, and validation to ensure a smooth and effective debugging experience.

Related links

Comments
Ahper
Associate

Hi @B.Montanari ,

Nice and detailed tutorial! Bravo!
That ‘first’ breakpoint isn’t necessary if you use a simple trick in the Appli main() with a debug flag or a user button.

  BSP_PB_Init (BUTTON_USER1, BUTTON_MODE_GPIO);
  // loop that can be used to catch the application for debug.Set mini to 1, build and flash target as normally.
  // Set BOOT1 switch to the left position, press reset without removing cable.
  // Use the Connect to running debug config to connect, then change mini value from watch.
  // For breakpoints to work they seem to need to be manually changed to ahrdware breakpoint
  static volatile int debugFlag = 1;
  while (debugFlag == 1 && BSP_PB_GetState(BUTTON_USER1) == 0)
  {
    __NOP();
  }

 

Version history
Last update:
‎2025-08-26 5:54 AM
Updated by: