quinta-feira, 5 de novembro de 2015

Facilitando as coisas com o utilitário MAKE

No último post codificamos e executamos nosso primeiro programa para o microcontrolador STM32F051R8T6. Uma série de comandos na ordem certa são necessários para perfazer essa tarefa. Vamos aproveitar que as coisas ainda estão simples e introduzir um utilitário que facilita muito o trabalho de quem desenvolve programas: o utilitário MAKE. Em um post anterior, nós o baixamos e instalamos através de uma versão de utilitários POSIX: o MINGW. O utilitário MAKE busca dependências e comandos em um arquivo a parte que, por padrão, chama-se makefile. Vamos começar de uma forma simples e aos poucos automatizando mais o processo.

O utilitário MAKE trabalha com dependências e procedimentos. No nosso primeiro programa, o arquivo final era o flash.bin que dependia do rst_0.bin que, por sua vez, dependia do rst_0.elf e assim por diante até chegarmos na primeira dependência que era o programa assembly rst_0.s propriamente dito. Se usarmos o símbolo ':' para marcar a dependência, teremos:

  1. flash.bin: rst_0.bin
  2. rst_0.bin: rst_0.elf
  3. rst_0.elf: rst_0.o
  4. rst_0.o: rst_0.s

Do lado esquerdo dos dois-pontos temos o produto e do lado direito o produto necessário para gerá-lo. Dessa forma, temos uma cadeia de dependências:

flash.bin: rst_0.bin: rst_0.elf: rst_0.o: rst_0.s

Essa é a primeira construção do makefile.

Dissemos que o utilitário MAKE trabalha com dependências e procedimentos. Para satisfazer as dependências temos os procedimentos. Como incluir um procedimento no arquivo makefile?
Simples: na linha seguinte à dependência, iniciando com o caractere TAB (0x09), codificamos o(s) comando(s) que satisfaz(em) essa dependência. Veja o exemplo abaixo:

  1. rst_0.o: rst_0.s
  2. arm-none-eabi-as -a -o rst_0.o rst_0.s

Para satisfazer a dependência da linha 1, o utilitário MAKE executa o comando que esta na linha 2. Simples assim. O arquivo makefile completo para o nosso promeiro programa está abaixo:

  1. flash.bin: rst_0.bin
  2. dd if=/dev/zero of=flash.bin bs=4096 count=16
  3. dd if=rst_0.bin of=flash.bin bs=4096 conv=notrunc
  4. rst_0.bin: rst_0.elf
  5. arm-none-eabi-objcopy -O binary rst_0.elf rst_0.bin
  6. rst_0.elf: rst_0.o
  7. arm-none-eabi-ld -Ttext=0x00000000 -o rst_0.elf rst_0.o
  8. arm-none-eabi-nm rst_0.elf
  9. rst_0.o: rst_0.s
  10. arm-none-eabi-as -a -o rst_0.o rst_0.s
  11. clean:
  12. rm *.bin
  13. rm *.elf
  14. rm *.o

Todas as dependências estão satisfeitas por comandos. Notem uma dependência nova na linha 13. Na verdade, podemos executar o utilitário MAKE dizendo a ele qual dependência queremos satisfazer.  A linha 13 permite uma opção para limpar todos os arquivos de saída dos procedimentos executados. Vejamos sua execução:

  1. U:\arm_00>make clean
  2. rm *.bin
  3. rm *.elf
  4. rm *.o
  5. U:\arm_00>
A dependência clean executa o comando rm (remove) para excluir os arquivos desejados.

