7 Comentários

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.

Software » GNU ARM Cross-toolchain – Configurando stack e heap
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

WEBINAR
 
Redes Mesh para Monitoramento
e Controle de Sensores

Data: 15/07 às 14:00h Apoio: Artimar| Microchip| Tecsus
 
INSCREVA-SE AGORA »



 
close-link