2. SIL基础概念

各位做SIL测试的同行,咱们今天聊聊故障注入的基础。说白了,就是搞清楚「我们要模拟什么样的故障」以及「什么时候下手」。这两件事搞不明白,后面的测试全是瞎忙活。

我记得刚入行那会儿,带我的老工程师跟我说过一句话,我一直记到现在——「你连故障都分不清,怎么证明你的系统是安全的?」嗯,这话糙理不糙。

2.1 故障模型的四大金刚

在嵌入式系统里,我们常碰到的故障模型就四种。我按实战中出现的频率排个序,你感受一下。

故障模型 本质描述 典型场景 我见过的坑
Stuck-at(固定故障) 信号线或寄存器卡死在0或1 焊点虚焊、引脚短路、IO口损坏 某项目CAN总线一直发0x00,查了三天发现是收发器引脚虚焊
位翻转(Bit Flip) 存储单元或数据总线的某一位发生反转 宇宙射线、电磁干扰、电源噪声 曾经在高原测试时,SRAM频繁出现单比特翻转
延迟故障(Delay) 信号传播时间超出设计约束 温度漂移、老化、工艺偏差 某ECU在-40℃时时钟抖动超标,导致SPI通信错位
瞬态故障(Transient) 短时间内出现的异常脉冲或毛刺 ESD放电、继电器切换、大功率负载启停 有一次电机启动瞬间,把ADC采样值直接打飞了

2.1.1 Stuck-at 故障

这是最经典的故障模型,从芯片测试时代就一直在用。它的逻辑很简单:某个节点要么卡在逻辑0,要么卡在逻辑1,死活变不了。

我个人习惯把Stuck-at分成两类:

  • Stuck-at-0 (SA0):信号永远为低。比如一个GPIO输出引脚对地短路了。
  • Stuck-at-1 (SA1):信号永远为高。比如上拉电阻焊错了,或者内部驱动管击穿。

实战要点:在SIL测试中,Stuck-at故障通常通过修改内存映射或强制赋值来模拟。比如在测试代码里直接写 *(volatile uint32_t*)0x40020010 = 0x00000000; 来模拟一个寄存器被拉低。

2.1.2 位翻转故障

位翻转这玩意儿,在汽车电子里越来越常见。为什么?因为芯片制程越来越小,工作电压越来越低,一个α粒子就能让SRAM里的数据翻个跟头。

我曾经在一个ADAS项目里遇到过:摄像头采集的图像数据在DMA传输过程中,某一位被翻转了。结果就是——车道线识别突然跳变,车辆开始画龙。嗯,那段时间我们团队没少加班。

位翻转的注入方式一般有两种:

  1. 软件注入:在运行时通过调试接口修改寄存器或内存的某一位。
  2. 硬件注入:用电磁脉冲枪或者激光照射芯片的特定区域。

我的建议:做SIL测试时优先用软件注入。可控、可重复、可追溯。硬件注入那是做芯片级验证才用的,成本高不说,复现性还差。

2.1.3 延迟故障

延迟故障不像前两种那么「暴力」,它更隐蔽。信号没断、数据没翻,就是来得晚了一点。但在高速通信里,晚那么几纳秒,整个时序就崩了。

我遇到过最典型的案例:某款MCU的SPI模块,在85℃环境下,SCK时钟的上升沿比设计值晚了12ns。结果就是——从设备采到的数据全是错的,而且不是每次都错,是时好时坏。这种故障最难排查。

延迟故障的注入手段:

  • 时钟抖动注入:在时钟路径上叠加可控的相位噪声。
  • 路径延迟调整:通过修改FPGA的布线约束,人为增加信号路径长度。
  • 温度应力:把被测设备放到温箱里,通过温度变化诱发延迟漂移。

2.1.4 瞬态故障

瞬态故障,说白了就是「抽风」。来无影去无踪,可能就持续几个时钟周期,但造成的后果可能是灾难性的。

我记得有一次做BMS(电池管理系统)的SIL测试,在模拟继电器切换的瞬间,一个毛刺直接触发了MCU的看门狗复位。整个系统在高速路上突然重启——你想想看,这要是真车,后果不堪设想。

瞬态故障的注入方式:

  1. 电源毛刺注入:在供电线上叠加一个窄脉冲。
  2. 电磁干扰注入:用近场探头在特定位置施加瞬态磁场。
  3. 软件脉冲注入:通过中断服务程序临时修改关键变量的值,然后恢复。

