嵌入式问题分析思路

问题分析思路

BUG解决大致的思路:

  1. 定位 - 缩小范围,关注最近的变化,相关人员分析讨论,核实细节。
  2. 解决、 验证 - 修改不应引入其他问题,问题相关全面验证。

建议:

  1. 错误、异常及时处理。
  2. 使用前检查有效性。
  3. 注意结构、过程的使用场景。
  4. 关键节点的进行输入输出单元测试。
  5. 关注事件时序,事件以不同的顺序达到,连续多次同一个事件,没有事件,事件产生的条件等情况。

问题复现

稳定复现问题才能正确的对问题进行定位、解决以及验证。一般来说,越容易复现的问题越容易解决。

  1. 模拟复现条件
    有的问题存在于特定的条件下,只需要模拟出现问题的条件即可复现。对于依赖外部输入的条件,如果条件比较复杂难以模拟可以考虑程序里预设直接进入对应状态。

  2. 提高相关任务执行频率
    例如某个任务长时间运行才出现异常则可以提高该任务的执行频率。

  3. 增大测试样本量
    程序长时间运行后出现异常,问题难以复现,可以搭建测试环境多套设备同时进行测试。

问题定位

缩小排查范围,确认引入问题的任务、函数、语句。

  1. LOG分析
    根据问题的现象,在抱有疑问的代码处增加LOG输出,以此来追踪程序执行流程以及关键变量的值,观察是否与预期相符。

  2. 在线调试
    在线调试可以起到和打印LOG类似的作用,另外此方法特别适合排查程序崩溃类的BUG,当程序陷入异常中断(HardFault,看门狗中断等)的时候可以直接STOP查看call stack以及内核寄存器的值,快速定位问题点。

  3. 版本回退
    使用版本管理工具时可以通过不断回退版本并测试验证来定位首次引入该问题的版本,之后可以围绕该版本增改的代码进行排查。

  4. 二分注释
    “二分注释”即以类似二分查找法的方式注释掉部分代码,以此判断问题是否由注释掉的这部分代码引起。具体方法为将与问题不相干的部分代码注释掉一半,看问题是否解决,未解决则注释另一半,如果解决则继续将注释范围缩小一半,以此类推逐渐缩小问题的范围。

  5. 保存内核寄存器快照
    Cortex M内核陷入异常中断时会将几个内核寄存器的值压入栈中,如下图:
    在这里插入图片描述
    我们可以在陷入异常中断时将栈上的内核寄存器值写入RAM的一段复位后保留默认值的区域内,执行复位操作后再从RAM将该信息读出并分析,通过PC、LR确认当时执行的函数,通过R0-R3分析当时处理的变量是否异常,通过SP分析是否可能出现栈溢出等。

问题分析处理

结合问题现象以及定位的问题代码位置分析造成问题的原因。

程序仍能运行

1. 数值异常
  1. 软件问题

    • 数组越界
      写数组时下标超出数组长度,导致对应地址内容被修改。
      此类问题通常需要结合map文件进行分析,通过map文件观察被篡改变量地址附近的数组,查看对该数组的写入操作是否存在如上图所示不安全的代码,将其修改为安全的代码。

    • 栈溢出
      在这里插入图片描述
      如上,此类问题也需要结合map文件进行分析。假设栈从高地址往低地址增长,如果发生栈溢出,则g_val的值会被栈上的值覆盖。出现栈溢出时要分析栈的最大使用情况,函数调用层数过多,中断服务函数内进行函数调用,函数内部申明了较大的临时变量等都有可能导致栈溢出。

      解决此类问题有以下方法:

      1. 在设计阶段应该合理分配内存资源,为栈设置合适的大小;
      2. 将函数内较大的临时变量加”static”关键字转化为静态变量,或者使用malloc()动态分配,将其放到堆上;
      3. 改变函数调用方式,降低调用层数。
    • 判断语句条件写错
      判断语句的条件容易把相等运算符“==”写成赋值运算符“=”导致被判断的变量值被更改,该类错误编译期不会报错且总是返回真。建议将要判断的变量写到运算符的右边,这样错写为赋值运算符时会在编译期报错。还可以使用一些静态代码检查工具来发现此类问题。

    • 同步问题
      例如操作队列时,出队操作执行的过程中发生中断(任务切换),并且在中断(切换后的任务)中执行入队操作则可能破坏队列结构,对于这类情况应该操作时关中断(使用互斥锁同步)。

    • 优化问题
      编译器优化后可能会对某些标志变量进行优化,而不是实时获取标志变量,此时需要给标志变量前加“volatile”关键字

  2. 硬件问题

    • 芯片BUG
      芯片本身存在BUG,在某些特定情况下给单片机返回一个错误的值,需要程序对读回的值进行判断,过滤异常值。

    • 通信时序错误
      例如电源管理芯片Isl78600,假设现在两片级联,当同时读取两片的电压采样数据时,高端芯片会以固定周期通过菊花链将数据传送到低端芯片,而低端芯片上只有一个缓存区,如果单片机不在规定时间内将低端芯片上的数据读走那么新的数据到来时将会覆盖当前数据,导致数据丢失。此类问题需要仔细分析芯片的数据手册,严格满足芯片通信的时序要求。

