viernes, mayo 22, 2020

Calcular el stack en S32 Design Studio for ARM (S32K144)

El día de hoy me encontraba programando una FFT en un S32K144 en el IDE S32 Design Studio for ARM. El S32K144 tiene 64kB de RAM por lo que no me preocupé en declarar variables inmensas.

Las variables globales que declaré fueron:

#define FFT_SAMPLE_MAX                                  (2048U)
#define FFT_FREQUENCY_RESP_SIZE                         ((FFT_SAMPLE_MAX / 2) + 1)
#define FFT_FREQ_BANDS                                  (10U)

static uint16_t FFT_AudioSamples[FFT_SAMPLE_MAX];
static float FFT_HammingWindow[FFT_SAMPLE_MAX];
static float FFT_Frequency_Bands[FFT_FREQ_BANDS];

Alrededor de 13kB, entre otras variables estáticas locales que no contabilicé.

Posteriormente, dentro de una función declaré más variables pero esta vez como variables locales que se depositarán en el stack.

void FFT_GetFrequencyResponse(float* freqResponsePerBand)
{
    if (1 == FFT_bufferReady)
    {
        float audioReal[FFT_SAMPLE_MAX];
        float audioImag[FFT_SAMPLE_MAX];
        float freqResp[FFT_FREQUENCY_RESP_SIZE];

Alrededor de 20kB en esa función únicamente. Si sigo llamando funciones con nuevas variables en stack, éste seguirá creciendo.

Después de ver que mi código sospechosamente estaba corrompiendo las variables me di cuenta de que no había modificado el tamaño del stack y que por ello se estaban sobreeescribiendo.

Utilicé C/C++ Watchpoints para ver cuándo se escribía y leía la variable corrupta. Basta con abrir el Memory Browser (Menú Window, Show view) y poner el nombre de la variable. Se le da click derecho y se agrega el watchpoint (Range = 40 porque pesa 40 bytes mi arreglo). Esto también se conoce como Hardware Breakpoint.



Si uno abre el archivo .map de la aplicación encontrará el tamaño del stack configurado así como el del heap, que en mi caso no necesito:

                0x00000400                HEAP_SIZE = DEFINED (__heap_size__)?__heap_size__:0x400
                0x00000400                STACK_SIZE = DEFINED (__stack_size__)?__stack_size__:0x400

0x400 = 1kB

Ese kB es mucho menos que lo que necesito.

Claro, pude haber declarado las variables como globales para evitar el problema... pero no habría aprendido cómo cambiar el stack en este IDE.

El tamaño de heap y stack se configuran en el archivo *.ld (linker directive) del proyecto:

HEAP_SIZE  = DEFINED(__heap_size__)  ? __heap_size__  : 0x00000400;
STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x00000400;

Cambiémoslos por algo más sensato:

HEAP_SIZE  = DEFINED(__heap_size__)  ? __heap_size__  : 0x00000000;
STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x00005400;

También agregué el siguiente argumento para obtener el stack por función. GCC lo reporta en un archivo .su (stack usage) por cada .c:

-fstack-usage



fft_app.c:128:6:FFT_GetFrequencyResponse 20528 static

Compilé, pero empecé a invadir la sección .bss:

arm-none-eabi-gcc -o "s32k1_st7735.elf" "@s32k1_st7735.args" 
d:/s32k1/s32ds/s32ds/build_tools/gcc_v6.3/gcc-6.3-arm32-eabi/bin/../lib/gcc/arm-none-eabi/6.3.1/../../../../arm-none-eabi/bin/real-ld.exe: region m_data_2 overflowed with stack and heap
d:/s32k1/s32ds/s32ds/build_tools/gcc_v6.3/gcc-6.3-arm32-eabi/bin/../lib/gcc/arm-none-eabi/6.3.1/../../../../arm-none-eabi/bin/real-ld.exe: section .stack VMA [20001c00,20006fff] overlaps section .bss VMA [20000000,200030e7]

El stack crece hacia abajo.

Por lo que revisé cómo es que la RAM del S32K144 está distribuida:

0x14000000 - 0x14000FFF - FlexRAM (4kB)
0x1FFF8000 - 0x1FFFFFFF - SRAM L (32kB)
0x20000000 - 0x20006FFF - SRAM U (28kB)

La sección bss estaba ubicada en SRAM U:

/* Specify the memory areas */
MEMORY
{
  /* Flash */
  m_interrupts          (RX)  : ORIGIN = 0x00000000, LENGTH = 0x00000400
  m_flash_config        (RX)  : ORIGIN = 0x00000400, LENGTH = 0x00000010
  m_text                (RX)  : ORIGIN = 0x00000410, LENGTH = 0x0007FBF0

  /* SRAM_L */
  m_data                (RW)  : ORIGIN = 0x1FFF8000, LENGTH = 0x00008000

  /* SRAM_U */
  m_data_2              (RW)  : ORIGIN = 0x20000000, LENGTH = 0x00007000
}

  /* Uninitialized data section. */
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss section. */
    . = ALIGN(4);
    __BSS_START = .;
    __bss_start__ = .;
    *(.bss)
    *(.bss*)
    *(COMMON)
    . = ALIGN(4);
    __bss_end__ = .;
    __BSS_END = .;
  } > m_data_2

Que estaba siendo invadida por el stack. Por lo tanto reubiqué la sección bss en m_data, donde hay suficiente espacio:

  /* Uninitialized data section. */
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss section. */
    . = ALIGN(4);
    __BSS_START = .;
    __bss_start__ = .;
    *(.bss)
    *(.bss*)
    *(COMMON)
    . = ALIGN(4);
    __bss_end__ = .;
    __BSS_END = .;
  } > m_data

Esta vez sí pasó el proceso de linking.

.bss
                0x1fff86a0                __bss_start__ = .
                0x1fffb788                __bss_end__ = .

Line 3144:                 0x20001c00                __stack_start__ = .
Line 3147:                 0x20007000                __stack_end__ = .

bss y stack ya están ubicadas donde se espera.



No hay comentarios.: