Sunday, July 14, 2013

Plotting memory content of an embedded device - simple script

Welcome.
In this post I will try to describe how to dump memory contents of en embedded system and plot them using Gnuplot or some other program. To dump the memory I will connect to the target with openOCD and GDB debugger.
All of the above operations can be done with a simple script. My target platform will be STM32F105 microcontroller.

1. To get started We will need to connect to the target with openOCD. It can be done from the console or from IDE such as eclipse.
Here is the command that I'm using and the openOCD output:
>openocd -f interface/ocd.cfg -f target/stm32f1x.cfg

Open On-Chip Debugger 0.5.0 (2011-08-09-23:21)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.berlios.de/doc/doxygen/bugs.html
Info : only one transport option; autoselect 'jtag'
3000 kHz
3000 kHz
adapter_nsrst_delay: 100
jtag_ntrst_delay: 100
cortex_m3 reset_config sysresetreq
Info : clock speed 3000 kHz
Info : JTAG tap: stm32.cpu tap/device found: 0x3ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x3)
Info : JTAG tap: stm32.bs tap/device found: 0x06418041 (mfg: 0x020, part: 0x6418, ver: 0x0)
Info : stm32.cpu: hardware has 6 breakpoints, 4 watchpoints

2. After connecting We will need to start a GDB debug session. GDB allows the user to dump memory content with "dump" command. As input parameters it needs: dump type, file location, start address and end address. To dump the memory We will need to stop the target. It can be done with a breakpoint. Here are the commands for GDB and output:
gdb -ex "target remote localhost:3333"
-ex "monitor reset halt" -ex "break *0x080029dd" -ex "continue"
-ex "dump binary memory c:/data/p1.hex  0x20000d88 0x20000DEC" 
-ex "dump binary memory c:/data/p2.hex  0x20000e50 0x20000EB4"
-ex "dump binary memory c:/data/p3.hex  0x20000f18 0x20000F7C"
-ex "quit" --batch

0x200004cd in ?? ()
JTAG tap: stm32.cpu tap/device found: 0x3ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x3)
JTAG tap: stm32.bs tap/device found: 0x06420041 (mfg: 0x020, part: 0x6420, ver: 0x0)
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x0800013c msp: 0x20002000
Breakpoint 1 at 0x80029dd
Note: automatically using hardware breakpoints for read-only addresses.

Program received signal SIGTRAP, Trace/breakpoint trap.
0x200004cd in ?? ()
A debugging session is active.

Inferior 1 [Remote target] will be detached.

Quit anyway? (y or n) [answered Y; input not from terminal]

3. After successfully storing contents of memory We will need to convert the binary memory file to a file that can be plotted. To do this I have written a simple console application. It has 3 input parameters: data size, binary file and plot file. It reads bytes from binary file and writes argument and value (1-8 bytes) to specified plot file:
Mem2plot.exe 2 c:/data/p1.hex c:/data/p1.plot
Mem2plot.exe 2 c:/data/p2.hex c:/data/p2.plot
Mem2plot.exe 2 c:/data/p3.hex c:/data/p3.plot

4. Last but not least We have to plot the memory content. It can be done with Gnuplot or the plot file can be imported to some spreadsheet program. For this example I will use Gnuplot executed from command line:
gnuplot.exe -e "set terminal png size 1024,768;set output 'c:/data/pwm.png';
set ls 1 lw 2; set ls 2 lw 2;set ls 3 lw 2;
plot 'c:/data/p1.plot' with lines ls 1 title 'PWM1',
'c:/data/p2.plot' with lines ls 2 title 'PWM2',
'c:/data/p3.plot' with lines ls 3 title 'PWM3';"

Simple script:
@echo off
REM directories
set Pathname=c:/data
set Pathname_plot=c:/Progra~1/gnuplot/bin
set Pathname_m2p=c:/mem2plot

REM files
set  VAR_MEM_FILE=%Pathname%/2.raw
set  VAR_PLOT_FILE=%Pathname%/2.plot
set  VAR_PNG_FILE=%Pathname%/2.png