2. 动作异常
  1. 软件问题

    • 设计问题
      设计中存在错误或者疏漏,需要重新评审设计文档。

    • 实现与设计不符
      代码的实现与设计文档不相符需要增加单元测试覆盖所有条件分支,进行代码交叉review。

    • 状态变量异常
      例如记录状态机当前状态的变量被篡改,分析该类问题的方法同前文数值异常部分。

  2. 硬件问题

    • 硬件失效
      目标IC失效,接收控制指令后不动作,需要排查硬件。

    • 通信异常
      与目标IC通信错误,无法正确执行控制命令,需要使用示波器或逻辑分析仪去观察通信时序,分析是否发出的信号不对或者受到外部干扰。

程序崩溃

1. 停止运行
  1. 软件问题

    • HardFault
      以下情况会造成HardFault:

      1. 在外设时钟门未使能的情况下操作该外设的寄存器;
      2. 跳转函数地址越界,通常发生在函数指针被篡改,排查方法同数值异常;
      3. 解引用指针时出现对齐问题:

      以小端序为例,如果我们声明了一个强制对齐的结构体如下:
      在这里插入图片描述

      地址0x000000000x000000010x00000002
      变量名Val0Val1_lowVal1_high
      0x120x560x34

      此时a.val1的地址为0x00000001,如果以uint16_t类型去解引用此地址则会因为对齐问题进入HardFault,如果一定要用指针方式操作该变量则应当使用memcpy()。

    • 中断服务函数中未清除中断标志
      中断服务函数退出前不正确清除中断标志,当程序执行从中断服务函数内退出后又会立刻进入中断服务函数,表现出程序的“假死”现象。

    • NMI中断
      调试时曾遇到SPI的MISO引脚复用NMI功能,当通过SPI连接的外设损坏时MISO被拉高,导致单片机复位后在把NMI引脚配置成SPI功能之前就直接进入NMI中断,程序挂死在NMI中断中。这种情况可以在NMI的中断服务函数内禁用NMI功能来使其退出NMI中断。

  2. 硬件问题
    晶振未起振
    供电电压不足
    复位引脚拉低

2. 复位
  1. 软件问题

    • 看门狗复位

    • 除了喂狗超时导致的复位以外,还要注意看门狗配置的特殊要求,以Freescale KEA单片机为例,该单片机看门狗在配置时需要执行解锁序列(向其寄存器连续写入两个不同的值),该解锁序列必须在16个总线时钟内完成,超时则会引起看门狗复位。此类问题只能熟读单片机数据手册,注意类似的细节问题。

  2. 硬件问题

    • 供电电压不稳
    • 电源带载能力不足

回归测试

问题解决后需要进行回归测试,一方面确认问题是否不再复现,另一方面要确认修改不会引入其他问题。

经验总结

总结本次问题产生的原因及解决问题的方法,思考类似问题今后如何防范,对相同平台产品是否值得借鉴,做到举一反三,从失败中吸取经验。

