Simulación de código C para AVR

embedded
Cómo depurar un programa en C para AVR en GNU/Linux con avr-gdb

Introducción


Después de haberlo intentado varias veces, habiéndome dado por vencido siempre, he vuelto a las andadas, ésta vez para no parar hasta conseguir depurar un programa escrito que utiliza avr-libc (se puede consultar la receta sobre instalar la toolchain para AVR en éste mismo blog).

Para la depuración he utilizado avr-gdb y simulavr desde distintas máquinas, consiguiendo de ese modo ver en una la ejecución del programa objeto de la prueba y en otra el contenido de los registros y de la memoria del micro.

A partir de ahora, explico cómo lo he hecho todo, desde la compilación (se necesitan parámetros específicos para el enlazador) hasta la puesta en funcionamiento del simulador. Doy por sentando que se conoce el uso de gdb para depurar.

Praparándolo todo

Bueno, para que podamos funcionar tan solo necesitamos simulavr y avr-gdb. En nuestro querido Debian ya sabeis:

javieralso@avalon:~$ sudo apt-get install avr-gdb simulavr

Compilación del código a simular


Para poder simular el código, debemos pasar la opción g a avr-gcc. Además, de eso, debemos decirle al enlazador que utilice la opción -gstabs. Os paso directamente el Makefile del proyecto que utilicé como prueba para que lo veais mas claro. Está un poco “guarro”, pero servirá para ilustrar el ejemplo Eye-wink:


MCU=atmega128
CC=avr-gcc
OBJCOPY=avr-objcopy
CFLAGS=-g -mmcu=$(MCU) -Wall -Wstrict-prototypes
#—————————-
all: rs232toRS485.hex rs232toRS485.bin
#—————————-
%.hex : %.out $(OBJCOPY) -j .text -O ihex rs232toRS485.out rs232toRS485.hex

%.bin : %.out $(OBJCOPY) -j .text -O binary rs232toRS485.out rs232toRS485.bin

rs232toRS485.out : main.o $(CC) $(CFLAGS) -o rs232toRS485.out -Wl,-Map,rs232toRS485.map -Wa,-gstabs main.o avr-objdump -S rs232toRS485.out > rs232toRS485.lst

%.o : %.c $(CC) $(CFLAGS) -O0 -c $^

Además de lo comentado anteriormente, también se ha marcado una regla que se encarga de la creación de una imagen binaria del código de salida. Ésta imagen es importante, ya que es la que cargará simulavr para poder llevar a cabo la simulación del micro.

NOTA: Para que se pueda ver el contenido de las variables locales dentro del entorno de simulación, es muy importante que el flag de optimización esté puesto a 0. ($(CC) $(CFLAGS) -O0 -c $^).

Simulando


Bueno, una vez que ya tenemos nuestro programita compilado toca simularlo. Supondremos que vamos a correr simulavr y avr-gdb en la misma máquina, aunque ésto no tiene por qué ser así, de hecho yo lo hago en máquinas separadas para poder ver la traza y el estado de los registros. Pero ésto ya va a gusto del consumidor Sticking out tongue.

Para nuestros propósitos simulavr funcionará como un gdbserver, es decir, le dirá a avr-gdb cómo debe comportarse. Por ello tenemos que arrancarlo primero. Para ello lo lanzamos de la siguiente manera:

javieralso@avalon:~$ simulavr -d atmega128 -P simulavr-disp -g rs232toRS485.bin

¿Qué hemos hecho? pues hemos llamado a simulavr diciéndole que vamos a simular un atmega128. Como queremos ver el contenido de los registros hemos llamado tambíen a simulavr-disp ya que simulavr solo dice a avr-gdb cómo comportarse, pero no muestra información alguna. simualvr-disp es el frontend que nos permite ver el contenido de los registros internos de la máquina que estamos simulando.

Para lanzar avr-gdb crearemos antes un archivo de comandos a ejecutar para “automatizar” un poco el uso. Éste archivo tiene algo como ésto:

file rs232toRS485.out
target remote localhost:1212
load
break main
continue

En él decimos a gdb que cargue el archivo a simular, se conecte con en esta mísma máquina (localhost) y escuchando en el puerto 1212 que es donde por defecto escucha simulavr.
Lanzamos avr-gdb (a mi en especial me gusta mas avr-gdbtui) de la siguiente manera:

javieralso@avalon:~$ avr-gdbtui -x comandos.gdb

siendo comandos.gdb el archivo en el que guardamos los comandos anteriores.
Podremos ver como inmediatamente cambian los valores de los registros mostrados en simulavr y avr-gdb queda parado en el main de nuestro programa. Ésto sucede porque nuestro programa empieza a simularse inmediatamente después de lanzar el depurador y para en el primer breakpoint que encuentra, que se encuentra en main (breakpoint por defecto). Cómo antes de eso se tiene que ejecutar código (arranque del micro, configuración de la pila, registros de configuración iniciales, inicialización de memoria, etc….) vemos los cambios en los registros de la máquina reflejados en simulavr. Ahora ya podremos simular/depurar como se hace usualmente con gdb.

¡¡Ale, a disfrutarlo Laughing out loud!!

En el futuro…


Actualmente también me estoy peleando con avarice que permite borrar, programar, leer, verificar y depurar “on board” muchos dispositivos de la familia AVR utilizando un AVR JTAG ICE y avr-gdb o insight. En cuanto sea capaz de terminar de hacerlo funcionar, prometo explicarlo en otra recetilla. Mientras tanto a esperar pacientemente jugueteando con todo lo que ya tenemos sobre AVR….

Referencias