3. SIL测试基础

好,咱们进入实操环节。SIL测试环境搭建,说白了就是让代码在PC上跑起来,模拟ECU的行为。我见过不少团队,一上来就怼硬件,结果问题一大堆。其实,先把SIL环境搭稳了,很多坑在PC上就能发现。

3.1 基于Simulink的SIL环境搭建

Simulink做SIL,核心就是模型在环。但我们要的是代码在环,所以得把模型生成代码,再跑回Simulink里验证。我个人习惯,先搭一个干净的测试平台。

核心思路: 将Simulink模型通过Embedded Coder生成C代码,再通过S-Function或SIL模块,将代码集成回Simulink仿真环境。这样,仿真时跑的就是目标代码,而不是模型本身。

具体步骤,我列个清单:

  1. 准备被测模型:确保模型是离散的,固定步长。连续模型在SIL里跑,容易出时序问题。
  2. 配置数据字典:定义好输入输出接口,数据类型要跟目标芯片一致。我曾经遇到过,模型里用double,芯片用float,结果SIL通过,HIL就崩了。
  3. 创建SIL测试框架:在Simulink中新建一个模型,用SIL模块(或Model block配置为SIL模式)替代原模型。
  4. 连接测试激励:用Signal Builder或From Workspace模块,注入测试用例。
  5. 设置数据记录:用To Workspace或Scope,抓取输出,用于后续比对。

小技巧: 我习惯在SIL模型里加一个Assertion模块,用来做运行时检查。比如,信号超出范围,直接报错,省得事后翻数据。

3.2 代码生成配置

代码生成配置,是SIL环境搭建的关键中的关键。配置不对,生成的代码跟模型行为不一致,SIL就白做了。你想想看,代码生成器也是软件,它有自己的脾气。

我常用的配置项,整理成了一张表:

配置项 推荐设置 说明
System target file ert.tlc 嵌入式实时目标,生成效率高,适合SIL
Language C 别选C++,除非你有特殊需求
Generate code only off 让Simulink自动编译,省事
Support floating-point numbers 匹配目标芯片 如果芯片不支持浮点,选single或fixed-point
Default parameter behavior Tunable 或 Inlined SIL测试建议用Inlined,减少变量
Generate report on 生成代码报告,方便追溯

注意: 代码生成配置,一定要跟最终产品的配置保持一致。我见过一个项目,SIL用ert.tlc,产品用grt.tlc,结果SIL过了,产品上跑出bug。说白了,SIL环境要跟目标环境对齐,否则就是自欺欺人。

配置好之后,生成代码。我个人习惯,生成后先看一眼model.c和model.h,确认接口函数名、参数类型跟模型一致。这一步,花不了几分钟,但能避免很多低级错误。

3.3 测试脚本框架搭建

测试脚本框架,是SIL测试的骨架。没有框架,测试就是一团乱麻。我建议,用MATLAB脚本或Python,搭建一个可复用的测试框架。

为什么?因为SIL测试要跑很多轮,手动点仿真,效率太低。而且,故障注入需要自动化,手动操作容易遗漏。

我常用的框架结构,画了个图:

SIL测试脚本框架 主控脚本 (run_tests.m) 配置模块 (config.m) 激励生成 (stimulus.m) 执行引擎 (sim_engine.m) 数据采集 (collector.m) 结果比对 (comparator.m) 报告生成 (reporter.m) 框架说明: 主控脚本依次调用各模块,形成闭环。 配置模块定义测试参数;激励生成模块产生输入信号; 执行引擎运行SIL仿真;数据采集模块抓取结果;比对模块判断Pass/Fail;报告模块输出测试报告。

框架搭好了,写脚本就快了。我贴一段核心代码,展示主控脚本怎么调用各模块:

% run_tests.m - SIL测试主控脚本
% 作者:资深工程师老李
% 功能:自动化执行SIL测试用例

clear; clc; close all;

% 1. 加载配置
config = load_config('config.m');
fprintf('[INFO] 配置加载完成\n');

% 2. 获取测试用例列表
test_cases = get_test_cases(config.test_dir);
fprintf('[INFO] 共发现 %d 个测试用例\n', length(test_cases));

% 3. 循环执行
for i = 1:length(test_cases)
    tc = test_cases{i};
    fprintf('[INFO] 执行测试用例: %s\n', tc.name);
    
    % 生成激励
    stimulus = generate_stimulus(tc, config);
    
    % 运行SIL仿真
    results = run_sil_simulation(config.model_name, stimulus, config.sim_time);
    
    % 采集数据
    data = collect_results(results, config.signals);
    
    % 比对结果
    [pass_flag, details] = compare_results(data, tc.expected, config.tolerance);
    
    % 记录结果
    record_result(tc.name, pass_flag, details, config.report_dir);
    
    fprintf('[INFO] 测试用例 %s: %s\n', tc.name, iif(pass_flag, 'PASS', 'FAIL'));
end

fprintf('[INFO] 所有测试用例执行完毕\n');

避坑指南: 我曾经在脚本里忘了加close_system,结果Simulink模型越开越多,内存爆了。建议每次仿真完,强制关闭模型,释放资源。

3.4 故障注入接口预留

SIL环境搭好了,别忘了预留故障注入接口。因为咱们这门课的核心就是故障注入。我一般会在模型里加一个故障注入层,通过一个全局结构体,控制故障的注入。

举个例子:

// 故障注入控制结构体
typedef struct {
    uint8_t fault_id;      // 故障ID
    float   fault_value;   // 故障值
    uint8_t fault_enable;  // 使能标志
} FaultInjection_Type;

// 全局变量
FaultInjection_Type FaultCtrl;

在Simulink模型里,用S-Function或MATLAB Function模块,读取这个结构体,修改信号值。这样,测试脚本就能通过修改FaultCtrl,实现故障注入。

注意: 故障注入接口,一定要在SIL环境搭建时就设计好。等测试用例写完了再补,容易漏掉关键信号。我建议,把模型中所有可能出问题的信号,都列出来,逐一考虑是否需要注入故障。

嗯,到这里,SIL环境搭建的核心内容就讲完了。记住,环境搭得越扎实,后面的测试就越顺利。别急着跑用例,先把地基打牢。