- rst_0.bin: rst_0.elf
- @echo ///////////////////
- @echo Extraindo o arquivo binado sem os simbolos
- @echo ///////////////////
- @echo
- arm-none-eabi-objcopy -O binary rst_0.elf rst_0.bin
- rst_0.elf: rst_0.o
- @echo ///////////////////
- @echo Executando o linker
- @echo ///////////////////
- @echo
- arm-none-eabi-ld -Ttext=0x08000000 -o rst_0.elf rst_0.o
- @echo ///////////////////
- @echo Mostrando os simbolos apos o linker
- @echo ///////////////////
- @echo
- arm-none-eabi-nm rst_0.elf
- @echo ///////////////////
- @echo Dump das sessoes do programa
- @echo ///////////////////
- @echo
- arm-none-eabi-objdump -s rst_0.elf
- rst_0.o: rst_0.s
- @echo ///////////////////
- @echo Montando o programa assembly
- @echo ///////////////////
- @echo
- arm-none-eabi-as -mcpu=cortex-m0 -g -a -o rst_0.o rst_0.s
- clean:
- @echo ///////////////////
- @echo Eliminando os arquivos de saida
- @echo ///////////////////
- @echo
- rm *.bin
- rm *.elf
- rm *.o
Esse parâmetro diz ao montador para incluir no arquivo de saída, informações necessárias para executar com clareza a depuração. Além disso, dizemos ao montador qual o processador que estamos usando através do parâmetro '-mcpu=cortex-m0'. Isso garante que o montador cheque o conjunto de instruções específico para esse processador e evite exceções de hardware na execução. Mais uma execução de ordem prática para o momento da depuração é acertar o ponto de carga para o endereço físico real da memória flash do microcontrolador. Isso é feito alterando o parâmetro -T do linker de text=0x00000000 para text=0x08000000. Além disso, eliminamos o passo de geração do flash.bin e acrescentamos um dump das sessões dos nosso programa.
Façamos algumas modificações no nosso programa:
- // Avancando no primeiro programa para ARM Cortex-M0
- .thumb // Define código como THUMB
- .globl _start // Necessario para o linker
- .equ STACK_INIT,0x020002000
- .text // Inicio da área de código
- _start:
- // Inicio do Vector Table
- .word STACK_INIT // Inicio da pilha
- .word reset_handler // Endereco do Reset Handler
- // define o label como função para que o linker
- // resolva como código THUMB
- .type reset_handler, function
- // tabela de bytes para somar 1
- tbl:
- .byte 3,7,9,0
- .align
- reset_handler: // inicio do tratamento do reset
- ldr r0,=tbl // carrega r0 com o endereco da tabela
- mov r1,#1 // move o valor 1 para r1
- segue:
- ldrb r2,[r0] // carrega em r2 o byte enderecado por r0
- cmp r2,#0 // se r2 for zero,
- beq _stop // encerra
- add r3,r2,r1 // soma r1 e r2 guardando em r3
- add r0,#1 // avanca na tabela
- b segue // continua
- _stop:
- b . // loop infinito
Primeiramente, na linha 3, substituímos a diretiva .code 16 pela diretiva .thumb que produz o mesmo efeito e dá maior clareza ao entendimento do código.
Antes de prosseguirmos, vamos ver um novo registrador interno da arquitetura ARM Cortex M: o registrador APSR (Application Program Status Register) que pode ser visto na figura abaixo:
Nesse registrador estão presentes 4 bits de estado que refletem o resultado da última instrução executada: N=negativo, Z=zero, C=carry e V=overflow. Após a execução de algumas instruções, caso o valor resultante seja menor que zero, o bit Z do registrador APSR é ligado; caso o valor seja zero, é a vez do bit Z ser ligado, os bits C e V são usados no resultados de instruções aritméticas e serão vistos mais adiante. Esses bits são usados diretamente nas instruções de desvios condicionais. è possível executar algumas instruções forçando o acionamento dos bits do registrador APSR através da inclusão do sufixo 'S' em seu mnemônico. Por exemplo, a instrução MOV usada em nosso programa:
MOV R1,#0 // movimenta o valor imediato zero para o registrador R1
e
MOVS R1,#0 // movimenta o valor imediato zero para o registrador R1 e liga o bit Z do APSR.
Outros dois registradores igualmente importantes são o Interrupt Program Status Register (IPSR) e o Execution Program Status Register (EPSR). Ambos serão vistos mais adiante mas, por enquanto, podemos dizer que o IPSR contem o valor da interrupção a que o processador está submetido as quais as funções de tratamento tem seus endereços no nosso Vector Table e o EPSR diz, em seu bit T ligado se o processador está executando instruções do conjunto Thumb que será o nosso caso. Os três registradores são, na verdade, vistos de forma combinada em um único registrador de nome xPSR mostrado abaixo:
Outros dois registradores igualmente importantes são o Interrupt Program Status Register (IPSR) e o Execution Program Status Register (EPSR). Ambos serão vistos mais adiante mas, por enquanto, podemos dizer que o IPSR contem o valor da interrupção a que o processador está submetido as quais as funções de tratamento tem seus endereços no nosso Vector Table e o EPSR diz, em seu bit T ligado se o processador está executando instruções do conjunto Thumb que será o nosso caso. Os três registradores são, na verdade, vistos de forma combinada em um único registrador de nome xPSR mostrado abaixo:
Vamos alterar o nosso programa para executar algumas instruções de desvio condicional. Na linha 23 temos a definição de uma tabela de bytes através da diretiva .byte. Assim como a diretiva .word, essa diretiva aloca espaço de memória de 1 byte cada e inicializa com o(s) valor(es) especificado(s) no código. Esse código pode ter o valor de 0 a 255 (ou 0x0 a 0xff). No nosso caso, a tabela endereçada pelo label tbl: possui os valores 3, 7, 9 e zero. O intuito é fazer um laço de instruções que some o valor de dois registradores sendo um desses valores os valores presentes nessa tabela. Na linha 24 temos uma nova diretiva: .align. Essa diretiva faz o alinhamento do código à frente para o valor de bytes especificados na diretiva. Caso nenhum valor for especificado, será alinhada em 32 bits ou 4 bytes. Isso se torna necessário uma vez que a diretiva anterior (.byte) pode desalinhar os endereços e a instrução seguinte não ocupe endereço múltiplo de 32 bits.
Nossa nova lógica, na linha 28, carrega no registrador R0 o endereço de nossa tabela de bytes. A linha 29 inicializa o registrador R1 em 1. A próxima instrução, na linha 30, possui o label 'segue:' e será o ponto de retomada de nosso laço de instruções. Aqui, o registrador R2 é carregado com o byte endereçado por R0. Na primeira passagem R2 deve receber o primeiro byte de nossa tabela, ou seja, o valor 3. Na sequência, o fim do laço é testado: na linha 32 o valor presente em R2 é comparado com zero. Se for igual (linha 33), o programa desvia para o loop infinito no label _stop. Não sendo o fim da tabela, os valores contidos em R1 e R2 são somados e o resultado depositado no registrador R3 (linha 34). O ponteiro da tabela presente em R0 é avançado em um byte na linha 35 e o programa volta ao laço na linha 36 através de um desvio incondicional para a linha 31.
Esse programa é simples ainda mas já mostra avanços no controle de lógica. Algumas diretivas novas foram mostradas assim como endereçamento indireto e controle de fluxo.
Vamos então executá-lo só que, desta vez, depurando linha a linha via o depurador interno do processador. Para tal usaremos dois utilitários: OpenOCD e GDB.
OpenOCD (open on-chip-debbuger) é um utilitário de código aberto desenvolvido para Linux mas que possui binários para MS-Windows disponíveis aqui ou aqui. Ele se comunica com o processador presente no microcontrolador via comunicação serial dependente da implementação. No caso da placa STM32F0Discovery, a interface presente é a ST-LINK V2 já descrita em post anterior. Esta fala de um lado com o processador ARM Cortex M0 via comunicação bidirecional a dois fios (SWD) e, do outro lado, via comunicação USB como o computador host (MS-Windows ou Linux). Na outra ponta dessa comunicação, está exatamente o utilitário OpenOCD que possui um driver para falar de um lado com a interface ST-Link V2 e, da outra ponta, abre 2 ports TCP: 4444 para interação com o próprio OpenOCD e, pelo port 3333 para interação com um depurador que, no nosso caso, usaremos a versão GNU: arm-none-eabi-gdb.
O utilitário OpenOCD deve ser baixado, descomprimido em um diretório conhecido, como por exemplo o raiz. O caminho do diretório bin deve ser adicionado à variável de ambiente PATH. Após concluído, abrir uma janela de linha de comando e executar o comando da figura abaixo:
- T:\>openocd -v
- GNU ARM Eclipse 64-bits Open On-Chip Debugger 0.9.0-00073-gdd34716-dirty (2015-05-19-09:55)
- Licensed under GNU GPL v2
- For bug reports, read
- http://openocd.org/doc/doxygen/bugs.html
- T:\>
A resposta pode ser diferente dependendo da versão baixada, a origem do binário e o tipo de código: 32 ou 64 bits.
Vamos então preparar o nosso código para execução e depuração:
- U:\arm_01>make
- ///////////////////
- Montando o programa assembly
- ///////////////////
- arm-none-eabi-as -mcpu=cortex-m0 -g -a -o rst_0.o rst_0.s
- ARM GAS rst_0.s page 1
- 1 // Avancando no primeiro programa para ARM Cortex-M0
- 2
- 3 .thumb // Define c├│digo como THUMB
- 4 .globl _start // Necessario para o linker
- 5
- 6 .equ STACK_INIT,0x020002000
- 7
- 8 .text // Inicio da área de código
- 9 _start:
- 10
- 11 // Inicio do Vector Table
- 12
- 13 0000 00200020 .word STACK_INIT // Inicio da pilha
- 14 0004 00000000 .word reset_handler // Endereco do Reset Handler
- 15
- 16 // define o label como função para que o linker
- 17 // resolva como c├│digo THUMB
- 18
- 19 .type reset_handler, function
- 20
- 21 // tabela de bytes para somar 1
- 22 tbl:
- 23 0008 03070900 .byte 3,7,9,0
- 24 .align
- 25
- 26 reset_handler: // inicio do tratamento do reset
- 27
- 28 000c 0448 ldr r0,=tbl // carrega r0 com o endereco da tabela
- 29 000e 0121 mov r1,#1 // move o valor 1 para r1
- 30 segue:
- 31 0010 0278 ldrb r2,[r0] // carrega em r2 o byte enderecado por r0
- 32 0012 002A cmp r2,#0 // se r2 for zero,
- 33 0014 02D0 beq _stop // encerra
- 34 0016 5318 add r3,r2,r1 // soma r1 e r2 guardando em r3
- 35 0018 0130 add r0,#1 // avanca na tabela
- 36 001a F9E7 b segue // continua
- 37 _stop:
- 38 001c FEE70000 b . // loop infinito
- 38 08000000
- ♀ARM GAS rst_0.s page 2
- DEFINED SYMBOLS
- rst_0.s:9 .text:00000000 _start
- rst_0.s:6 *ABS*:20002000 STACK_INIT
- rst_0.s:26 .text:0000000c reset_handler
- rst_0.s:22 .text:00000008 tbl
- rst_0.s:24 .text:0000000c $t
- rst_0.s:30 .text:00000010 segue
- rst_0.s:37 .text:0000001c _stop
- rst_0.s:38 .text:0000001e $d
- .debug_aranges:0000000c $d
- NO UNDEFINED SYMBOLS
- ///////////////////
- Executando o linker
- ///////////////////
- arm-none-eabi-ld -Ttext=0x08000000 -o rst_0.elf rst_0.o
- ///////////////////
- Mostrando os simbolos apos o linker
- ///////////////////
- arm-none-eabi-nm rst_0.elf
- 08008024 T __bss_end__
- 08008024 T __bss_start
- 08008024 T __bss_start__
- 08008024 T __data_start
- 08008024 T __end__
- 08008024 T _bss_end__
- 08008024 T _edata
- 08008024 T _end
- 00080000 N _stack
- 08000000 T _start
- 0800001c t _stop
- 0800000c t reset_handler
- 08000010 t segue
- 20002000 a STACK_INIT
- 08000008 t tbl
- ///////////////////
- Dump das sessoes do programa
- ///////////////////
- arm-none-eabi-objdump -s rst_0.elf
- rst_0.elf: file format elf32-littlearm
- Contents of section .text:
- 8000000 00200020 0d000008 03070900 04480121 . . .........H.!
- 8000010 0278002a 02d05318 0130f9e7 fee70000 .x.*..S..0......
- 8000020 08000008 ....
- Contents of section .debug_aranges:
- 0000 1c000000 02000000 00000400 00000000 ................
- 0010 00000008 24000000 00000000 00000000 ....$...........
- Contents of section .debug_info:
- 0000 36000000 02000000 00000401 00000000 6...............
- 0010 00000008 24000008 7273745f 302e7300 ....$...rst_0.s.
- 0020 553a5c61 726d5f30 3100474e 55204153 U:\arm_01.GNU AS
- 0030 20322e32 342e3000 0180 2.24.0...
- Contents of section .debug_abbrev:
- 0000 01110010 06110112 0103081b 08250813 .............%..
- 0010 05000000 ....
- Contents of section .debug_line:
- 0000 3e000000 02001e00 00000201 fb0e0d00 >...............
- 0010 01010101 00000001 00000100 7273745f ............rst_
- 0020 302e7300 00000000 0005020c 00000803 0.s.............
- 0030 1b012122 21212121 21220376 2e020200 ..!"!!!!!".v....
- 0040 0101 ..
- Contents of section .ARM.attributes:
- 0000 41200000 00616561 62690001 16000000 A ...aeabi......
- 0010 05436f72 7465782d 4d300006 0c074d09 .Cortex-M0....M.
- 0020 01 .
- ///////////////////
- Extraindo o arquivo binado sem os simbolos
- ///////////////////
- arm-none-eabi-objcopy -O binary rst_0.elf rst_0.bin
- U:\arm_01>
Uma vez pronto o programa, vamos depurá-lo. Primeiramente, iniciemos o OpenOCD:
- T:\arm_01>openocd -f board/stm32f0discovery.cfg
- GNU ARM Eclipse 64-bits Open On-Chip Debugger 0.9.0-00073-gdd34716-dirty (2015-05-19-09:55)
- Licensed under GNU GPL v2
- For bug reports, read
- http://openocd.org/doc/doxygen/bugs.html
- Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
- adapter speed: 1000 kHz
- adapter_nsrst_delay: 100
- none separate
- srst_only separate srst_nogate srst_open_drain connect_deassert_srst
- 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 v24 API v2 SWIM v0 VID 0x0483 PID 0x3748
- Info : using stlink api v2
- Info : Target voltage: 2.900648
- Info : stm32f0x.cpu: hardware has 4 breakpoints, 2 watchpoints
Ao iniciar o utilitário OpenOCD, devemos informar qual dispositivo estamos usando. Há uma série de dispositivos pré-configurados no diretório scripts/board do diretório de instalação do OpenOCD.
No caso, informamos a placa STM32F0Discovery a partir de seu arquivo de configuração presente na instalação do OpenOCD. O utilitário reconheceu a interface ST-Link V2 e está pronto para receber conexões TCP. Faremos isso a seguir executando o comando a seguir em uma nova janela de linha de comando:
- T:\arm_01>arm-none-eabi-gdb rst_0.elf
- GNU gdb (GNU Tools for ARM Embedded Processors) 7.8.0.20150604-cvs
- Copyright (C) 2014 Free Software Foundation, Inc.
- License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
- This is free software: you are free to change and redistribute it.
- There is NO WARRANTY, to the extent permitted by law. Type "show copying"
- and "show warranty" for details.
- This GDB was configured as "--host=i686-w64-mingw32 --target=arm-none-eabi".
- Type "show configuration" for configuration details.
- For bug reporting instructions, please see:
- <http://www.gnu.org/software/gdb/bugs/>.
- Find the GDB manual and other documentation resources online at:
- <http://www.gnu.org/software/gdb/documentation/>.
- For help, type "help".
- Type "apropos word" to search for commands related to "word"...
- Reading symbols from rst_0.elf...done.
- (gdb)
Novamente vemos que o depurador tem como objeto de depuração código ARM embarcado (arm-none-eabi). O depurador foi iniciado já informando o arquivo ELF gerado. Esse arquivo, como dito anteriormente, possui o código objeto executável e os símbolos úteis para depuração. Na linha 16, o depurador carrega os símbolos e está pronto para receber comandos ao mostrar o prompt (gdb) na linha 17.
A seguir efetuaremos alguns comandos para se comunicar com o OpenOCD e, por consequência, com o processador da placa STM32F0Discovery; carregar o código objeto em sua memória flash e executá-lo passo a passo observando o conteúdo de seus registradores.
- (gdb) target remote localhost:3333
- Remote debugging using localhost:3333
- 0x00000000 in ?? ()
- (gdb) info reg
- r0 0x0 0
- r1 0x0 0
- 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 0x0 0x0
- lr 0x0 0
- pc 0x0 0x0
- xPSR 0x0 0
- (gdb)
Na linha 1 efetuamos a conexão TCP no próprio computador e port 3333. A resposta mostra o conteúdo da próxima instrução do processador presente no registrador PC. Para se certificar, usamos o comando info reg para ver o conteúdo dos registradores do processador. Estamos conectados ao processador. Agora vamos carregar na sua memória flash o código objeto presente no arquivo rst_0.elf usado na chamada do utilitário arm-none-eabi-gdb. Para tal, usaremos a sequência abaixo:
- (gdb) monitor reset halt
- target state: halted
- target halted due to debug-request, current mode: Thread
- xPSR: 0xc1000000 pc: 0xfffffffe msp: 0xfffffffc
- (gdb) monitor flash erase_sector 0 0 last
- erased sectors 0 through 63 on flash bank 0 in 0.029801s
- (gdb) load
- Loading section .text, size 0x24 lma 0x8000000
- Start address 0x8000000, load size 36
- Transfer rate: 220 bytes/sec, 36 bytes/write.
- (gdb) info reg
- r0 0x0 0
- r1 0x0 0
- 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 0x0 0x0
- lr 0x0 0
- pc 0x8000000 0x8000000 <_start>
- xPSR 0x1000000 16777216
- (gdb)
Na linha 1 enviamos um comando de reset ao processador com a consequente entrada no estado suspenso (halt). Isso evita que o processador comece a executar nosso reset_handler. Esse comando foi submetido através do comando monitor. O comando monitor ou mon, na verdade, executa um comando do OpenOCD e trás de volta o seu resultado. Na linha 5, pedimos ao OpenOCD, executar uma limpeza da memória flash. Finalmente, na linha 7, o utilitário arm-none-eabi-gdb efetua a carga na memória flash do programa objeto presente no arquivo rst_0.elf. Podemos ver, na linha 8, que o código, indicado pela sessão .text, foi carregado a partir do endereço 0x080000000. Se não houvéssemos alterado esse endereço no passo do linker no nosso makefile, a carga aqui não seria possível pois o endereço físico real da memória flash para esse microcontrolador é essa. Podemos ver ainda o registrador PC com o o endereço de nossa sessão .text que era de se esperar. Temos também o registrador xPSR que está com o bit T ligado indicando código thumb.
Através do comando list, vemos o código fonte armazenado nos símbolos do arquivo ELF carregado:
- (gdb) list 1,99999
- 1 // Avancando no primeiro programa para ARM Cortex-M0
- 2
- 3 .thumb // Define código como THUMB
- 4 .globl _start // Necessario para o linker
- 5
- 6 .equ STACK_INIT,0x020002000
- 7
- 8 .text // Inicio da área de código
- 9 _start:
- 10
- 11 // Inicio do Vector Table
- 12
- 13 .word STACK_INIT // Inicio da pilha
- 14 .word reset_handler // Endereco do Reset Handler
- 15
- 16 // define o label como função para que o linker
- 17 // resolva como código THUMB
- 18
- 19 .type reset_handler, function
- 20
- 21 // tabela de bytes para somar 1
- 22 tbl:
- 23 .byte 3,7,9,0
- 24 .align
- ---Type <return> to continue, or q <return> to quit---
- 25
- 26 reset_handler: // inicio do tratamento do reset
- 27
- 28 ldr r0,=tbl // carrega r0 com o endereco da tabela
- 29 mov r1,#1 // move o valor 1 para r1
- 30 segue:
- 31 ldrb r2,[r0] // carrega em r2 o byte enderecado por r0
- 32 cmp r2,#0 // se r2 for zero,
- 33 beq _stop // encerra
- 34 add r3,r2,r1 // soma r1 e r2 guardando em r3
- 35 add r0,#1 // avanca na tabela
- 36 b segue // continua
- 37 _stop:
- 38 b . // loop infinito
- (gdb)
Antes de continuarmos, vamos ver alguns comandos de depuração úteis para nós. à medida que vamos avançando no conhecimento e complexidade do desenvolvimento para microcontroladores com processadores ARM Cortex M, novos comandos de depuração vão sendo mostrados e usados. Relembrando que o comando monitor ou mon executa, na verdade, comandos OpenOCD. Para encerrar a depuração, usamos o comando quit.
Para executarmos uma instrução de máquina, usamos o comando stepi. Para executarmos instruções até a próxima linha de código fonte, usamos simplesmente step. Essa é a diferença dos dois comandos. Se uma linha de código fonte a ser executada representa uma função (uma chamada com desvio e retorno) e não desejamos depurar essa função, usamos o comando next. Caso desejemos monitorar algum valor de registrador ou área de memória de memória a cada passo da depuração, usamos o comando display. Esse comando guarda uma lista de objetos que desejamos que sejam mostrados a cada iteração de depuração. Vejam o exemplo abaixo:
- (gdb) info display
- There are no auto-display expressions now.
- (gdb) display /x $r0
- 1: /x $r0 = 0x800000b
- (gdb) display /x $r2
- 2: /x $r2 = 0x0
- (gdb) display /x $r3
- 3: /x $r3 = 0xa
- (gdb) display /x $xPSR
- 4: /x $xPSR = 0x61000000
- (gdb) info display
- Auto-display expressions now in effect:
- Num Enb Expression
- 4: y /x $xPSR
- 3: y /x $r3
- 2: y /x $r2
- 1: y /x $r0
- (gdb)
Na linha 1, o comando info display mostra o que há definido como auto-display. No caso não há nada. Nas linhas 3, 5, 7 e 9, definimos para o gdb mostrar, no formato hexadecimal (/x) o conteúdo dos registradores R0, R2, R3 e do registrador xPRS. Finalmente, na linha 11, o comando info display executado novamente mostra os 4 comandos desejados. Se desejarmos retirar algum comando do auto-display usamos o comando undisplay. Pronto, agora podemos começar a depurar nosso programa. Para tal, ainda vamos ver mais um comando: flushregs. Muitas vezes, os valores dos registradores guardados pelo gdb ficam desatualizados em relação aos valores reais do processador, principalmente após comandos monitor. Para forçar o gdb atualizá-los, usamos o comando flushregs. Se não digitarmos um novo comando e dermos enter no prompt do gdb, ele executa o último comando recebido.
- (gdb) mon reset halt
- target state: halted
- target halted due to debug-request, current mode: Thread
- xPSR: 0xc1000000 pc: 0x0800000c msp: 0x20002000
- (gdb) flushregs
- Register cache flushed.
- (gdb) frame
- #0 reset_handler () at rst_0.s:28
- 28 ldr r0,=tbl // carrega r0 com o endereco da tabela
- (gdb) si
- 29 mov r1,#1 // move o valor 1 para r1
- 4: /x $xPSR = 0xc1000000
- 3: /x $r3 = 0xffffffff
- 2: /x $r2 = 0xffffffff
- 1: /x $r0 = 0x8000008
- (gdb)
- segue () at rst_0.s:31
- 31 ldrb r2,[r0] // carrega em r2 o byte enderecado por r0
- 4: /x $xPSR = 0x1000000
- 3: /x $r3 = 0xffffffff
- 2: /x $r2 = 0xffffffff
- 1: /x $r0 = 0x8000008
- (gdb)
- 32 cmp r2,#0 // se r2 for zero,
- 4: /x $xPSR = 0x1000000
- 3: /x $r3 = 0xffffffff
- 2: /x $r2 = 0x3
- 1: /x $r0 = 0x8000008
- (gdb)
- 33 beq _stop // encerra
- 4: /x $xPSR = 0x21000000
- 3: /x $r3 = 0xffffffff
- 2: /x $r2 = 0x3
- 1: /x $r0 = 0x8000008
- (gdb)
- 34 add r3,r2,r1 // soma r1 e r2 guardando em r3
- 4: /x $xPSR = 0x21000000
- 3: /x $r3 = 0xffffffff
- 2: /x $r2 = 0x3
- 1: /x $r0 = 0x8000008
- (gdb)
- 35 add r0,#1 // avanca na tabela
- 4: /x $xPSR = 0x1000000
- 3: /x $r3 = 0x4
- 2: /x $r2 = 0x3
- 1: /x $r0 = 0x8000008
- (gdb)
- 36 b segue // continua
- 4: /x $xPSR = 0x1000000
- 3: /x $r3 = 0x4
- 2: /x $r2 = 0x3
- 1: /x $r0 = 0x8000009
- (gdb)
- 31 ldrb r2,[r0] // carrega em r2 o byte enderecado por r0
- 4: /x $xPSR = 0x1000000
- 3: /x $r3 = 0x4
- 2: /x $r2 = 0x3
- 1: /x $r0 = 0x8000009
- (gdb)
- 32 cmp r2,#0 // se r2 for zero,
- 4: /x $xPSR = 0x1000000
- 3: /x $r3 = 0x4
- 2: /x $r2 = 0x7
- 1: /x $r0 = 0x8000009
- (gdb)
- 33 beq _stop // encerra
- 4: /x $xPSR = 0x21000000
- 3: /x $r3 = 0x4
- 2: /x $r2 = 0x7
- 1: /x $r0 = 0x8000009
- (gdb)
- 34 add r3,r2,r1 // soma r1 e r2 guardando em r3
- 4: /x $xPSR = 0x21000000
- 3: /x $r3 = 0x4
- 2: /x $r2 = 0x7
- 1: /x $r0 = 0x8000009
- (gdb)
- 35 add r0,#1 // avanca na tabela
- 4: /x $xPSR = 0x1000000
- 3: /x $r3 = 0x8
- 2: /x $r2 = 0x7
- 1: /x $r0 = 0x8000009
- (gdb)
- 36 b segue // continua
- 4: /x $xPSR = 0x1000000
- 3: /x $r3 = 0x8
- 2: /x $r2 = 0x7
- 1: /x $r0 = 0x800000a
- (gdb)
- 31 ldrb r2,[r0] // carrega em r2 o byte enderecado por r0
- 4: /x $xPSR = 0x1000000
- 3: /x $r3 = 0x8
- 2: /x $r2 = 0x7
- 1: /x $r0 = 0x800000a
- (gdb)
- 32 cmp r2,#0 // se r2 for zero,
- 4: /x $xPSR = 0x1000000
- 3: /x $r3 = 0x8
- 2: /x $r2 = 0x9
- 1: /x $r0 = 0x800000a
- (gdb)
- 33 beq _stop // encerra
- 4: /x $xPSR = 0x21000000
- 3: /x $r3 = 0x8
- 2: /x $r2 = 0x9
- 1: /x $r0 = 0x800000a
- (gdb)
- 34 add r3,r2,r1 // soma r1 e r2 guardando em r3
- 4: /x $xPSR = 0x21000000
- 3: /x $r3 = 0x8
- 2: /x $r2 = 0x9
- 1: /x $r0 = 0x800000a
- (gdb)
- 35 add r0,#1 // avanca na tabela
- 4: /x $xPSR = 0x1000000
- 3: /x $r3 = 0xa
- 2: /x $r2 = 0x9
- 1: /x $r0 = 0x800000a
- (gdb)
- 36 b segue // continua
- 4: /x $xPSR = 0x1000000
- 3: /x $r3 = 0xa
- 2: /x $r2 = 0x9
- 1: /x $r0 = 0x800000b
- (gdb)
- 31 ldrb r2,[r0] // carrega em r2 o byte enderecado por r0
- 4: /x $xPSR = 0x1000000
- 3: /x $r3 = 0xa
- 2: /x $r2 = 0x9
- 1: /x $r0 = 0x800000b
- (gdb)
- 32 cmp r2,#0 // se r2 for zero,
- 4: /x $xPSR = 0x1000000
- 3: /x $r3 = 0xa
- 2: /x $r2 = 0x0
- 1: /x $r0 = 0x800000b
- (gdb)
- 33 beq _stop // encerra
- 4: /x $xPSR = 0x61000000
- 3: /x $r3 = 0xa
- 2: /x $r2 = 0x0
- 1: /x $r0 = 0x800000b
- (gdb)
- _stop () at rst_0.s:38
- 38 b . // loop infinito
- 4: /x $xPSR = 0x61000000
- 3: /x $r3 = 0xa
- 2: /x $r2 = 0x0
- 1: /x $r0 = 0x800000b
- (gdb)
- 38 b . // loop infinito
- 4: /x $xPSR = 0x61000000
- 3: /x $r3 = 0xa
- 2: /x $r2 = 0x0
- 1: /x $r0 = 0x800000b
- (gdb) quit
- A debugging session is active.
- Inferior 1 [Remote target] will be detached.
- Quit anyway? (y or n) y
- Detaching from program: T:\arm_01\rst_0.elf, Remote target
- Ending remote debugging.
- T:\arm_01>
Podemos ver claramente, passo a passo, o programa sendo executado. Inicialmente, executamos o reset halt na linha 1 o que carrega o registrador PC com o endereço da primeira instrução: reset_haldler. Para confirmar isso, temos mais um comando na linha 7: frame que pede ao gdb mostrar qual instrução está apontada pelo registrador PC. Antes, na linha 5, forçamos o gdb a atualizar os valores dos registradores através do comando flushregs. Na sequência, executamos uma serie de comandos stepi (que poderia ser na sua forma abreviada: si). A cada comando executado, o gdb embute um comando frame o qual mostra a próxima instrução a ser executada uma vez que, após executar uma instrução, o processador atualiza o registrador PC. Como desejávamos, a cada iteração, o gdb mostra os conteúdos dos registradores R0, R2, R3 e xPSR. O controle do fluxo é executado pelas instruções CMP e BEQ. A linha 23 executa o comando mostrado na linha 18. Podemos ver o conteúdo do registrador R2 sendo modificado após essa execução, conforme comparação entre as linhas 21 e 27. Como R2 está com o valor 0x03, a comparação sendo executada na linha 29 não força o desvio na próxima instrução (execução na linha 35). O programa segue até a execução da linha 143 que força o desvio para o loop infinito devido ao resultado da comparação executada na linha 137 ter ligado o bit Z do registrador xPSR (linha 146).
Nos próximos posts veremos mais profundamente a programação do processador ARM Cortex M além de novos recursos de depuração.
REFERÊNCIAS
Thumb instruction summary - http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0210c/CACBCAAE.html
Thumb instruction set - http://bear.ces.cwru.edu/eecs_382/ARM7-TDMI-manual-pt3.pdf
ARM and Thumb-2 Instruction Set - Quick Reference Guide - https://www.lri.fr/~de/ARM.pdf
OpenOCD User’s Guide - http://openocd.org/doc/html/index.html#Top
Debugging with gdb - https://sourceware.org/gdb/onlinedocs/gdb/index.html#Top
Nenhum comentário:
Postar um comentário