Debugging a remote core from the Linux terminal#
This section is a continuation of the information in Debug the LED Blink Program from the Linux terminal.
CCS gives the developer many debug tools that Linux does not have access to: pausing code, stepping through instructions, viewing internal registers, and more. If using CCS to debug is an option, we suggest using CCS instead of trying to do the entire debug through Linux. For more information about CCS debug while Linux is running, reference section Debug the remote core program from CCS while Linux is running.
If CCS is not a debugging option, there are several basic debugging tools that are still available from the Linux command line.
Print statements#
As long as the remote core project is built with the trace log enabled, then print statements that are generated with the DebugP_log command will show up in the Linux trace0 log. These print statements can be placed throughout the remote core to help the user see how far into the code the remote core gets, what path the code takes through functions, print out variable values, etc.
A basic DebugP_log command may look like this:
DebugP_log("[IPC RPMSG ECHO] Message exchange started with RTOS cores !!!\r\n");
For more information about verifying that the trace log is enabled in SysConfig, refer to section How to create remote core firmware that can be initialized by Linux.
For more information about reading the trace0 log from the Linux terminal, refer to section Debug the LED Blink Program from the Linux terminal.
Use devmem2 to read and write memory#
the devmem2 tool allows Linux to implement several debug strategies by reading and writing data to and from the remote core. RPMsg can also be used to pass data back and forth between Linux and the remote core, but this section will assume that RPMsg is not used in the design.
How to find what memory to read and write#
The memory that Linux and the remote core is using to read and write can be anywhere in the processor system. However, one common usecase is to identify a specific region of DDR memory that is used to pass data back and forth.
Let’s take a Linux RPMsg Echo example. First, build the example. Then, read the .map file that is generated: examples/drivers/ipc/ipc_rpmsg_echo_linux/<board>/<core>/ti-arm-clang/ipc_rpmsg_echo_linux.release.map
In this particular example, the first part of the .map file looks like this:
****************************************************************************** TI ARM Clang Linker Unix v2.1.3 ****************************************************************************** >> Linked Sun Sep 17 16:43:46 2023 OUTPUT FILE NAME: <ipc_rpmsg_echo_linux.release.out> ENTRY POINT SYMBOL: "_vectors" address: 00000000 MEMORY CONFIGURATION name origin length used unused attr fill ---------------------- -------- --------- -------- -------- ---- -------- R5F_VECS 00000000 00000040 00000040 00000000 RWIX R5F_TCMA 00000040 00007fc0 000010f8 00006ec8 RWIX R5F_TCMB0 41010000 00008000 00000000 00008000 RWIX FLASH 60100000 00080000 00000000 00080000 RWIX MSRAM 70080000 00040000 00000000 00040000 RWIX DDR_0 a0100000 00100000 00001000 000ff000 RWIX DDR_1 a0200000 00e00000 00023cc0 00ddc340 RWIX RTOS_NORTOS_IPC_SHM_M a5000000 00800000 00006680 007f9980 RWIX USER_SHM_MEM a5800000 00000080 00000000 00000080 RWIX LOG_SHM_MEM a5800080 00003f80 00000000 00003f80 RWIX
DDR_1 is the DDR memory set aside by Linux for the R5F core to use. We see that 0xE0_0000 of memory is reserved, from 0xA020_0000 to 0xA0FF_FFFF. However, only 0x2_3CC0 of that memory region is currently used. That means we can use the rest of that memory region for passing data between Linux and the remote core, and no other code in the system will corrupt the data.
For example, we could use 0xA050_0000 as the starting address for the memory addresses that Linux and the remote core firmware used to pass data back and forth.
Usecases for reading from memory#
Usecases here are similar to the usecases when the developer would want to add DebugP_log print statements.
Usecases for writing to memory#
Writing to memory is useful in multiple situations, like being able to easily pass different values to the remote core program to see how it responds. However, one of the most powerful usecases for writing to memory that the remote core is reading is implementing software breakpoints.
For example, if the developer wants to inspect memory values at a certain point during the remote core execution, they could implement a breakpoint in their remote core firmware where the remote core waits for a value to be written to a known memory address before it continues executing code. That allows the developer to inspect memory values once the breakpoint has been reached, and then tell the remote core to continue running by writing a value to a known memory address.
The logic to implement a software breakpoint on the remote core side could look like this:
set the value of address 0xA050_0004 to 0 while the value at 0xA050_0004 = 0, loop // once the user is ready for the program to continue running, // use devmem2 w to write a value to the memory address and break the loop