RTOS应用中常见问题

  • 堆栈溢出
    在基于实时内核的应用中,每个任务都需要自己的堆栈。任务所需堆栈的大小取决于应用程序。如果堆栈大于任务要求,则会浪费内存。如果堆栈太小,堆栈可能溢出。我们可以通过分配更多内存来减少堆栈溢出的机会,通常需要25-50%的额外堆栈空间。一些CPU,比如基于ARMv8M架构的CPU,内置了堆栈溢出检测机制。然而,该特性并不能帮助确定合适的堆栈大小,它只是防止堆栈溢出的负面后果。

    通常不建议在嵌入式软件中进行动态内存分配,但有时会出于各种原因需要进行动态内存分配。问题在于,如果使用它,则必须确保一旦内存块不再使用时,释放每个已分配的内存块。如果在某些情况下遗漏了释放操作就会出现内存泄漏,并最终耗尽内存导致严重错误。

    堆栈分配时,首先为任务堆栈分配更多空间,然后在已知最坏情况下运行应用程序,监视实际堆栈使用情况。

    下图显示了μC/Probe对测试应用程序的μ/OS-III内核感知的截图。Stack Usage列显示每个任务在给定时间的最大堆栈使用情况。μC/Probe将更新并实时显示堆栈使用信息,无需停止目标应用。绿色表示最大堆栈使用量一直保持在70%。黄色表示堆栈使用量在70%到90%之间。红色表示堆栈使用量已超过90%。显然,使用92%堆栈的任务应该增大堆栈,使其回到70%以下。###
    在这里插入图片描述

  • 中断响应
    在临界代码处理时,RTOS和应用程序代码通常必须禁用中断。关中断会影响系统对事件的响应,RTOS应用中尽量减少中断禁用时间。

    μC/OS-III会监测每个任务最坏情况下的中断禁用时间,下图所示。如果应用需要满足实时截止时间,这些信息非常有用。

    中断被禁用的时间很大程度上取决于CPU、其时钟速率、应用程序和调用的RTOS服务。禁用中断最长的任务用红色高亮显示,帮助用户快速识别潜在的异常值。
    在这里插入图片描述

    如果最大中断禁用时间是由RTOS引起的,可以:
    🔸 查找并使用中断禁用时间较低的RTOS API。
    🔸 增加CPU的时钟速率。
    🔸 使用非内核感知中断来处理高度时间敏感的代码。

  • 优先级反转
    具有固定优先级调度程序的 RTOS 的核心思想是,应该在具有较低优先级的任务之前安排高优先级任务执行。但是当两个或多个任务需要协调使用全局数据区等共享资源或外围设备时,可能会导致低优先级的任务先执行,优先级反转的问题就发生了。出现优先级反转的现象是系统可能暂时失去响应,导致随机崩溃。使用RTOS优先级继承机制,如互斥信号量,可以降低优先级反转带来的影响,但要完全消除需要在设计时避免。

    优先级反转发生在低优先级任务持有高优先级任务需要的资源时。当中等优先级的任务抢占占用资源的低优先级的任务时,问题就会加剧。“优先级反转”一词指的是,低优先级任务似乎比高优先级任务具有更高的优先权,至少在共享该资源时是如此。

    优先级反转是实时系统中的一个问题,当使用基于优先级的抢占式内核时会发生。如下图所示,SystemView展示了一个优先级反转场景。

    App HPT具有最高优先级,App MPT 具有中优先级,App LPT优先级最低。
    在这里插入图片描述
    可以使用RTOS的互斥量机制来解决上面描述的优先级反转问题。优先级反转被限定为LPT访问共享资源所需的时间,下图所示。LPT和HPT都使用互斥量而非信号量来获得对共享资源的访问权。如果没有SystemView这样的工具,优先级反转将很难识别和校正。
    在这里插入图片描述
    注意,如果LPT只是HPT的下一个优先级级别,则可以使用信号量。在这种情况下,RTOS不需要更改LPT的优先级,信号量是首选项,它比互斥信号量快。

  • 死锁
    任务突然停止执行,但当前并没有更高优先级任务在执行,这时可能就发生了死锁。当两个或多个应用程序任务阻塞在一起等待对方时,导致任务都无法继续进行,就会发生死锁。只能在设计时避免死锁的发生,例如所有的任务都按照固定的顺序使用共享资源,或者同一时间任务不持有多个共享资源,使用超时机制,以及服务器任务专门用于共享资源的访问等。

    死锁是至少两个任务相互等待另一个任务拥有的资源。死锁可能不会立即发生,它很大程度上取决于两个任务何时需要彼此的资源。如图8所示,μC/Probe的内核感知视图有一列,显示每个任务的执行频率(RTOS切换任务的频率)。通过监视此列可以检测死锁。如果至少两个任务的计数已停止(μC/Prboe在CPU运行时更新这些计数器),则可能存在死锁。对于这种情况,不需要使用μC/Probe等工具也可以判定锁定行为,该工具使它更加明显。
    在这里插入图片描述
    可以通过以下方法来避免死锁:

    1. 任务先获取所有必需的资源,以相同的顺序获取它们,以相反的顺序释放它们。
    2. 在RTOS API调用中使用超时机制,以避免永远等待资源可用。检查RTOS API返回的错误代码,以确保对所需资源的请求成功。
  • 线程饥饿
    在嵌入式多任务系统中,一些任务可能会执行缓慢,或者甚至得不到执行,常见的原因是由于优先级顺序设置不正确导致。高优先级的任务使用了太多的CPU时间,低优先级的任务可能就没有足够的时间执行,这就是所谓的线程饥饿。将受影响的任务的优先级提高,确实可以改善问题,但违背了使用优先级的意义。相反,高优先级应该保留给可预测、循环执行、且循环周期较短的任务。对于高优先级、CPU占用时间长的任务应该拆分为多个任务,缩小时间关键代码,通过任务同步机制,将占用CPU时间多的工作交给中或低优先级任务处理。
    饥饿指高优先级任务消耗了所有CPU带宽,低优先级任务没有CPU时间或很少。饥饿的影响是响应性和产品特性的下降,例如嵌入式目标的显示更新缓慢、通信堆栈中的数据包丢失、操作界面响应迟缓等。为解决饥饿问题,可以:

    1. 优化消耗大多数CPU带宽的代码。
    2. 提高CPU的时钟速度。
      在这里插入图片描述
  • 抖动
    周期性执行的任务,随机发生的延迟时间叫做抖动。虽然轻微的抖动很难避免,但是抖动太严重就会导致性能变差,间歇性的数据丢失。例如,每隔5ms调整电机的控制参数,如果控制任务的抖动过大就会使得控制性能就变差。除了线程饥饿会导致抖动之外,RTOS系统配置也会有影响,例如系统节拍定时器节拍频率。理想情况下,两个节拍之间的时间应该比系统中最频繁任务的周期时间短得多。

  • 总结
    IDE内置的调试器通常不足以调试基于RTOS的实时系统。

    幸运的是,有专门为调试基于RTOS的系统而设计的工具。其中一个工具是Segger的SystemView,它会按时间顺序显示ISR和任务的执行,收集运行时统计数据,如最小和最大执行时间、ISR与任务之间的关系、CPU负载等。

    另一个可以补充SystemView的工具是Micrium的μC/Probe,这是一个通用工具,它允许开发人员在不干扰CPU的情况下,可视化和改变正在运行的嵌入式目标的行为。μC/Probe可以用于裸机或基于RTOS的应用中。对于基于RTOS的应用程序,μC/Probe包括非侵入性的、实时内核感知以及TCP/IP协议栈感知功能。SystemView和μC/Probe可以在整个开发周期中使用,提供关于嵌入式目标的运行时行为的反馈。

