Direção de crescimento da pilha no FreeRTOS: armadilhas de compatibilidade de hardware e estratégias de proteção

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:

  1. Verifique a documentação da CPU ao definir portSTACK_GROWTH em portmacro.h.
  2. Utilize ferramentas de análise estática para detectar incompatibilidades entre configurações de pilha e alocação.
  3. 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.

Tags: FreeRTOS ESP32-C3 portabilidade de RTOS gestão de pilha RTOS para RISC-V

Publicado em 7-12 19:46