2. 电驱系统基础
各位做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传输过程中,某一位被翻转了。结果就是——车道线识别突然跳变,车辆开始画龙。嗯,那段时间我们团队没少加班。
位翻转的注入方式一般有两种:
- 软件注入:在运行时通过调试接口修改寄存器或内存的某一位。
- 硬件注入:用电磁脉冲枪或者激光照射芯片的特定区域。
我的建议:做SIL测试时优先用软件注入。可控、可重复、可追溯。硬件注入那是做芯片级验证才用的,成本高不说,复现性还差。
2.1.3 延迟故障
延迟故障不像前两种那么「暴力」,它更隐蔽。信号没断、数据没翻,就是来得晚了一点。但在高速通信里,晚那么几纳秒,整个时序就崩了。
我遇到过最典型的案例:某款MCU的SPI模块,在85℃环境下,SCK时钟的上升沿比设计值晚了12ns。结果就是——从设备采到的数据全是错的,而且不是每次都错,是时好时坏。这种故障最难排查。
延迟故障的注入手段:
- 时钟抖动注入:在时钟路径上叠加可控的相位噪声。
- 路径延迟调整:通过修改FPGA的布线约束,人为增加信号路径长度。
- 温度应力:把被测设备放到温箱里,通过温度变化诱发延迟漂移。
2.1.4 瞬态故障
瞬态故障,说白了就是「抽风」。来无影去无踪,可能就持续几个时钟周期,但造成的后果可能是灾难性的。
我记得有一次做BMS(电池管理系统)的SIL测试,在模拟继电器切换的瞬间,一个毛刺直接触发了MCU的看门狗复位。整个系统在高速路上突然重启——你想想看,这要是真车,后果不堪设想。
瞬态故障的注入方式:
- 电源毛刺注入:在供电线上叠加一个窄脉冲。
- 电磁干扰注入:用近场探头在特定位置施加瞬态磁场。
- 软件脉冲注入:通过中断服务程序临时修改关键变量的值,然后恢复。
注意:瞬态故障的注入时机非常关键。注入早了,系统还没跑到敏感代码段;注入晚了,故障已经被容错机制消化了。后面我们会专门讲怎么卡这个时间点。
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个测试场景。每次做测试计划,我就对着这张图打勾。
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任务调度是否会影响结果。这些细节,我们后面几章会逐个展开。