RTOS调试手段

上面列举了多任务系统设计开发时比较典型的几种bug,它们都有一个特点,就是通过阅读源代码或者传统的调试手段很难发现这一类的问题。通常对RTOS系统设计有着丰富经验的开发者,通过测试和基础的调试获得的信息也能够定位到问题所在,即便如此,分析问题的效率也十分低下。

  • IDE的RTOS调试插件和RTOS自身调试特性
    RTOS可能会自带一些调试功能,用于观察系统任务的情况,比如任务优先级,当前的任务状态和堆栈使用情况等。下图是FreeRTOS通过开启调试功能后调用vTaskList() API函数获取的任务状态信息。使用一个精度比系统节拍高得多的定时器,FreeRTOS还可以分析任务的执行时间信息。
    在这里插入图片描述
    一些IDE中支持RTOS内核识别调试插件,例如IAR EWARM支持μC/OS、FreeRTOS、embOS、ThreadX等在内的众多RTOS。Eclipse中的Code Confidence Tools for FreeRTOS。下图是IAR EWARM的μC/OS-III调试插件显示的内核和任务的各项信息,例如堆栈使用,CPU使用率等。
    在这里插入图片描述

  • RTOS跟踪和可视化分析工具
    可以发现不论是RTOS自带的一些调试功能,还是IDE提供的调试插件,虽然能显示系统中任务的一些信息,但如果要作为一个bug分析工具,其功能还是非常有限。我们只能知道暂停运行的这一刻任务的信息,并不清楚任务的状态是何时及何原因而改变,更不清楚各个任务之间的交互关系。

    更进一步,虽然设计的系统没有明显的问题,但是出于低功耗和性能调优的原因需要优化系统的设计,减少不必要的任务切换,降低消息或者信息的阻塞时间等,现有的这些工具和方法已经显得力不从心。这说明之前的开发工具已经跟不上现在的需求了,这需要从更高层级的系统视图,观测和跟踪系统的运行。

    现在很多的RTOS都自带跟踪接口,例如FreeRTOS、μC/OS-III都提供了跟踪接口,可以支持Tracealyzer和SystemView等RTOS可视化分析工具。Linux系统可用LTTng内核事件跟踪组件。借助这些可视化的OS事件记录和分析工具,可以极大提升bug的查找效率,还能快速验证系统设计是否如期,性能是否达标。

    以Tracealyzer为例,它可以实时记录RTOS运行时的各种系统事件,例如任务的调度,任务与任务之间、任务与中断之间的通信等,以此分析出系统在运行期间的行为,并通过图形或文件可视化提供给开发人员直观的查看,可以很快的发现传统调试中难以捕捉的系统层级的行为。以前面提到的RTOS系统常见的bug之一的优先级反转为例,使用Tracealyzer的执行实例视图,开发者立刻就能发现问题。
    在这里插入图片描述
    再比如抖动的问题,Tracealyzer通过记录任务执行的时间,在以时间为坐标的图上很快可以发现抖动很大的执行实例,假如这是对抖动敏感的任务,这可能就是导致系统问题的原因了。
    在这里插入图片描述
    对于任务与任务、任务与及中断之间的通信,Tracealyzer能够绘制完整的通信流图,如果系统中数据流出现问题,在此图中一眼就能识别。
    在这里插入图片描述
    事件记录以时间戳排序,文本方式记录了运行期间系统产生的各系统事件(如任务调度、通信、动态内存分配)和用户自定义事件(类似于printf功能,可以被记录并显示在各对应的视图中)。
    在这里插入图片描述
    除了Tracealyzer以外,RTOS跟踪和分析工具还有SEGGER公司推出的SystemView。该工具也提供了多个视图,包括时间轴,显示任务和中断的执行记录,事件记录和CPU负载等。
    在这里插入图片描述
    在这里插入图片描述
    它们的工作方式也都相似,有一个用于事件跟踪的库,需要与项目一起构建,在目标系统中运行。在运行期间记录RTOS的各个事件并存储到位于RAM的事件缓存中,待系统停止运行后读取出来加载到位于PC端的分析软件中,通过各种视图窗口显示出来。这种工作方式一般称为快照模式。

    快照模式受限于芯片可用的RAM空间,一般记录的时间比较短。需要长时间记录,则可以使用数据流模式,通过调试接口和调试器(J-Link)或者串口等通信接口,实时的将数据传输到PC端的分析软件,实现长时间的实时跟踪和分析,只有电脑的硬盘空间足够,记录就可以一直持续。

    以上只是简单展示了RTOS分析工具的用途,它可以弥补现有的开发工具不足以满足基于RTOS的嵌入式软件的开发,对于bug的查找很有帮助。对于想要深入学习RTOS的工作机制的开发者,这也是一个很好的辅助工具。不过Tracealyzer和SystemView是商用软件,商用时需要付费,但SystemView用于教育和评估是完全免费的。另外之前Micrium公司开发的μC/Probe,现在已经可以免费使用,能用于μC/OS-III和FreeRTOS的系统信息观察,虽然没有记录和跟踪的功能,但是它提供了类似工业组态软件的图形控制,功能也很强。
    在这里插入图片描述

