Bienvenido: Ingresar
location: LabElectronica / SimulacionGDB

Simulación de núcleo ARM7tdmi con Toolchain GNUARM

Toolchain GNUARM

Se decidió utilizar como Toolchain de desarrollo para los microcontroladores ARM7 a GNUARM. La misma al ser un cast de GNU para núcleos ARM es Software Libre contando con <a target="top" href="http://www.gnu.org/copyleft/gpl.html">GNU General Public License</a>_.

Instalación

Se debe bajar el paquete binario desde alguno de los siguientes links o bien desde la la página http://www.gnuarm.com/. Existen otros paquetes para el emulador de Linux Cygwin en Windows y otra en Mac OS.

GCC-3.4 toolchain(GNU/Linux (x86)):

GCC-4.0 toolchain(GNU/Linux (x86_64)):

binutils-2.16.1, gcc-4.0.1-c-c++, newlib-1.13.0, insight-6.1, TAR BZ2 [61.6MB]

binutils-2.16.1, gcc-4.0.2-c-c++, newlib-1.14.0, insight-6.4, TAR BZ2 [65.5MB]

Luego se descomprime el archivo en una ubicación en el disco y ya se encuentra listo para su uso.

Se puede realizar un PATH en el .bashrc de modo de que no se tenga que nombrar toda la ruta cuando realizamos un proyecto con makefile:

y agregamos la linea:

De este modo completamos la instalacion del paquete en GNU/Linux.

Descripción breve de paquetes del Toolchain

Binutils (GNU Binary Utilities)

' Convierte direcciones en nombres de archivo y números de línea.

' Crear, modificar, y extraer desde archivos.

' Demangle encoded símbolos C++.

' Lista símbolos desde archivos objeto.

' Copiar y traducir archivos objeto.

' Muestra información de archivos objeto.

' Genera indices al contenido de archivos.

' Muestra el contenido de archivos de formato ELF.

' Lista el tamaño de cada sección y el tamaño total.

' Lista cadenas imprimibles desde archivos.

GCC (GNU Compiler Collection):

'%BLACK% - Compilador%ENDCOLOR%

'%BLACK% - Linker%ENDCOLOR%

%BLACK% - Ensamblador%ENDCOLOR%

GDB (Debugger GNU):

'

Insight (Debugger GNU con interface TCL/TK):

'

Newlib (Librería C para sistemas embebidos)

Uso del compilador GCC:

Se utiliza ejecutando arm-elf-gcc desde la línea de comandos como el siguiente ejemplo:

Se necesitará reemplazar stage-opt con una de las opciones listadas más adelante, other-opts con cualquier otra opción que necesite, in-file con el nombre de su archivo de entrada, y out-file con el nombre de salida. Ambos nombres de archivo deben tener la extensión apropiada.

Para convertir código fuente C a archivo objeto binario:

Para convertir múltiples archivos objeto binarios en archivo ejecutable (Caso general):

Para convertir código fuente C en un ejecutable (para usar con Insight solamente):

Para convertir código fuente C en código ensamblador:

Etapas de Compilación:

El compilador GNU pasa a través de 4 distintas etapas para convertir el programa C en un archivo ejecutable: Este preprocesa el archivo fuente, luego lo compila, lo ensambla y finalmente lo linkea. Estas etapas se muestran en el siguiente diagrama:

Preprocesamiento:

La primera etapa del compilador GNU convierte el archivo de entrada en una salida preprocesada. El preprocesamiento convierte todos las declaraciones como #include, #define y #ifdef en código C verdadero. La entrada debe tener la extensión .c para significando código fuente C. Uno puede detener el compilador en esta etapa poniendo -E como opción. Si especifica -E, la salida debe tener extensión .i. En la práctica, uno casi nunca quiere detener el proceso en esta etapa.

Compilación:

La segunda etapa del compilador convierte la entrada preprocesada en lenguaje ensamblador. Se puede arrancar con el compilador en esta etapa especificando la entrada con la extensión .i. Se pude también detener el compilador en esta etapa especificando -S, en cuyo caso la extensión de la salida será .s.En la práctica, uno nunca empieza en esta etapa, siempre se quiere empezar por la de preprocesamiento. Hay veces, de todas formas, que uno quiere detenerse en esta etapa, para chequear la calidad del código producido por el compilador.

Ensamblado:

