Damos inicio a un interesante viaje directo a las entrañas inhóspitas de los microcontroladores y su parte más importante, Su CPU. Que en este caso sera un Cortex-M0.

En esta nueva serie de posts aprenderemos a programar en ensamblador un micro con un cpu Cortex-M0, como el que tiene el micro de nuestra tarjeta Nucleos-f072rb.

Ensamblador, debes estar bromeando, eso es cosa de la prehistoria!!!!.

Mmmmm No. Es verdad que mayormente se prefiere crear programas en un lenguaje como C, pero el ensamblador no esta desfasado y mucho menos obsoleto. Elejimos ensamblador porque es la mejor manera de conocer el funcionamiento a detalle de la máquina que estas programando, y a mayor nivel de conocimiento, mayor de nivel de control y confiabilidad tendrán sobre tus programas.

No voy a entrar en detalles describiendo la arquitectura del micro y como estan constituidas sus diferentes partes, para eso existe un excelente libro que lo explica a mucho mayor detalle de lo que lo haría yo. The Definitive Guide to the ARM Cortex-M0.

Sin mas preambulos, crea una nueva carpeta que albergará tu proyecto y dentro de ella crea el archivo test.s.

1
2
3
$ mkdir ~/test_asm
$ touch ~/test_asm/test.s
$ cd ~/test_asm

Abre el archivo test.s con tu editor de texto favorito y escribe tu primer programa en ensamblador

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    .text   
    .syntax unified
    .thumb

    .global _start
    .type start, %function

_start:
    .word       0x20003FFF /*stack pointer,   direccion 0x08000000*/
    .word       start      /*vector de reset, direccion 0x08000004*/

/* Inicio del programa en la dirección 0x0800008 */
start:  
    movs        r0, #10    /*carga un 10 al registro r0*/
    movs        r1, #5     /*carga un  5 al registro r1*/
    
deadloop:   
    b           deadloop

    .end

Y aquí la explicación de lo que acabamos de escribir. Primero que nada en este archivo existen dos tipos de instrucciones, aquellas que son para el propio compilador y aquellas que son para que las ejecute el microcontrolador. Las primeras son todas las que inician con un “.”

  • .text esta directiva indica que inicia una región de programa que necesitara ser ensamblada ( o interpretada )
  • .syntax unified indica que la sintaxis de ensamblador sera la versión unificada
  • .thumb indica que el programa usará instrucciones de tipo Thumb
  • .global permite que un símbolo o etiqueta permita ser compartida por otros archivos que compondrán tu programa
  • _start es una etiqueta muy especial y representa el punto de incio de tu programa

Antes de continuar, es justo explicar que a partir de la etiqueta _start empezara tu programa. Con la etiqueta .word reservamos 4 direcciones de memoria para, en inicio almacenar el valor del stack pointer y después almacenar la direccion donde inciara tu programa. Que en este caso esta representado por la etiqueta start.

Las instrucciones que componen el programa que ejecutará tu micro inician a partir de la etiqueta start. Y por último la instrucción del compilador .end indica el fin del programa.

Bien, hora de ensamblar el programa. En tu terminal escribe.

1
$ arm-none-eabi-as -mcpu=cortex-m0 -mthumb -g -o test.o test.s

Esto generará el archivo test.o y con este archivo deberemos enlazar nuestro program para que se ejecute a partir de la direccion 0x08000000.

1
$ arm-none-eabi-ld -Ttext=0x08000000 -o test.elf test.o

Nos conectamos a la tarjeta usando OpenOCD.

1
$ sudo openocd -f interface/stlink-v2-1.cfg -f target/stm32f0x_stlink.cfg

Nuestro programa ya esta listo para cargarse en el micro de la tarjeta Nucleos-f072rb, y para ello usaremos gdb.

1
$ arm-none-eabi-gdb test.elf

Conectate al puerto de OpenOCD

1
(gdb) target remote localhost:3333

Cargamos el programa al micro

1
(gdb) load

Aplicamos un reset para iniciar en la primera direccion de tu programa

1
2
3
(gdb) mon reset halt
target halted due to debug-request, current mode: Thread 
xPSR: 0xc1000000 pc: 0x08000008 msp: 0x20003ffc

Tu programa esta detenido justo en la direccion 0x08000008 en la cual esta la instrucción movs r0, #10.

1
(gdb) p/x $pc

Ojo, que no se ha ejecutado ninguna instrucción. Vamos ejecutando el programa paso a paso, para esto solo escribe

1
(gdb) step

Ya se ejecutó la primera instrucción la cual cargaba el valor 10 al registro r0. Revisamos, y de una vez preguntamos por el registro pc y observamos como avanzó dos direcciones. (gdb) p $r0 $2 = 10 (gdb) p/x $pc $3 = 0x800000a

Vamos dando otro step para ejecutar la siguiente instrucción donde cargamos el valor 5 al registro r1.

1
2
3
4
(gdb) p $r1
$4 = 5
(gdb) p/x $pc
$5 = 0x800000a

La última parte del programa es tan solo un bucle infinito en el que se queda clavado nuestro micro. Podemos considerar que ya se acabo nuestro primer programa.

q para salir de gdb y un Ctrl+c para salir de OpenOCD

Comments