如何确定嵌入式软件所需最小的栈空间大小
为什么嵌入式软件系统很难评估出所需栈的实际大小?
栈的原理
如上图:栈是一块内存区域主要有如下功能,增长方向是向下生长
1.保存局部变量
2.保存返回地址(函数调用深度越大,栈的开销越大)
3.函数参数,一般情况下函数的参数是R0-R3(cortex M0),如果函数的参数大于4个一般就需要通过栈进行辅助参数,所以函数一般不建议编写大于4个参数,如果大于4个参数的函数一定要完成一个比较大的功能
4.中断的上下文切换,中断的时候会通过栈保存现场
5.其它特殊情况
本文不在这详细阐述栈的原理,主要探讨如何判断一个嵌入式软件所需最小的栈的大小。在讨论这个问题之前,我们先看看为什么嵌入式软件系统很难评估出所需栈的实际大小?
为什么嵌入式软件系统很难评估出所需栈的实际大小
1. 难找到最大深度的函数
原则上一些工具好像能通过调用关系能够分析出那个调用深度最深的函数,但是函数指针的使用使得函数最大深度的计算变的更加困难,甚至有些程序完全是2个不同的工程,工程编译的代码通过函数指针去直接跳转。所以很难有一个比较好的分析方法和工具去计算这个最大深度是多少。
2.可抢占的中断让这个问题变得更加复杂
由于中断可以发生在任意时刻,且大多数嵌入式芯片都支持中断抢占,那么最大深度变的更加扑簌迷离。
如上图最大的深度肯定在中断E,由于中断发生实际受事件和场景驱动,没法计算那个点的会产生最大的深度。
综合1和2点我们很难有个计算或者分析的方式评估出栈的大小。那么我们设置栈的大小呢?
如何设置栈的大小
没有中断驱动的情况,且很容易找到最大深度的函数
如上图,栈是向下增长的,代码开始计算栈顶的位置,代码最深的地方计算最低的位置,然后代码结束的时候打印一些栈的使用情况。
注意上图只是分析一个思路:正常的嵌入式代码的main函数肯定不会结束的,我们既可以在最深的地方打印栈的使用情况,也可以在main函数轮询的时候打印,当然为了不频繁打印可以触发某个深度打印一下。
有中断驱动的情况,且很难找到最大深度的函数一
如上图我们打开一个定时器中断,且优先级设为最高,典型的中断频率在10-250 kHz。我们很容易就能找到最低的位置。
- 为什么可以这样做?因为我们定时的频率足够快一定可以抢占在最深的时候函数情况,抢占它。所以一定要保证中断优先解最高,且速度足够快。
- 测试的时候一定要保证系统软件跑到最够多的场景和最够长的时间。
有中断驱动的情况,且很难找到最大深度的函数二
第一个方法虽然非常好且很大部分的嵌入式软件系统都能够部署(确定好了栈的大小后就可以去掉这个功能,减轻中断的负载)。但是很多嵌入式软件可能会被这个定时中断破坏了工作,特别是通过IO模拟通信实现的时候会被打断等。如果第一个方法真的很难实现的话,还有一个比较好的方式。
如上图我们在栈和全局变量之间填充一大块守卫区域,这个区域填充一些特殊的pattern。比如0xcd。程序运行后我们在空闲时候检查这个区域是否发生变化。发生改变就减少guard区域的大小,不发生变化就增大区域。不断实验测出来实际的栈的大小。
注意:一般栈的大小设置不够系统运行起来会出现hardfault,这不意味着栈异常一定会产生hardfault,通过不断设着栈大小,观察是否出现hardfault的方法是不太严密的
[1]: Mastering stack and heap for system reliability