La tercera etapa del compilador convierte el código ensamblador en un objeto binario a la salida. El compilador GNU en ese momento llama al ensamblador , para hacer el trabajo de ensamblado. Por otra parte, el objeto binario que es generado por esta etapa es siempre deseado. Por ejemplo, se puede crear un número de archivos objeto de código fuente C (llamando varias veces al compilador) lo que puede ser después linkeado junto para formar un ejecutable. Se puede hacer detener al compilador en esta etapa especificando un -c como opcion de etapa, en cuyo caso se tendrá un archivo de salida de extensión .o.

Linkeo:

La cuarta y última etapa es linkear los archivos objeto en un archivo de salida ejecutable. El compilador GNU usa el GNU Linker, , nosotros mismos. La razón es que el compilador GNU automaticamente linkea un número de librerias del sistema estandar en el archivo ejecutable. Estas librerías le permiten a nuestro programa interactuar con el sistema operativo, para usar funciones de librerías estandar C, para usar ciertos beneficios y operaciones (como la división). Si uno quiere ver exactamente que librerías estan siendo linkeadas en el ejecutable, uno debe poner el flag verbose -v en el compilador.Esto tiene importancia en sistemas embebidos. Estos sistemas no tienen sistema operativo.

Otras opciones:

Hay cientos de opciones que se pueden pasar al compilador GNU. Afortunadamente, solamente se necesitarán algunas que debemos tener a mano para una rutina básica y serán nombradas a continuación. Si está interesado en conocer todo el espectro de opciones que existen, puede dirigirse a el manual 1 de arm-elf-gcc, tipeando ' en la línea de comandos.

+ Opciones de Debugging:

Las opciones de debugging del compilador GNU son para producir información que lo ayudará con el debug de sus programas.

Las más comunes son: <table width="450" align="center" cellspacing="0" cellpadding="0" border="0"><tbody><tr align="left"><td align="center" style="width: 60px">

*-g* </td><td>

Incluye información de debugging en los archivos objeto binarios o en los archivos ejecutables. Esta información le permite usar el GNU Debugger para debugear su programa a nivel código fuente. La opción no modifica su programa de ninguna forma, y por esto puede ser usada siempre.

</td></tr><tr align="left"><td align="center"> *-Wall* </td><td>

Alerta acerca de potenciales bugs y construcciones cuestionadas que pueden existir en su código C. Muy recomendado! </td></tr><tr align="left"><td align="center"> *-fverbose-asm* </td><td>

Usted debe incluir esta opción si está convirtiendo el código fuente C en lenguaje ensamblador (opción -S). Esto hace que el compilador inserte comentarios en la salida para ayudarlo a interpretar el código ensamblador resultante. </td></tr><tr align="left"><td align="center"> *-v* </td><td>

Hace que el compilador GNU muestre la linea de comando actual que este usa para realizar las diferentes etapas de compilación, con información adicional. Muy importante si quiere ver exactamente la opción de línea de comando y las librerias usadas para crear el ejecutable final. </td></tr></tbody></table>

+ Opciones de optimización:

Las opciones de optimización afectan directamente la calidad y el tamaño del código de salida producido por el compilador GNU. En particular, estos dirijen el compilador para producir la mejor calidad de código.

Las opciones de optimización más comunes son: <table width="450" align="center" cellspacing="0" cellpadding="0" border="0"><tbody><tr align="left"><td align="center" style="width: 50px">

*-O0* </td><td>No optimiza para nada el código. Esta opción hace que el compilador produzca código que coincide exactamente con el programa original en C. Esto hace el debug a nivel de lenguaje assembler fácil, ya que cada sentencia en C es traducida en un bloque independiente de código.El segundo caracter es una O de optimización y el tercer es un cero que significa sin optimización.</td></tr><tr align="left"><td align="center"> *-O1* </td><td>

Trata moderadamente de producir código en lenguaje assembler que sea rápido y de tamaño reducido. Esto hace que el compilador demore un poco más de tiempo en compilar. </td></tr><tr align="left"><td align="center"> *-O2* </td><td>

Trata más firmemente de producir código en lenguaje ensamblador óptimamente. Esto puede hacer al compilador tomar todavía más tiempo para terminar su trabajo. </td></tr><tr align="left"><td align="center"> *-O3* </td><td>