Finalmente, eis a execução de nosso makefile para gerar nosso primeiro programa:


  1. U:\arm_00>make
  2. arm-none-eabi-as -a -o rst_0.o rst_0.s
  3. ARM GAS rst_0.s page 1
  4. 1 // Primeiro programa para ARM Cortex-M0
  5. 2
  6. 3 .code 16 // Define c├│digo como THUMB
  7. 4 .globl _start // Necessario para o linker
  8. 5
  9. 6 .equ STACK_INIT,0x020002000
  10. 7
  11. 8 .text // Inicio da área de código
  12. 9 _start:
  13. 10
  14. 11 // Inicio do Vector Table
  15. 12
  16. 13 0000 00200020 .word STACK_INIT // Inicio da pilha
  17. 14 0004 00000000 .word reset_handler // Endereco do Reset Handler
  18. 15
  19. 16 // define o label como função para que o linker
  20. 17 // resolva como c├│digo THUMB
  21. 18
  22. 19 .type reset_handler, function
  23. 20
  24. 21 reset_handler: // inicio do tratamento do reset
  25. 22
  26. 23 0008 0320 mov r0, #3 // carrega r0 com o valor 3
  27. 24 000a 0421 mov r1, #4 // carrega r1 com o valor 4
  28. 25 000c 0A18 add r2, r1, r0 // soma r0 e r1 guardando em r2
  29. 26
  30. 27 000e FEE7 _stop: b . // loop infinito
  31. ♀ARM GAS rst_0.s page 2
  32. DEFINED SYMBOLS
  33. rst_0.s:9 .text:00000000 _start
  34. rst_0.s:6 *ABS*:20002000 STACK_INIT
  35. rst_0.s:21 .text:00000008 reset_handler
  36. .text:00000000 $d
  37. rst_0.s:23 .text:00000008 $t
  38. rst_0.s:27 .text:0000000e _stop
  39. NO UNDEFINED SYMBOLS
  40. arm-none-eabi-ld -Ttext=0x00000000 -o rst_0.elf rst_0.o
  41. arm-none-eabi-nm rst_0.elf
  42. 00008010 T __bss_end__
  43. 00008010 T __bss_start
  44. 00008010 T __bss_start__
  45. 00008010 T __data_start
  46. 00008010 T __end__
  47. 00008010 T _bss_end__
  48. 00008010 T _edata
  49. 00008010 T _end
  50. 00080000 T _stack
  51. 00000000 T _start
  52. 0000000e t _stop
  53. 00000008 t reset_handler
  54. 20002000 a STACK_INIT
  55. arm-none-eabi-objcopy -O binary rst_0.elf rst_0.bin
  56. dd if=/dev/zero of=flash.bin bs=4096 count=16
  57. 16+0 records in
  58. 16+0 records out
  59. 65536 bytes (66 kB) copied, 0 seconds, Infinity B/s
  60. dd if=rst_0.bin of=flash.bin bs=4096 conv=notrunc
  61. 0+1 records in
  62. 0+1 records out
  63. 16 bytes (16 B) copied, 0 seconds, Infinity B/s
  64. U:\arm_00>

Por último, para deixarmos mais fácil a leitura das saídas das sequências de comandos, usaremos o comando @echo para sinalizar o que está sendo executado:

  1. flash.bin: rst_0.bin
  2. @echo //////////////////////////////////////////////////
  3. @echo Gerando a imagem final para carga na memoria flash
  4. @echo //////////////////////////////////////////////////
  5. dd if=/dev/zero of=flash.bin bs=4096 count=16
  6. dd if=rst_0.bin of=flash.bin bs=4096 conv=notrunc
  7. rst_0.bin: rst_0.elf
  8. @echo //////////////////////////////////////////////////
  9. @echo Extraindo o arquivo binado sem os simbolos
  10. @echo //////////////////////////////////////////////////
  11. arm-none-eabi-objcopy -O binary rst_0.elf rst_0.bin
  12. rst_0.elf: rst_0.o
  13. @echo //////////////////////////////////////////////////
  14. @echo Executando o linker
  15. @echo //////////////////////////////////////////////////
  16. arm-none-eabi-ld -Ttext=0x00000000 -o rst_0.elf rst_0.o
  17. @echo //////////////////////////////////////////////////
  18. @echo Mostrando os simbolos apos o linker
  19. @echo //////////////////////////////////////////////////
  20. arm-none-eabi-nm rst_0.elf
  21. rst_0.o: rst_0.s
  22. @echo //////////////////////////////////////////////////
  23. @echo Montando o programa assembly
  24. @echo //////////////////////////////////////////////////
  25. arm-none-eabi-as -a -o rst_0.o rst_0.s
  26. clean:
  27. @echo //////////////////////////////////////////////////
  28. @echo Eliminando os arquivos de saida
  29. @echo //////////////////////////////////////////////////
  30. rm *.bin
  31. rm *.elf
  32. rm *.o

