Азы распределения памяти и кода

Разбираемся как устроена память микроконтроллера на примере STM32F205VGT6.

Так же мы используем CubeMX для подготовки проекта.

Где хранятся данные , где стек, где код программы?

Генерим сначала пустой проект, оставляем только пустой main().

читаем ld файл

Linker script for STM32F205VG Device with ** 1024KByte FLASH, 128KByte RAM.

ld файл это основа, здесь CubeMx прописывает все исходные параметры памяти, стека, кода. Именно его надо спокойно/внимательно изучить, здесь все очевидно на самом деле.


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

Стек в микроконтроллерах на ядрах ARM растёт сверху вниз.

в ld файле:

_estack = 0x20020000;    /* end of RAM */   //  0x20000=131072

RAM по даташиту Up to 128 4 Kbytes of SRAM. То есть есть еще какие-то 4Kb SRAM в конце...

Стек располагается отдельно от остальных блоков памяти, в конце ОЗУ. Конец ОЗУ по мнению CubeMX это 0x20020000=..131072 примерно (128 4Kb).

Для контроля минимальных запасов стека и кучи в CubeMX есть такие установки

в ld файле:


_Min_Heap_Size = 0x200;      /* required amount of heap  */
_Min_Stack_Size = 0x400; /* required amount of stack */

(_user_heap_stack = _Min_Heap_Size _Min_Stack_Size )

list полезный по информации файл на выходе сборки

.text — /* The program code and other data goes into FLASH */ , это скомпилированный машинный код - помещается во FLASH;
.data — /* Initialized data sections goes into RAM, load LMA copy after code */ - Переменные, это помещается в RAM; и зачем-то копия помещается еще во FLASH.
.rodata — /* Constant data goes into FLASH */ - аналог .data для неизменяемых данных, но помещается во FLASH;
.bss — /* Uninitialized data section */ , глобальные и статические переменные, которые при старте содержат нулевое значение - помещаются в RAM.

Момент истины

FLASH может содержать только неизменяемые данные , т.е. их нельзя в процессе исполнения перезаписать. (FLASH (rx) ) rx = read execute . Отсюда и идет весь этот винигред.

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .isr_vector   00000184  08000000  08000000  00010000  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .text         0000015c  08000184  08000184  00010184  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .rodata       00000000  080002e0  080002e8  000102e8  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  3 .init_array   00000004  080002e0  080002e0  000102e0  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  4 .fini_array   00000004  080002e4  080002e4  000102e4  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  5 .data         00000000  20000000  20000000  000102e8  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  6 .bss          00000020  20000000  080002e8  00020000  2**2
                  ALLOC
  7 ._user_heap_stack 00000600  20000020  080002e8  00020020  2**0
                  ALLOC

ld файл прописывает где и куда какие данные сохранить

Все очевидно :

  .bss :
  {
    /* This is used by the startup in order to initialize the .bss secion */
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)

    . = ALIGN(4);
    _ebss = .;         /* define a global symbol at bss end */
    __bss_end__ = _ebss;
  } >RAM

>RAM - это помещается в RAM !

И так далее...

файл startup_stm32f205xx.s

Этот файл генерируется CubeMX. Он на ассемблере.

Смысл его в том, чтобы начать выполнение программы: для этого надо сначала установить указатель стека на начало стека :

Reset_Handler:  
  ldr   sp, =_estack     /* set stack pointer */

Теперь для закрепления понимания поэкспериментируем

добавим массив :

char buf[10000];
main()
{..

эффекта изменения распределения памяти не произойдет

сделаем так : char buf[10000]={0}; - ничего не меняется

теперь в main сделаем так :


for(int i=0; i < sizeof(buf); i  )
	buf[i]=1;

и .bss увеличивается с 00000020 до 00002730 (10032!) . То есть ушло в RAM!

теперь делаем так :


const char buf[10000]={0};
main
{
char ch=0;
for(int i=0; i < sizeof(buf); i  )
	char ch=buf[i];
printf("%c",ch);

И .rodata увеличивается с 10 до 00002784 (10116) ! То есть ушло во FLASH (кстати только при условии явной инициализации={0}).

Все логично !

Выводы такие :

Побольше const

Если RAM памяти не хватает или не хватает стека (он всегда есть часть RAM), то можно попробовать все что не меняется обозвать const , инициализировать обязательно и тогда это попадет не в RAM , а в FLASH.

section `._user_heap_stack' will not fit in region `RAM'

Вот такая бяка появляется, когда RAM не хватает, например добавили нового кода , сторонние библиотеки ....

У меня (на самом деле) эта ошибки ушла после удаления файла startupxxx.s и перегенерации заново проекта(и файла startup) из CubeMx.

Файлы для скачивания

* STM32F205VGT6_MemoryUsed [zip]
пустышка для изучения распределения памяти в контроллере STM32