9 STM32存储结构及堆栈空间梳理分析

芯片STM32F415
IDEIAR
系统FreeRTOS(庆科)

0 背景

由于代码本身基于就是庆科修改过的FreeRTOS,又加入了别的没有完全开源的库,再加上不是自己从头写的祖传代码,自己又经验不足,因此几个月来一直被这套代码折磨。其中尤为突出的一个问题就是程序复位问题。表现出来一般都是提示栈空间溢出,实际上这个提示粒度非常大,很难定位问题。
甚至发现,有些逻辑线程,只在刚开始启动时执行了一次,后来就再也没有被调用。这个就叫做程序会发生不可预知的问题。
为了彻底解决这个问题,用了一个星期的时间,重新学习梳理了一下内存、系统相关的知识。最后问题得到了良好的解决。
前两个是准备工作,后面是分析和梳理。

1 IAR的map文件

IAR可以生成一个map文件,在这个文件里包含了程序编译的情况,堆栈分析,函数入口地址,存储位置等内容。调试异常的bug时很好用。
在工程上右键,option-linker-list,勾上generate linker map file:


map

在advance选项卡里,勾上栈分析:


stack

编译程序之后,在工程中就会生成一个.map文件:
map

2 IAR的icf文件

在工程中搜索,会找到一个后缀为icf的文件,在这个文件里,配置了芯片的堆栈等信息:

/*###ICF### Section handled by ICF editor, don't touch! ****/
/*-Editor annotation file-*/
/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\cortex_v1_0.xml" */
/*-Specials-*/
define symbol __ICFEDIT_intvec_start__ = 0x08008000;                   /*中断向量表开始地址*/
/*-Memory Regions-*/
define symbol __ICFEDIT_region_ROM_start__ = 0x08008000;                   /*闪存起始地址*/
define symbol __ICFEDIT_region_ROM_end__   = 0x080FFFFF;                   /*闪存结束地址---flash大小1M*/
define symbol __ICFEDIT_region_RAM_start__ = 0x20000000;                   /*SRAM起始地址*/
define symbol __ICFEDIT_region_RAM_end__   = 0x2003FFFF;                   /*SRAM结束地址---SRAM大小256k*/

/*-Sizes-*/
define symbol __ICFEDIT_size_cstack__ = 0x300;                                                 /*栈大小*/
define symbol __ICFEDIT_size_heap__   = 0x20000;                   /*堆大小*/
/**** End of ICF editor section. ###ICF###*/

define memory mem with size = 4G;
define region ROM_region   = mem:[from __ICFEDIT_region_ROM_start__   to __ICFEDIT_region_ROM_end__];
define region RAM_region   = mem:[from __ICFEDIT_region_RAM_start__   to __ICFEDIT_region_RAM_end__];

define block CSTACK    with alignment = 8, size = __ICFEDIT_size_cstack__   { };
define block HEAP      with alignment = 8, size = __ICFEDIT_size_heap__     { };

initialize by copy { readwrite };
do not initialize  { section .noinit };

place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec };

place in ROM_region   { readonly };
place in RAM_region   { readwrite,
                        block CSTACK, block HEAP };

具体的含义写在了注释里,可以在IAR中修改:


icf

3 STM32 存储结构基本知识

3.1 cortex-M内核

这里只讨论存储结构,我这个芯片是cortex-M4内核,实际上cortex-M内核的存储结构是一样的。
32位的芯片,PC指针的寻址空间为2^32=4G。我觉得这个跟误传的所谓32位芯片只能用4G内存是两码事。
这个4G只是寻址空间为4G,分为8大块,分别为代码,SRAM,外设,外部RAM,外部设备,专用外设总线-内,专用外设总线-外,特定厂商。而我们俗称的内存,指的是SRAM。
下面这个是M3内核与STM32存储的对应关系:


cortex-M3

M3内核做了存储结构的设计,STM32以其设计为各个部分分配地址,这个“房间”是不一定要放满的,只要对于的东西在对应的房间就好了。
这里用的芯片可用flash大小是1M,就是从0x8000000到0x80fffff,但是icf里面写的是0x8008000到0x80fffff,前面的0x8000实际上分配给了BootLoader程序。
这个芯片的SRAM是256K,即从0x200000-0x2003ffff。

3.2 程序与存储

程序经过编译后,会形成以下几个部分:

  • .bss
    未初始化的全局变量或初始化为0的全局变量。未初始化的全局变量和初始化为0的全局变量(当然还有静态变量),都会被单独存放。这样的好处是节省编译后代码的空间,比如定义了100个变量都初始化为0或者没初始化,默认是0.在编译好的文件里,就不需要记录每个变量及它的值了,只需要记下来这些变量都是0就可以了。也就是说,凡是已经初始化了的全局变量,实际上在编译出的文件里,还要记录下它的初始化的值。
  • .text段
    代码段。在程序运行前,其大小以及确定,在存储器中的位置也是确定的。包括函数的入口地址都已经分配好了。
  • .data段
    数据段,已经初始化的全局变量。参考.bss段。
  • .rodata段
    rodata段就是read only data,即常量的存储区域。

上面这些编译好的部分,下载后会存储在芯片的flash中,也就是上面说的1M的ROM空间里。
程序运行的时候,局部变量会占用栈空间(由编译器生成的程序会将全局变量复制到RAM中,全局变量不占用栈空间,是单独分配的空间)。栈空间的大小就是前面设置的那个。
前面设置的栈空间和堆空间,使用的其实都是RAM空间,设置的栈空间会被全部初始化为0xcd,堆空间会被全部初始化为0xa5。程序就是通过判断0xcd和0xa5还剩余多少,来推测堆栈空间还剩多少的。所以基本上只是个参考意见。大多数情况下是对的。
,局部变量和函数调用占用栈空间,不过一般都不会占用很多。仿真时,可以打开IAR的View->Stack->Stack Window来监测栈空间还有多少。当栈提示溢出时,也不一定是真的溢出了。


stack

当需要定义一个很大的数组时,推荐使用内存分配的方式来做,这部分空间是从堆分配的。如果是加密使用的数组,只需要只读就可以了,建议定义为常量数组,这样它会存储在ROM中,比分配空间还要好。全局变量虽然不会占用栈空间,但是过多很大的全局变量也会引发问题,至少会占用很多RAM。
堆空间除了使用malloc分配使用以外,对于有操作系统的程序。比如FreeRTOS,它在建立线程时,分配的栈空间实际上是在堆中分配的,而且,线程中使用的局部变量,调用函数的局部变量,调用函数的栈开销,也是占用的分配的堆空间。反正都是RAM空间,一样的用。需要注意的是不会占用上面所说的全局分配的栈空间,所以有操作系统之后,原来的栈空间并不需要分得很大。

4 map和线程栈空间分配

可以通过map文件,来大概的确定线程的栈空间大小。
只要不是过多使用大量很大的临时变量的线程,map计算的栈空间一般都够用。为了保证内存对齐,以防发生奇怪的bug,线程的栈空间分配应该保持是8的倍数。
安全起见,可以多分64个字节的栈空间。
在map文件中找到STACK USAGE:


STACK USAGE

搜索对应的函数,就可以看到大概的栈空间值:


thread

在map文件的最后,还能看到总的空间使用统计:

  256 660 bytes of readonly  code memory
   23 842 bytes of readonly  data memory
  172 218 bytes of readwrite data memory

你可能感兴趣的:(9 STM32存储结构及堆栈空间梳理分析)