Mostrando postagens com marcador MAKE. Mostrar todas as postagens
Mostrando postagens com marcador MAKE. Mostrar todas as postagens

sexta-feira, 13 de novembro de 2015

Tornando o programa mais portável com linker script

Até agora, nosso programa tinha particularidades para o processador da placa STM32F0Discovery. As principais delas dizem respeito a mapeamento e tamanho das memórias flash e ram. A forma mais prática de se fazer isso é separando as particularidades de cada processador em um arquivo do linker: o linker script. Vamos começar com algo simples:


  1. /* Definindo as areas de memoria
  2. */
  3. MEMORY
  4. {
  5. FLASH : ORIGIN = 0x08000000, LENGTH = 64K
  6. RAM : ORIGIN = 0x20000000, LENGTH = 8K
  7. }
  8. /* Definindo o endereco do inicio
  9. * da pilha de sistema
  10. */
  11. _stack_init = ORIGIN(RAM)+LENGTH(RAM);
  12. /* Definindo as sessoes
  13. * por enquanto temos apenas uma
  14. * sessao de interesse: a de código
  15. */
  16. SECTIONS
  17. {
  18. .text : { *(.text) } > FLASH
  19. .data : { *(.data) } > RAM
  20. }

O linker script é um arquivo texto onde definimos as particularidades que desejamos para o processo do linker. Primeiramente faremos um arquivo chamado stm32f0discovery.ld que, como o nome diz, será particular para o processador STM32F05xx da placa STM32F0Discovery. Começamos pelo comando MEMORYEsse comando dá ao linker as características dos tipos de memória que o nosso processador tem. No nosso caso, o processador STM32F05xx possui 64k KBytes de memória flash iniciando no endereço 0x08000000 e 8 KBytes de memória ram iniciando no endereço 0x20000000. Pronto: já definimos o tipo, tamanho e endereço de nossa memória física. Com isso, na linha 14, criamos um símbolo que possui, automaticamente, o endereço da pilha de sistema.

O próximo comando faz o mapeamento entre as sessões internas (definidas no programas) com as sessões externas nas áreas e endereços físicos da(s) memória(s) do processador. Temos, como padrão, as seguintes sessões:

  • .text : contém as instruções do programa a serem executadas;
  • .data : contém as áreas de memórias pré-inicializadas com valores;
  • .bss : contém as áreas de memórias não inicializadas com valores.

Por enquanto, nos preocuparemos com a sessão .text. A sessão .data está presente por ser obrigatória no processo do linker mas nós a utilizaremos efetivamente mais adiante. No nosso arquivo, começamos esse mapeamento através do comando SECTIONS. De forma simples, mapeamos a sessão .text externa para a memória flash e a sessão externa .data para a memória ram. Como o processo do linker pode unir muitos programas em um único módulo objeto (aliás essa é sua função principal), determinamos que todas as sessões .text internas (dos programas) serão alocadas na memória flash e todas as sessões .data internas para a memória ram. Isso se dá pelo caractere coringa '*' presente no comando. Em suma:

.text : { *(.text) } > FLASH

.text : a sessão .text do módulo objeto de saída
... } engloba
*(.text) todas as sessões .text dos arquivos objetos dos programas individuais
> FLASH  e residirá na memória flash

Raciocínio semelhante é usado para a sessão .data.

Completando o processo, faremos as alterações necessárias no nosso programa e no makefile.


  1. // Segundo programa para ARM Cortex-M0 configurado para linker script
  2. .thumb // Define código como THUMB
  3. .globl _start // Necessario para o linker
  4. .globl _reset_handler // visto externamente
  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. .weak _reset_handler // pode ser redefinido em outro modulo
  13. .type _reset_handler, function
  14. // tabela de bytes para somar 1
  15. tbl:
  16. .byte 3,7,9,0
  17. .align
  18. _reset_handler: // inicio do tratamento do reset
  19. ldr r0,=tbl // carrega r0 com o endereco da tabela
  20. mov r1,#1 // move o valor 1 para r1
  21. segue:
  22. ldrb r2,[r0] // carrega em r2 o byte enderecado por r0
  23. cmp r2,#0 // se r2 for zero,
  24. beq _stop // encerra
  25. add r3, r2, r1 // soma r1 e r2 guardando em r3
  26. add r0,#1 // avanca na tabela
  27. b segue // continua
  28. _stop:
  29. b . // loop infinito

