随着越来越多的工业应用对产品的可靠性和安全性要求越来越高,我们在做产品设计的时候不仅要正确的实现产品功能,同时也需要通过一些功能安全认证,比如家电行业的 IEC60730 等或者 ISO13849 等。一般的系统故障可以通过设计的迭代和严格测试来避免,但是硬件的随机失效理论上是无法完全消除的,所以要想提高硬件随机失效的诊断覆盖率,就需要软硬件诊断机制来保障。

 

作为系统的核心控制部分,MCU 主平台的诊断机制就是最关键的部分。针对一般通用的 MCU,以 Piccolo C2000 系列为例,硬件上提供了一些诊断或者校验机制,如下所示:

 

 

同时 TI 也提供了一些软件诊断方案,如 MSP430 IEC60730 Software Package 和 C2000 SafeTI 60730 SW Packages 软件库等,可以提供很多的诊断测试功能,例如 CPU、时钟、外设、RAM 等的诊断,已经可以满足一部分的需求。如下图所示为 C2000 SafeTI 60730 SW Packages 中的功能和资源消耗。

 

然而在实际的应用中,有些安全标准要求对 RAM 进行周期性的在线诊断,同时不能影响程序的正常运行。但是程序在运行过程中存储在 RAM 中的数据会实时的变化,而 RAM 的诊断往往会破坏这些存储的数据,比如电机控制类的实时性要求较高的场合。所以在没有 ECC 的情况下,如何对 RAM 进行实时在线的诊断是一个值得讨论的问题。

 

 

下面以电机控制为例,讨论硬件校验的实现,尤其是 RAM 在线检测的过程。

 

1. 系统软件流程

非破坏性的诊断可以放在背景循环里面进行,这些软件诊断不会对实时性中断造成影响,例如看门狗测试,内部晶振测试,FLASH CRC 校验,静态变量 RAM CRC 校验,堆栈溢出判断,以及 GPIO 口诊断等。另外一些破坏性的或者对实时控制有影响的诊断,可以放到主中断中进行,如 RAM March 校验,ALU 诊断以及 CPU 寄存器诊断等。具体流程图如下所示:

 

 

2. RAM 诊断的方法

以 C2000 SafeTI 60730 SW Packages 为例,主要提供了两种 RAM 检测方式。

 

一种是 CRC 检测 STL_CRC_TEST_testRam,此功能用于测试 RAM 的位错误。该测试以 0 和 1 的交替模式填充被测 RAM 区域,并使用 PSA 计算 RAM 的 CRC。对于给定的 RAM 存储器区域,如果 RAM 存储器中没有任何 stuck bit,则 CRC 值应始终相同。并行串行分析器(PSA)是 c28x 器件中的一个模块,可用于生成 40 位给定存储区域上的 CRC。 PSA 多项式为 Y = x40 + x21 + x19 + x2 + 1。PSA 通过监视数据读取数据总线(DRDB)来计算 CRC 值。 一旦激活就会监控 Data Read Data Bus (DRDB),当 CPU 通过 DRDB 读取数据时,PSA 每个时钟周期会为 DRDB 上的数据生成一个 CRC。由于此测试具有破坏性,因此需要将要测试的 RAM 内容保存到单独的 RAM 位置。

 

 

当然也可以使用软件 CRC 的方式,使用起来更灵活,并且可以选择非破坏性的方式来计算 CRC,对一些静态常量存储的区域可以考虑这种 CRC 方式。另外一点是软件 CRC 算法可以更方便的进行代码评估,以满足不同安全标准的要求。

 

另一种是 MARCH 检测 STL_MARCH_TEST_testRam,此功能直接对 RAM 进行 32bit 的读写测试,可以选择进行 MarchC 13N 或者 MarchC- 测试。由于此测试具有破坏性,因此也需要将要测试的 RAM 内容保存到单独的 RAM 位置。

 

 

3. RAM 在线检测的实现

由于需要周期性的 RAM 检测,以电机控制为例,可以将 RAM 检测放到主中断里面执行。同时关键是不能影响控制程序的运行和实时性,所以主要考虑两点:

 

第一是主中断时间有限,要尽可能减小 RAM 检测的时间,所以可以将 RAM 分成多个小段进行检测,每段 RAM 越小,占用中断的时间越小,但是所有 RAM 检测一遍的时间会变长,这个需要综合考虑。

 

第二是不能破坏 RAM 中的变量值,所以在检测是之前将 RAM 段中的内容保存到专门区域,戴检测完成并且通过之后,再将保存好的数据恢复过来,使用 memCopy 来提高效率。

 

具体实现方法如下:

 

首先定义好各个 RAM 区间的地址范围,可以参考具体的数据手册,如下所示:

 

 

然后定义好检测的范围和每次检测的数据长度:

 

 

注意由于 STL_MARCH_TEST_testRam 函数执行 32 位读 / 写测试,而在测试 RAM 单元阵列时,由于 RAM 单元的 16 位体系结构,所以要求起始地址为偶数,结束地址为奇数,可以测试的最大内存范围限制为 65535 个 32 位字。所以要求测试长度也需要为奇数。

 

在主中断里面的 RAM 在线检测函数里,首先将要检测区域的 RAM 值保存下来:

 

 

if ((gStructSTLMonitor.NowRamAddrStart >= MARCH_RAM_START)

 

       && (gStructSTLMonitor.NowRamAddrStart <= (MARCH_RAM_END-RAM_CHK_NUM)))

 

{

 

gStructSTLMonitor.NowRamAddrEnd = gStructSTLMonitor.NowRamAddrStart + RAM_CHK_NUM;

 

memCopy((uint16_t *)gStructSTLMonitor.NowRamAddrStart,(uint16_t *)

 

         gStructSTLMonitor.NowRamAddrEnd,(uint16_t *)MARCH_RAM_BK);

 

}

 

然后进行检测:

 

gStructSTLMonitor.status = STL_MARCH_TEST_testRam((uint32_t *)

 

   gStructSTLMonitor.NowRamAddrStart,(uint32_t *)gStructSTLMonitor.NowRamAddrEnd);

 

if(gStructSTLMonitor.status != SIG_RAM_MARCH_TEST)

 

{

 

STL_SetFail();

 

}

 

else

 

{

 

memCopy((uint16_t *)MARCH_RAM_BK,(uint16_t *)(MARCH_RAM_BK + RAM_CHK_NUM),

 

(uint16_t *)gStructSTLMonitor.NowRamAddrStart);

 

gStructSTLMonitor.NowRamAddrStart = gStructSTLMonitor.NowRamAddrEnd + 1;

 

gStructSTLMonitor.gTestStep++;

 

}

 

注意检测成功之后马上恢复当前区域的 RAM 值,并为下一次检测做好准备。如果检测发现故障,则进入故障处理函数。