
Existen varias alternativas para realizar el debug. Cuando programamos para el escritorio la primera forma y la más sencilla para realizar el debug es por medio de impresiones a pantalla con instrucciones printf. Es la más sencilla pero también la más intrusiva.
En los sistemas embebidos, esto es totalmente distinto, porque de alguna forma le tenemos que comunicar eso que queremos mostrar a nuestra PC de desarrollo.
Aunque parezca fácil, necesitamos crear una infraestructura para soportar el envío de esa información a la PC, y podría llegar a ser demasiado intrusiva y consumir recursos innecesarios. Para ello los ARM Cortex ofrecen varias formas de realizar el debug, siendo los más comunes el JTAG y el SWD, soportados, ambos, por OpenOCD, el cual fue instalado siguiendo las instrucciones en la parte primera de este tutorial.
En esta primera entrega nos enfocaremos en como iniciar el proceso usando OpenOCD.
OpenOCD
Para ejecutar openOCD, es necesario pasarle como parámetro un archivo de configuración, por lo general estos archivos de configuración los encontramos dentro de /usr/local/share/opencd/scripts/boards, aunque no siempre vamos a encontrar la configuración de nuestra tarjeta, por lo que tenemos que copiar y modificar una existente.
Por ejemplo, para la tarjeta que estoy usando de ejemplo más el st-link v2, ese archivo de configuración quedaría de la siguiente forma.
# This is for using with STLINK/V2
source [find interface/stlink-v2.cfg]
# increase working area to 8KB
set WORKAREASIZE 0x2000
# chip name
set CHIPNAME STM32F103C8T6
source [find target/stm32f1x_stlink.cfg]
# use hardware reset, connect under reset
reset_config srst_only srst_nogate
Colocamos este archivo dentro del directorio del proyecto
ex0$ ls *.cfg
stm32f103c8t6.cfg
Nota: En caso de que la tarjeta que tengamos este soportada, simplemente apuntamos openocd al lugar indicado.
En este momento ya tenemos preparado todo lo necesario para realizar nuestro primera sesión de debug, para este paso tenemos que abrir 3 terminales en el directorio del proyecto.
En la primera terminal ejecutamos
openocd -f archiv.cfg
O en mi caso particular
openocd -f stm32f103c8t6.cfg
Si todo ha ido bien veremos algo como
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : clock speed 950 kHz
Info : STLINK v2 JTAG v17 API v2 SWIM v4 VID 0x0483 PID 0x3748
Info : using stlink api v2
Info : Target voltage: 3.205222
Info : STM32F103C8T6.cpu: hardware has 6 breakpoints, 4 watchpoints
Uno de los aspectos que nos informa del MCU es la cantidad de breakpoints disponibles en el hardware.
Cuando veamos esa información, abrimos otra consola y ejecutamos
telnet localhost 4444
Salida
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
>
En estos momentos ya estamos preparados para tomar el control del MCU por medio de un reset halt
> reset halt
timed out while waiting for target halted
TARGET: STM32F103C8T6.cpu - Not halted
in procedure 'reset'
in procedure 'ocd_bouncer'
>
En la tarjeta presionamos el boton de reset
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x08000008 msp: 0x20005000
>
Con esto ya tenemos el control total del MCU y podemos observar el contenido de los registros del sistema con reg
> reg
===== arm v7m registers
(0) r0 (/32): 0x00057E40
(1) r1 (/32): 0xFFFDFFFF
(2) r2 (/32): 0x000562B1
(3) r3 (/32): 0x000562B2
(4) r4 (/32): 0xFFFFFFFF
(5) r5 (/32): 0x7FFFFFFE
(6) r6 (/32): 0xB5EFB7AA
(7) r7 (/32): 0x20004FE8
(8) r8 (/32): 0xFFFE75DC
(9) r9 (/32): 0xFFFFFFDF
(10) r10 (/32): 0x2A7BF9DD
(11) r11 (/32): 0xA63F5073
(12) r12 (/32): 0xFFFFFFFF
(13) sp (/32): 0x20005000
(14) lr (/32): 0xFFFFFFFF
(15) pc (/32): 0x08000008
(16) xPSR (/32): 0x01000000
(17) msp (/32): 0x20005000
(18) psp (/32): 0x6AFF7268
(19) primask (/1): 0x00
(20) basepri (/8): 0x00
(21) faultmask (/1): 0x00
(22) control (/2): 0x00
===== Cortex-M DWT registers
(23) dwt_ctrl (/32)
(24) dwt_cyccnt (/32)
(25) dwt_0_comp (/32)
(26) dwt_0_mask (/4)
(27) dwt_0_function (/32)
(28) dwt_1_comp (/32)
(29) dwt_1_mask (/4)
(30) dwt_1_function (/32)
(31) dwt_2_comp (/32)
(32) dwt_2_mask (/4)
(33) dwt_2_function (/32)
(34) dwt_3_comp (/32)
(35) dwt_3_mask (/4)
(36) dwt_3_function (/32)
Con help, obtenemos una lista detallada de los comandos disponibles
Enseguida abrimos una tercera terminal para ejecutar arm-none-eabi-gdb
arm-none-eabi-gdb main.elf
Y nos enlazamos con el target remoto
(gdb) target remote localhost:3333
Remote debugging using localhost:3333
main () at main.c:36
36 {
Aunque probablemente quieran usar una GUI, por lo que tenemos varias alternativas, una de ellas es ddd, que está incluida en los repositorios de ubuntu y en lugar de
arm-none-eabi-gdb main.elf
Hacemos un
ddd --eval-command="target remote localhost:3333" --debugger arm-none-eabi-gdb main.elf

En la siguiente entrega mostraré como imprimir secciones de memoria del MCU al igual que variables.