keskiviikko 29. heinäkuuta 2015

Pay attention to your linker scripts!


If you are like me with GCC, your project most likely is based on some example that you found somewhere in the interned, with everything including makefiles, system sources and some mysterious files you haven't checked out included.

All models of STM32F4 have total of 256kB of memory included; 192kB of this is "normal" bus-accessible (S)RAM and 64kB is special Core Coupled Memory (CCM) that is not bus-accessible. For now you only need to know that if peripheral needs to access memory, you can't use CCM for that data, but you can use it for data that is accessed by core alone (i.e. your software), and specifically if you want that part of program to be as fast as possible (as access to CCM has kind of priority).

Anyway, back to the story. So there I am, happily writing code and consuming available SRAM of the MCU until suddenly the project started crashing. It didn't take me long to figure out that the reason was stack corruption; program never returned from a certain function, which just happened to be one that went through few fairly big chunks of data. And I just done some major rewriting on that function.

I then spent some time going through that function, only to find out that there were no overflows there - program stayed strictly withing data bounds.

At this point I remembered linker scripts GCC uses to actually place all code, data and other pieces into the final binary. And so there, linker script I used was originally for some lesser chip if I remember correctly, with just 128kB of SRAM included. As the data used my program grew towards this, the extra space allocated to stack (typically placed at the top of memory, in this case starting at 128kB and "growing" down) eventually ran out when it and data (starting at zero and growing up) overlapped. Causing data to overwrite stack and to cause crash.

As you can guess, this wasn't first time I have dealt with this issue, and I have no reason to expect this to be last time either, which is why it didn't take longer to track out this problem.

So you might want to check your linker scripts too, especially if you are using example I linked in earlier post. File is stm32_flash.ld and beginning looks somewhat like this:

/* Entry Point */
ENTRY(Reset_Handler)

/* Highest address of the user mode stack */
_estack = 0x20020000;    /* end of 128K RAM on AHB bus*/

/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0;      /* required amount of heap  */
_Min_Stack_Size = 0x400; /* required amount of stack */

/* Specify the memory areas */
MEMORY
{
  FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 1024K
  RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 192K
  MEMORY_B1 (rx)  : ORIGIN = 0x60000000, LENGTH = 0K
}

_estack is is the problem; it sets stack pointer to 128kB. With STM32F4 you want it to be set at 0x20030000, i.e. 192kB so you have (almost) everything below for you program to use as it wishes (and just admit it, if you use LCD_TFT without external memory, it all is gone in a flash).
And although STM32F4 has 256kB of RAM, only 192kB is to be listed on RAM row here as general purpose memory; 64kB should appear as its own row as CCM. This script doesn't include 64kB of CCM memory but that is topic for another time.

And mandatory disclaimer; there is loads of magic going on in linker files I still haven't figured out so when I discuss this, so there is bound to be errors. All I can say as an excuse is that it seems to work for me -- for now.





Ei kommentteja:

Lähetä kommentti