REM GDB
set VAR_GDB_TARGET=remote localhost:3333
set VAR_GDB_RESET=monitor reset halt
set VAR_GDB_BREAK=0x8005dd4
set VAR_GDB_START=0x20002374
set VAR_GDB_END=0x200023D8
set VAR_GDB_CMD=-ex "target %VAR_GDB_TARGET%" -ex "%VAR_GDB_RESET%"
-ex "break *%VAR_GDB_BREAK%" -ex "continue"
-ex "dump binary memory %VAR_MEM_FILE%  %VAR_GDB_START% %VAR_GDB_END%"
-ex "quit"

REM MEM2PLOT
set VAR_M2P_SIZE=2

REM GNUPLOT
set VAR_GNUPLOT_CMD=set terminal png;set output '%VAR_PNG_FILE%';
plot '%VAR_PLOT_FILE%'
REM set VAR_GNUPLOT_CMD=set terminal windows;plot '%VAR_PLOT_FILE%';pause -1;

@echo on

REM actual script
gdb %VAR_GDB_CMD% --batch
cd %Pathname_m2p%
Mem2plot.exe %VAR_M2P_SIZE% %VAR_MEM_FILE% %VAR_PLOT_FILE%
cd %Pathname_plot%
gnuplot.exe -e "%VAR_GNUPLOT_CMD%"

These are all the things that We need to do in order to plot memory contents of an embedded system.
Modified script:
@echo off
REM directories
set Pathname=c:/data
set Pathname_plot=c:/Progra~1/gnuplot/bin
set Pathname_m2p=c:/mem2plot

REM files
set VAR_PNG_FILE=%Pathname%/pwm.png

REM GDB
set VAR_GDB_TARGET=remote localhost:3333
set VAR_GDB_RESET=monitor reset halt
set VAR_GDB_BREAK=0x080029dd
set VAR_GDB_CMD=-ex "target %VAR_GDB_TARGET%"
-ex "%VAR_GDB_RESET%" -ex "break %VAR_GDB_BREAK%" -ex "continue"
-ex "dump binary memory %pathname%/p1.hex  0x20000d88 0x20000DEC"
-ex "dump binary memory %pathname%/p2.hex  0x20000e50 0x20000EB4"
-ex "dump binary memory %pathname%/p3.hex  0x20000f18 0x20000F7C" -ex "quit"

REM MEM2PLOT
set VAR_M2P_SIZE=2

REM GNUPLOT
set VAR_GNUPLOT_CMD=set terminal png size 1024,768;set output '%VAR_PNG_FILE%';
set ls 1 lw 2; set ls 1 lw 2; set ls 3 lw 2;
plot '%pathname%/p1.plot' with lines ls 1 title 'PWM1',
'%pathname%/p2.plot' with lines ls 2 title 'PWM2',
'%pathname%/p3.plot' with lines ls 3 title 'PWM3';

@echo on

REM actual script
gdb %VAR_GDB_CMD% --batch
cd %Pathname_m2p%
Mem2plot.exe %VAR_M2P_SIZE% %pathname%/p1.hex %pathname%/p1.plot
Mem2plot.exe %VAR_M2P_SIZE% %pathname%/p2.hex %pathname%/p2.plot
Mem2plot.exe %VAR_M2P_SIZE% %pathname%/p3.hex %pathname%/p3.plot
cd %Pathname_plot%
gnuplot.exe -e "%VAR_GNUPLOT_CMD%"

And the result:

Contents of the buffer viewed in eclipse:


What gave me the idea for the script?
The idea came after using Texas Instruments Code Composer Studio IDE which gives the possibility to plot memory contents on a graph. At work I was doing a project on STM32F1 series microcontroller. The device was doing some voltage and current measuring with the internal ADC. I thought it would be good if I could plot the data without the need for sending it through serial port or some interfaces.

How to trigger the memory dump breakpoint?

To dump the memory we need to halt the target. It can be done with a breakpoint.
In my opinion it is good to set the breakpoint at some line we can trigger from an external interface.
For example if Your device uses serial port for debugging or any non critical transmission.
You can send a command that triggers the breakpoint. Another way is to implement a trigger in Your code. You can put an additional line which will be triggered on spacial occasions a and will be used for the breakpoint only.