Trata mucho más fuertemente de producir código ensamblador rápido. El énfasis lo pone en lo rápido que es el código ensamblador: el código resultante puede ocupar mucho más espacio en memoria. Esto es porque ciertas funciones son puestas en linea y los bucles desarmados y escritos de modo independiente. </td></tr> <tr align="left"><td align="center"> *-Os* </td><td>

Trata de producir código reducido: el énfasis está en el tamaño, no en la velocidad. </td></tr></tbody></table>

La mejor forma de ver los efectos de usar estas opciones en un programa en C, es detener el compilador GNU en la etapa de ensamblado y examinar el código ensamblador resultante. La opción más recomendada es la *-O2*.

+ Opciones de especificación de procesador:

<table width="450" align="center" cellspacing="0" cellpadding="0" border="0"><tbody><tr align="left"><td align="center" style="width: 50px">

*-mcpu=arm7tdmi* </td><td>Produce código que es específico a la arquitectura !ARM7TDMI, Esta arquitectura es también conocida como !ARM4v4T. Usted siempre debe especificar esta opción.</td></tr></tbody></table>

+ Opciones de desempaquetado de objetos binarios:

Esta Toolchain incluye un gran número de herramientas que pueden ser utilizadas para investigar y manipular archivos objeto binario y archivos ejecutables. Uno de los más utilizados de estas es la utilidad muestra información contenida en estos archivos. En particular, se puede usar esta utilidad para desensamblar archivos objeto, lo que le permite determinar las instrucciones exactas que fueron usadas por el compilador y ensamblador.Se pude también obtener información sobre las direcciones de memoria que serán usadas cuando se cargue el archivo ejecutable. La herramienta puede ser invocada como:

'

Simplemente reemplace con el nombre de su archivo objeto o ejecutable, como archivo.o u archivo.elf, y alguna de las opciones siguientes:

<table width="450" align="center" cellspacing="0" cellpadding="0" border="0"><tbody><tr align="left"><td align="center" style="width: 50px">

*-d* </td><td> Desensambla cada sección en el archivo objeto o en el archivo ejecutable que contenga código de máquina ARM, dejándonos además la dirección y el patrón hexadecimal de cada instrucción. Recordar que estas direcciones no son las direcciones de memoria. Este es el trabajo del linker: convertir un objeto binario en archivo ejecutable (un archivo que puede ser cargado en la memoria)</td></tr><tr align="left"><td align="center"> *-x* </td><td>

Muestra información acerca de los archivos objeto o ejecutables, como ser la tabla de símbolos, alguna reubicación que es necesaria y la dirección de dicha sección de código.La opción es más utilizada con archivos ejecutables, ya que esta nos permite ver exactamente dónde cada sección de programa será ubicado en la memoria. </td></tr></tbody></table>

Uso del debugger GDB:

El debugger GNU puede correr en 2 modos diferentes: con una interface gráfica llamada 'insight, y con la tradicional interface en línea de comando. La interfase gráfica hace el dubug básico mucho más fácil, mientras que la de linea de comandos es mas poderosa para tareas más complicadas.

Modificando Makefile para debugger: Flags recomendados

Es fundamental para el correcto funcionamiento del debugger que tenga los flags necesarios para realizar la acción. Sin ellos el programa no tendrá la información suficiente y nos dará un mensaje de error del tipo: Psymtab error.

La solucion a esto se pasa a explicar a continuación con el siguiente ejemplo funcionando:

CC      = arm-elf-gcc
AS      = arm-elf-as
LD      = arm-elf-ld
OBJCOPY = arm-elf-objcopy
OBJDUMP = arm-elf-objdump

GRABADOR=lpc21isp

main: main.c uart.h
   $(CC) -Wall -fomit-frame-pointer -g -O0 -mcpu=arm7tdmi -Wa,-gstabs+ -c main.c -o main.o

uart: uart.c
   $(CC) -Wall -fomit-frame-pointer -g -O0 -Wa,-gstabs+ -mcpu=arm7tdmi -c uart.c -o uart.o

head: head.s
   $(AS) -gstabs+ -mcpu=arm7tdmi -o head.o head.s

control: head.o uart.o main.o
   $(LD) -Tlpc2114_flash.ld head.o uart.o main.o -o control.elf
   $(OBJCOPY) -O ihex control.elf control.hex
   $(OBJDUMP) -D control.elf > control.lst
   $(OBJDUMP) -t control.elf > control.map

clean:
   rm *.elf *.hex *.map *.lst

realclean:
   rm *.elf *.hex *.map *.lst *.o

