PRU Example - Debugging multiple processors at once


The goal of this example is to build on the rpmsg_echo example in PRU_example_rpmsg_echo and show how to step through both programs at the same time.

This will involve the following:

  • Add a debugger wait loop to the rpmsg_echo program for PRU_1
  • Starting the rpmsg_echo program using remoteproc
  • Attaching to the program in PRU_1 with Code Composer through the JTAG connection
  • Start the rpmsg_arm program with gdbserver on the ARM
  • Connect Code Composer to the gdbserver in a second debug connection.
  • Plant breakpoints in both programs and let each program run


Add a debugger wait loop to the rpmsg_echo program for PRU_1

  • Modify the rpmsg_pru_user_space_echo.c source file
    // CYCLES_PER_SECOND will be same as speed of the processor
    #define CYCLES_PER_SECOND (250000000)
    void main(void)
        struct pru_rpmsg_transport transport;
        uint16_t src, dst, len;
        volatile uint8_t *status;
        int ii;
        // wait for 120 seconds
        for (ii = 0; ii < 120; ++ii)
  • Build the program
    • Hit ctrl-B or Project->Build All
  • Copy program to target. (from Local Terminal View in Code Composer)
    cd <workspace_directory>/rpmsg_pru/Debug
    scp rpmsg_pru.out am62x:/lib/firmware/pru_demos

Starting the rpmsg_echo program using remoteproc

  • In the Local Terminal View
    • After starting PRU_1, you have 2 minutes to connect the debugger to PRU_1
      ssh am62x "echo stop > /sys/class/remoteproc/remoteproc3/state" 
      ssh am62x "echo start > /sys/class/remoteproc/remoteproc3/state" 

Attaching to the program in PRU_1 with Code Composer through the JTAG connection

  • Open the Target Configurations View. Windows->Show View->Target Configurations
  • Select the am62x.ccxml target configuration
  • Launch the configuration (right-mouse->Launch Selected Configuration)
  • Remove clutter from the Debug View
    • In the Debug View, select all processors except the PRU_1 line
    • right-mouse->Hide Core(s)
  • In the Debug View, select the PRU_1 processor
  • Connect to the PRU_1 target (right-mouse->Connect Target)
  • Load the symbols. (Run->Load->Load Symbols) Pick the rpmsg_pru.out program
  • The program should be stopped at the __delay_cycles() at the beginning of the main() function

Get out of the debugger wait loop

  • Select the first line of code after the delay loop.
  • Right-mouse->Move to Line
  • Tell the program to run
    • Run->Resume

Start the rpmsg_arm program with gdbserver on the ARM

  • In the terminal View, Local Terminal
    ssh am62x gdbserver :10000 ./rpmsg_arm
  • Create a Debug Configuration for the ARM program
    • Run->Debug Configurations...
    • Select C/C++ Remote Application->right-mouse->New Configuration
    • Change Name to "rpmsg_arm Debug"
    • Main Tab
      • Project is rpmsg_arm,
      • Application is Debug/rpmsg_arm
      • At the bottom on the line "Using GDB Automatic Remote Debugging Launcher" click on Select other...
        • Check the Use configuration specific settings
        • Select GDB Manual Remote Debugging Launcher
        • Click on OK
    • Debugger Tab
      • Debugger Options Main Tab
        • GDB debugger: Change gdb to gdb-multiarch
      • Debugger Options Connection Tab
        • If ssh config for am62x uses a local port mapping different from 10000, change the 10000 to whatever is specified
    • Click on Debug to start the debug session

Control both the PRU_1 and ARM programs from Code Composer.

After starting the ARM debug configuration, the Debug View in Code Composer now shows 2 sets of connected targets.

Code Composer offers two Debug Perspectives in this case. In the upper right of Code Composer, there are icons for the active Perspectives. A Perspective is just a set of Views. The three active Perspectives are:

  • CCS Edit
  • CCS Debug
  • Debug

After starting the ARM session, Code Composer will have switched to the Debug perspective. The CCS Debug perspective sometimes offers more options that are useful for the JTAG connections. When breakpoints are hit in an ARM program though, Code Composer will want to switch to the Debug perspective. For the most part, you can just use the Debug perspective and not have to switch so much.

The Debug view shows both the JTAG and the gdbserver connections.

  • am62x.ccxml
    • This set of connected processors is from the JTAG connection and includes the PRU_1.
    • Previously, everything except PRU_1 was hidden so you should just see PRU_1
    • At this point, PRU_1 is running
  • rpmsg_arm
    • This is the ARM program and is currently stopped at the beginning of main().

Let's set some breakpoints in the ARM program.

  • In the Debug view, select the line showing main() under rpmsg_arm
    • This tells Code Composer which debug context you want to set breakpoints in
    • Set a breakpoint at the line which calls write() to send a message to the PRU
    • Set a breakpoint at the line which calls read() to get a message from the PRU
    • Set a breakpoint at the line which prints a message saying all messages have been received.
  • Open the Breakpoints View. Window->Show View->Breakpoints
  • In the Breakpoints View, there is an View Menu icon consisting of 3 dots in a vertical column. Click on this icon.
    • Select Group By->Projects
    • This will show the ARM breakpoints in one grouping and the PRU_1 breakpoints in a different grouping

Now run the ARM program and let it send/receive a couple of messages.

Now add a breakpoint in PRU_1

  • In the Debug view, select the am62x.ccxml line for the PRU_1.
  • Hit the Pause icon to stop PRU_1
  • Set a breakpoint where the call to pru_rpmsg_send() is made.
  • Hit resume so PRU_1 starts to run again.

Now let the ARM program run to right after the call to write(). The PRU_1 should stop as soon as write() is called.


In this example, the following was done:

  • Added a debugger wait loop to the PRU_1 program
  • The PRU_1 program was started from remoteproc
  • Connected to PRU_1, attached to the existing program and manually broke out of the debugger wait loop and let the PRU_1 continue execution
  • Started a gdbserver-based debug session for the ARM program
  • Set breakpoints in the ARM program. Ran and saw that messages were sent and received.
  • Stopped the PRU_1, set a breakpoint and saw that it was hit after the ARM called write()

This has been a simple case of debugging programs on multiple processors. Basic control has been demonstrated.

Note that multiprocessor debugging can be complicated and there are limitations to what can be done in Code Composer. For example, if you have two instances of the same program running, it would be nice to have a set of breakpoints for instance_1 and a different but possibly overlapping set of breakpoints for instance_2. Code Composer has a hard time allowing breakpoints for both instances if they are using the same source file. When there are two instances of the same executable program, all the source files would be the same. But even if it is 2 different programs, library source files would likely be the same and then you can have the same problem. It may be possible to solve the problem by using a Source Path Mapping and having multiple copies of the source files so even though the executables have the same source file paths, the debug configurations can have different path mappings so that different files are used. This could take a fair amount of set up but could be worthwhile in certain circumstances.

Go to top
Add picture from clipboard (Maximum size: 1 GB)