Durante uma portabilidade do FreeRTOS para o ESP32-C3, encontramos uma falha crítica: o TCB (Bloco de Controle de Tarefa) de uma tarefa foi gradualmente corrompido, causando panicos no escalonador. A raiz do problema foi uma configuração incorreta da direção de crescimento da pilha, definida pelo macro portSTACK_GROWTH. Este caso ilustra como uma característica aparentemente trivial do hardware pode gerar falhas complexas em sistemas embarcados.
1. Diferenças arquiteturais na gestão da pilha
A direção de crescimento da pilha é uma propriedade fundamental da CPU e impacta diretamente a alocação de memória no FreeRTOS. Principais padrões:
- Pilha descendente (ex: ARM Cortex-M): o ponteiro de pilha (SP) diminui ao adicionar dados. Usado em STM32, nRF52.
- Pilha ascendente (ex: RISC-V, AVR): o SP aumenta ao adicionar dados. Usado em ESP32-C3, ATmega328P.
- Pilha configurável (ex: MSP430): a direção pode ser alterada por registrador, exigindo cuidado extra na inicialização.
Em arquiteturas com pilha descendente, o TCB é tipicamente alocado em endereços mais altos, protegido contra crescimento da pilha. Em pilhas ascendentse, a ordem de alocação deve ser invertida.
2. Lógica de alocação de memória no FreeRTOS
Dentro de xTaskCreate(), a ordem de alocação depende de portSTACK_GROWTH. Veja uma versão simplificada do mecanismo:
// Alocação para pilha ascendente (cresce para endereços altos)
if (portSTACK_GROWTH > 0) {
pxNovoTCB = pvPortMalloc(sizeof(TCB_t)); // Primeiro: TCB (endereço mais baixo)
pxNovoTCB->pxPilha = pvPortMalloc(tamanho * sizeof(StackType_t)); // Depois: pilha (endereço mais alto)
}
// Alocação para pilha descendente (cresce para endereços baixos)
else {
StackType_t *pxPilha = pvPortMalloc(tamanho * sizeof(StackType_t)); // Primeiro: pilha (endereço mais baixo)
pxNovoTCB = pvPortMalloc(sizeof(TCB_t)); // Depois: TCB (endereço mais alto)
pxNovoTCB->pxPilha = pxPilha;
}
A lógica garante que o TCB fique sempre "fora do caminho" do crescimento da pilha, prevenidno a sobrescrita. Alocadores de memória como pvPortMalloc geralmente retornam endereços consecutivos crescentes.
3. Implicações práticas e estratégias de prevenção
Para evitar erros em portabilidades:
- Verifique a documentação da CPU ao definir
portSTACK_GROWTHemportmacro.h. - Utilize ferramentas de análise estática para detectar incompatibilidades entre configurações de pilha e alocação.
- Implemente sentinelas de memória para monitorar estouros de pilha antes que atinjam o TCB.
Exemplo de configuração correta para ESP32-C3 (RISC-V):
// No arquivo portmacro.h da porta específica
#define portSTACK_GROWTH (1) // Pilha ascendente para RISC-V
Quando a configuração está incorreta, o TCB pode ser corrompido gradualmente, causando falhas intermitentes difíceis de diagnosticar. Verificar essa definição é um passo essencial durante qualquer portabilidade do FreeRTOS.