grabar:
   $(GRABADOR) -wipe -hex control.hex /dev/ttyS0 115200 14745

Lo que vamos a describir ahora son solamente las opciones de debug que usamos con arm-elf-gcc y arm-elf-as para realizar un buen debug.

<table width="600" align="center" cellspacing="0" cellpadding="0" border="0">

<tbody><tr><td align="center" style="width: 100px"> *- Wall* </td><td> Fue comentado unas líneas mas arriba.</td></tr><tr><td align="center"> *-g* </td><td>Pasa información de debug a nivel de código fuente (C) Nos permite ver como va avanzando el programa en el código. </td></tr><tr><td align="center"> *-O* </td><td> Para debug se recomienda usar la opción cero, para evitar errores de interpretación del compilador. </td></tr><tr><td align="center"> *- mcpu=* </td><td>Ahi va el núcleo con el cual estemos trabajando. </td></tr><tr><td align="center" valign="middle"> *-Wa,<options>* </td><td>Pasa separadas por coma opciones de compilación a nivel de assembler como la que utilizamos a continuación: </td></tr><tr><td align="center"> *-gstabs+* </td><td>Le pasa información debug en el archivo ejecutable a nivel de ensamblador. </td></tr><tr><td> </td><td> </td></tr><tr><td> </td><td> </td></tr><tr><td> </td><td> </td></tr></tbody></table>

Con esto tenemos nuestro ejemplo compilado para simular en las herramientas visuales como Insight o GDB en línea de comando.

Iniciando la interface gráfica:

Uno puede iniciar el debugger en modo gráfico entrando en la terminal: 'Naturalmente, reemplazando filename.elf con el nombre del archivo ejecutable.La siguiente ventana debe aparecer:

Antes de que pueda iniciar la actual pantalla de debugging de su ejecutable ARM, usted debe cargar este ejecutable desde la memoria al simulador. Esto se llama Connecting and downloading to the target.Para hacer esto, primero seleccione File » Target Settings del menu principal. La ventana Target Selection aparecerá, permitiéndole elegir su destino.Si tiene una placa de hardware real enfrente, seleccione el destino apropiado, baud ate and puerto. Asegúrese que Set breakpoint at main, Set breakpoint at exit y Set breakpoint at _start estan seleccionados. Cliquee OK para cerrar el cuadro de diálogo. Si no tiene una placa enfrente simplemente seleccione Simulator como destino.

El simulador es una pieza de software en la computadora que pretende ser un núcleo de hardware real. Asegurarse de que los 3 breakpoint esten seleccionados como se muestra abajo en la imágen. Cliquee OK para cerrar la ventana.

Ahora esta casi listo para correr el programa. Seleccione Run » Connect to Target en el menú principal para conectar el hardware o el simulador, después Run » Download para cargar el programa a la memoria de la placa. Finalmente, seleccione Run » Run del menú principal para correr el programa.

Iniciando la interface en línea de comandos:

Usted puede iniciar el modo línea de comandos del debugger GNU entrando lo siguiente:

'

Obviamente deberá cambiar el nombre del ejecutable con el nombre de su ejecutable. El sistema va a mostrar un par de parámetros como host y target para los cuales se encuentra configurado el gdb y más abajo se visualizará la línea de comando con el promt %u201C(gdb)%u201D.Como en el modo de interface gráfica, necesitará conectar al target y cargar el programa a este.

Si se quiere conectar a software real, tipee: '

'

Si quiere usar el simulador en cambio, typee:

Debe setear unos cuantos breakpoints, para su conveniencia, antes de empezar a correr el programa:

Ahora puede empezar a correr el programa hasta que este llegue al primer breakpoint:

''''Comandos básicos GDB:''''

