开发人员喜欢相信运行他们软件的微处理器会忠实地遵循他们创建的程序流程,并按照预期无限期地运行。事实是,有时事情会出错,程序会被忽略。出现这种情况有许多可能的原因,例如覆盖了数组的边界,在初始化之前使用了指针,或者许多其他可能的原因。当发生这种情况时,嵌入式开发人员恢复系统的最有用的技术之一是用可以捕获故障的东西来填充未使用的内存。
有多种可能性可以填充未使用的内存。第一个是重置向量位置。如果事情出了差错,CPU将加载重置向量,程序将从头开始。这将更像是应用程序的一种软的、不受控制的终止,几乎没有关于原因的调试信息。硬件将处于不确定状态,无法保证系统能从复位状态中正常恢复。
第二种可能更合适的行为是将一个已知的中断向量放在空内存中。在这种情况下,当事情变得非常糟糕时,应用程序有机会捕获事件并提供一些线索,如CPU寄存器状态,以了解发生了什么。这使得开发人员可以重现和调试原因,而不是盲目猜测。
如果对调试信息不感兴趣,可以使用的第三种选择是用暂停或转移到自身指令来填充存储器。这将具有停止微控制器运行的效果。这种技术的优点是防止微控制器失控并继续错误地执行代码。相反,一切都停止了,值得信赖的看门狗定时器(每个嵌入式开发人员都会启用,我希望如此)通过超时和执行系统硬复位来拯救我们。
最后一个不推荐的选择是用胡言乱语填充内存。选择一个位模式,如0x55、0xAA、0xAA55,并期待最好的结果。在这种情况下,填充存储器实际上并不是为了提高应用程序的健壮性,而是为了提供除0xFF之外的已知值,以便执行ROM校验和。在网上简单搜索一下就发现了一些巧妙的位模式比如0xDEADC0DE,0xDEADBEEF等等。
现在有许多不同的方法来用特定的位模式填充内存。它们主要属于两个不同的类别;基于IDE或链接器。在IDE实现中,隐藏在属性菜单深处的是一个“填充内存”的选项。根据IDE和工具链的不同,可能会有额外的选项来设置特定的模式,或者工具可以自行决定。当选择“填充内存”选项时,大多数IDE工具要求开发人员选择将要填充的内存范围。这反过来又迫使嵌入式开发人员不断地监视应用程序在。链接器的文本部分。
基于链接器的方法为开发人员提供了更加灵活地定制填充解决方案的能力。例如,使用GNU工具链,可以创建一个包含FILL()链接器命令的新部分输出。向该命令传递应该用于填充存储器的位模式。从这个内存段中,可以使用原点和长度函数来获得将要被填充的内存段的大小。允许用位模式自动填充存储空间,而不需要开发者管理。以下是可以找到一个带有名为m_text的. text部分的GNU链接器的填充部分的例子。
.fillsection :
{
FILL(0xAA55AA55);
. = ORIGIN(m_text) + LENGTH(m_text) – 1;
BYTE(0xAA)
} > m_text
空内存的结果是,在查看编译期间生成的内存文件时,请求的位模式会填满内存:
S记录中的位模式
使用链接器文件的一个优点是,项目可以配置为在填充内存和不填充内存的链接器之间切换。使用内存填充的缺点之一是每个内存位置都被写入。这意味着,如果你试图调试应用程序,每次更改代码时,你都必须等待所有闪存写入,即使应用程序不会影响它。这可能会给实施阶段增加大量时间。因此,内存填充应该在开始时设置,并作为任何代码提交的一部分,但仍然提供为调试启用和禁用它的能力。请记住,开发人员希望尽可能多地使用这种内存填充来捕捉任何意外的行为。
用中断向量或停止命令正确地实现填充命令可以增加软件系统的完整性。如果出现错误指针、单事件扰乱或其他问题,导致应用程序从意外的存储位置开始执行代码,则填充位模式可以将程序执行引导回已知位置,在该位置,嵌入式开发人员进行适当的错误处理过程可以恢复系统。每个IDE和MCU实现该功能的方式略有不同,但通常不会比查看如何使用FILL命令的链接器数据表复杂多少。