A execução ficaria assim:

  1. U:\arm_00>make
  2. //////////////////////////////////////////////////
  3. Montando o programa assembly
  4. //////////////////////////////////////////////////
  5. arm-none-eabi-as -a -o rst_0.o rst_0.s
  6. ARM GAS rst_0.s page 1
  7. 1 // Primeiro programa para ARM Cortex-M0
  8. 2
  9. 3 .code 16 // 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 reset_handler: // inicio do tratamento do reset
  28. 22
  29. 23 0008 0320 mov r0, #3 // carrega r0 com o valor 3
  30. 24 000a 0421 mov r1, #4 // carrega r1 com o valor 4
  31. 25 000c 0A18 add r2, r1, r0 // soma r0 e r1 guardando em r2
  32. 26
  33. 27 000e FEE7 _stop: b . // loop infinito
  34. ♀ARM GAS rst_0.s page 2
  35. DEFINED SYMBOLS
  36. rst_0.s:9 .text:00000000 _start
  37. rst_0.s:6 *ABS*:20002000 STACK_INIT
  38. rst_0.s:21 .text:00000008 reset_handler
  39. .text:00000000 $d
  40. rst_0.s:23 .text:00000008 $t
  41. rst_0.s:27 .text:0000000e _stop
  42. NO UNDEFINED SYMBOLS
  43. //////////////////////////////////////////////////
  44. Executando o linker
  45. //////////////////////////////////////////////////
  46. arm-none-eabi-ld -Ttext=0x00000000 -o rst_0.elf rst_0.o
  47. //////////////////////////////////////////////////
  48. Mostrando os simbolos apos o linker
  49. //////////////////////////////////////////////////
  50. arm-none-eabi-nm rst_0.elf
  51. 00008010 T __bss_end__
  52. 00008010 T __bss_start
  53. 00008010 T __bss_start__
  54. 00008010 T __data_start
  55. 00008010 T __end__
  56. 00008010 T _bss_end__
  57. 00008010 T _edata
  58. 00008010 T _end
  59. 00080000 T _stack
  60. 00000000 T _start
  61. 0000000e t _stop
  62. 00000008 t reset_handler
  63. 20002000 a STACK_INIT
  64. //////////////////////////////////////////////////
  65. Extraindo o arquivo binado sem os simbolos
  66. //////////////////////////////////////////////////
  67. arm-none-eabi-objcopy -O binary rst_0.elf rst_0.bin
  68. //////////////////////////////////////////////////
  69. Gerando a imagem final para carga na memoria flash
  70. //////////////////////////////////////////////////
  71. dd if=/dev/zero of=flash.bin bs=4096 count=16
  72. 16+0 records in
  73. 16+0 records out
  74. 65536 bytes (66 kB) copied, 0 seconds, Infinity B/s
  75. dd if=rst_0.bin of=flash.bin bs=4096 conv=notrunc
  76. 0+1 records in
  77. 0+1 records out
  78. 16 bytes (16 B) copied, 0 seconds, Infinity B/s
  79. U:\arm_00>

Pode parecer muito esforço para algo simples mas, quando uma aplicação tem muitos fontes, o utilitário MAKE facilitará o trabalho uma vez que ele sabe quais dependências precisam ser satisfeitas apenas executando procedimentos que estão desatualizados pela data dos arquivos.

Nenhum comentário:

Postar um comentário