<table width="550" align="center" cellspacing="5" cellpadding="5" border="0"><tbody><tr><td align="center" style="width: 150px"> h </td><td>Ayuda con los comandos.</td></tr><tr><td align="center"> q </td><td>Cierra el Debugger. </td></tr><tr><td align="center"> r </td><td>Corre su programa desde el principio.</td></tr><tr><td align="center"> b location </td><td>Setea un breakpoint en una ubicación, que puede ser etiquetada, un número de linea en el código fuente o dirección *(ejemplo,0x8000). </td></tr><tr><td align="center"> i b </td><td>Muestra información acerca de los breakpoints en su programa.</td></tr><tr><td align="center"> d num </td><td>Borra el breakpoint num.</td></tr><tr><td align="center"> en num </td><td>Habilita el breakpoint num.</td></tr><tr><td align="center"> dis num </td><td> Deshabilita el breakpoint num.</td></tr><tr><td align="center"> c </td><td> Continua corriendo su programa hasta que este alcance un breakpoint o el final.</td></tr><tr><td align="center"> u location </td><td> Corre hasta que el programa alcanza una ubicación (la ubicación tiene la misma sintaxis que en el comando b).</td></tr><tr><td align="center"> s </td><td> Un paso hacia la próxima línea de código fuente.</td></tr><tr><td align="center"> n </td><td> Un paso hasta la próxima linea, sin entrar dentro de los llamados a funciones que corren sin detenerse.</td></tr><tr><td align="center"> si </td><td> Un paso hacia la próxima instrucción en lenguaje assembly.</td></tr><tr><td align="center"> ni </td><td> Como con si, pero no sigue la traza a través de funciones o saltos hacia atrás.</td></tr><tr><td align="center"> i r </td><td> Muestra todos los registros del procesador ARM.</td></tr><tr><td align="center"> x/nfu address </td><td> Examina la memoria a una dirección. Los formatos f más usados son x para hexadecimal, d para decimal signado, u para decimal sin signo, t para binarios e i para instrucciones ARM. Las unidades más usadas son b para bytes, h para half-words y w para words. Ejemplos de dirección: 0x1234, $r0, &dst.</td></tr><tr><td align="center"> p expression </td><td> Imprime el valor de una expresión (que puede ser alguna expresión complicada en C).</td></tr><tr><td align="center"> p/f expression </td><td> Imprime el valor de una expresión usando el formato f (vea el comando x para una lista de ellos).</td></tr><tr><td> </td><td> </td></tr><tr><td> </td><td> </td></tr></tbody></table>

Corriendo un ejemplo en GDB:

ejemplo.tar.gz: Ejemplo en C para simular con GDB

mt@ciii-m147:~$ cd Desktop/Ejemplo/
mt@ciii-m147:~/Desktop/Ejemplo$ ls
lpc2114_flash.ld  mainexample.c    mainexample.hex  startuppropio.s
lpc2114.h         mainexample.c~   Makefile         startuppropio.s~
main.c~           mainexample.elf  Makefile~
mt@ciii-m147:~/Desktop/Ejemplo$ make
arm-elf-gcc -Wall -gstabs -O2 -mcpu=arm7tdmi lpc2114_flash.ld -Ttext=0 -nostartfiles     startuppropio.s mainexample.c -o mainexample.elf
arm-elf-objcopy -O ihex mainexample.elf mainexample.hex
mt@ciii-m147:~/Desktop/Ejemplo$ arm-elf-gdb mainexample.elf 
GNU gdb 6.1
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "--host=i686-pc-linux-gnu --target=arm-elf"...
(gdb) target sim
Connected to the simulator.
(gdb) load
Loading section .text, size 0xa8 vma 0x0
Start address 0x0
Transfer rate: 1344 bits in <1 sec.
(gdb) i r
r0             0xe0028004   -536707068
r1             0xe0028000   -536707072
r2             0x0   0
r3             0x0   0
r4             0x0   0
r5             0x0   0
r6             0x0   0
r7             0x0   0
r8             0x0   0
r9             0x0   0
r10            0x0   0
r11            0x0   0
r12            0x0   0
sp             0x800   2048
lr             0x58   88
pc             0xa0   160
fps            0x0   0
cpsr           0x60000013   1610612755
(gdb) i b
Num Type           Disp Enb Address    What
1   breakpoint     keep y   0x00000078 in main at mainexample.c:27
   breakpoint already hit 1 time

(gdb) p *(0xE01FC088)=0x00400   //Estoy seteando el PLL
$4 = 1024

(gdb) p *(0xE0028008)=0x00200000
$4 = 2097152

(gdb) p *(0xE0028008)
$5 = 2097152
(gdb) c
Continuing.
^C
Program received signal SIGINT, Interrupt.
main () at mainexample.c:32
32                {  a=100;
(gdb)quit

Ejemplo corriendo en Insight:

  • Ejemplo.tar.gz: Versiòn actualizada con Makefile optimizado y simulando correctamente.

None: LabElectronica/SimulacionGDB (última edición 2010-06-07 22:15:49 efectuada por GonzaloPerezPaina)