quarta-feira, 11 de novembro de 2015

Um pouco mais de instruções Assembly e depurando nosso programa com OpenOCD

Vamos introduzir algumas novas instruções Assembly ao nosso primeiro programa. Na sequência veremos como depurar nossos programas ARM Cortex M com a solução OpenOCD. Comecemos incluindo um novo parâmetro '-g' na montagem do programa no nosso makefile:

  1. rst_0.bin: rst_0.elf
  2. @echo ///////////////////
  3. @echo Extraindo o arquivo binado sem os simbolos
  4. @echo ///////////////////
  5. @echo
  6. arm-none-eabi-objcopy -O binary rst_0.elf rst_0.bin
  7. rst_0.elf: rst_0.o
  8. @echo ///////////////////
  9. @echo Executando o linker
  10. @echo ///////////////////
  11. @echo
  12. arm-none-eabi-ld -Ttext=0x08000000 -o rst_0.elf rst_0.o
  13. @echo ///////////////////
  14. @echo Mostrando os simbolos apos o linker
  15. @echo ///////////////////
  16. @echo
  17. arm-none-eabi-nm rst_0.elf
  18. @echo ///////////////////
  19. @echo Dump das sessoes do programa
  20. @echo ///////////////////
  21. @echo
  22. arm-none-eabi-objdump -s rst_0.elf
  23. rst_0.o: rst_0.s
  24. @echo ///////////////////
  25. @echo Montando o programa assembly
  26. @echo ///////////////////
  27. @echo
  28. arm-none-eabi-as -mcpu=cortex-m0 -g -a -o rst_0.o rst_0.s
  29. clean:
  30. @echo ///////////////////
  31. @echo Eliminando os arquivos de saida
  32. @echo ///////////////////
  33. @echo
  34. rm *.bin
  35. rm *.elf
  36. 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:

  1. // Avancando no primeiro programa para ARM Cortex-M0
  2. .thumb // Define código como THUMB
  3. .globl _start // Necessario para o linker
  4. .equ STACK_INIT,0x020002000
  5. .text // Inicio da área de código
  6. _start:
  7. // Inicio do Vector Table
  8. .word STACK_INIT // Inicio da pilha
  9. .word reset_handler // Endereco do Reset Handler
  10. // define o label como função para que o linker
  11. // resolva como código THUMB
  12. .type reset_handler, function
  13. // tabela de bytes para somar 1
  14. tbl:
  15. .byte 3,7,9,0
  16. .align
  17. reset_handler: // inicio do tratamento do reset
  18. ldr r0,=tbl // carrega r0 com o endereco da tabela
  19. mov r1,#1 // move o valor 1 para r1
  20. segue:
  21. ldrb r2,[r0] // carrega em r2 o byte enderecado por r0
  22. cmp r2,#0 // se r2 for zero,
  23. beq _stop // encerra
  24. add r3,r2,r1 // soma r1 e r2 guardando em r3
  25. add r0,#1 // avanca na tabela
  26. b segue // continua
  27. _stop:
  28. 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:



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:

  1. T:\>openocd -v
  2. GNU ARM Eclipse 64-bits Open On-Chip Debugger 0.9.0-00073-gdd34716-dirty (2015-05-19-09:55)
  3. Licensed under GNU GPL v2
  4. For bug reports, read
  5. http://openocd.org/doc/doxygen/bugs.html
  6. 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:

  1. U:\arm_01>make
  2. ///////////////////
  3. Montando o programa assembly
  4. ///////////////////
  5. arm-none-eabi-as -mcpu=cortex-m0 -g -a -o rst_0.o rst_0.s
  6. ARM GAS rst_0.s page 1
  7. 1 // Avancando no primeiro programa para ARM Cortex-M0
  8. 2
  9. 3 .thumb // Define c├│digo como THUMB
  10. 4 .globl _start // Necessario para o linker
  11. 5
  12. 6 .equ STACK_INIT,0x020002000
  13. 7
  14. 8 .text // Inicio da área de código
  15. 9 _start:
  16. 10
  17. 11 // Inicio do Vector Table
  18. 12
  19. 13 0000 00200020 .word STACK_INIT // Inicio da pilha
  20. 14 0004 00000000 .word reset_handler // Endereco do Reset Handler
  21. 15
  22. 16 // define o label como função para que o linker
  23. 17 // resolva como c├│digo THUMB
  24. 18
  25. 19 .type reset_handler, function
  26. 20
  27. 21 // tabela de bytes para somar 1
  28. 22 tbl:
  29. 23 0008 03070900 .byte 3,7,9,0
  30. 24 .align
  31. 25
  32. 26 reset_handler: // inicio do tratamento do reset
  33. 27
  34. 28 000c 0448 ldr r0,=tbl // carrega r0 com o endereco da tabela
  35. 29 000e 0121 mov r1,#1 // move o valor 1 para r1
  36. 30 segue:
  37. 31 0010 0278 ldrb r2,[r0] // carrega em r2 o byte enderecado por r0
  38. 32 0012 002A cmp r2,#0 // se r2 for zero,
  39. 33 0014 02D0 beq _stop // encerra
  40. 34 0016 5318 add r3,r2,r1 // soma r1 e r2 guardando em r3
  41. 35 0018 0130 add r0,#1 // avanca na tabela
  42. 36 001a F9E7 b segue // continua
  43. 37 _stop:
  44. 38 001c FEE70000 b . // loop infinito
  45. 38 08000000
  46. ♀ARM GAS rst_0.s page 2
  47. DEFINED SYMBOLS
  48. rst_0.s:9 .text:00000000 _start
  49. rst_0.s:6 *ABS*:20002000 STACK_INIT
  50. rst_0.s:26 .text:0000000c reset_handler
  51. rst_0.s:22 .text:00000008 tbl
  52. rst_0.s:24 .text:0000000c $t
  53. rst_0.s:30 .text:00000010 segue
  54. rst_0.s:37 .text:0000001c _stop
  55. rst_0.s:38 .text:0000001e $d
  56. .debug_aranges:0000000c $d
  57. NO UNDEFINED SYMBOLS
  58. ///////////////////
  59. Executando o linker
  60. ///////////////////
  61. arm-none-eabi-ld -Ttext=0x08000000 -o rst_0.elf rst_0.o
  62. ///////////////////
  63. Mostrando os simbolos apos o linker
  64. ///////////////////
  65. arm-none-eabi-nm rst_0.elf
  66. 08008024 T __bss_end__
  67. 08008024 T __bss_start
  68. 08008024 T __bss_start__
  69. 08008024 T __data_start
  70. 08008024 T __end__
  71. 08008024 T _bss_end__
  72. 08008024 T _edata
  73. 08008024 T _end
  74. 00080000 N _stack
  75. 08000000 T _start
  76. 0800001c t _stop
  77. 0800000c t reset_handler
  78. 08000010 t segue
  79. 20002000 a STACK_INIT
  80. 08000008 t tbl
  81. ///////////////////
  82. Dump das sessoes do programa
  83. ///////////////////
  84. arm-none-eabi-objdump -s rst_0.elf
  85. rst_0.elf: file format elf32-littlearm
  86. Contents of section .text:
  87. 8000000 00200020 0d000008 03070900 04480121 . . .........H.!
  88. 8000010 0278002a 02d05318 0130f9e7 fee70000 .x.*..S..0......
  89. 8000020 08000008 ....
  90. Contents of section .debug_aranges:
  91. 0000 1c000000 02000000 00000400 00000000 ................
  92. 0010 00000008 24000000 00000000 00000000 ....$...........
  93. Contents of section .debug_info:
  94. 0000 36000000 02000000 00000401 00000000 6...............
  95. 0010 00000008 24000008 7273745f 302e7300 ....$...rst_0.s.
  96. 0020 553a5c61 726d5f30 3100474e 55204153 U:\arm_01.GNU AS
  97. 0030 20322e32 342e3000 0180 2.24.0...
  98. Contents of section .debug_abbrev:
  99. 0000 01110010 06110112 0103081b 08250813 .............%..
  100. 0010 05000000 ....
  101. Contents of section .debug_line:
  102. 0000 3e000000 02001e00 00000201 fb0e0d00 >...............
  103. 0010 01010101 00000001 00000100 7273745f ............rst_
  104. 0020 302e7300 00000000 0005020c 00000803 0.s.............
  105. 0030 1b012122 21212121 21220376 2e020200 ..!"!!!!!".v....
  106. 0040 0101 ..
  107. Contents of section .ARM.attributes:
  108. 0000 41200000 00616561 62690001 16000000 A ...aeabi......
  109. 0010 05436f72 7465782d 4d300006 0c074d09 .Cortex-M0....M.
  110. 0020 01 .
  111. ///////////////////
  112. Extraindo o arquivo binado sem os simbolos
  113. ///////////////////
  114. arm-none-eabi-objcopy -O binary rst_0.elf rst_0.bin
  115. U:\arm_01>