Primeiramente, rebatizamos nosso função de tratamento de reset como _reset_handler (linha 25) e o definimos como global (linha 5) e, com a diretiva .weak (linha 18), é possível que outro módulo externo ao nosso redefina essa função. O inicio da pilha do sistema (_stack_init) agora é uma referência resolvida externamente no linker script e não mais inicializada dentro do programa.

Quanto ao makefile, retiramos os passos de geração do arquivo binário que não usaremos por agora. Além disso, retiramos o parâmetro -Ttext=0x00000000 do passo do linker substituindo-o pelo parâmetro informando nosso arquivo de linker script: -T stm32f0discovery.ld.


  1. rst_0.elf: rst_0.o
  2. @echo&echo&echo&echo&echo&echo ///////////// Executando o linker
  3. @echo
  4. arm-none-eabi-ld -o rst_0.elf rst_0.o -T stm32f0discovery.ld
  5. @echo&echo&echo&echo&echo&echo ///////////// Mostrando os simbolos apos o linker
  6. @echo
  7. arm-none-eabi-nm rst_0.elf
  8. @echo&echo&echo&echo&echo&echo ///////////// Dump das sessoes do programa
  9. @echo
  10. arm-none-eabi-objdump -h rst_0.elf
  11. rst_0.o: rst_0.s
  12. @echo&echo&echo&echo&echo&echo ///////////// Montando o programa assembly
  13. @echo
  14. arm-none-eabi-as -mcpu=cortex-m0 -g -o rst_0.o rst_0.s
  15. clean:
  16. @echo&echo&echo&echo&echo&echo ///////////// Eliminando os arquivos de saida
  17. @echo
  18. rm *.o
  19. rm *.elf

Pronto. Nosso processo de geração do módulo objeto pode ser executado:


  1. T:\arm_03>make
  2. ///////////// Montando o programa assembly
  3. arm-none-eabi-as -mcpu=cortex-m0 -g -o rst_0.o rst_0.s
  4. ///////////// Executando o linker
  5. arm-none-eabi-ld -o rst_0.elf rst_0.o -T stm32f0discovery.ld
  6. ///////////// Mostrando os simbolos apos o linker
  7. arm-none-eabi-nm rst_0.elf
  8. 0800000c W _reset_handler
  9. 20002000 A _stack_init
  10. 08000000 T _start
  11. 0800001c t _stop
  12. 08000010 t segue
  13. 08000008 t tbl
  14. ///////////// Dump das sessoes do programa
  15. arm-none-eabi-objdump -h rst_0.elf
  16. rst_0.elf: file format elf32-littlearm
  17. Sections:
  18. Idx Name Size VMA LMA File off Algn
  19. 0 .text 00000024 08000000 08000000 00008000 2**2
  20. CONTENTS, ALLOC, LOAD, READONLY, CODE
  21. 1 .ARM.attributes 00000021 00000000 00000000 00008024 2**0
  22. CONTENTS, READONLY
  23. 2 .debug_line 00000042 00000000 00000000 00008045 2**0
  24. CONTENTS, READONLY, DEBUGGING
  25. 3 .debug_info 0000003a 00000000 00000000 00008087 2**0
  26. CONTENTS, READONLY, DEBUGGING
  27. 4 .debug_abbrev 00000014 00000000 00000000 000080c1 2**0
  28. CONTENTS, READONLY, DEBUGGING
  29. 5 .debug_aranges 00000020 00000000 00000000 000080d8 2**3
  30. CONTENTS, READONLY, DEBUGGING
  31. T:\arm_03>


Vemos duas informações importantes: na linha 27 o símbolo _stack_init foi gerado pelo linker e com conteúdo correto: 0x20002000. Nas linhas 44 e 45 temos as informações por ora relevantes da nossa sessão .text do nosso módulo objeto: ela possui um tamanho de 0x24 bytes, e possui os endereços de memória VMA e LMA iguais a 0x08000000 que é o nosso endereço da memória flash. Por enquanto é o suficiente. No próximo post quando falarmos da utilização da memória ram, descreveremos as diferenças e usos dos endereços VMA e LMA.

Agora é só depurarmos da mesma forma como feito no post anterior. Boa sorte.


REFERÊNCIAS
The GNU linker - http://www.eecs.umich.edu/courses/eecs373/readings/Linker.pdf
Using AS - http://www.eecs.umich.edu/courses/eecs373/readings/Assembler.pdf

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.