GNU ARM Cross-toolchain – Configurando stack e heap

GNU ARM Cross-toolchain heap

Na primeira parte da série, foi cross-compilada uma aplicação simples e em seguida foi utilizado o OpenOCD para realizar a gravação do binário gerado na memória Flash do microcontrolador. Mas caso seja necessário escrever uma aplicação mais complexa, que faça uso, por exemplo, de alocação dinâmica de memória, o que é necessário alterar no projeto? Além disso, é interessante deixar o tamanho da pilha de uma aplicação configurável. Como fazer isso? Este artigo responderá essas duas perguntas.

Antes de entrarmos em maiores detalhes, é necessário possuir uma cópia local do projeto disponível no github. Caso não tenham sido realizados os passos listados no artigo mencionado acima, é interessante que esses sejam executados de forma a tirar maior proveito dos conceitos apresentados a seguir.

Uma vez que o ambiente esteja criado corretamente, basta atualizar o projeto, tendo em vista que ele vem sofrendo alterações/modificações com o tempo. Execute os seguintes comandos:

Alocação dinâmica de memória

Para uso de alocação dinâmica de memória, a biblioteca C padrão, como especificado pelo padrão ISO/IEC 9899:2011, oferece algumas funções que gerenciam o pool de memória do sistema, tais como:

  • malloc;
  • calloc;
  • realloc;
  • free.

Para um teste inicial, vamos usar a função malloc para requisitar um bloco de memória da área de memória chamada heap. Você deve estar se perguntando agora: “Onde foi configurado o tamanho dessa área de memória?”. Nos próximos parágrafos vamos responder a essa pergunta!

É necessário que a macro PROJ_TEST_DINAMIC_MEM esteja definida no projeto para que esse teste seja realizado. Mas nada impede que sejam adicionadas ao projeto outras chamadas às funções mencionadas anteriormente. Portanto, altere o arquivo src/project.h de forma a conter a seguinte diretiva de compilação:

Pronto! Agora é só compilar o projeto.

Funcionou, correto? Não? Ocorreram erros de compilação? Algo como isso?

Faltou alguma coisa? Sim! Precisamos entender o conceito de syscalls, mas antes disso é necessário estudar a implementação da “C runtime library” utilizada pelo cross-toolchain, a Newlib.

Newlib

A Newlib, embora não seja um produto GNU, é uma “C runtime library” muito utilizada em projetos de sistemas embarcados bare-metal e GNU-based, já que suporta uma grande quantidade de arquiteturas de processadores. Como explicado nesse artigo, uma parte da biblioteca é dependente do sistema target, chamada Syscalls.

Syscalls

Uma syscall é a ligação entre a biblioteca C padrão e o dispositivo target. São funções que executam serviços referentes a recursos do ambiente de runtime do sistema target. Quando utilizado um sistema operacional, esse é responsável por fornecer tais funções já implementadas. No entanto, quando é construído um sistema bare-metal, as implementações de acesso ao sistema target devem ser fornecidas pelo próprio desenvolvedor. Entre tais funções, pode-se citar: acesso de baixo nível do sistema de arquivos, requisições para expansão da memória de dados, gerenciamento de processos, etc.

O erro obtido durante a compilação anterior é justamente referente à ausência da implementação de uma dessas funções, _sbrk, cuja responsabilidade é receber requisições para expansão da memória de dados do programa, dentro da memória heap. A implementação da função malloc da newlib faz uso do algoritmo de Doug Lea, que usa a macro MORECORE, definida da seguinte forma no seu código:

Agora é possível entender a causa do erro, certo? Mais outra dúvida: o que é a função _sbrk_r? A função _sbrk_r é a versão reentrante da função _sbrk, que recebe um argumento extra, um ponteiro para uma estrutura de controle de reentrância. Não é o foco deste artigo tratar reentrância, pois geralmente esse problema é resolvido usando um RTOS. Geralmente esse tipo de sistema operacional traz a sua própria implementação de memory allocator, não sendo necessário, portanto, o uso da função malloc da biblioteca C.

A seguir é fornecida a implementação da função _sbrk utilizada no projeto. Percebe-se que é feito uso de duas variáveis não definidas na aplicação: _end e _heap_end. Elas são os delimitadores da memória heap disponível no sistema target. Quando a memória disponível para alocação dinâmica acaba, _sbrk indica esse evento à função malloc, e essa, por sua vez, notifica a aplicação retornando o valor NULL. Caso contrário, é retornado o próximo endereço livre no heap à função malloc.