Uma vez pronto o programa, vamos depurá-lo. Primeiramente, iniciemos o OpenOCD:

  1. T:\arm_01>openocd -f board/stm32f0discovery.cfg
  2. GNU ARM Eclipse 64-bits Open On-Chip Debugger 0.9.0-00073-gdd34716-dirty (2015-05-19-09:55)
  3. Licensed under GNU GPL v2
  4. For bug reports, read
  5. http://openocd.org/doc/doxygen/bugs.html
  6. Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
  7. adapter speed: 1000 kHz
  8. adapter_nsrst_delay: 100
  9. none separate
  10. srst_only separate srst_nogate srst_open_drain connect_deassert_srst
  11. Info : Unable to match requested speed 1000 kHz, using 950 kHz
  12. Info : Unable to match requested speed 1000 kHz, using 950 kHz
  13. Info : clock speed 950 kHz
  14. Info : STLINK v2 JTAG v24 API v2 SWIM v0 VID 0x0483 PID 0x3748
  15. Info : using stlink api v2
  16. Info : Target voltage: 2.900648
  17. 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:


  1. T:\arm_01>arm-none-eabi-gdb rst_0.elf
  2. GNU gdb (GNU Tools for ARM Embedded Processors) 7.8.0.20150604-cvs
  3. Copyright (C) 2014 Free Software Foundation, Inc.
  4. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
  5. This is free software: you are free to change and redistribute it.
  6. There is NO WARRANTY, to the extent permitted by law. Type "show copying"
  7. and "show warranty" for details.
  8. This GDB was configured as "--host=i686-w64-mingw32 --target=arm-none-eabi".
  9. Type "show configuration" for configuration details.
  10. For bug reporting instructions, please see:
  11. <http://www.gnu.org/software/gdb/bugs/>.
  12. Find the GDB manual and other documentation resources online at:
  13. <http://www.gnu.org/software/gdb/documentation/>.
  14. For help, type "help".
  15. Type "apropos word" to search for commands related to "word"...
  16. Reading symbols from rst_0.elf...done.
  17. (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.


  1. (gdb) target remote localhost:3333
  2. Remote debugging using localhost:3333
  3. 0x00000000 in ?? ()
  4. (gdb) info reg
  5. r0 0x0 0
  6. r1 0x0 0
  7. r2 0x0 0
  8. r3 0x0 0
  9. r4 0x0 0
  10. r5 0x0 0
  11. r6 0x0 0
  12. r7 0x0 0
  13. r8 0x0 0
  14. r9 0x0 0
  15. r10 0x0 0
  16. r11 0x0 0
  17. r12 0x0 0
  18. sp 0x0 0x0
  19. lr 0x0 0
  20. pc 0x0 0x0
  21. xPSR 0x0 0
  22. (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:


  1. (gdb) monitor reset halt
  2. target state: halted
  3. target halted due to debug-request, current mode: Thread
  4. xPSR: 0xc1000000 pc: 0xfffffffe msp: 0xfffffffc
  5. (gdb) monitor flash erase_sector 0 0 last
  6. erased sectors 0 through 63 on flash bank 0 in 0.029801s
  7. (gdb) load
  8. Loading section .text, size 0x24 lma 0x8000000
  9. Start address 0x8000000, load size 36
  10. Transfer rate: 220 bytes/sec, 36 bytes/write.
  11. (gdb) info reg
  12. r0 0x0 0
  13. r1 0x0 0
  14. r2 0x0 0
  15. r3 0x0 0
  16. r4 0x0 0
  17. r5 0x0 0
  18. r6 0x0 0
  19. r7 0x0 0
  20. r8 0x0 0
  21. r9 0x0 0
  22. r10 0x0 0
  23. r11 0x0 0
  24. r12 0x0 0
  25. sp 0x0 0x0
  26. lr 0x0 0
  27. pc 0x8000000 0x8000000 <_start>
  28. xPSR 0x1000000 16777216
  29. (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:

  1. (gdb) list 1,99999
  2. 1 // Avancando no primeiro programa para ARM Cortex-M0
  3. 2
  4. 3 .thumb // Define código como THUMB
  5. 4 .globl _start // Necessario para o linker
  6. 5
  7. 6 .equ STACK_INIT,0x020002000
  8. 7
  9. 8 .text // Inicio da área de código
  10. 9 _start:
  11. 10
  12. 11 // Inicio do Vector Table
  13. 12
  14. 13 .word STACK_INIT // Inicio da pilha
  15. 14 .word reset_handler // Endereco do Reset Handler
  16. 15
  17. 16 // define o label como função para que o linker
  18. 17 // resolva como código THUMB
  19. 18
  20. 19 .type reset_handler, function
  21. 20
  22. 21 // tabela de bytes para somar 1
  23. 22 tbl:
  24. 23 .byte 3,7,9,0
  25. 24 .align
  26. ---Type <return> to continue, or q <return> to quit---
  27. 25
  28. 26 reset_handler: // inicio do tratamento do reset
  29. 27
  30. 28 ldr r0,=tbl // carrega r0 com o endereco da tabela
  31. 29 mov r1,#1 // move o valor 1 para r1
  32. 30 segue:
  33. 31 ldrb r2,[r0] // carrega em r2 o byte enderecado por r0
  34. 32 cmp r2,#0 // se r2 for zero,
  35. 33 beq _stop // encerra
  36. 34 add r3,r2,r1 // soma r1 e r2 guardando em r3
  37. 35 add r0,#1 // avanca na tabela
  38. 36 b segue // continua
  39. 37 _stop:
  40. 38 b . // loop infinito
  41. (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:

  1. (gdb) info display
  2. There are no auto-display expressions now.
  3. (gdb) display /x $r0
  4. 1: /x $r0 = 0x800000b
  5. (gdb) display /x $r2
  6. 2: /x $r2 = 0x0
  7. (gdb) display /x $r3
  8. 3: /x $r3 = 0xa
  9. (gdb) display /x $xPSR
  10. 4: /x $xPSR = 0x61000000
  11. (gdb) info display
  12. Auto-display expressions now in effect:
  13. Num Enb Expression
  14. 4: y /x $xPSR
  15. 3: y /x $r3
  16. 2: y /x $r2
  17. 1: y /x $r0
  18. (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.


  1. (gdb) mon reset halt
  2. target state: halted
  3. target halted due to debug-request, current mode: Thread
  4. xPSR: 0xc1000000 pc: 0x0800000c msp: 0x20002000
  5. (gdb) flushregs
  6. Register cache flushed.
  7. (gdb) frame
  8. #0 reset_handler () at rst_0.s:28
  9. 28 ldr r0,=tbl // carrega r0 com o endereco da tabela
  10. (gdb) si
  11. 29 mov r1,#1 // move o valor 1 para r1
  12. 4: /x $xPSR = 0xc1000000
  13. 3: /x $r3 = 0xffffffff
  14. 2: /x $r2 = 0xffffffff
  15. 1: /x $r0 = 0x8000008
  16. (gdb)
  17. segue () at rst_0.s:31
  18. 31 ldrb r2,[r0] // carrega em r2 o byte enderecado por r0
  19. 4: /x $xPSR = 0x1000000
  20. 3: /x $r3 = 0xffffffff
  21. 2: /x $r2 = 0xffffffff
  22. 1: /x $r0 = 0x8000008
  23. (gdb)
  24. 32 cmp r2,#0 // se r2 for zero,
  25. 4: /x $xPSR = 0x1000000
  26. 3: /x $r3 = 0xffffffff
  27. 2: /x $r2 = 0x3
  28. 1: /x $r0 = 0x8000008
  29. (gdb)
  30. 33 beq _stop // encerra
  31. 4: /x $xPSR = 0x21000000
  32. 3: /x $r3 = 0xffffffff
  33. 2: /x $r2 = 0x3
  34. 1: /x $r0 = 0x8000008
  35. (gdb)
  36. 34 add r3,r2,r1 // soma r1 e r2 guardando em r3
  37. 4: /x $xPSR = 0x21000000
  38. 3: /x $r3 = 0xffffffff
  39. 2: /x $r2 = 0x3
  40. 1: /x $r0 = 0x8000008
  41. (gdb)
  42. 35 add r0,#1 // avanca na tabela
  43. 4: /x $xPSR = 0x1000000
  44. 3: /x $r3 = 0x4
  45. 2: /x $r2 = 0x3
  46. 1: /x $r0 = 0x8000008
  47. (gdb)
  48. 36 b segue // continua
  49. 4: /x $xPSR = 0x1000000
  50. 3: /x $r3 = 0x4
  51. 2: /x $r2 = 0x3
  52. 1: /x $r0 = 0x8000009
  53. (gdb)
  54. 31 ldrb r2,[r0] // carrega em r2 o byte enderecado por r0
  55. 4: /x $xPSR = 0x1000000
  56. 3: /x $r3 = 0x4
  57. 2: /x $r2 = 0x3
  58. 1: /x $r0 = 0x8000009
  59. (gdb)
  60. 32 cmp r2,#0 // se r2 for zero,
  61. 4: /x $xPSR = 0x1000000
  62. 3: /x $r3 = 0x4
  63. 2: /x $r2 = 0x7
  64. 1: /x $r0 = 0x8000009
  65. (gdb)
  66. 33 beq _stop // encerra
  67. 4: /x $xPSR = 0x21000000
  68. 3: /x $r3 = 0x4
  69. 2: /x $r2 = 0x7
  70. 1: /x $r0 = 0x8000009
  71. (gdb)
  72. 34 add r3,r2,r1 // soma r1 e r2 guardando em r3
  73. 4: /x $xPSR = 0x21000000
  74. 3: /x $r3 = 0x4
  75. 2: /x $r2 = 0x7
  76. 1: /x $r0 = 0x8000009
  77. (gdb)
  78. 35 add r0,#1 // avanca na tabela
  79. 4: /x $xPSR = 0x1000000
  80. 3: /x $r3 = 0x8
  81. 2: /x $r2 = 0x7
  82. 1: /x $r0 = 0x8000009
  83. (gdb)
  84. 36 b segue // continua
  85. 4: /x $xPSR = 0x1000000
  86. 3: /x $r3 = 0x8
  87. 2: /x $r2 = 0x7
  88. 1: /x $r0 = 0x800000a
  89. (gdb)
  90. 31 ldrb r2,[r0] // carrega em r2 o byte enderecado por r0
  91. 4: /x $xPSR = 0x1000000
  92. 3: /x $r3 = 0x8
  93. 2: /x $r2 = 0x7
  94. 1: /x $r0 = 0x800000a
  95. (gdb)
  96. 32 cmp r2,#0 // se r2 for zero,
  97. 4: /x $xPSR = 0x1000000
  98. 3: /x $r3 = 0x8
  99. 2: /x $r2 = 0x9
  100. 1: /x $r0 = 0x800000a
  101. (gdb)
  102. 33 beq _stop // encerra
  103. 4: /x $xPSR = 0x21000000
  104. 3: /x $r3 = 0x8
  105. 2: /x $r2 = 0x9
  106. 1: /x $r0 = 0x800000a
  107. (gdb)
  108. 34 add r3,r2,r1 // soma r1 e r2 guardando em r3
  109. 4: /x $xPSR = 0x21000000
  110. 3: /x $r3 = 0x8
  111. 2: /x $r2 = 0x9
  112. 1: /x $r0 = 0x800000a
  113. (gdb)
  114. 35 add r0,#1 // avanca na tabela
  115. 4: /x $xPSR = 0x1000000
  116. 3: /x $r3 = 0xa
  117. 2: /x $r2 = 0x9
  118. 1: /x $r0 = 0x800000a
  119. (gdb)
  120. 36 b segue // continua
  121. 4: /x $xPSR = 0x1000000
  122. 3: /x $r3 = 0xa
  123. 2: /x $r2 = 0x9
  124. 1: /x $r0 = 0x800000b
  125. (gdb)
  126. 31 ldrb r2,[r0] // carrega em r2 o byte enderecado por r0
  127. 4: /x $xPSR = 0x1000000
  128. 3: /x $r3 = 0xa
  129. 2: /x $r2 = 0x9
  130. 1: /x $r0 = 0x800000b
  131. (gdb)
  132. 32 cmp r2,#0 // se r2 for zero,
  133. 4: /x $xPSR = 0x1000000
  134. 3: /x $r3 = 0xa
  135. 2: /x $r2 = 0x0
  136. 1: /x $r0 = 0x800000b
  137. (gdb)
  138. 33 beq _stop // encerra
  139. 4: /x $xPSR = 0x61000000
  140. 3: /x $r3 = 0xa
  141. 2: /x $r2 = 0x0
  142. 1: /x $r0 = 0x800000b
  143. (gdb)
  144. _stop () at rst_0.s:38
  145. 38 b . // loop infinito
  146. 4: /x $xPSR = 0x61000000
  147. 3: /x $r3 = 0xa
  148. 2: /x $r2 = 0x0
  149. 1: /x $r0 = 0x800000b
  150. (gdb)
  151. 38 b . // loop infinito
  152. 4: /x $xPSR = 0x61000000
  153. 3: /x $r3 = 0xa
  154. 2: /x $r2 = 0x0
  155. 1: /x $r0 = 0x800000b
  156. (gdb) quit
  157. A debugging session is active.
  158. Inferior 1 [Remote target] will be detached.
  159. Quit anyway? (y or n) y
  160. Detaching from program: T:\arm_01\rst_0.elf, Remote target
  161. Ending remote debugging.
  162. 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