文章主要摘录自:嵌入式开发常见问题解决方法,整合而成。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1.硬件设计 (1)选用龙芯2k1000la处理器作为平台的核心,该处理器采用了国产的LoongISA架构,支持64位指令集,具有较高的性能和安全性。 (2)采用高速DDR3内存作为主存储器,以保证平台运行的速度。 (3)选用可信平台模块(TPM)芯片,实现硬件级别的安全控制,包括密钥管理、加密、数字签名等功能。 (4)采用嵌入式安全模块(ESM)芯片,实现对外围设备的安全控制,包括设备认证、访问控制等功能。 (5)选用工业级存储器,实现数据的可靠性和可持久化。 (6)设计可编程的安全开关,实现对外围设备的安全控制。 2.软件设计 (1)采用嵌入式操作系统,如嵌入式Linux系统,实现平台的基本功能。 (2)通过加密、数字签名等技术,保证平台软件的安全性。 (3)实现远程管理功能,包括远程监控、远程控制等。 (4)采用安全开发流程,包括需求分析、设计、开发、测试等环节,确保软件的安全性。 (5)实现安全审计功能,记录平台的日志信息,以便后期的安全分析和审计。 3.应用设计 (1)结合平台的硬件和软件特点,设计可信的应用程序,如安全通信、加密存储等应用。 (2)采用人机交互界面,实现用户友好的操作体验。 (3)针对特定应用场景,设计相应的安全策略,如访问控制、数据加密等。 (4)实现应用程序的远程升级和维护功能,提高平台的可维护性和可扩展性。 综上所述,基于龙芯2k1000la的可信嵌入式平台设计思路需要从硬件、软件和应用三个层面进行考虑,以确保平台的安全性和可信度。同时,还需要注重平台的可维护性和可扩展性,以适应不同的应用需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值