Para que essa função seja definida no projeto, altere o arquivo src/project.h e habilite o define PROJ_IMPLEMMENT_SYSCALLS, da seguinte forma:

Recompile a aplicação e verifique que o erro deixou de ocorrer. Vamos configurar as memórias heap e stack agora?

Configurando Heap e Stack

Tanto o heap quanto o stack são regiões alocadas na memória RAM do sistema, as quais podem ser configuradas de diversas formas. Neste artigo, esse procedimento é realizado no linker script do projeto. Tal arquivo é responsável por especificar a localização e tamanho dos blocos de memória do target, além de descrever o layout do arquivo executável, detalhando o mapeamento das seções dos arquivos de entrada no arquivo de saída.

Foram criados dois linker scripts no projeto:

  • project/stm32f4_flash.ld: especifica os blocos de memória do microcontrolador (RAM, FLASH, etc.) e configura os tamanhos das regiões heap e stack;
  • project/sections.ld: detalha o layout da memória do arquivo executável.

O arquivo sections.ld pode ser reaproveitado em outros projetos, pois trata-se de uma espécie de template de configuração de um arquivo executável. Já o arquivo stm32f4_flash.ld traz configurações de um projeto em específico, tal como mapeamento físico da memória do microcontrolador. Nesse último arquivo foram criadas algumas variáveis para configuração do heap e stack:

  • _heap_size: tamanho do heap em bytes;
  • _stack_size: tamanho do stack em bytes.

Para facilitar a visualização do layout da memória utilizado no projeto, veja a figura abaixo. O executável final, assim como os objetos compilados ao longo do processo de build, são formados por seções, tais como .text, .data e .bss, especificadas pelo padrão ELF. O trabalho de alocação dessas seções no arquivo binário executável é responsabilidade do linker, orientado pelas configurações indicadas no linker script.

Mapeamento da memoria

A memória destinada para stack está localizada no final da região RAM, cujo endereço inicial é calculado pelo linker e é indicado no vetor de interrupções/exceções do microcontrolador. Logo abaixo dessa região está localizada a memória para heap, utilizada para alocação dinâmica de memória.

O sentido de crescimento da memória de stack é o oposto da memória de heap. A primeira cresce para endereços mais baixos, já a segunda avança para endereços maiores. Dessa forma, caso haja um erro no dimensionamento dessas regiões, uma pode sobrepor a outra em algum momento durante a execução da aplicação. Como dimensionar corretamente tais regiões? Esse é um assunto para outro artigo.

E vocês? Como configuram essas regiões em seus projetos? Deixem suas dicas!

Outros artigos da série

<< GNU ARM Cross-toolchain – Compilação e OpenOCDGNU ARM Cross-toolchain – OpenOCD + GDB >>
Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

[wpseo_breadcrumb]
Comentários:
Notificações
Notificar
guest
7 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
trackback
18/06/2014 16:03

[…] artigo GNU ARM Cross-toolchain – Configuração stack e heap foi mencionado como reservar espaços da memória volátil do sistema para tais finalidades. Em […]

trackback
31/05/2014 22:44

[…] GNU ARM Cross-toolchain – Configurando stack e heap […]

trackback
20/04/2014 13:48

[…] GNU ARM Cross-toolchain – Configurando stack e heap […]

trackback
19/04/2014 22:27

[…] GNU ARM Cross-toolchain – Configurando stack e heap […]

trackback
17/04/2014 08:45

[…] GNU ARM Cross-toolchain – Configurando stack e heap […]

trackback
17/04/2014 08:32

[…] GNU ARM Cross-toolchain – Configurando stack e heap […]

trackback
18/12/2013 20:40

[…] GNU ARM Cross-toolchain – Configurando stack e heap […]

Talvez você goste:

Séries



Outros da Série

Menu
Privacy Settings saved!
Configurações de Privacidade

Entenda quais dados e informações usamos para ter melhor entrega de conteúdo personalizado para você.

These cookies are necessary for the website to function and cannot be switched off in our systems.

Para usar este site, usamos os seguintes cookies tecnicamente exigidos

  • wordpress_test_cookie
  • wordpress_logged_in_
  • wordpress_sec

Decline all Services
Accept all Services