Debugging the ARM Cortex-M4 Core of the STM32MP1 Devices

This tutorial shows how to create a basic project for the Cortex-M4 core of the STM32MP1 device and debug it via the on-board ST-Link device in both engineering and production modes. We will create a basic “Blinking LED” project using Visual Studio and VisualGDB and will show how to upload it to the STM32MP1 device and debug it. Before you begin, ensure you have VisualGDB 5.4R11 or later installed.

  1. Start Visual Studio and open the VisualGDB Embedded Project Wizard:
  2. Specify the location and the name for the project that is going to be created:
  3. Proceed with the default settings on the first page of the VisualGDB’s wizard and click “Next” to go the next page:
  4. On the Device Selection page choose the ARM toolchain and select your STM32MP1 device from the list. If you have not created STM32MP1 projects before, click the “Install” button to automatically download the necessary files to your computer:
  5. In this tutorial we will use the STM32MP157C-DK2 board, hence we select the STM32MP157C device. If you are using a different board, select the device that matches your board:
  6. On the next page select the default “LEDBlink” example and pick the GPIO group and pin where the LED is connected. On the STM32MP157C-DK2 board the LED is connected to GPIOH7:
  7. Switch the board into engineering mode via the boot jumpers and connect the power and ST-Link USB connectors as shown below:
  8. In engineering mode, the Cortex-M4 core will be automatically started once you power the board and the Cortex-A core will not run the regular SD card boot process. This allows quickly prototyping Cortex-M4 firmware without configuring the Linux-level settings (we will show those settings later in the tutorial). Ensure ST-Link is detected on the Debug Method page of the VisualGDB’s project wizard and select “STM32MP1xx (Engineering Mode)” as the debugged device:
  9. Press the “Test” button to verify the JTAG connection to the board. As the Cortex-A core is suspended in engineering mode, VisualGDB will display a warning about the registers having zero values (that would normally imply a JTAG issue). Ignore the warning and press “Finish” to create the project:
  10. Once the project is created, build it by pressing Ctrl-Shift-B:
  11. Press F5 to start debugging. Observe how the on-board LED begins to blink:
  12. Set a breakpoint inside the loop in main() and wait for it to trigger. Once the debugging session stops at the breakpoint, you will be able to step through the code and evaluate variables as usual:
  13. Now we will show how to control the STM32MP1’s Cortex-M4 core from the Linux running on the Cortex-A core and how to debug this setup. First of all, switch the board’s boot jumpers back to the default state, plug in the network connector and wait for the board to boot the Linux:
  14. Locate the ELF file produced by VisualGDB (it will normally be located under <Project folder>\VisualGDB\Debug\<Project Name> and will have no extension):
  15. Upload the file into the /lib/firmware folder on your STM32MP1 board (you can use SmarTTY to do this via a simple drag-and-drop). Then you will be able to use the following commands to control the Cortex-M4 core from the Linux running on the Cortex-A core:
    Action Command
    Stop and disable the Cortex-M4 core echo stop > /sys/class/remoteproc/remoteproc0/state
    Set the name of the firmware file that will be loaded into the Cortex-M4 core once it is started echo [name of the file in /lib/firmware] >  /sys/class/remoteproc/remoteproc0/firmware
    Load the firmware file specified earlier to the Cortex-M4 core and start it echo start > /sys/class/remoteproc/remoteproc0/state

    Note that if the core was already stopped, the “echo stop” command will return an error that can be safely ignored.

  16. Verify that the LED started blinking the same way it did in the engineering mode. Then open VisualGDB Project Properties and switch the Debugged Device from STM32MP1 (Engineering Mode) to  STM32MP1 (with PMIC):
  17. Start the debug session. VisualGDB will automatically reload and restart the firmware in the Cortex-M4 core, allowing you to debug it:
  18. If you are using the Custom edition of VisualGDB, you can configure it to automatically upload the firmware and run the Linux-side commands each time you start debugging. To do this, open VisualGDB Project Properties on the Custom Debug Steps page and add the following actions:
    1. Copy $(TargetPath) to /lib/firmware/$(TargetFileName) on root@stm32mp1
    2. Run “echo stop >/sys/class/remoteproc/remoteproc0/state || echo “Already stopped”” in “/” on root@stm32mp1. Note that the ” || echo “Already Stopped”” part is needed to prevent VisualGDB from aborting the debug session when the “stop” command fails if the firmware was already running.
    3. Run “echo $(TargetFileName) > /sys/class/remoteproc/remoteproc0/firmware
    4. Run “echo stop >/sys/class/remoteproc/remoteproc0/state“.

    You can copy the following text to the Clipboard and then use the “Paste” button in the Custom Debug Steps editor to automatically paste the actions into VisualGDB project Properties:

    &lt;?xml version="1.0" encoding="utf-16"?&gt;
    &lt;ArrayOfCustomActionBase xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
      &lt;CustomActionBase xsi:type="FileTransferAction"&gt;
        &lt;SkipWhenRunningCommandList&gt;false&lt;/SkipWhenRunningCommandList&gt;
        &lt;SourceHost&gt;
          &lt;HostName&gt;BuildMachine&lt;/HostName&gt;
          &lt;Transport&gt;BuiltinShortcut&lt;/Transport&gt;
        &lt;/SourceHost&gt;
        &lt;DestinationHost&gt;
          &lt;HostName&gt;stm32mp1&lt;/HostName&gt;
          &lt;Transport&gt;SSH&lt;/Transport&gt;
          &lt;UserName&gt;root&lt;/UserName&gt;
        &lt;/DestinationHost&gt;
        &lt;SourceFilePath&gt;$(TargetPath)&lt;/SourceFilePath&gt;
        &lt;DestinationFilePath&gt;/lib/firmware/$(TargetFileName)&lt;/DestinationFilePath&gt;
        &lt;OverwriteTrigger&gt;Always&lt;/OverwriteTrigger&gt;
      &lt;/CustomActionBase&gt;
      &lt;CustomActionBase xsi:type="CommandLineAction"&gt;
        &lt;SkipWhenRunningCommandList&gt;false&lt;/SkipWhenRunningCommandList&gt;
        &lt;RemoteHost&gt;
          &lt;HostName&gt;stm32mp1&lt;/HostName&gt;
          &lt;Transport&gt;SSH&lt;/Transport&gt;
          &lt;UserName&gt;root&lt;/UserName&gt;
        &lt;/RemoteHost&gt;
        &lt;Command&gt;echo&lt;/Command&gt;
        &lt;Arguments&gt;stop &amp;gt; /sys/class/remoteproc/remoteproc0/state || echo "Already stopped"&lt;/Arguments&gt;
        &lt;WorkingDirectory&gt;/&lt;/WorkingDirectory&gt;
        &lt;BackgroundMode&gt;false&lt;/BackgroundMode&gt;
      &lt;/CustomActionBase&gt;
      &lt;CustomActionBase xsi:type="CommandLineAction"&gt;
        &lt;SkipWhenRunningCommandList&gt;false&lt;/SkipWhenRunningCommandList&gt;
        &lt;RemoteHost&gt;
          &lt;HostName&gt;stm32mp1&lt;/HostName&gt;
          &lt;Transport&gt;SSH&lt;/Transport&gt;
          &lt;UserName&gt;root&lt;/UserName&gt;
        &lt;/RemoteHost&gt;
        &lt;Command&gt;echo&lt;/Command&gt;
        &lt;Arguments&gt;$(TargetFileName) &amp;gt; /sys/class/remoteproc/remoteproc0/firmware&lt;/Arguments&gt;
        &lt;WorkingDirectory&gt;/&lt;/WorkingDirectory&gt;
        &lt;BackgroundMode&gt;false&lt;/BackgroundMode&gt;
      &lt;/CustomActionBase&gt;
      &lt;CustomActionBase xsi:type="CommandLineAction"&gt;
        &lt;SkipWhenRunningCommandList&gt;false&lt;/SkipWhenRunningCommandList&gt;
        &lt;RemoteHost&gt;
          &lt;HostName&gt;stm32mp1&lt;/HostName&gt;
          &lt;Transport&gt;SSH&lt;/Transport&gt;
          &lt;UserName&gt;root&lt;/UserName&gt;
        &lt;/RemoteHost&gt;
        &lt;Command&gt;echo&lt;/Command&gt;
        &lt;Arguments&gt;start &amp;gt; /sys/class/remoteproc/remoteproc0/state&lt;/Arguments&gt;
        &lt;WorkingDirectory&gt;/&lt;/WorkingDirectory&gt;
        &lt;BackgroundMode&gt;false&lt;/BackgroundMode&gt;
      &lt;/CustomActionBase&gt;
    &lt;/ArrayOfCustomActionBase&gt;