注意:瞬态故障的注入时机非常关键。注入早了,系统还没跑到敏感代码段;注入晚了,故障已经被容错机制消化了。后面我们会专门讲怎么卡这个时间点。

2.2 故障注入的时机与目标

搞清楚了故障模型,接下来就是「什么时候下手」和「冲着谁下手」。这两个问题,我建议你反过来想——先定目标,再选时机。

2.2.1 注入目标:你到底想测什么?

我个人习惯把注入目标分成三个层级:

层级 目标对象 典型故障 测试目的
L1:信号级 GPIO、ADC、PWM、通信总线 Stuck-at、瞬态毛刺 验证IO接口的故障检测机制
L2:数据级 寄存器、RAM、Flash、DMA缓冲区 位翻转、Stuck-at 验证ECC校验、CRC、三模冗余
L3:时序级 时钟树、同步逻辑、状态机 延迟、瞬态 验证时序容限和看门狗机制

选目标的时候,我有一条铁律:优先覆盖安全机制直接保护的对象。比如你的系统有ECC保护RAM,那就必须往RAM里注入位翻转,看ECC能不能正确纠正或报错。

2.2.2 注入时机:早一秒晚一秒,结果天差地别

时机这东西,我吃过不少亏。曾经有一个项目,我们在系统启动阶段注入了一个Stuck-at故障,结果安全机制根本没触发——因为那时候安全监控线程还没跑起来。你说冤不冤?

常见的注入时机分为以下几类:

  • 初始化阶段:系统上电、自检、配置外设。这时候注入,主要测启动自检(PBIT)能不能发现故障。
  • 正常运行阶段:系统在执行主循环或任务调度。这时候注入,测的是运行时故障检测和容错能力。
  • 特定操作阶段:比如正在写Flash、正在做ADC采样、正在发送CAN报文。这时候注入,测的是关键操作的健壮性。
  • 故障恢复阶段:系统刚从一个故障中恢复,这时候再注入一个故障,测的是恢复后的状态是否干净。

实战技巧:我建议你在测试计划里,对每个故障模型至少安排三个不同的注入时机。比如位翻转,分别在初始化后1ms、主循环第5轮、以及DMA传输过程中各注入一次。你会发现,同一个故障在不同时机下的系统行为可能完全不同。

2.3 故障模型与注入时机的组合矩阵

下面这张图,是我做SIL测试时一直贴在工位上的。它把四种故障模型和四个注入时机组合起来,形成了16个测试场景。每次做测试计划,我就对着这张图打勾。

故障模型 × 注入时机 组合矩阵 初始化阶段 正常运行 特定操作 故障恢复 Stuck-at 位翻转 延迟 瞬态 PBIT检测 运行时监控 关键路径 恢复后自检 ECC初始化 数据校验 DMA传输 内存清理 时钟稳定 时序容限 同步操作 时序恢复 上电冲击 噪声免疫 负载切换 恢复毛刺 使用说明: 1. 每个单元格代表一个测试场景,共16个基础组合。 2. 实际测试时,每个组合至少执行3次,取最差结果。 3. 如果某个组合连续3次都触发安全机制正确响应,可以降低该组合的测试频次。 4. 反之,如果某个组合出现未覆盖的故障行为,需要增加测试样本量并深入分析。 5. 我曾经用这个矩阵发现过一个隐藏bug——在「瞬态+故障恢复」组合下,看门狗计数器没有正确清零。

2.4 一个简单的注入示例

光说不练假把式。我写个最简单的Stuck-at故障注入代码,你感受一下实际怎么操作。

// 目标:模拟GPIO端口A的Pin 5 stuck-at-0
// 平台:STM32F407,SIL测试环境

void fault_inject_SA0_GPIOA_PIN5(void)
{
    uint32_t temp;
    
    // 步骤1:保存当前状态(方便恢复)
    temp = GPIOA->ODR;
    
    // 步骤2:强制将Pin 5拉低
    GPIOA->ODR &= ~(1 << 5);
    
    // 步骤3:等待一段时间,让安全机制有时间响应
    delay_ms(100);
    
    // 步骤4:检查安全机制是否触发
    if (fault_detected_flag == 1) {
        // 安全机制正确响应
        test_result_pass();
    } else {
        // 故障未被检测到 —— 这是个问题
        test_result_fail();
    }
    
    // 步骤5:恢复原始状态
    GPIOA->ODR = temp;
}

注意:实际项目中,故障注入代码不能这么「裸奔」。你需要考虑注入时是否有关中断、DMA是否在传输、RTOS任务调度是否会影响结果。这些细节,我们后面几章会逐个展开。