
对于为嵌入式系统应用程序设计的操作系统,错误处理不是最常见的事情。 这是资源有限的必然结果,因为所有嵌入式系统都有一定的限制。 而且,只有少数此类系统具有类似于台式机系统的性能,也就是说,为用户提供了在发生特殊事件时选择操作的能力。
通常,在Nucleus SE中,存在三种类型的错误检查:
- 验证所选配置的运行状况以确保所选参数不会导致错误的方法;
- 用于检查运行时行为的可选代码;
- 某些API函数有助于开发更强大的代码。
本文将讨论所有这些内容,以及有关用户诊断的一些想法。
验证设置
Nucleus SE的设计重点是高用户可配置性,这应确保最佳利用可用资源。 这种可配置性是一项复杂的任务,因为可能的参数数量以及它们之间的相互依赖性很大。 如之前的许多文章中所述,大多数用户配置Nucleus SE的步骤都是使用
nuse_config.h文件中的
#define指令执行的。
为了帮助识别配置错误,文件
nuse_config.c至
#include包括文件
nuse_config_check.h ,该文件对
#define指令执行完整性检查。 以下是此文件的摘要:
/*** Tasks and task control ***/ #if NUSE_TASK_NUMBER < 1 || NUSE_TASK_NUMBER > 16 #error NUSE: invalid number of tasks - must be 1-16 #endif #if NUSE_TASK_RELINQUISH && (NUSE_SCHEDULER_TYPE == NUSE_PRIORITY_SCHEDULER) #error NUSE: NUSE_Task_Relinquish() selected - not valid with priority scheduler #endif #if NUSE_TASK_RESUME && !NUSE_SUSPEND_ENABLE #error NUSE: NUSE_Task_Resume() selected - task suspend not enabled #endif #if NUSE_TASK_SUSPEND && !NUSE_SUSPEND_ENABLE #error NUSE: NUSE_Task_Suspend() selected - task suspend not enabled #endif #if NUSE_INITIAL_TASK_STATE_SUPPORT && !NUSE_SUSPEND_ENABLE #error NUSE: Initial task state enabled - task suspend not enabled #endif /*** Partition pools ***/ #if NUSE_PARTITION_POOL_NUMBER > 16 #error NUSE: invalid number of partition pools - must be 0-16 #endif #if NUSE_PARTITION_POOL_NUMBER == 0 #if NUSE_PARTITION_ALLOCATE #error NUSE: NUSE_Partition_Allocate() enabled – no partition pools configured #endif #if NUSE_PARTITION_DEALLOCATE #error NUSE: NUSE_Partition_Deallocate() enabled – no partition pools configured #endif #if NUSE_PARTITION_POOL_INFORMATION #error NUSE: NUSE_Partition_Pool_Information() enabled – no partition pools configured #endif #endif
上面的代码执行以下检查:
- 检查是否配置了至少一项但不超过十六项任务;
- 确认所选的API函数与所选的调度程序和其他指定参数兼容;
- 验证所创建的其他内核对象的实例不超过16个;
- 尚未选择与未声明对象相关的API函数的确认。
- 确保停用这些服务的支持时不使用信号和系统时间的API函数;
- 验证所选类型的调度程序和相关参数。
在所有情况下,检测到错误都会导致在编译时执行
#error指令。 这通常会导致编译停止并显示相应的消息。
该文件不能保证不可能创建不正确的配置,但是非常不可能。
检查API设置
与Nucleus RTOS一样,Nucleus SE能够包含代码以在运行时检查调用API函数的参数。 通常,这仅在初始调试和测试期间使用,因为在最终程序代码中不希望过多的内存消耗。
通过将
nuse_config.h文件中的
NUSE_API_PARAMETER_CHECKING参数设置为
TRUE可以激活参数检查。 这将导致所需的附加代码的编译。 以下是检查API函数参数的示例:
STATUS NUSE_Mailbox_Send(NUSE_MAILBOX mailbox, ADDR *message, U8 suspend) { STATUS return_value; #if NUSE_API_PARAMETER_CHECKING if (mailbox >= NUSE_MAILBOX_NUMBER) { return NUSE_INVALID_MAILBOX; } if (message == NULL) { return NUSE_INVALID_POINTER; } #if NUSE_BLOCKING_ENABLE if ((suspend != NUSE_NO_SUSPEND) && (suspend != NUSE_SUSPEND)) { return NUSE_INVALID_SUSPEND; } #else if (suspend != NUSE_NO_SUSPEND) { return NUSE_INVALID_SUSPEND; } #endif #endif
对参数的这种检查可能导致API调用将输出错误代码的事实。 它是
NUSE_INVALID_xxx形式的
负值 (例如
NUSE_INVALID_POINTER )-文件
nuse_codes.h中包含完整的定义集。
要处理错误值,可以添加其他应用程序代码(可能使用条件编译创建),但是要检测到它们,最好使用现代固件调试器的数据监视工具。
检查参数会导致额外的内存消耗(附加代码)并影响代码的性能,因此,其使用将影响整个系统。 由于所有Nucleus SE源代码都可供开发人员使用,因此如果需要绝对准确性,则可以在最终应用程序代码上手动进行验证和调试。
检查任务堆栈
在使用“运行到完成计划程序”之前,Nucleus SE可以检查任务堆栈,这类似于Nucleus RTOS中的类似功能,并显示了堆栈上的剩余空间。 在
上一篇文章 (#12)中详细描述了该API实用程序调用(
NUSE_Task_Check_Stack() )。 本文稍后在“自定义诊断”部分中提供了一些检查堆栈错误的想法。
版本信息
Nucleus RTOS和Nucleus SE具有一个API函数,该函数仅返回内核版本/构建信息。
Nucleus RTOS API调用
服务电话原型:
CHAR * NU_Release_Information(VOID);
参数:无。
返回值:
指向包含以空字节结尾的版本信息的字符串的指针。
Nucleus SE API调用
该API调用支持Nucleus RTOS API的核心功能。
服务电话原型:
字符* NUSE_Release_Information(void);
参数:无。
返回值:
指向包含以空字节结尾的版本信息的字符串的指针。
致电获取Nucleus SE装配信息
实现此API调用非常简单。 指针返回到常量行
NUSE_Release_Info ,该常量在文件
nuse_globals.c中声明和初始化。
该行的形式为Nucleus SE-
Xyymmmdd ,其中:
X-生成状态:
A = alpha;
B = beta;
R =释放
yy-发布年份
mm-发布月
dd-发布日
兼容Nucleus RTOS
Nucleus RTOS包含可选的历史杂志支持。 内核记录各种系统操作的详细信息。 有API函数允许程序执行以下操作:
- 激活/停用日志记录;
- 创建日记帐分录;
- 获取日记条目。
Nucleus SE不支持此功能。
Nucleus RTOS还具有多个错误管理宏,这些宏可让您不执行任何错误确认(ASSERT),并提供调用用户定义的严重错误功能的功能。 它们可选地包含在OS组件中。 Nucleus SE不支持此功能。
用户诊断
到目前为止,在本文中,我们已经研究了Nucleus SE本身提供的诊断和错误检查工具。 现在值得一说的是,如何使用内核提供的工具和/或我们对其内部结构和实现的了解来实现用户设置或针对特定应用程序的诊断工具
特定于应用程序的诊断
在几乎每个应用程序中,您都可以添加其他代码以在运行时验证其完整性。 多任务处理核心使创建这项任务的特殊任务变得容易和简单。 显然,在本文中,我们将不会考虑过分异常的诊断情况,而是会考虑一些一般性想法。
内存检查
显然,正确的存储器操作对于任何处理器系统的完整性都是至关重要的。 显而易见的是,严重错误将不允许您运行,不仅是诊断程序,而是整个软件产品(
译者注:顺便说一句,正是我们在“ Fake Blue Pill”一文中研究的情况 )。 但是,在某些情况下会出现某些错误,这是一个令人担忧的严重原因,但不会干扰代码执行。 内存测试是一个相当复杂的主题,不在本文的讨论范围之内,因此我仅给出一些一般性的想法。
RAM中发生的两个最常见的错误是:
- 当该位的值为0或1时,即为“固定位”,无法更改;
- 当相邻位引起相互干扰时,即为“串扰”。
可以通过依次向每个RAM区域写入和读取某些测试模式来检查这两个错误。 某些检查只能在启动时执行,即使在堆栈形成之前也是如此(
译者注:在上述文章中,原来是堆栈的第一次使用立即破坏了所有内容 )。 例如,“运行单元”检查,其中为存储器的每个位分配一个值,其他所有位则检查以确保它们等于零。 如果在RAM区域损坏的情况下不会发生上下文切换,则可以在操作期间执行其他按位测试模式。 使用
Nucleus SE临界区限制宏
NUSE_CS_Enter()和
NUSE_CS_Exit()非常简单且可扩展。
不同类型的ROM也容易出现周期性错误,但是检查它们的工具并不多。 汇编代码后计算出的校验和在这里可能很有用。 该检查可以在引导时执行,也可以在运行时执行。
存储器寻址逻辑中的错误可能会影响ROM和RAM。 您可以对此错误进行特殊检查,但是很可能将其检测为上述检查的一部分。
测试外围设备
除了CPU外,外围电路也容易出错。 当然,系统之间的差异很大,但是,在大多数设备中,有多种使用诊断软件检查其完整性的方法。 例如,通信信道可以具有回送验证模式,其中,进入该信道的任何数据都将立即返回。
看门狗服务
嵌入式开发人员经常使用看门狗。 这是一个外围设备,它要么中断CPU并等待响应,要么(最好是)要求定期从软件进行访问。 在这两种情况下,看门狗定时器的常见结果都是系统复位。
在多任务环境中有效使用看门狗是一个复杂的问题。 如果您创建了一个定期访问它的任务(看门狗计时器),它将确认此特定任务正在运行。 一种可能的解决方案是实施“调度程序任务”。 下面将给出此类任务的示例。
堆栈溢出检查
如果您不使用“运行完成计划”,则会为Nucleus SE应用程序中的每个任务创建一个堆栈。 这些堆栈的完整性非常重要,但是RAM的数量可能会受到限制,因此,使应用程序大小达到最佳很重要。 可以静态地预测每个任务的堆栈需求,但是非常困难。 堆栈应该足够大,即使是最嵌套的函数,以及最苛刻的中断处理程序。 解决此问题的更简单方法是使用详尽的运行时测试。
一般来说,堆栈验证有两种方法。 如果使用复杂的嵌入式软件调试器,则可以监视堆栈的边界,并且将检测到所有违规情况。 Nucleus SE堆栈的
位置和大小在全局ROM数据结构中可用:
NUSE_Task_Stack_Base []和
NUSE_Task_Stack_Size [] 。
一种替代方法是运行时测试。 一种常见的方法是在每个堆栈的末尾使用“保护字”,通常是堆栈数据每个区域的第一个元素。 这些字用公认的非零值初始化。 然后,服务/诊断任务将检查这些单词是否已更改,并执行适当的操作。 混用安全字并不意味着堆栈已满,而是表示这将要发生。 因此,该软件可能会继续运行,您可能需要采取纠正措施或向用户报告错误。
主管任务
尽管Nucleus SE并未根据自身需求保留十六种可能的任务中的任何一项,但用户仍可以选择一项任务进行诊断。 这可以是仅使用任何“空闲”处理器时间的低优先级任务,也可以是需要短时间周期定期运行的高优先级任务,以确保定期执行诊断。
下面是一个类似的任务可能如何工作的示例。
调度程序任务的信号标志用于跟踪六个关键系统任务的操作。 这些任务中的每一个都使用一个特定的标志(从位0到位5),并应定期进行设置。 调度程序任务将重置所有标志,然后将其工作暂停一段时间。 当她恢复工作时,她希望通过设置适当的标志来“检查”所有六个任务,然后寻找与
b00111111值完全匹配的(从文件
nuse_binary.h中 )。 如果一切都符合要求,它将重置标志并再次暂停。 如果没有,它将调用严重错误处理例程,该例程进而可以例如重新引导系统。
在替代实施方式中,可以使用事件标志组。 如果信号没有在应用程序的其他地方使用(这将导致所有任务过度使用RAM),尤其是如果事件标志用于其他目的,则这是有意义的。
跟踪和分析
尽管事实上许多现代嵌入式软件调试器都具有高度的定制能力,并且可以与RTOS一起使用,但是调试多线程应用程序仍然很困难。 执行后分析是一种广泛使用的方法,其中实施了代码(RTOS),以便可以回顾其工作的详细审核。 通常,此类服务的实现包括两个组件:
- 附加代码已添加到RTOS以记录操作。 通常,它将包装在预处理器指令中以使用条件编译。 发生重要事件时(例如,调用API函数或切换上下文),此代码记录了几个字节的信息。 这些信息可能包括:
- 当前地址(PC);
- 当前任务的ID(索引);
- 其他使用过的物体的索引;
- 与执行的操作相对应的代码。
- 为将配置文件信息缓冲区卸载到外部存储(通常是主机)而分配的任务。
对这样获得的数据进行分析也需要一些工作,但是它并不比使用常规的Excel电子表格复杂。
在下一篇文章中,我们将详细检查Nucleus SE和Nucleus RTOS的兼容性。
关于作者: Colin Walls在电子行业工作了30多年,大部分时间用于固件。 他现在是Mentor Embedded(Mentor Graphics的一个部门)的固件工程师。 Colin Walls经常在会议和研讨会上发表演讲,他撰写了许多技术文章并撰写了两本有关固件的书。 居住在英国。
Colin的专业
博客 ,电子邮件:colin_walls@mentor.com。